In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
Editor to share with you the example analysis of http.createServer in node.js, I believe that most people do not know much about it, so share this article for your reference, I hope you can learn a lot after reading this article, let's go to know it!
Is the code that nodejs creates a server. Next, let's analyze this process.
Var http = require ('http'); http.createServer (function (request, response) {response.end (' Hello World');}) .subscription (9297)
First, let's go to the lib/http.js module and take a look at the code for this function.
Function createServer (requestListener) {return new Server (requestListener);}
It's just some encapsulation of _ http_server.js. Let's move on.
Function Server (requestListener) {if (! (this instanceof Server)) return new Server (requestListener); net.Server.call (this, {allowHalfOpen: true}); / / callback if (requestListener) {this.on ('request', requestListener) executed upon receipt of http request;} this.httpAllowHalfOpen = false; / / callback this.on (' connection', connectionListener) establishing tcp connection; this.timeout = 2 * 60 * 1000; this.keepAliveTimeout = 5000; this._pendingResponseData = 0; this.maxHeadersCount = null } util.inherits (Server, net.Server)
Find that _ http_server.js doesn't have much logic, so continue to look at the code under lib/net.js.
Function Server (options, connectionListener) {if (! (this instanceof Server)) return new Server (options, connectionListener); EventEmitter.call (this); / connectionListener processed 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._unref [async _ id_symbol] =-1; this._handle = null; this._usingWorkers = false; this._workers = []; this._unref = false; this.allowHalfOpen = options.allowHalfOpen | | false; this.pauseOnConnect =! options.pauseOnConnect;}
At this point, the http.createServer implementation is over, and we find that the process does not involve a lot of logic, and still stays at the js level. Let's move on to the process of analyzing the listen function. This function is provided by the net module. We only look at the key code.
Server.prototype.listen = function (... args) {/ / handle input parameters. According to the documentation, we know that listen can receive several parameters. We only passed the port number 9297 var normalized = normalizeArgs (args); / / normalized = [{port: 9297}, null]; var options = normalized [0]; var cb = normalized [1] / / the listen will be created for the first time. If it is not empty, it means that if (this._handle) {throw new errors.Error ('ERR_SERVER_ALREADY_LISTEN');} has been 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);}
At this point we finally see the content of the tcp connection, each server creates a new handle and saves it, the handle is a TCP object. Then execute the bind and listen functions. Next let's take a look at the code for the TCP class. TCP is a class provided by C++. The corresponding file is tcp_wrap.cc. Let's see what happened at new TCP.
Void TCPWrap::New (const FunctionCallbackInfo& 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 ()-> Value (); TCPWrap::SocketType type = static_cast (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, ProviderType provider): ConnectionWrap (env, object, provider) {int r = uv_tcp_init (env- > event_loop (), & handle_); CHECK_EQ (r, 0);}
We can see that new TCP actually executes the uv_tcp_init function of libuv, initializing a structure of uv_tcp_t. First, let's take a look at the structure of the uv_tcp_t structure.
Uv_tcp_tuv_tcp_t// initializes the structure of a tcp stream int uv_tcp_init (uv_loop_t* loop, uv_tcp_t* tcp) {/ / No specified protocol 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 * / / the lower eight bits are domain domain = flags & 0xFF; if (domain! = AF_INET & & domain! = AF_INET6 & & domain! = AF_UNSPEC) return UV_EINVAL; / / except for the eighth bit is 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 if (err) {/ / error, remove the handle from the loop queue QUEUE_REMOVE (& tcp- > handle_queue); return err;}} return 0;}
Let's go on to see what uv__stream_init has done.
Void uv__stream_init (uv_loop_t* loop, uv_stream_t* stream, uv_handle_type type) {int err; uv__handle_init (loop, (uv_handle_t*) stream, type); stream- > read_cb = NULL; stream- > alloc_cb = NULL; stream- > close_cb = NULL; stream- > connection_cb = NULL; stream- > connect_req = NULL; stream- > shutdown_req = NULL; stream- > accepted_fd =-1 Stream- > queued_fds = NULL; stream- > delayed_error = 0; & stream- > write_queue); QUEUE_INIT (& stream- > write_completed_queue); stream- > write_queue_size = 0; if (loop- > emfile_fd = =-1) {err = uv__open_cloexec ("/ dev/null", O_RDONLY); if (err
< 0) /* In the rare case that "/dev/null" isn't mounted open "/" * instead. */ err = uv__open_cloexec("/", O_RDONLY); if (err >= 0) loop- > emfile_fd = err;} # if defined (_ _ APPLE__) stream- > select = NULL;#endif / * defined (_ APPLE_) * / / initialize io Observer uv__io_init (& stream- > io_watcher, uv__stream_io,-1);} void uv__io_init (uv__io_t* w, uv__io_cb cb, int fd) {assert (cb! = NULL); assert (fd > =-1) / / initialize queue, callback, fd QUEUE_INIT to listen to (& w-> pending_queue); QUEUE_INIT (& w-> watcher_queue); w-> cb = cb; w-> fd = fd; w-> events = 0; w-> pevents = 0 pevents if defined (UV_HAVE_KQUEUE) w > rcount = 0; w-> wcount = 0
As you can see from the code, only some initialization operations have been done on the uv_tcp_t structure. At this point, the logic of new TCP is finished. The next step is to continue to classify the logic of calling bind and listen in nodejs. The bind of nodejs corresponds to the function of libuv, and the function of uv__tcp_bind,listen corresponds to uv_tcp_listen.
First look at a core code of bind.
/ * Cannot set IPv6-only mode on non-IPv6 socket. * / if ((flags & UV_TCP_IPV6ONLY) & & addr- > sa_family! = AF_INET6) return UV_EINVAL; / / get a socket and set some tags err = maybe_new_socket (tcp, addr- > sa_family, 0); if (err) return err; on = 1 / / set reusable if (setsockopt (tcp- > io_watcher.fd, SOL_SOCKET, SO_REUSEADDR, & on, sizeof (on)) return UV__ERR (errno); bind (tcp- > io_watcher.fd, addr, addrlen) & & errno! = EADDRINUSEstatic int maybe_new_socket (uv_tcp_t* handle, int domain, unsigned long flags) {struct sockaddr_storage saddr; socklen_t slen; if (domain = = AF_UNSPEC) {handle- > flags | = flags; return 0 } return new_socket (handle, domain, flags);} static int new_socket (uv_tcp_t* handle, int domain, unsigned long flags) {struct sockaddr_storage saddr; socklen_t slen; int sockfd; int err; / / get a socket err = uv__socket (domain, SOCK_STREAM, 0); if (err
< 0) return err; sockfd = err; // 设置选项和保存socket的文件描述符到io观察者中 err = uv__stream_open((uv_stream_t*) handle, sockfd, flags); if (err) { uv__close(sockfd); return err; } ... return 0;}int uv__stream_open(uv_stream_t* stream, int fd, int flags) { if (!(stream->Io_watcher.fd = =-1 | | stream- > io_watcher.fd = = fd) return UV_EBUSY; assert (fd > = 0); stream- > flags | = flags; if (stream- > type = = UV_TCP) {if ((stream- > flags & UV_HANDLE_TCP_NODELAY) & & uv__tcp_nodelay (fd, 1) return UV__ERR (errno); / * TODO Use delay the user passed in. * / if ((stream- > flags & UV_HANDLE_TCP_KEEPALIVE) & & uv__tcp_keepalive (fd, 1,60)) {return UV__ERR (errno);}}. / / Save the file descriptor corresponding to socket to the io viewer, and libuv will listen on the file descriptor stream- > io_watcher.fd = fd; return 0;} during the io poll phase.
The above series of operations are mainly to create a new socket file descriptor, set some flag, and then save the file descriptor to the IO viewer. Libuv will listen to the file descriptor during the poll IO phase. If an event comes, it will execute the set callback function, which is the uv__stream_io set in uv__stream_init. Finally, the bind function is executed for binding operation. Finally, let's analyze the listen function. First, take a look at the tcp_wrapper.cc code.
Void TCPWrap::Listen (const FunctionCallbackInfo& args) {TCPWrap* wrap; ASSIGN_OR_RETURN_UNWRAP (& wrap, args.Holder (), args.GetReturnValue (). Set (UV_EBADF)); int backlog = args [0]-> Int32Value (); int err = uv_listen (reinterpret_cast (& wrap- > handle_), backlog, OnConnection); args.GetReturnValue (). Set (err);}
A very important part of the code is the OnConnection function. Nodejs sets a callback function OnConnection to the listen function, which is called when a connection arrives in the file descriptor saved in the IO viewer. The OnConnection function is defined in connection_wrap.cc, and tcp_wrapper inherits connection_wrap. Let's take a look at uv_listen first. This function calls uv_tcp_listen. The core code for this function is as follows.
If (listen (tcp- > io_watcher.fd, backlog) return UV__ERR (errno); / / cb is OnConnection tcp- > connection_cb = cb; tcp- > flags | = UV_HANDLE_BOUND; / / libuv layer callback when a connection arrives, overriding the value tcp- > io_watcher.cb = uv__server_io; / / registered event uv__io_start (tcp- > loop, & tcp- > io_watcher, POLLIN) set when uv_stream_init
During the poll IO phase of libuv, the epoll_wait supervisor hears the incoming connection and then invokes uv__server_io. The following is the core code of the function.
/ / continue to register events and wait for connection uv__io_start (stream- > loop, & stream- > io_watcher, POLLIN); err = uv__accept (uv__stream_fd (stream)); / / Save socket stream- > accepted_fd = err; / / execute nodejs layer callback stream- > connection_cb (stream, 0)
Libuv will remove a connection and get the corresponding socket. Then execute the callback of the nodejs layer, and at this point let's take a look at the OnConnection code.
OnConnection (uv_stream_t* handle,int status) if (status = 0) {/ / create a new uv_tcp_t structure Local client_obj = WrapType::Instantiate (env, wrap_data, WrapType::SOCKET); WrapType* wrap; ASSIGN_OR_RETURN_UNWRAP (& wrap, client_obj); uv_stream_t* client_handle = reinterpret_cast (& wrap- > handle_) / / uv_accept returns 0: successful if (uv_accept (handle, client_handle)) return; argv [1] = client_obj;} / / executes the upper callback, which is onconnection wrap_data- > MakeCallback (env- > onconnection_string (), arraysize (argv), argv) set by net.js.
OnConnection created a new uv_tcp_t structure. Represents this connection. Then call uv_accept.
Int uv_accept (uv_stream_t* server, uv_stream_t* client) {. / / the newly created uv_tcp_t structure is associated with accept_fd and registers the read and write event uv__stream_open (client, server- > accepted_fd, UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);.}
Finally, the callback of nodejs is executed.
Function onconnection (err, clientHandle) {var handle = this; var self = handle.owner; if (err) {self.emit ('error', errnoException (err,' accept')); return;} if (self.maxConnections & & self._connections > = self.maxConnections) {clientHandle.close (); return;} var socket = new Socket ({handle: clientHandle, allowHalfOpen: self.allowHalfOpen, pauseOnCreate: self.pauseOnConnect}); socket.readable = socket.writable = true; self._connections++; socket.server = self Socket._server = self; DTRACE_NET_SERVER_CONNECTION (socket); LTTNG_NET_SERVER_CONNECTION (socket); COUNTER_NET_SERVER_CONNECTION (socket); / / trigger the connectionListener callback self.emit ('connection', socket) set in _ http_server.js;}
The overall logic of the listen function is to set socket to listener, then register the event and wait for the arrival of the connection. When the connection arrives, call accept to obtain the newly established connection, the callback of tcp_wrapper.cc creates a new uv_tcp_t structure to represent the new connection, then sets the read / write event, and sets the callback to uv__stream_io, waiting for the data to arrive. Finally, the callback connectionListener set by _ http_server.js is executed. At this point, the process of starting the server and receiving the connection is complete. The next step is to read and write user data. When the user sends data, the function to process the data is uv__stream_io. The read and write of the parsed data will continue later.
The above is all the content of the article "sample Analysis of http.createServer in node.js". Thank you for reading! I believe we all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to follow the industry information channel!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.