提问者:小点点

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


根据这里:

HTTP Upgrade报头请求服务器将应用层协议从HTTP切换到WebSocket协议。

客户端握手在IE10和服务器之间建立了HTTP-on-TCP连接。在服务器返回其101响应之后,应用层协议从HTTP切换到使用先前建立的TCP连接的WebSockets。

在这一点上,HTTP是完全不存在的。使用轻量级WebSocket有线协议,消息现在可以在任何时候由任一个endpoint发送或接收。

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

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

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

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

我想补充几点想法。

WebSocketHTTP都只是应用程序级协议。通常它们都依赖TCP协议作为传输。

WebSocket设计有意选择服务器端口80来进行握手和后续通信。我认为设计人员希望从传输级别的角度(即服务器端口号仍然是80)使WebSocket通信看起来像正常的HTTP通信。但是根据JFriend00的回答,这种伎俩并不总是欺骗网络基础设施。

来自RFC 6455-WebSocket协议

基本上,考虑到Web的约束,它的目的是尽可能接近于只向脚本公开原始TCP。它还设计成这样一种方式,即它的服务器可以与HTTP服务器共享一个端口,方法是让它的握手是一个有效的HTTP升级请求。人们可以在概念上使用其他协议来建立客户机-服务器消息传递,但是WebSockets的目的是提供一个相对简单的协议,它可以与HTTP和已部署的HTTP基础结构(例如代理)共存,并且在考虑到安全因素的情况下,它与TCP非常接近,并且可以安全地与这种基础结构一起使用,还可以有针对性地添加一些内容来简化使用并使简单的事情保持简单(例如添加消息语义)。

因此,我认为以下说法是错误的:

握手请求模仿HTTP请求,但随后的通信不模仿HTTP请求。握手请求到达端口80上的服务器。因为它是80个端口,服务器将用HTTP协议处理它。这就是WebSocket握手请求必须采用HTTP格式的原因。如果是这样,我认为必须修改/扩展HTTP协议来识别那些WebSocket特定的东西。否则它不会意识到它应该屈服于WebSocket协议。

我觉得应该这样理解:

WebSocket通信从客户端到服务器的有效HTTP请求开始。因此,服务器遵循HTTP协议来解析握手请求并识别请求更改协议的请求。而切换协议的正是服务器。因此HTTP协议不需要更改。HTTP协议甚至不需要知道WebSocket。

所以WebSocket不同于Comet技术,因为WebSocket并不局限于当前的HTTP领域来解决双向通信问题。

一个相关的问题:浏览器如何与80端口的web服务器建立连接?细节?


共3个答案

匿名用户

你的问题太棒了!

我想尝试从涉及系统调用listen()accept()的角度来回答这个问题。了解这两次通话的行为我认为是相当有洞察力的,足以回答你的问题。

对于问题的核心部分,HTTP或WebSocket的不同确实没有区别。共同点是IP上的TCP。发送HTTP请求需要在双方之间建立TCP/IP连接(我已经尝试在这里更详细地阐述这一点)。

在简单的web浏览器/web服务器场景中

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

在此交换之后,基础TCP连接不再需要,并且通常会被破坏/断开。如果出现所谓的“HTTP升级请求”(可以认为是:“嘿,服务器!请将此升级到WebSocket连接!”),底层TCP连接将继续运行,WebSocket通信将通过最初创建的TCP连接(上面的步骤(1))。

这很有希望澄清WebSocket和HTTP之间的关键区别是在高层协议(从HTTP到WebSocket)中的交换,而不改变底层传输通道(TCP/IP连接)。

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

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

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

现在,套接字对象通常被认为代表一个连接。但这并不完全正确。它们可能代表不同的事物:它们可以是主动的,也可以是被动的。被动/侦听模式下的套接字对象会做一些非常特殊的事情,这对于回答您的问题非常重要。

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

listen()将sockfd引用的套接字标记为被动套接字,即标记为将用于使用accept(2)接受传入连接请求的套接字。

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

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

accept()系统调用与基于连接的套接字类型(SOCK_STREAM,SOCK_SEQPACKET)一起使用。它提取侦听套接字sockfd的挂起连接队列中的第一个连接请求,创建一个新的已连接套接字,并返回一个引用该套接字的新文件描述符。新创建的套接字未处于侦听状态。原始套接字sockfd不受此调用的影响。

我们仔细消化一下这个,我觉得这个现在确实回答了你的问题。

accept()不会更改之前创建的被动套接字的状态。它返回一个活动的(连接的)套接字(这样的套接字表示上面的五个信息状态--很简单,对吧?)。

通常,这个新创建的活动套接字对象然后被移交给另一个进程或线程,或者仅仅是负责连接的“实体”。在accept()返回此连接的套接字对象之后,可以在被动套接字上再次调用accept(),并且一次又一次地调用--这就是所谓的接受循环。

但是调用accept()需要时间,对吗?它不会错过传入的连接请求吗?在刚刚引用的帮助文本中还有更多必不可少的信息:有一个挂起的连接请求队列!它由操作系统的TCP/IP堆栈自动处理。

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

匿名用户

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

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

因此,您可以使用这种类型的序列:

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

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

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

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

对您的进一步问题的答复:

1)为什么选择80作为默认端口?设计人员是否希望使WebSocket通信从传输级别的角度看起来像正常的HTTP通信?(即。服务器端口是旧的80)。

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

2)我正在尝试描述服务器上的协议转换是如何发生的。我想象有不同的软件模块来处理HTTP或WebSocket流量。第一个服务器使用HTTP模块来处理正常的HTTP请求。当它发现升级请求时,它将切换到使用WebSocket模块。

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

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

匿名用户

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

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

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

如果您想知道HTTP1.1和升级请求对所有这些都起到了什么作用,这篇MSDN关于它的文章给出了非常清楚的说明:

WebSocket协议有两个部分:建立升级连接的握手,然后是实际的数据传输。首先,客户端通过使用“upgrade:websocket”和“connection:upgrade”头以及一些特定于协议的头来请求websocket连接,以建立正在使用的版本并建立握手。服务器,如果它支持该协议,则用相同的“upgrade:websocket”和“connection:upgrade”报头进行回复,并完成握手。一旦握手成功完成,数据传输开始。

只有Websocket服务通常没有内置到web服务器中,因此并不是真正用于侦听端口80,由于web服务器的透明转发,只能通过它访问。Apache Web server使用mod_proxy_wstunnel执行此操作。

当然,您也可以有一个内置web套接字实现的web服务器:例如Apache Tomcat。

这里主要的是:Websocket协议不是HTTP。它有不同的目的。它是一个独立的应用层通信协议,也构建在TCP之上(尽管TCP不是必需的要求,但它是一个适合Websockets应用层协议要求的传输层协议)。

Websocket服务是与web服务器服务一起运行的并行服务。

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

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

主要的优点是:Websocket服务可以在需要的时候向客户端发送消息(“你们中的一个伙伴已经连接了!”“你的团队刚刚进了一个球!”),而不必等待客户端明确的更新请求。

您可以使用HTTP1.1建立一个持久连接,但是HTTP并不意味着除了根据请求为一组资源提供服务,然后关闭连接之外的任何事情。

直到最近,在所有主要浏览器都提供WebSockets支持之前,在web应用程序上实现实时更新只有两种选择:

>

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

    使用/构建一个浏览器插件(例如一个Java小程序支持插件),以便能够与您建立一个非HTTP连接更新服务,这比长时间轮询更有效,但甚至更痛苦。

    就服务的共享侦听端口而言(它可以是任何TCP端口,甚至不必对internet开放,因为大多数web服务器支持web套接字连接的透明转发),它的工作方式与任何其他TCP服务完全相同:服务只是侦听它,当TCP握手结束时,存在一个TCP套接字,以便服务与客户机通信。

    通常,当分配唯一的client_ip:client_TCP_port对时,到侦听特定TCP套接字(server_ip:service_tcp_port)上的服务的所有连接将被区分,client_TCP_port由客户端在其可用TCP端口中随机选择)。

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