【问题标题】:What does the .listen() method in express look like?express 中的 .listen() 方法是什么样的?
【发布时间】:2021-12-16 19:59:14
【问题描述】:

我阅读了有关 express 中使用的 .listen() 方法的文档。我可以使用该方法并设置一个正在监听 HTTP 请求的服务器。

但是,由于我对编码还很陌生,因此很难掌握使用 .listen() 方法时真正发生的事情。 “监听连接”的高级解释对我没有帮助。

我认为,如果我能真正看到函数而不是仅仅调用它,这会变得更容易。

非常感谢任何帮助

【问题讨论】:

  • Express 是开源的,你可以去看看。
  • 你可以看到源代码,但我敢打赌你很难理解。
  • nodejs.org/api/http.html#serverlisten。 Express 只是 Node 内置库的一个精美包装器

标签: node.js express


【解决方案1】:

简而言之,Express app.listen() 方法创建一个 http 服务器对象,然后将其配置为接收特定端口和 IP 地址上的传入 TCP 连接,以便当客户端请求到该端口的连接并发送 http 请求时,服务器可以接收该 http 请求并对其进行处理,然后发送响应。 app.listen() 中的代码在后面的答案中显示 - 尽管它所做的只是调用 http 服务器对象中的更下一层。

以下是有关其工作原理的较低级别的详细信息。

当服务器希望开始侦听传入连接时,它会通过创建套接字和binding 到特定端口和 IP 地址来通知本地 TCP 堆栈。这实质上为该特定服务器保留了传入端口(不允许其他服务器也绑定到该端口)。因此,例如,在默认端口上的常规 http 服务器上,您将绑定到端口 80。这种类型的绑定套接字仅用于传入连接,而不用于与客户端的双向通信。

然后,服务器通知 TCP 堆栈它已准备好接收传入连接。在 TCP 级别,这称为listen。在 nodejs 中,绑定和侦听步骤合并为一个称为listen 的步骤。

从那时起,只要本地 TCP 堆栈收到传入的连接请求,其目标是服务器绑定到的 IP 地址和端口,那么该传入连接将被接受并插入到配置为的服务器的队列中那个IP地址和端口。通常会有一个最大数量的传入连接可以以这种方式排队,如果超过该数量,则连接将被拒绝。如果服务器得到“备份”并且在处理传入连接时落后,这可以管理负载并保护主机。

然后,TCP 堆栈将通知服务器每个新的传入连接。一旦服务器接受该连接,它就可以开始读取客户端通过套接字发送的任何数据。在 HTTP 服务器使用 HTTP 协议的情况下,这将是初始请求协议、方法、版本、标头和任何正文数据。对于不同类型的服务器,数据的格式会有所不同。

这是一个有用的服务器图表:

来源:https://medium.com/javarevisited/fundamentals-of-socket-programming-in-java-bc9acc30eaf4

  1. 服务器创建一个套接字用于服务器接受新连接..
  2. binds 将套接字连接到特定的 IP 地址和端口,因此它只会被告知以该 IP 地址和端口为目标的传入连接。
  3. 该端口上的listens 通知 TCP 堆栈它已准备好接受传入连接。
  4. 当收到传入连接通知时,它会accepts 该传入连接。
  5. 然后它可以通过新套接字读取和写入该新连接。
  6. 然后,稍后,传入的套接字将关闭以完成客户端事务。

Express 中的app.listen() 方法封装了这些步骤和其他一些步骤。在内部(在 Express 中),代码如下所示:

app.listen = function listen() {
  var server = http.createServer(this);
  return server.listen.apply(server, arguments);
};

您可以在开源repository 中看到该方法。

为了让 http 服务器为上述步骤 1-6 做好准备,这将在 nodejs 中创建 http 服务器对象,然后将 app 注册为该服务器对象的请求侦听器(因此将收到传入 http 请求的通知) .

然后,对server.listen() 的调用封装了上面的步骤1-3。

第 4 步发生在 http 服务器对象实现中,当新连接建立并且新的 HTTP 请求可用时调用 app 对象。 http 服务器读取初始请求并解析 http 协议,并且该初始请求已经可供app 用于路由到适当的处理程序。

然后,res.send()res.json() 等后续调用会在 http 套接字上写回响应并关闭套接字,或者 res.end() 将直接关闭它(上面的步骤 5 和 6)。


其他一些有用的参考资料:

Why is bind() used in TCP? Why is it used only on server side and not in client side? - 帮助解释端口和 IP 地址如何定义由服务器表示的 TCP 端点。客户端必须知道此端口,以便它可以专门请求连接到该端口。 socket的客户端也有一个IP地址和一个端口,但是它的端口是可以动态分配的,因此客户端不必自己绑定特定的端口。四个数据[服务器IP、服务器端口、客户端IP、客户端端口]定义了一个具体的TCP连接。

How TCP sockets work - 有一个很好的部分介绍了与服务器的新连接如何工作。

Understanding socket and port in TCP - 谈论主动和被动套接字。被动套接字是处于“侦听”模式的套接字,用于接受传入连接。活动套接字是两个 TCP 端点之间的双向通信通道。

Transmission Control Protocol (TCP) - 有关 TCP 各个方面的更多详细信息,从启动侦听服务器、启动与该服务器的客户端连接、通过数据包传输到关闭套接字。

网络上有大量关于该主题的其他参考资料。您可能会找到 1000 篇关于 TCP 任何一个方面的文章,您可能想了解更多信息。

我认为,如果我能真正看到函数而不是仅仅调用它,这会变得更容易。

listen 的底层代码位于操作系统的 TCP 堆栈中,不属于 nodejs 或 Express。 Express 依赖 nodejs http server 对象作为其接口,nodejs http server 对象使用本机代码(内置于 nodejs)调用libuv(这是 nodejs 用于网络和其他东西的跨平台 C 库)。然后,libuv 与底层操作系统 API 对话以到达目标主机上的实际 TCP 堆栈。所有这一切都是为了将​​服务器套接字置于listen 模式,以便可以通知到该目标 IP 地址和端口的新传入客户端连接。


如果您想了解底层 TCP 接口和该接口的描述,这里有一些关于 Linux TCP API 相关部分的文档:

socket() - https://linux.die.net/man/7/socket

bind() - https://linux.die.net/man/2/bind

listen() - https://linux.die.net/man/2/listen


还有,nodejs 用于网络的libuv library 的一部分:

TCP 句柄 - http://docs.libuv.org/en/v1.x/tcp.html

服务器 listen()accept() - http://docs.libuv.org/en/v1.x/stream.html#c.uv_listen

【讨论】:

  • 非常感谢您的深入解释!!! :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-04-26
  • 2020-05-27
  • 1970-01-01
  • 2018-02-20
  • 2015-01-29
  • 2015-10-15
  • 2018-06-22
相关资源
最近更新 更多