WebSockets服务器架构如何工作?

19

我正在努力了解使用服务端架构的WebSockets是如何工作的,目标是将其实现在嵌入式应用程序中。看起来这里涉及到 3 个不同的服务端软件组件:1)Web服务器用于提供静态HTTP页面和处理升级请求,2)WebSockets库(例如libwebsockets)用于处理WebSockets通信的"核心部分",3)我的自定义应用程序实际上负责处理传入数据。它们如何结合起来呢?是否常见有单独的Web服务器和WebSocket处理模块,也就是WebSocket服务器/守护进程?

我的应用程序如何与Web服务器和/或WebSockets库进行通信以发送/接收数据?例如,对于CGI,Web服务器使用环境变量向自定义应用程序发送信息,并使用stdout接收响应。这里是否有等效的通信系统?或者通常将WebSocket库链接到自定义应用程序中?但是那么Web服务器到WebSocket库+自定义应用程序的通信会如何运作?或者将全部3个组件合并为单个组件?

我询问的原因是,我正在使用boa Web服务器,在具有受限内存的Blackfin处理器上的uClinux/无MMU平台上运行。boa没有本地WebSocket支持,只有CGI。我正在尝试弄清楚如何将WebSockets支持添加到其中。我更喜欢使用编译的解决方案,而不是像JavaScript、Python或PHP这样的解释性语言。我的当前应用程序使用长轮询通过CGI,这对计划中的增强性能来说不足够。

1个回答

38
首先,了解WebSocket连接的建立方式非常重要,因为这与WebSocket连接和您的Web服务器之间的重要关系有关。
每个WebSocket连接都始于一个HTTP请求。浏览器向请求WebSocket连接的主机/端口发送HTTP请求。该请求可能类似于以下内容:
GET /chat HTTP/1.1
Host: example.com:8000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

这个请求与向该服务器发出的其他HTTP请求的区别在于请求中的Upgrade: websocket头部。这告诉HTTP服务器,这个特定的请求实际上是一个用于初始化webSocket连接的请求。该头部还允许Web服务器区分普通HTTP请求和打开webSocket连接的请求。这在架构中非常重要,并且完全是有意为之的。这允许使用完全相同的服务器和端口来为您的Web请求和webSocket连接提供服务。只需要在Web服务器上添加一个组件,它会查找所有传入的HTTP连接中的此Upgrade头部,并且如果找到,它将接管连接并将其转换为webSocket连接。
一旦服务器识别了这个upgrade头部,它会响应一个合法的HTTP响应,但其中信号客户端已经接受了升级到webSocket协议,如下所示:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

在此时,客户端和服务器都保持原始HTTP请求的套接字开放,并同时切换到webSocket协议。
现在,回答你的具体问题:
应用程序如何与Web服务器和/或WebSocket库通信以发送/接收数据?
您的应用程序可以使用现代浏览器中内置的webSocket支持,并可以像这样启动webSocket连接:
var socket = new WebSocket("ws://www.example.com");

这将指示浏览器启动与www.example.com的webSocket连接,使用与当前网页连接相同的端口。由于浏览器内置了webSocket支持,客户端会自动处理上述HTTP请求和升级协议。
在服务器端,您需要确保使用具有传入webSocket支持的Web服务器,并启用和配置该支持。由于一旦建立webSocket连接就是连续连接,它根本不遵循CGI模型。必须至少有一个长时间运行的过程来处理实时webSocket连接。在服务器模型(如CGI)中,您需要某种支持此长时间运行过程的webServer附加组件,以支持webSocket连接。在像已经是长时间运行过程的node.js这样的服务器环境中,添加webSockets在架构上没有任何变化 - 而只是为了支持webSocket协议而添加的其他库。
我建议您阅读本文,因为它讨论了从CGI-style单个请求处理到webSocket的连续套接字连接的过渡可能会很有趣:

Web Evolution:从CGI到Websockets(以及它如何帮助您更好地监视云基础架构)

如果您确实想坚持使用stdin/stdout模型,那么有一些为webSockets建模的库可供使用。这里是一个这样的库。他们的标语是"就像CGI一样,二十年后,适用于WebSockets"

我正在尝试弄清楚如何添加WebSockets支持。我更喜欢使用编译解决方案,而不是JavaScript、Python或PHP等解释性语言。

抱歉,我不熟悉那个特定的服务器环境。找出您的选择可能需要进行深入搜索。由于webSocket连接是连续的连接,因此您将需要一个持续运行的进程,可以成为webSocket连接的服务器端部分。这可以是内置于您的webServer中的内容,也可以是webServer启动并转发传入连接的附加进程。

FYI,我在家里使用树莓派搭建了一个定制应用程序,它使用WebSockets与浏览器网页进行实时通信,并且运行良好。我恰好使用node.js作为服务器环境,并使用socket.io库在WebSockets上运行,以便在WebSockets的基础上提供更高级别的接口。我的服务器代码定期检查几个硬件传感器,然后每当有新的/更改的数据要报告时,就会向任何开放的WebSockets发送消息,以便连接的浏览器可以实时更新传感器读数。
您可能需要一些长时间运行的应用程序,将传入的WebSockets连接从Web服务器传递到您的长时间运行的进程,或者您需要在不同于Web服务器的端口上进行WebSockets连接(以便可以由完全不同的服务器进程处理),在这种情况下,您将拥有一个完全独立的服务器来处理WebSockets请求和套接字(此服务器还必须支持CORS以使浏览器能够连接到它,因为它将是与Web页面不同的端口)。

感谢您详细的回复。在您的树莓派项目中,我假设您有一个长时间运行的进程来检查传感器?那个进程如何与node.js通信以将传感器数据发送到网页?这是我试图更好地理解架构图片的一部分。 - Jon
@Jon - 我的node.js / Raspberry Pi系统中只有一个长时间运行的进程,那就是一个自定义的node.js脚本,它既是我的Web服务器,也是检查传感器并在任何连接的WebSockets上发送数据的逻辑(从技术上讲,它是多个脚本,但都作为一个长时间运行的node.js应用程序的一部分运行)。 - jfriend00
所以你已经将我的三个拼图组合成一个组件了。如果你必须将传感器部分作为单独的进程运行,你有什么想法吗? - Jon
@Jon - 我给了你几个链接,涉及试图将CGI和webSocket世界联系起来的文章和产品。那将是你理解它的起点。正如我在我的回答中所描述的,最终,您将需要在单独端口上运行一个仅用于websocket连接的单独服务器,或获取某种插件并了解如何使用该插件将完全取决于其设计方式。 正如您现在可能已经看到的那样,CGI和webSocket在某种程度上是架构上的相反物。 - jfriend00
非常好的解释,谢谢,但是websocket连接真的是tcp连接吗? - allel
1
@allel - 在低层次上,WebSocket连接就像HTTP连接一样是TCP连接。但你必须使用WebSocket数据帧和WebSocket安全协议来进行通信。你不能将其作为原始的TCP连接使用。 - jfriend00

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接