WebSocket服务器如何处理多个传入的连接请求?
据此:
HTTP升级头请求服务器将应用层协议从HTTP切换到WebSocket协议 。
客户端握手build立了IE10和服务器之间的HTTP-on-TCP连接。 在服务器返回其101响应之后,应用层协议从HTTP切换到使用先前build立的TCP连接的WebSocket。
在这一点上, HTTP完全没有了 。 使用轻量级的WebSocket有线协议,消息现在可以随时由任一端点发送或接收。
所以,我的理解是,在第一个客户端与服务器握手后,服务器的80端口将被WebSocket协议垄断 。 HTTP不再在80端口上工作 。
那么第二个客户端怎么会和服务器交换握手呢? 毕竟WebSocket握手是HTTP格式的。
ADD 1
感谢所有迄今为止的答案。 他们真的很有帮助。
现在我明白,同一个服务器的80端口是由多个TCP
连接共享的。 而且这种共享是完全可以的,因为Jan-Philip Gehrcke
指出TCP
连接是由一个5元组元素来标识的。
我想补充一些想法。
WebSocket
和HTTP
都只是应用程序级别的协议。 通常他们都依赖TCP
协议作为传输。
为什么select80端口?
WebSocketdevise有意select服务器端口80进行握手和后续通信 。 我认为devise人员希望使WebSocket通信看起来像 从传输层面(即服务器端口号仍为80)的普通HTTP通信。 但根据jfriend00
的回答,这个伎俩并不总是欺骗networking基础设施。
协议如何从HTTP转换到WebSocket发生?
从RFC 6455 – WebSocket协议
基本上,它打算尽可能接近暴露原始的TCP到脚本在给定的Web约束下。 它的devise也使得它的服务器可以与HTTP服务器共享一个端口,因为它的握手是一个有效的HTTP升级请求。 人们可以从概念上使用其他协议来build立客户端 – 服务器消息传递,但是WebSockets的目的是提供一个相对简单的协议,它可以与HTTP共存并部署HTTP基础设施(如代理服务器),并且与安全的TCP在给定安全考虑的情况下与这样的基础设施一起使用,有针对性的添加以简化使用并简单简单的事情(例如添加消息语义)。
所以我觉得我错了下面的说法:
握手请求模仿 HTTP请求而不是后面的通信。 握手请求到达端口80上的服务器。 因为它是80端口,服务器将使用HTTP协议处理它。 这就是为什么WebSocket握手请求必须是HTTP格式。 如果是这样,我认为HTTP协议必须被修改/扩展来识别那些WebSocket特定的东西。 否则,它不会意识到它应该产生 WebSocket协议。
我认为应该这样理解:
WebSocket通信从客户端到服务器的有效 HTTP请求开始。 所以服务器遵循HTTP协议来parsing握手请求,并确定协议变更请求。 它是切换协议的服务器。 所以HTTP协议不需要改变。 HTTP协议甚至不需要了解WebSocket。
WebSocket和Comet
所以WebSocket与Comet技术的不同之处在于WebSoket并不局限于当前的HTTP领域来解决双向通信问题。
ADD 2
一个相关的问题: 浏览器如何build立与80端口上的Web服务器的连接? 细节?
其他答案已经有帮助。 我想指出,你的问题是一个很好的问题,并且要从listen()
和accept()
的angular度来回答。 这两个系统调用的行为应该足以回答你的问题。
您对TCP / IP如何工作感兴趣!
对于问题的核心部分,根据HTTP或WebSocket确实没有区别:共同点是TCP over IP,足以回答你的问题。 不过,你应该得到一个关于WebSocket如何与TCP相关的答案(我在这里试图详细阐述一下 ):发送一个HTTP请求需要两方之间build立的TCP / IP连接。 在简单的Web浏览器/ Web服务器scheme的情况下
- 首先在两者之间build立TCP连接(由客户端发起)
- 那么通过该 TCP连接发送一个HTTP请求(从客户端到服务器)
- 那么通过相同的 TCP连接发送HTTP响应(从服务器到客户端的另一个方向)
在这个交换之后,底层的TCP连接不再需要,通常会被破坏/断开。 在HTTP升级请求的情况下,底层的TCP连接刚刚生效,并且WebSocket通信经过与最初创build的TCP连接(步骤(1))相同的TCP连接。
正如您所看到的,WebSocket和标准HTTP之间的唯一区别是高级协议(从HTTP到WebSocket)的切换,而不会更改底层传输通道(TCP / IP连接)。
如何通过同一套接字处理多个IP连接尝试?
这是我曾一度与自己斗争的一个话题,也有很多人不明白。 然而,当了解操作系统提供的与基本套接字相关的系统调用是如何工作的时候,这个概念实际上是非常简单的。
首先,需要了解的是,IP连接由五条信息唯一地定义:
IP:机器A的端口和IP:机器B的端口和协议(TCP或UDP)
现在, 套接字对象通常被认为代表连接。 但这不完全正确。 他们可能代表不同的事物:他们可以是主动的或被动的。 被动/监听模式下的套接字对象做了一些非常特殊的事情,这对于回答您的问题非常重要。 http://linux.die.net/man/2/listen说:;
listen()将sockfd引用的套接字标记为被动套接字,也就是将其用作使用accept(2)接受传入连接请求的套接字。
所以,我们可以创build一个被动的套接字来监听传入的连接请求。 根据定义,这样的套接字永远不能代表连接。 它只是侦听连接请求。
让我们来accept()
( http://linux.die.net/man/2/accept ):
accept()系统调用与基于连接的套接字types(SOCK_STREAM,SOCK_SEQPACKET)一起使用。 它提取监听套接字的挂起连接队列上的第一个连接请求,sockfd,创build一个新的连接套接字,并返回一个引用该套接字的新文件描述符。 新创build的套接字不处于侦听状态。 原始套接字sockfd不受此调用的影响。
为了回答你的问题,我们需要知道这一切。 accept()
不会更改之前创build的被动套接字的状态。 它返回一个活动(连接)的套接字(这样的套接字,然后代表上面的五个信息状态 – 简单,对吧?)。 通常,这个新创build的活动套接字对象然后被交给另一个进程或线程或者只是负责连接的“实体”。 accept()
返回这个连接的socket对象之后,可以在被动套接字上再次调用accept()
,并且一次又一次地 – 被称为accept循环的东西。 但是调用accept()
需要时间,对吧? 它不能错过传入的连接请求? 刚刚引用的帮助文本中有更多重要的信息:有一个挂起的连接请求队列! 它由您的操作系统的TCP / IP堆栈自动处理。 这意味着虽然accept()
只能一个接一个地处理传入的连接请求,但即使传入的请求同时以高速率或(准)传入,也不会丢失。 有人可能会说accept()
的行为是限制您的机器可以处理的传入连接请求的频率。 然而,这是一个快速的系统调用,在实践中,其他的限制首先出现 – 通常是处理所有迄今为止已经接受的连接。
你似乎在这里丢失的相对简单的东西是每个连接到服务器(特别是你的HTTP服务器) 创build它自己的套接字 ,然后在该套接字上运行。 在一个套接字上发生的事情完全独立于当前连接的任何其他套接字上发生的事情。 所以,当一个套接字切换到webSocket协议时,并不会改变其他当前或传入套接字连接的情况。 那些自己决定如何处理。
所以,打开的套接字可以使用webSocket协议,而其他传入的连接可以是常规的HTTP请求或请求来创build新的webSocket连接。
所以,你可以有这种types的序列:
- 客户端A通过HTTP请求连接到端口80上的服务器以启动webSocket连接。 这个过程在两者之间创build一个套接字。
- 服务器响应是,升级到webSocket请求,客户端和服务器都将此套接字的协议仅切换到webSocket协议。
- 客户端A和服务器开始使用webSocket协议交换数据包,并在接下来的几个小时继续这样做。
- 客户端B使用常规的HTTP请求连接到端口80上的相同服务器。 这个过程在两者之间创build一个新的套接字。
- 服务器看到传入的请求是一个正常的HTTP请求并发送响应。
- 当客户端B收到请求时,套接字被closures。
- 客户端C通过HTTP请求连接到端口80上的相同服务器以升级到webSocket。
- 服务器响应是,升级到webSocket请求,客户端和服务器都将此套接字的协议仅切换到webSocket协议。
- 此时,有两个使用webSocket协议的开放套接字可以进行通信,服务器仍然接受新的连接,这些连接既可以是常规的HTTP请求,也可以是请求更新到webSocket协议的请求。
所以,在任何时候,服务器仍然在端口80上接受新的连接,这些新的连接可以是常规的HTTP请求,也可以是请求升级到webSocket协议的HTTP请求(从而启动webSocket连接)。 而且,当所有这些都在进行时,已经build立的webSocket连接使用webSocket协议通过它们自己的套接字进行通信。
webSocket连接和通信scheme的devise非常精心,具有以下特点:
- 没有新的端口是必需的。 传入端口(最常见的端口80)可用于常规的HTTP请求和webSocket通信。
- 由于不需要新的端口,防火墙或其他networking基础设施的更改通常不是必需的。 事实certificate,情况并非总是如此,因为预计HTTPstream量的某些代理或caching可能不得不进行修改以处理(或避免)webSocket协议stream量。
- 相同的服务器进程可以轻松处理HTTP请求和webSocket请求。
- HTTP cookie和/或其他基于HTTP的authentication手段可以在build立webSocket连接的过程中使用。
解答您的其他问题:
1)为什么select80作为默认端口? devise者是否希望使WebSocket通信看起来像从传输级别angular度来看正常的HTTP通信? (即服务器端口是好老80)。
是的,看到我上面的第1-4点。 webSocket可以在现有的HTTP通道上build立,所以通常不需要改变networking基础设施。 我会添加到这些点,不需要新的服务器或服务器进程,因为现有的HTTP服务器可以简单地添加webSocket支持。
2)我想了解协议如何在服务器上发生变化。 我的图像有不同的软件模块来处理HTTP或WebSocketstream量。 第一台服务器使用HTTP模块来处理正常的HTTP请求。 当它find升级请求时,它将切换到使用WebSocket模块。
不同的服务器体系结构将不同地处理webSocket数据包和HTTP请求之间的划分。 在一些情况下,webSocket连接甚至可能被转发到一个新的进程。 在其他情况下,在同一进程中,它可能只是一个不同的事件处理程序,它在已被切换到webSocket协议的套接字上注册传入数据包通信。 这完全取决于Web服务器体系结构以及它如何select处理webSocketstream量。 实现webSocket应用程序服务器端的开发人员很可能会select与其特定Web服务器体系结构兼容的现有WebSocket实现,然后编写在该框架内工作的代码。
就我而言,我select了与node.js(这是我的服务器体系结构)配合使用的socket.io库。 该库为我提供了一个对象,支持新连接的webSocket的事件,然后是用于读取传入消息或发送传出消息的一组事件。 初始webSocket连接的细节全部由库处理,我不必担心这些。 如果我想在build立连接之前要求authentication,socket.io库有一个插入的方法。然后我可以接收来自任何客户端的消息,将消息发送给任何一个客户端或者向所有客户端广播信息。 我主要使用它来进行广播,以保持网页中的一些信息“活”,以便网页显示始终是最新的。 只要服务器上的值发生变化,我就会将新的值广播给所有连接的客户端。
为了回答你的问题:同时处理端口80的Websocket和HTTP连接
与端口80同时HTTP连接的处理方式完全相同!
这意味着:在满意的TCP握手之后,在serviceip:80上侦听的服务继续产生一个新的进程或线程,并将该连接的所有通信切换到该进程(或者通过执行与该事件相关联的callback作为asynchronousnodejs的确如jfriend00正确指出的那样)。
然后等待或处理队列中的下一个传入请求。
如果您想知道HTTP 1.1和UPGRADE请求在这一切中所起的作用, MSDN上的这篇文章就非常清楚:
WebSocket协议有两部分:build立升级连接的握手,然后是实际的数据传输。 首先,客户端通过使用“Upgrade:websocket”和“Connection:Upgrade”头文件以及一些协议专用头文件来请求websocket连接,以build立正在使用的版本并build立握手。 如果服务器支持该协议,则使用相同的“Upgrade:websocket”和“Connection:Upgrade”标题进行回复,并完成握手。 一旦握手成功完成,数据传输就开始了。
只有Websocket服务通常不会内置到Web服务器中,所以并不是真正意图在端口80上进行监听,只需通过它就可以访问Web服务器的透明转发。 Apache Web服务器使用mod_proxy_wstunnel 。
当然,您也可以使用内置Web套接字实现的Web服务器: Apache Tomcat 。
这里主要的是:Websocket协议不是HTTP。 它服务于不同的目的。 它是一个build立在TCP之上的独立的应用层通信协议(尽pipeTCP不是必需的,但是符合Websockets应用层协议要求的传输层协议)。
Websocket服务是与Web服务器服务一起运行的PARALLEL服务。
它使用Websocket协议,为现代Web浏览器提供支持,实现接口的客户端部分。
您build立或build立一个Websocket服务,以便在Websocket客户端(通常是Web浏览器)和该服务之间build立持久的非HTTP连接。
主要优点是:Websocket服务可以在需要的时候向客户端发送一条消息(“你们中的一个伙伴已经连接了!”“你的团队刚刚打进了一个目标!”),而不必等待客户端明确的REQUEST进行更新。
您可以使用HTTP 1.1build立持久连接,但HTTP并不适用于服务一组资源UPON REQUEST ,然后closures连接。
直到最近,在所有主要浏览器都支持Websockets之前,您只有两个select来实现Web应用程序的实时更新:
-
实现AJAX长轮询请求,这是一个痛苦和低效的过程。
-
使用/构build浏览器插件(例如Java applet支持插件)以便能够与您build立非HTTP连接更新服务,这比长时间轮询更高效但更痛苦。
就服务的共享监听端口而言(它可以是任何TCP端口,甚至不必向互联网开放,因为大多数web服务器都支持web套接字连接的透明转发),它的工作方式与任何其他TCP服务:服务只是监听它,当TCP握手结束时,存在一个TCP套接字,服务与客户端进行通信。
像往常一样,当指定一个唯一的client_ip:client_TCP_port对,client_TCP_port由客户端在其可用的TCP端口中随机select时,与在特定的TCP套接字(server_ip:service_TCP_port)上侦听的服务的所有连接将被区分。
如果您仍然怀疑在Websocket连接握手过程中发生的HTTP-> Websocket应用程序协议切换,以及它如何与底层的TCP连接没有任何关系,那么我请您介绍Jan-Philip Gehrcke的答案 ,这个答案非常清晰一个有启发性的,可能是你实际上在寻找的东西。
这是一个相当简单的概念,所以让我试着用简单的语言来描述它,以免其他一些失落的灵魂想要去理解它,而不必阅读所有这些长长的解释。
多个连接
-
Web服务器开始监听连接。 有时候是这样的:
- Web服务器的主进程在端口
80
上打开一个处于listen
状态的被动套接字,例如9.9.9.9
是服务器IP,80
是端口)。
- Web服务器的主进程在端口
-
浏览器向服务器上的端口
80
发出请求。 有时候是这样的:-
操作系统(简称OS)在客户端分配一个随机出站端口,如:
1.1.1.1
:1.1.1.1:6747
(1.1.1.1
是客户端IP,6747
是随机端口)。 -
操作系统发送源地址为
1.1.1.1:6747
,目的地址为9.9.9.9:80
的数据包。 它通过各种路由器和交换机到达目的服务器。
-
-
服务器接收数据包。 有时候是这样的:
-
服务器操作系统看到数据包的目的地址是它自己的IP地址之一,并根据目的地端口将它传递给与端口
80
相关的应用程序。 -
Web服务器的主进程接受创build一个新的活动套接字的连接。 然后它通常分叉一个新的subprocess,它接pipe活动套接字。 被动套接字保持打开状态以接受新的传入连接。
-
现在从服务器发送到客户端的每个数据包将具有这些地址:
- 来源:
9.9.9.9:1553
:9.9.9.9:1553
; 目的地:1.1.1.1:80
:1.1.1.1:80
从客户端发送到服务器的每个数据包都有这些地址:
- 来源:
1.1.1.1:80
:1.1.1.1:80
; 目的地:9.9.9.9:1553
:9.9.9.9:1553
HTTP – > WebSocket握手
HTTP是基于文本的协议。 请参阅HTTP wiki以获取可用命令的列表。 浏览器发送这些命令之一,Web服务器响应。
WebSocket不基于HTTP。 这是一个二进制协议,可以同时在两个方向上发送多个消息stream(全双工模式)。 因为这样就不可能直接build立一个WebSocket连接而不需要引入一个新的HTTP标准,例如HTTP / 2 。 但是,只有在以下情况下才有可能:
-
WebSocket支持HTTP动词/请求
-
WebSocket特定的通信有一个不同于
80
的新的专用端口。
第一个不在协议范围内,第二个会打破现有的networking基础设施。 因为客户端/浏览器可以与同一个服务器build立多个HTTP连接,将它们中的一些从HTTP切换到WebSocket是两全其美的 – 保持相同的端口80
但是允许与HTTP不同的协议。 交换机通过客户端发起的协议握手进行。