websocket vs socket性能对比(WebSocket技术解析和应用)
websocket vs socket性能对比(WebSocket技术解析和应用)我们可以看到不同于一般的请求, WebSocket URL 前缀为 ws ,它告诉游览器自己不是 HTTP 请求,而是 WebSocket 请求,此时游览器便会自动对协议进行升级。WebSocket 协议在游览器中的显示是这样的:而 WebSocket 就是针对这种场景而设计的,伴随着HTTP5而出现的它,其实跟HTTP协议没有任何关系(虽然 HTTP1.1 出了一个 keep-alive 的属性,可以将多个请求合并为一个),可以说 WebSocket 是一个全新的,用于客户端与服务端进行长连接的全双工协议。虽然 WebSocket 只需要建立一次就可以让客户端和服务端建立起连接,但是因为是基于 TCP 协议构建的,所以本质上还是要进行三次握手。TCP 本身是持久连接,三次握手和四次挥手就不老调重弹了。而 HTTP 之所以是单向的,是因为规范规定了服务器只能响应请求,而不能主动发送数据。所
原文作者:bigkai
原文地址:https://juejin.im/post/6854573210152009735
在以前的HTTP协议中,如果我们想实现即时消息推送,使用的方法只有两种:
- ajax轮询技术 :客户端每次在指定间隔的时间发送请求,询问服务器是否有新的数据。
- long polling长轮询技术 :客户端向服务器发送一次请求,然后一直处于 pedding 阻塞状态,直到服务器返回数据。
这两种方法都很简单粗暴,唯一的缺点就是只能建立起HTTP连接,然后被动地接收服务端的数据。要是有成千上万的用户在等待消息推送,突然服务器不堪重负而罢工,这就尴尬了。
而 WebSocket 就是针对这种场景而设计的,伴随着HTTP5而出现的它,其实跟HTTP协议没有任何关系(虽然 HTTP1.1 出了一个 keep-alive 的属性,可以将多个请求合并为一个),可以说 WebSocket 是一个全新的,用于客户端与服务端进行长连接的全双工协议。
虽然 WebSocket 只需要建立一次就可以让客户端和服务端建立起连接,但是因为是基于 TCP 协议构建的,所以本质上还是要进行三次握手。
TCP 本身是持久连接,三次握手和四次挥手就不老调重弹了。而 HTTP 之所以是单向的,是因为规范规定了服务器只能响应请求,而不能主动发送数据。所以说 WebSocket 可以看做是HTTP的一个补丁
协议概览WebSocket 协议在游览器中的显示是这样的:
我们可以看到不同于一般的请求, WebSocket URL 前缀为 ws ,它告诉游览器自己不是 HTTP 请求,而是 WebSocket 请求,此时游览器便会自动对协议进行升级。
默认 ws 端口是 80 , wss 端口是 443 。
wss 就是通过 TLS 加密后的 ws 。
不同于一般的HTTP请求, WebSocket 请求添加了几个字段来作为应用,主要的有:
- Sec-WebSocket-Accept 和 Sec-WebSocket-Key :只有当 Sec-WebSocket-Key 的值经过固定算法加密后的数据和响应头里的 Sec-WebSocket-Accept 的值保持一致,该连接才会被认可建立,避免跨协议攻击。
- Sec-WebSocket-Version :这个header字段的值必须为13,因为在它之前有很多测试的版本,比如9、10、11、12,这些版本现在都不被认为是有效的 Sec-WebSocket-Version 。
- Sec-WebSocket-Extensions :该属性存储客户端的扩展,在连接建立时服务端可以针对该扩展进行处理。
- Upgrade :告诉游览器该HTTP协议已经升级到了 WebSocket 。
当客户端对服务端发起 WebSocket 请求时,只有在当前连接已经建立的情况下才能再次建立连接(客户端会对剩下的连接进行排序)。因为 WebSocket 是长连接,所以客户端需要注意限制同一个主机的连接数量,避免脚本通过创建大量的 WebSocket 连接来进行 DDOS 攻击。如果客户端是通过代理访问服务的,那么客户端应该连接到那个代理并且通过这个代理去和服务端建立一个 TCP 连接。
在服务端接收客户端的 WebSocket 请求后,需要对该请求进行解析,获取它的 Sec-WebSocket-Key 、 Sec-WebSocket-Version 、 Sec-WebSocket-Extensions ,还有客户端的源地址、请求的资源名称等。当解析完成后,如果能与服务端连接,那么服务端将会返回给客户端一个响应,响应里面包含 Sec-WebSocket-Accept ,这是与客户端对接的标识符。
当客户端与服务端建立好连接后,两者就可以通信了:
协议结构WebSocket 协议的全局结构大概如下所示,我们来大概解析一下它各个字段的含义:
- FIN :表示这是消息的最后一个字段(设置为1,默认为0)。
- RESV1 / RESV2 / RESV3 :标识是否有扩展协议,如果为1,那么在 EXTEND PAYLOAD 为0的情况下,就会断开 WebSocket 连接。
- OPCODE :标识操作码,这是一个操作帧,用来指示 WebSocket 的动作。默认的标识码有:%x0 :一个连续的消息分片%x1 :一个文本类型的消息分片%x2 :一个二进制类型的消息分片%x3-7:预留给以后的数据帧%x8 :一个关闭连接的指令%x9 :一个ping包%xA :一个pong包%xB-F:预留给以后的控制帧ping包 和 pong包 是用来做心跳检测的。
- MASK :标识数据是否有加掩码 如果设置为1 掩码键必须放在 MASKING KEY 区域。
- PAYED LENGTH :传输的数据的长度(不包括 MASKING-KEY )。
- MASKING-KEY :掩码键。
- PAYLOAD DATA :传输的数据(扩展数据 EXTEND PAYLOAD 应用数据 APPLICATION PAYLOAD )。扩展数据:自己定义的扩展协议。应用数据:基础的数据帧。
HTML5封装好了处理方法,只需要调用其API就可以了。
直接创建一个 WebSocket 对象,然后将 onopen \ onclose 等方法绑定到对象。
相关API可以查看 MDN WEB——API WebSocket 。
后端处理用 Netty 的 WebSocketServerProtocolHandler 举例,当我们创建 WebSocket 服务时,必然要加入一个 WebSocket 协议的处理器,将其协议内容封装为一个便于使用的包装类,在Netty中,我们可以这样定制 WebSocket 服务:
进入 WebSocketServerProtocolHandler 类,可以看到它定义的属性有:
它两个方法有:
一个是 handlerAdded ,它会在Channel连接后回调,每次都会插入 WebSocketServerProtocolHandshakeHandler 。
一个是 decode ,它会针对进行的数据帧进行操作。
在close前要进行 frame.retain(); ,是因为在关闭时需要用到 frame ,在Netty的所有操作都是异步的情况下,这样就可以防止 frame 在没有用完时就被释放掉了。
来看下 WebSocketServerProtocolHandshakeHandler.channelRead 方法:
在 WebSocketServerProtocolHandshakeHandler.channelRead 绑定触发事件时,为了保持兼容性,所以设置了两个,第一个是过时的,下面那个是新的。
对于通过握手器工厂 WebSocketServerHandshakerFactory 创建的 WebSocketServerHandshaker ,我们需要注意它的 handshake 方法。该方法其实就是来发送响应数据的。
它先把跟HTTP聚合,压缩的处理器移除,然后看有没有 HttpRequestDecoder ,如果没有,那就在前面添加 WebSocket 的编解码器。如果有HTTP编解码器,就把编解码器替换成 WebSocket 编解码器,等发送响应成功了,就移除掉 HttpServerCodec 或 HttpResponseEncoder 。
这样处理完之后,就把和HTTP编解码器移除出去了,这样的话就可以保证使用者即使添加了错误的处理器,程序也可以正常执行 WebSocket 连接。
总结WebSocket 协议用于长连接传输数据,本质也不过是定义了一种协议格式,然后往里面放数据。从功能上来说唯一和HTTP的区别就是客户端和服务端是可以相互推送消息的,而非被动。
之前说过 HTTP 1.1 添加了一个 keep-alive 请求头属性,可以作用于长连接。但是这里的长连接和 WebSocket 的长连接不同。 keep-alive 的作用是保持连接,可以让其它的HTTP请求可以复用这个通道,每次HTTP请求还是要携带请求头的。而 WebSocket 的长连接的每一个连接对应一个客户端。
一个很明显的比方就是打电话给客服。 keep-alive 的表示是一方讲完之后就把电话给了自己身后的人,然后身后的人跟客服反映新的问题。 WebSocket 表示一方讲完之后,听完客服的反馈就挂掉了电话,两人就断了联系。
原文作者:bigkai
原文地址:https://juejin.im/post/6854573210152009735