WebSocket 服务器如何处理多个传入的连接请求?

How WebSocket server handles multiple incoming connection requests?

根据here

The HTTP Upgrade header requests that the server switch the application-layer protocol from HTTP to the WebSocket protocol.

The client handshake established a HTTP-on-TCP connection between IE10 and server. After the server returns its 101 response, the application-layer protocol switches from HTTP to WebSockets which uses the previously established TCP connection.

HTTP is completely out of the picture at this point. Using the lightweight WebSocket wire protocol, messages can now be sent or received by either endpoint at any time.

所以,我的理解是,第一个客户端与服务器握手完成后,服务器的80端口将被独占 WebSocket 协议。 HTTP 不再在 80 端口 上工作。

那么第二个客户端如何与服务器交换握手。 毕竟WebSocket握手是HTTP格式的

加 1

感谢您到目前为止的所有回答。他们真的很有帮助。

现在我明白了同一个服务器的80端口被多个TCP连接共享。这种共享完全没问题,因为 TCP 连接由 5 元素元组标识,正如 Jan-Philip Gehrcke 指出的那样。

我想补充几点。

WebSocketHTTP都只是应用层协议。 通常 他们都依赖 TCP 协议作为他们的传输。

为什么选择80端口?

WebSocket 设计有意选择服务器的端口 80 用于握手和后续通信。我认为设计者希望从传输层的角度(即服务器端口号仍然是 80)看起来像正常的 HTTP 通信。但根据 jfriend00 的回答,这个技巧并不总是能骗过网络基础设施。

协议如何从 HTTP 转变为 WebSocket

来自 RFC 6455 - WebSocket 协议

Basically it is intended to be as close to just exposing raw TCP to script as possible given the constraints of the Web. It’s also designed in such a way that its servers can share a port with HTTP servers, by having its handshake be a valid HTTP Upgrade request. One could conceptually use other protocols to establish client-server messaging, but the intent of WebSockets is to provide a relatively simple protocol that can coexist with HTTP and deployed HTTP infrastructure (such as proxies) and that is as close to TCP as is safe for use with such infrastructure given security considerations, with targeted additions to simplify usage and keep simple things simple (such as the addition of message semantics).

所以我认为我在以下陈述上是错误的:

The handshake request mimic HTTP request but the communication that follows don't. The handshake request arrives at the server on port 80. Because it's 80 port, server will treat it with HTTP protocol. And that's why the WebSocket handshake request must be in HTTP format. If so, I think the HTTP protocol MUST be modified/extended to recognize those WebSocket-specific things. Otherwise it won't realize it should yield to WebSocket protocol.

我觉得应该这样理解:

WebSocket communication starts with a valid HTTP request from client to server. So it is the server that follows the HTTP protocol to parse the handshake request and identify the begging for protocol change. And it is the server that switches the protocol. So HTTP protocol doesn't need to change. HTTP protocol doesn't even need to know about WebSocket.

WebSocket 和 Comet

所以WebSocket与Comet技术的不同之处在于WebSoket并不局限于当前的HTTP领域来解决双向通信问题。

加 2

相关问题:

回答您的问题:同时处理到端口 80 的 Websocket 和 HTTP 连接...

与端口 80 的同时 HTTP 连接的处理方式完全相同!

这意味着:在满意的 TCP 握手后,侦听 serviceip:80 的服务继续生成一个新的进程或线程,并将该连接的所有通信移交给它(或者只是通过执行回调来处理请求正如 jfriend00 正确指出的那样,与异步 nodejs 一样与该事件相关联)。

然后等待或处理队列中的下一个传入请求。

如果您想知道 HTTP 1.1 和 UPGRADE 请求在所有这些方面发挥的作用,MSDN article 上的内容非常清楚:

The WebSocket Protocol has two parts: a handshake to establish the upgraded connection, then the actual data transfer. First, a client requests a websocket connection by using the "Upgrade: websocket" and "Connection: Upgrade" headers, along with a few protocol-specific headers to establish the version being used and set-up a handshake. The server, if it supports the protocol, replies with the same "Upgrade: websocket" and "Connection: Upgrade" headers and completes the handshake. Once the handshake is completed successfully, data transfer begins.

只有 Websocket 服务通常没有内置到 Web 服务器中,因此并不是真的打算在端口 80 上侦听,由于 Web 服务器的透明转发,只能通过它访问。 Apache Web 服务器使用 mod_proxy_wstunnel.

当然你也可以有一个带有内置网络套接字实现的网络服务器:例如Apache Tomcat

这里最主要的是:Websocket协议不是HTTP。它有不同的用途。它是一个独立的应用层通信协议,同样建立在TCP之上(虽然TCP不是必须的,但是符合Websockets应用层协议要求的传输层协议)。

Websocket 服务是一种并行服务 运行 以及 Web 服务器服务。

它使用现代网络浏览器支持的 Websocket 协议,实现接口的客户端部分。

您设置或构建 Websocket 服务,以便在 Websocket 客户端(通常是 Web 浏览器)和该服务之间建立持久的非 HTTP 连接。

主要优点是:Websocket 服务可以在需要时向客户端发送消息 ("one of you buddies has connected!" "your team just scored a goal!"),而不是等待客户端的明确请求更新。

您可以使用 HTTP 1.1 建立持久连接,但 HTTP 仅用于提供一组资源 UPON REQUEST 然后关闭连接。

直到最近,在所有主流浏览器都支持 Websockets 之前,您只有两种选择来在 Web 应用程序上实现实时更新:

  • 实现AJAX长轮询请求,这是一个痛苦且低效的过程。

  • 使用/构建浏览器插件(例如 Java applet 支持插件)以便能够与您建立非 HTTP 连接更新服务,这样效率更高但比长轮询更痛苦。

在服务的共享监听端口(可以是任何TCP端口,甚至不必对外开放,因为大多数web服务器都支持透明转发web socket连接)方面,是可以的与任何其他 TCP 服务完全一样:服务只是监听它,当 TCP 握手结束时,存在一个 TCP 套接字供服务与客户端通信。

像往常一样,所有连接到监听特定 TCP 套接字 (server_ip:service_TCP_port) 的服务的连接都将在分配唯一的 client_ip:client_TCP_port 对时区分,client_TCP_port 由客户端在其可用的 TCP 端口中随机选择。

如果您仍然对 Websocket 连接握手时发生的 HTTP->Websocket 应用程序协议切换以及它与底层 TCP 连接没有任何关系有疑问,我建议您参考 ,这非常清楚,很有启发性,可能是您真正想要的。

您在这里似乎缺少的相对简单的事情是每个与服务器的连接(特别是此处的 HTTP 服务器)创建它自己的套接字 然后在该套接字上运行。一个套接字上发生的事情完全独立于当前连接的任何其他套接字上发生的事情。因此,当一个套接字切换到 webSocket 协议时,不会改变其他当前或传入套接字连接发生的情况。那些人可以自行决定如何处理它们。

因此,打开的套接字可以使用 webSocket 协议,而其他传入连接可以是常规 HTTP 请求或创建新 webSocket 连接的请求。

所以,你可以得到这样的序列:

  1. 客户端 A 使用 HTTP 请求连接到端口 80 上的服务器以启动 webSocket 连接。这个过程在两者之间创建了一个套接字。
  2. 服务器响应是,升级到 webSocket 请求,并且客户端和服务器都将协议 仅针对此套接字切换到 webSocket协议。
  3. 客户端 A 和服务器开始使用 webSocket 协议交换数据包,并在接下来的几个小时内继续这样做。
  4. 客户端 B 通过常规 HTTP 请求连接到端口 80 上的同一台服务器。这个过程在两者之间创建了一个新的套接字。
  5. 服务器发现传入请求是正常的 HTTP 请求并发送响应。
  6. 客户端B收到响应后,关闭socket。
  7. 客户端 C 通过 HTTP 请求连接到端口 80 上的同一台服务器以升级到 webSocket。
  8. 服务器响应是,升级到 webSocket 请求,并且客户端和服务器都将协议 仅针对此套接字切换到 webSocket协议。
  9. 此时,有两个使用 webSocket 协议的开放套接字可以通信,服务器仍在接受新连接,这些连接可以是常规 HTTP 请求,也可以是升级到 webSocket 协议的请求。

因此,服务器始终在端口 80 上接受新连接,这些新连接可以是常规 HTTP 请求,也可以是请求升级到 webSocket 协议的 HTTP 请求(因此开始一个 webSocket 连接)。而且,在所有这一切进行的同时,已经建立的 webSocket 连接正在使用 webSocket 协议通过它们自己的套接字进行通信。

webSocket 连接和通信方案经过精心设计,具有以下特点:

  1. 不需要新端口。传入端口(最常见的端口 80)可用于常规 HTTP 请求和 webSocket 通信。
  2. 因为不需要新端口,所以不需要更改防火墙或其他网络基础设施"usually"。事实证明,情况并非总是如此,因为某些需要 HTTP 流量的代理或缓存可能必须进行修改才能处理(或避免)webSocket 协议流量。
  3. 同一个服务器进程可以轻松处理 HTTP 请求和 webSocket 请求。
  4. HTTP cookies and/or 在设置 webSocket 连接期间可以使用其他基于 HTTP 的身份验证方法。

您进一步问题的答案:

1) Why choose 80 as the default port? Does the designer want to make WebSocket communication look like normal HTTP communication from the transport level's perspective? (i.e. the server port is the good old 80).

是的,请参阅上面的第 1-4 点。 webSockets 可以在现有的 HTTP 通道上建立,因此它们通常不需要更改网络基础设施。我要补充的是,不需要新的服务器或服务器进程,因为现有的 HTTP 服务器可以简单地添加 webSocket 支持。

2) I am trying to picture how the protocol shift happens on server. I image there're different software modules for handling HTTP or WebSocket traffics. First server uses the HTTP module to handle the normal HTTP requests. When it finds an Upgrade request, it will switch to use WebSocket module.

不同的服务器架构会以不同的方式处理webSocket数据包和HTTP请求之间的划分。在某些情况下,webSocket 连接甚至可能被转发到新进程。在其他情况下,它可能只是同一进程中的不同事件处理程序,该处理程序在套接字上注册了传入的数据包流量,现在已切换到 webSocket 协议。这完全取决于 Web 服务器架构及其选择处理 webSocket 流量的方式。实现 webSocket 应用程序服务器端的开发人员很可能 select 与他们特定的 Web 服务器架构兼容的现有 webSocket 实现,然后编写在该框架内工作的代码。

在我的例子中,我 select 编辑了与 node.js 一起工作的 socket.io 库(这是我的服务器架构)。该库为我提供了一个对象,该对象支持新连接的 webSockets 的事件,然后是一组用于读取传入消息或发送传出消息的其他事件。初始 webSocket 连接的细节都由库处理,我不必担心这些。如果我想在建立连接之前要求身份验证,socket.io 库有一种方法可以让我插入。然后我可以从任何客户端接收消息,向任何单个客户端发送消息或向所有客户端广播信息.我主要使用它进行广播以在网页 "live" 中保留一些信息,以便网页显示始终是最新的。只要服务器上的值发生变化,我就会将新值广播给所有连接的客户端。

你的问题很好!

我想尝试从系统调用listen()accept()的角度来回答。我认为了解这两个调用的行为非常有见地并且足以回答您的问题。

剧透:我们可以通过研究 TCP/IP 的工作原理来回答您的问题:-)

对于问题的核心部分,HTTP 或 WebSocket 确实没有区别。共同点是 TCP over IP。发送 HTTP 请求需要在两方之间建立 TCP/IP 连接(我已尝试详细说明 here)。

对于简单的网络浏览器/网络服务器场景

  1. 首先,两者之间建立TCP连接(由客户端发起)
  2. 然后通过 那个 TCP 连接发送 HTTP 请求(从客户端到服务器)
  3. 然后通过相同 TCP 连接发送 HTTP 响应(在另一个方向,从服务器到客户端)

这次交换后,不再需要底层的 TCP 连接,通常变为 destroyed/disconnected。在所谓的 "HTTP Upgrade request"(可以认为是:"hey, server! Please upgrade this to a WebSocket connection!")的情况下,底层 TCP 连接继续存在,WebSocket 通信通过创建的相同 TCP 连接最初(上面的步骤 (1))。

这有望阐明 WebSocket 和 HTTP 之间的主要区别是高级协议中的切换(从 HTTP 到 WebSocket)无需更改底层传输通道(TCP/IP 连接).

如何通过同一个套接字处理多个 IP 连接尝试?

这是我曾经纠结过的一个话题,很多人不理解,因为它有点不直观。然而,当了解操作系统提供的与套接字相关的基本系统调用如何工作时,这个概念实际上非常简单。

首先,需要了解 IP 连接是 唯一5 条信息定义的:

IP:机器A的端口IP:机器B的端口协议(TCP或UDP )

现在,socket 对象通常被认为代表一个连接。但这并不完全正确。它们可能代表不同的事物:它们可以是主动的或被动的。 passive/listen 模式下的套接字对象做了一些非常特别的事情,这对回答你的问题很重要。

http://linux.die.net/man/2/listen 说:

listen() marks the socket referred to by sockfd as a passive socket, that is, as a socket that will be used to accept incoming connection requests using accept(2).

也就是说,我们可以创建一个被动套接字来侦听传入的连接请求。根据定义,这样的套接字永远不能表示连接。它只是监听连接请求。

让我们转到 accept() (http://linux.die.net/man/2/accept):

The accept() system call is used with connection-based socket types (SOCK_STREAM, SOCK_SEQPACKET). It extracts the first connection request on the queue of pending connections for the listening socket, sockfd, creates a new connected socket, and returns a new file descriptor referring to that socket. The newly created socket is not in the listening state. The original socket sockfd is unaffected by this call.

让我们仔细消化一下,我想这现在真的回答了你的问题。

accept()不会改变之前创建的passive套接字的状态。它 returns 一个 活动(已连接) 套接字(这样的套接字代表上面的五个信息状态——简单,对吧?)。

通常,这个新创建的活动套接字对象会被移交给另一个进程或线程,或者只是 "entity" 负责连接。在 accept() 返回了这个连接的套接字对象之后,accept() 可以在被动套接字上再次被调用 ,一次又一次——这被称为 接受循环.

但是调用 accept() 需要时间,对吧?它不能错过传入的连接请求吗?刚刚引用的帮助文本中有更重要的信息:有一列待处理的连接请求!它由操作系统的 TCP/IP 堆栈自动处理。

这意味着虽然 accept() 只能处理传入的连接请求 一个接一个 ,但即使传入的请求是按顺序传入的,也不会遗漏任何传入的请求高速率或(准)同时。可以说 accept() 的行为限制了您的机器可以处理的传入连接请求的频率。然而,这是一个快速的系统调用,在实践中,首先会遇到其他限制——通常是那些与处理所有已被接受的连接相关的限制到目前为止

这是一个非常简单的概念,所以让我尝试用简单的术语来描述它,以防其他迷失的灵魂想要掌握它而无需阅读所有那些冗长的解释。

多个连接

  1. 网络服务器开始侦听连接。发生这种情况:

    • Web 服务器的主进程在端口 80 上打开状态为 listen 的被动套接字,例如9.9.9.9:809.9.9.9是服务器IP,80是端口)。
  2. 浏览器向服务器上的 80 端口发出请求。发生这种情况:

    • 操作系统(简称OS)在客户端随机分配一个出站端口,比如:1.1.1.1:67471.1.1.1是客户端IP,6747是随机端口)。

    • OS发送一个数据包,源地址1.1.1.1:6747,目的地址9.9.9.9:80。经过各种路由器和交换机到达目的服务器。

  3. 服务器收到数据包。发生这种情况:

    • 服务器 OS 发现数据包的目标地址是它自己的 IP 地址之一,并根据目标端口将其传递给与端口 80 关联的应用程序.

    • Web 服务器的主进程接受连接并创建一个新的活动套接字。然后它通常会派生一个新的子进程,接管活动套接字。被动套接字保持打开状态以接受新的传入连接。

现在从服务器发送到客户端的每个数据包都将具有这些地址:

  • 来源:9.9.9.9:1553;目的地:1.1.1.1:80

从客户端发送到服务器的每个数据包都将包含这些地址:

  • 来源:1.1.1.1:80;目的地:9.9.9.9:1553

HTTP -> WebSocket 握手

HTTP 是一种基于文本的协议。有关可用命令的列表,请参阅 HTTP wiki。浏览器发送这些命令之一,Web 服务器相应地做出响应。

WebSocket 不基于 HTTP。它是一种二进制协议,可以同时在两个方向上发送多个消息流(全双工模式)。因此,如果不引入新的 HTTP 标准(例如 HTTP/2),就不可能直接建立 WebSocket 连接。但这只有在以下情况下才有可能:

  1. 支持 WebSocket HTTP verbs/requests

  2. 有一个不同于 80 的新专用端口用于特定于 WebSocket 的通信。

第一个不在协议的范围内,第二个会破坏现有的网络基础设施。因为 client/browser 可以与同一台服务器建立多个 HTTP 连接,将其中一些连接从 HTTP 切换到 WebSocket 是两全其美的方法 - 保持相同的端口 80 但允许使用与 HTTP 不同的协议。切换通过客户端发起的 protocol handshake 发生。