下面是nodejs创建一个服务器的代码。接下来我们一起分析这个过程。
var http = require('http'); http.createServer(function (request, response) { response.end('Hello World '); }).listen(9297);
首先我们去到lib/http.js模块看一下这个函数的代码。
function createServer(requestListener) { return new Server(requestListener); }
只是对_http_server.js做了些封装。我们继续往下看。
function Server(requestListener) { if (!(this instanceof Server)) return new Server(requestListener); net.Server.call(this, { allowHalfOpen: true }); // 收到http请求时执行的回调 if (requestListener) { this.on('request', requestListener); } this.httpAllowHalfOpen = false; // 建立tcp连接的回调 this.on('connection', connectionListener); this.timeout = 2 * 60 * 1000; this.keepAliveTimeout = 5000; this._pendingResponseData = 0; this.maxHeadersCount = null; } util.inherits(Server, net.Server);
发现_http_server.js也没有太多逻辑,继续看lib/net.js下的代码。
function Server(options, connectionListener) { if (!(this instanceof Server)) return new Server(options, connectionListener); EventEmitter.call(this); // connectionListener在http.js处理过了 if (typeof options === 'function') { connectionListener = options; options = {}; this.on('connection', connectionListener); } else if (options == null || typeof options === 'object') { options = options || {}; if (typeof connectionListener === 'function') { this.on('connection', connectionListener); } } else { throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'options', 'Object', options); } this._connections = 0; ...... this[async_id_symbol] = -1; this._handle = null; this._usingWorkers = false; this._workers = []; this._unref = false; this.allowHalfOpen = options.allowHalfOpen || false; this.pauseOnConnect = !!options.pauseOnConnect; }
至此http.createServer就执行结束了,我们发现这个过程还没有涉及到很多逻辑,并且还是停留到js层面。接下来我们继续分析listen函数的过程。该函数是net模块提供的。我们只看关键的代码。
Server.prototype.listen = function(...args) { // 处理入参,根据文档我们知道listen可以接收好几个参数,我们这里是只传了端口号9297 var normalized = normalizeArgs(args); // normalized = [{port: 9297}, null]; var options = normalized[0]; var cb = normalized[1]; // 第一次listen的时候会创建,如果非空说明已经listen过 if (this._handle) { throw new errors.Error('ERR_SERVER_ALREADY_LISTEN'); } ...... listenInCluster(this, null, options.port | 0, 4, backlog, undefined, options.exclusive); } function listenInCluster() { ... server._listen2(address, port, addressType, backlog, fd); } _listen2 = setupListenHandle = function() { ...... this._handle = createServerHandle(...); this._handle.listen(backlog || 511); } function createServerHandle() { handle = new TCP(TCPConstants.SERVER); handle.bind(address, port); }
到这我们终于看到了tcp连接的内容,每一个服务器新建一个handle并且保存他,该handle是一个TCP对象。然后执行bind和listen函数。接下来我们就看一下TCP类的代码。TCP是C++提供的类。对应的文件是tcp_wrap.cc。我们看看new TCP的时候发生了什么。
void TCPWrap::New(const FunctionCallbackInfo<Value>& args) { // This constructor should not be exposed to public javascript. // Therefore we assert that we are not trying to call this as a // normal function. CHECK(args.IsConstructCall()); CHECK(args[0]->IsInt32()); Environment* env = Environment::GetCurrent(args); int type_value = args[0].As<Int32>()->Value(); TCPWrap::SocketType type = static_cast<TCPWrap::SocketType>(type_value); ProviderType provider; switch (type) { case SOCKET: provider = PROVIDER_TCPWRAP; break; case SERVER: provider = PROVIDER_TCPSERVERWRAP; break; default: UNREACHABLE(); } new TCPWrap(env, args.This(), provider); } TCPWrap::TCPWrap(Environment* env, Local<Object> object, ProviderType provider) : ConnectionWrap(env, object, provider) { int r = uv_tcp_init(env->event_loop(), &handle_); CHECK_EQ(r, 0); }
我们看到,new TCP的时候其实是执行libuv的uv_tcp_init函数,初始化一个uv_tcp_t的结构体。首先我们先看一下uv_tcp_t结构体的结构。
uv_tcp_t uv_tcp_t // 初始化一个tcp流的结构体 int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* tcp) { // 未指定未指定协议 return uv_tcp_init_ex(loop, tcp, AF_UNSPEC); } int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* tcp, unsigned int flags) { int domain; /* Use the lower 8 bits for the domain */ // 低八位是domain domain = flags & 0xFF; if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC) return UV_EINVAL; // 除了第八位的其他位是flags if (flags & ~0xFF) return UV_EINVAL; uv__stream_init(loop, (uv_stream_t*)tcp, UV_TCP); /* If anything fails beyond this point we need to remove the handle from * the handle queue, since it was added by uv__handle_init in uv_stream_init. */ if (domain != AF_UNSPEC) { int err = maybe_new_socket(tcp, domain, 0); if (err) { // 出错则把该handle移除loop队列 QUEUE_REMOVE(&tcp->handle_queue); return err; } } return 0; }
我们接着看uv__stream_init做了什么事情。