介绍
免责声明:1周前,我对Websockets了解不多,我在WebSockets上获得的所有经验都是我使用JS框架开发了2016年的聊天应用程序,该框架试图成为Ruby在Rauds实施中,称为SailsJS,因此,我决定研究这项技术并消耗多个资源,这些资源将在此博客文章和每个部分中链接。
WebSockets是处理全双工通信(或双向通信)的一种方式,它们对于构建需要实时数据(例如聊天应用程序或库存仪表板)的应用程序非常有用。 WebSockets以相比以前的解决方案来到:
- 投票
最简单的解决方案包括以设定的间隔进行HTTP请求
- Long Polling
绝望的时代要求拼命措施
这只是意味着客户端将调用HTTP端点,并且服务器不会立即解决请求,但要等到需要向客户端传递消息,此方法有效,但有很多缺点,例如超时,延迟,延迟,从一开始就听起来很刺耳。
- HTTP流和服务器量事件(SSE)
这些解决方案运行良好,但更适合单向通信,因为在服务器需要向其用户发送通知的用例中,例如:创建了一个新帖子,等等。
。 WebSocket服务器可以(但不必)与普通的HTTP服务器一起运行,因为WebSockets使用了其他协议ws
和wss
(对于HTTPS
(例如HTTPS
)。
WebSockets从用户的角度来看是一个简单的协议,因为它们仅由3个不同的事件组成:
-
open
-
关闭
-
消息
连接(握手)
所有WebSocket连接都将从客户端始终发起的握手开始,如果成功将连接从HTTP
升级到ws/wss
协议。
注意:
-
客户端可以创建尽可能多的连接。
-
设置WebSocket连接时,无法在浏览器中设置任意标头。
以下所有行为都是由W3的HTML5 Websocket API规范在浏览器级别定义的,并通过RFC 6455在协议级别定义了“ WebSocket协议”,并且当然是由系统管理的(JavaScript中的浏览器),并且您可以DON需要担心,以下是出于信息目的
握手过程包括以下步骤:
- 客户端在标准
http(s)
请求下发送以下标题
HTTP GET ws://127.0.0.1:8000/ 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: hXcK3GM6B2r6yj/4L0Vuqw==
Origin: http://localhost:3000
Sec-WebSocket-Version: 13
Sec-WebSocket-Key
此标头是一个随机的字符串,服务器将采用这些字节并附加special string 58EAFA5-E914-47DA-95CA-C5AB0DC85B11
,将其放置并在base64中进行编码,然后返回Sec-WebSocket-Accept
header。
如果服务器接受连接,它将以以下标题响应:
HTTP GET ws://127.0.0.1:8000/ 101 Switching Protocols
Connection: Upgrade
Sec-WebSocket-Accept: rXzSb8mhB4ljxko8kbyiCohJ4Fc=
Upgrade: websocket
仅当服务器:
时,该连接才被视为成功-
用状态代码
101
回复
-
包括
Connection
标头,带有valueUpgrade
-
包括
Upgrade
标头,带有valuewebsocket
JavaScript中的示例
所有这些复杂性已经由浏览器处理,建立Websocket连接非常简单:
const socket = new WebSocket('wss://example.com/socket');
socket.addEventListener('open', (event) => {
console.log('WebSocket connection established!');
});
消息交换
建立了连接后,它将在服务器和客户端中保持活力。
服务器和客户端都可以随时在text
或binary
数据(BLOB或ARRAYBUFFER对象)中发送消息。在二进制数据中发送消息的速度更快,但速度不大,因为发送text
数据需要UTF-8转换,但是如今此过程非常快。
JavaScript中的示例
发送消息很简单:
socket.send(message); // e.g: '{a: 1]'
并收到它们:
socket.addEventListener('message', (event) => {
console.log('Received message:', event.data);
});
关闭
客户端或服务器可以随时关闭WebSocket连接。
从客户的角度来看
这很简单:
socket.close();
从服务器:
这可能会有些棘手,因为如果您等待客户断开连接,客户可能会悬挂并最终会引起性能问题,可能会有问题。解决此问题的两种方法是使用以下代码(从Stackoverflow获取):
- 硬关闭
使用这种方法,我们只是终止连接而不等待客户断开连接
// Soft close
socket.close();
process.nextTick(() => {
if ([socket.OPEN, socket.CLOSING].includes(socket.readyState)) {
// Socket still hangs, hard close
socket.terminate();
}
});
- 软关闭
在这里,我们给客户一些时间在终止之前断开连接
// First sweep, soft close
wss.clients.forEach((socket) => {
socket.close();
});
setTimeout(() => {
// Second sweep, hard close
// for everyone who's left
wss.clients.forEach((socket) => {
if ([socket.OPEN, socket.CLOSING].includes(socket.readyState)) {
socket.terminate();
}
});
}, 10000);
身份和安全性
不幸的是,WebSocket协议并没有提出一种处理身份验证的方法,但是随着时间的推移,许多人提出了许多解决方案。
如前所述,在WebSockets中,无法在浏览器中不支持发送自定义标头,这是在现代网络中使用标记为JWT
的最传统方式。
这使我们提供了像in this document的解决方案:
- 将凭据作为WebSocket连接中的第一条消息
此方法完全可靠,但是将身份验证移至应用程序层,如果您不够小心,可以将您的泄漏信息暴露于泄漏信息。另一个负数是,它允许每个人与服务器打开WebSocket连接。
- 在Websocket URI中添加凭据作为查询参数
另一种方法是在打开WebSocket连接时将令牌作为查询参数发送,例如:wss://localhost:3000/ws?token=myToken
的缺点是,此信息可能最终出现在您系统的logs
中,一种减轻此信息的方法是使用此信息。一次性代币,但行业倾向于将这种风险视为无法接受的。
- 在Websocket URI 的域上设置cookie
,只要您的WebSocket服务器与http
服务器在同一域中运行,则此解决方案也是可靠的,如果不是这种情况,则不会有可能在不同的起源。
但是有两种方法可以克服这个问题:
1. Move the WebSocket server to a subdomain of the main `http` server, e.g: `websocket.example.com`
2. Use an `iframe` running on the same WebSocket domain to set the cookie
如果您采用这种方法,则需要考虑到今天 [2023-07-18 TUE] Google Chrome chrome chrome chrome否则将使用这种方法还设置SameSite
和Secure
属性,例如
document.cookie = 'my_token=token; SameSite=None; Secure;'
跨站点Websocket劫持
要注意基于Cookie的身份验证解决方案的一件事是,Websocket连接不受same-origin policy
的限制,因此它打开了称为Cross-Site WebSocket Hijacking
的向量攻击。
这意味着,如果您为websocket domain
设置了一个cookie,则该cookie在连接到服务器时都会发送到网站,无论是哪个网站,Websocket Connection的网站都可以打开安全问题,因为MALIGN网站可以利用用户已经优势有一个cookie设置用于域,还可以使用WebSocket服务器订阅消息。
减轻此问题的一种方法是在建立连接之前始终验证客户端的Origin
。
您可以在此article
中提供更多信息资源
-
二进制与短信
https://stackoverflow.com/questions/7730260/binary-vs-string-transfer-over-a-stream -
关闭服务器中的WebSocket连接
https://stackoverflow.com/questions/41074052/how-to-terminate-a-websocket-connection/49791634#49791634 -
Websockets中的身份验证
https://websockets.readthedocs.io/en/stable/topics/authentication.html -
跨站点Websocket劫持
https://christian-schneider.net/CrossSiteWebSocketHijacking.html -
WebSocket框架协议
https://sookocheff.com/post/networking/how-do-websockets-work/