WebSocket协议 8 问

42次阅读

共计 3177 个字符,预计需要花费 8 分钟才能阅读完成。

WebSocket 是一种比较新的协议,它是伴随着 html5 规范而生的,虽然还比较年轻,但大多主流浏览器都已经支持。它使用方面、应用广泛,已经渗透到前后端开发的各种场景中。
对 http 一问一答中二式流程的不满,催生了支持双向通信的 WebSocket 诞生。WebSocket 是个不太干净协议。
一、WebSocket 协议只能浏览器发起么?
不是。目前此协议的受众的也不仅仅是 web 开发者。
WebSocket 只是一种协议,它和 http 协议一样,使用类似 okhttp 的组件,可以在任何地方进行调用,甚至可以借助 WebSocket 实现 RPC 框架。
二、WebSocket 和 HTTP 什么关系?
WebSocket 和 http 一样,都是处于 OSI 模型中的最高层:应用层。WebSocket 借助 http 协议进行握手,握手成功后,就会变身为 TCP 通道,从此与 http 不再相见。
使用 netstat 或者 ss,能够看到对应的连接,它与处于抽象层的 socket,在外观上没有区别。
三、WebSocket 和长轮询有什么区别?
长轮询,就是客户端发送一个请求,服务端将一直在这个连接上等待(当然有一个超长的超时时间),直到有数据才返回,它依然是一个一问一答的模式。比如著名的 comted。
WebSocket 在握手成功后,就是全双工的 TCP 通道,数据可以主动从服务端发送到客户端,处于链接两端的应用没有任何区别。
WebSocket 创建的连接和 Http 的长连接是不一样的。由于 Http 长连接底层依然是 Http 协议,所以它还是一问一答,只是 Hold 住了一条命长点的连接而已。
长轮询和 Http 长连接是阻塞的 I /O,但 WebSocket 可以是非阻塞的(具体是多路复用)。
四、如何创建一个连接?
WebSocket 的连接创建是借助 Http 协议进行的。这样设计主要是考虑兼容性,在浏览器中就可以很方便的发起请求,看起来比较具有迷惑性。
下图是一个典型的由浏览器发起的 ws 请求,可以看到和 http 请求长的是非常相似的。但是,它只是请求阶段长得像而已:请求的地址,一般是:ws://\*\*\*,或者是使用了 SSL/TLS 加密的安全协议 wss:,用来标识是 WebSocket 请求。
1、首先,通过 Http 头里面的 Upgrade 域,请求进行协议转换。如果服务端支持的话,就可以切换到 WebSocket 协议。简单点讲:连接已经在那了,通过握手切换成 ws 协议,就是切换了连接的一个状态而已。
1、Connection 域可以认为是与 Upgrade 域配对的头信息。像 nginx 等代理服务器,是要先处理 Connection,然后再发起协议转换的。
Sec-WebSocket-Key 是随机的字符串,服务器端会用这些数据来构造出一个 SHA-1 的信息摘要。如此操作,可以尽量避免普通 HTTP 请求被误认为 WebSocket 协议。
其他的,像 Sec-WebSocket* 字样的头信息,表明了客户端支持的子协议以及其他信息。像 loT 中很流行的 mqtt,就可以作为 WebSocket 的子协议。使用 javascript,可以很容易连接一个 WebSocket 服务端。
<script>
var ws = new WebSocket(‘ws://localhost:80’);
ws.onopen = function () {
console.log(‘ws onopen’);
ws.send(‘from client: hello’);
};
ws.onmessage = function (e) {
console.log(‘ws onmessage’);
console.log(‘from server: ‘ + e.data);
};

</script>
五、如何处理数据?
WebSocket 是通过事件通知的方式运行的。它包含四个事件和两个动作(发送和关闭)。
WebSocket 的事件

事件
钩子
备注

open
onopen
连接建立时触发

message
onmessage
客户端接收服务端数据时触发

error
onerror
通信发生错误时触发

close
onclose
连接关闭时触发

数据可直接通过 Socket.send() 方法进行传输。
通过 chrome 的 Inspect->Network->WS,可以看到页面上的 WebSocket 连接。如图 Opcode 为 2,表明它是一个二进制帧。WebSocket 有类似 tcp 协议的帧格式,在此不做过多解释。
参考:(https://tools.ietf.org/html/r…
心跳
心跳对应的 ping、pong 操作,opcode 分别是 0x9、0xA。收到心跳的一方需要自行更新心跳的更新时间。同使用 Netty,我们到底在开发些什么?介绍的类似,在一些移动环境中,需要更加智能的控制心跳。
六、如何使用 Nginx 做负载均衡?
nginx 官网已经给出了例子。主要是 Upgrade 和 Connection 头的设置。
map $http_upgrade $connection_upgrade {
default upgrade;
” close;
}

location /chat/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
需要注意的是,nginx 做负载均衡,不需要配置 ip_hash 等参数,nginx 天然支持。由于 ip_hash 仅使用 ip 地址的前三个数字做 hash,还有可能造成服务端的不均衡。
七、java 服务端怎么实现?
可以实现 javax.WebSocket 下的包,简单的实现 ws 服务端。目前基本可以通过注解的方式去编写代码,比如 ServerEndpoint。
推荐使用基于 netty 的 netty-socketio 进行服务端的编写。由于使用的是 netty,所以能够在多个层面进行切入,获取一些统计数据,执行一些控制指令。socketio 是一套解决方案,它有多个语言的客户端,并处理了市面上大多数的兼容问题。
八、WebSocket 能干些啥?
通知功能
保持一个长连接,当服务端游新的消息,能够实时的推送到使用方。像知乎的点赞通知、评论等,都可以使用 WebSocket 通信。
某些使用 H5 的客户端,为了简化开发,也会使用 WebSocket 进行消息的通知,由于它是实时推送的,会有更好的用户体验。
数据收集
一些次优级别的数据,比如行为日志、trace、异常执栈收集等,都可以开辟专门的 WebSocket 通道进行传输。这能够增加信息的集中度,并能及时的针对用户的行为进行合适的配置推送。由于大多数浏览器内核都支持,它将使客户端 APM 编程模型变得简单。
加密 && 认证
虽然使用 Fiddler、Charles 等能够抓到很多 WebSocket 包。但如果同时开启 SSL,传输加密后的二进制数据,会大幅增加破解的成本,会安全的多。
反向控制钩子
这个 … 由于是双工长连接,服务端完全可以推送一些钩子命令,甚至直接是代码,在客户端进行执行。比如截个屏,录个音,种个小马。用户只要通过了授权申请,剩下的就随你发挥了。
支付宝偷偷调用你的相机给你拍照的梗,我是相信的。
End
想当年,cometd 的出现,惊为天人,振奋了很久。但技术日新月异,cometd 已经衰老,而 Socket.io 得到了快速发展。WebSocket 经过一段时间的混沌期,规范已经越来越完善,使用也越来越方便,不需要再处理那么多的兼容。
但它的本质,还是新瓶装旧酒,换汤不换药。WebSocket 的发展得益于 HTML5 规范的制定。规范的意义,就是约束厂商们天马行空的实现,以及指明发展的方向。
这当然有典型的反例,那就是 ie。现在,只有一群公认的 **,还坚持在用。

正文完
 0