原文出处:https://blog.bitsrc.io/deep-dive-into-websockets-e6c4c7622423

在Internet发展的早期,Web应用程序是围绕由用户交互触发的HTTP请求而构建的。随着技术的进步,对实时数据传输和双向通信的需求出现了。这是低延迟应用程序的要求,例如

  • 多人在线游戏
  • 聊天应用
  • 实时更新社交供稿
  • 实时体育记分牌,体育行情自动收录器等

解决以上应用场景的解决方案就是WebSockets,随着它在技术领域被广泛应用,出现了许多现成的库加入了应用程序中。因此,这导致了许多开发人员在不了解其内部原理的情况下开始使用这个技术,以至在某些情况下影响程序的执行效率。

所以为了尽可能使程序执行效率更高效,在这篇文章中,我将为您介绍WebSocket的基本属性及实现原理。

WebSockets架构

WebSockets的核心是定义了一个在客户端和服务器之间建立套接字连接的Web API。它允许自Web浏览器或服务器从任何方向上的数据通讯。此外,与HTTP相比,它还进行了多项优化,使其更适合实时通讯的场景。

实时通信

在HTTP请求中,浏览器发送Cookie和其他头信息需要使用几百个字节,由于这陡增的数据容量,从而增加了实时通信的额外开销。

不过,如果使用WebSockets,信息的尺寸很小,只有6个字节的开销(其中2个用于header报头,4个用于掩码值),因此,WebSockets更适合实时数据传输,尤其适合低延迟的应用场景。

WebSocket连接

打开WebSocket连接很简单。如果需要指定子协议,也可以使用第二个参数来完成。

// 创建一个Websocket连接
let socketConnection = new WebSocket('ws://websocket.mysite.com');
//创建一个使用子协议WebSocket连接
let socketConnection = new WebSocket('ws://websocket.mysite.com', ['soap', 'xmpp']);

创建Socket连接后,您可以向其附加事件处理程序,这样您就可以知道连接什么时候打开、什么时候接收消息以及什么时候出现错误。

// 当连接打开时,一些数据会被发送到服务器上。
socketConnection.onopen = function () {
connection.send('Hello, the socket connection is open!'); // Send a message to the server
};
// 记录错误日志
socketConnection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};
// 记录来自服务器的消息
socketConnection.onmessage = function (e) {
console.log('Server: ' + e.data);
};

连接建立后,将在WebSocket实例上触发onopen事件。

这意味着握手的完成,从这个事件开始后,任何一方(服务器和客户端)都可以随时向对方发送数据。当WebSocket在客户端接收数据时,将触发OnMessage事件,OnError事件可用于错误处理。

这时候,你可能会有疑问,我们不是一直都在这样做吗,建立连接,监听消息。还有什么其他内容吗?

那么,我们下面就一起来看看,如何能更高效执行WebSocket。对于WebSockets,我们如何处理连接非常重要,同时我们如何处理连接和连接错误重试也将决定通信的总体容错能力。

容错连接重试

在使用WebSockets时,一个常见的问题就是连接中断。当客户端或服务器没有响应时,就会发生这种情况。为了避免由此产生的任何问题,您应该实现一种优雅地机制便于关闭套接字连接。特别在当 WebSocket 连接时间较长的情况下,需要实现不时刷新连接(关闭并再次打开连接)的方法,以实现流畅的通信系统。

扩展连接

由于WebSocket具有持久连接的特性,因此需要高可用性,所以服务器应该具有可伸缩性,以满足需要时的高需求。但是,在打开ws连接后,大部分时间它将处于空闲状态。

那么我们应该如何扩展WebSocket后端?

扩展WebSocket后端是一项复杂的任务,它需要持久存储任何服务器节点在出现故障时的连接和传递的消息。此外,考虑开放连接的数量,最好实施横向扩展策略。由于大多数用户可能不经常重新连接,因此基于开放式连接来提高可伸缩性更为有意义。

数据传输模式

在通过WebSocket传输数据时,您可以考虑不同的模式。您可以直接通过WebSockets传输消息,也可以向客户端发送通知,告知消息的可用性。

用于发送Web应用通知的WebSockets

发送应用内通知是WebSockets的常见用例。WebSocket连接仅用于提醒浏览器有新消息可用。

一旦用户收到通知并访问通知页面,应用程序就可以发送HTTP请求来检索消息内容。

所以在这种方法中,WebSocket并不发送实际的消息内容,而是作为一种信令机制来通知前端的通知可用性。

使用WebSocket进行实时数据传输

对于在实时多人游戏或聊天应用程序,需要无延迟地发送数据,因为总是有用户盯着屏幕等待数据。

在此场景中,我们可以通过WebSocket连接直接发送消息数据,以便更快地传递消息。

数据压缩

对于WebSockets,压缩不是经常讨论的话题。但是,如果需要实时发送大量数据,则使用压缩方法是有用的。

但是,要使用WebSockets实现数据压缩,客户端和服务器都需要在这一点上达成一致。

您知道WebSockets提供了数据压缩扩展吗?

当客户端通过在SEC-WebSocket-Extensions头中通告permessage-deflate扩展来发起协商时,服务器必须通过在其响应中回显来确认通告的扩展。

客户端启动:

GET /socket HTTP/1.1
Host: thirdparty.com
Origin: http://example.com
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Extensions: permessage-deflate
Server response:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Access-Control-Allow-Origin: http://example.com
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Extensions: permessage-deflate

WebSockets安全

WebSocket允许无限数量的消息到达服务器。这很容易让攻击者有权执行DoS攻击。

因此,必须使用身份验证机制来加强安全性。常见的用法之一是使用JWT令牌,这样可以更快地验证请求的签名。

此外,使用wss而不是ws是至关重要的,这将保护通信隧道,类似于HTTPS。

浏览器兼容性

WebSocket与几乎所有浏览器都具有良好的浏览器兼容性。

此外,WebSocket还内置了跨域通信。它允许与任何域上的任何方进行通信。这可以通过定义服务器可以与之通信的域进行控制,从而提高安全性。

此外,流行的WebSockets实现(如Socket.IO(NodeJS)或SignalR(.NET))在较旧的浏览器中支持后退到HTTP。

结论

每当您需要客户端和服务器之间更好的低延迟连接时,WebSockets都是您的最佳选择。

然而,WebSockets集成到您现有的Web基础设施中可能会令人沮丧,因为它需要更改架构。此外,您还可以查看Event Sourcing模式,该模式有效地使用WebSocket进行通信。

请在下面的评论中告诉我您想知道的关于WebSockets的更多详细信息。您可以点击这里查看WebSocket连接演示。