WebSocket
HTML5开始提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议。它基于TCP传输协议,并复用HTTP的握手通道。
简单来说,WebSocket就是一种实现双向通信的长连接方案,双方可以随时互发消息,常用于聊天、协同编辑等场景。除了WebSocket,常用的长连接方案还有一下几种:
-
Server-Sent Events (SSE): 客户端通过HTTP请求与服务器建立连接,可以主动向客户端推送消息。适合单向通讯场景。
-
短轮询(Short Polling): 原理非常简单,客户端每隔几秒发一个请求,询问服务器是否有新消息。
-
长轮询(Long Polling,comet): 和短轮询差不多,但在返回前会阻塞客户端向服务器发送一个HTTP请求,服务器一直保持连接处于挂起状态。
-
iframe流:利用iframe的长连接特性,从服务器实时获取数据并推送到客户端页面。同样适用于实时推送场景,维护难度大。
可以看出这几种方案都是客户端主动发起连接,服务器在这里是完全被动的。不管是长轮询还是短轮询其实都非常耗费资源,需要服务器有足够的处理能力和足够高的并发。而且HTTP是非状态性的,除了真正的数据部分外,服务器和客户端还要大量交换 HTTP header,会浪费非常多的性能和流量。
而Websocket只需要建立一次连接,它是持久的,服务端会一直持有你的信息,直到交互完成连接关闭,实现了真正的长连接。
HTTP的另一个主要缺点是它是半双工的。半双工通信的特点是在同一时刻数据只能单向流动,客户端的请求流向服务器,服务器的响应流向客户端,始终是一来一回的过程。WebSocket是全双工的,客户端和服务器的数据可以同时双向流动,彻底解决了服务器的被动问题。
作用
WebSocket的优点:支持双向通信,更灵活,更高效,可扩展性更好。
WebSocket的主要问题是容易断线,如果一段时间不发数据,很可能会失效。所以一定要在代码里处理好重连的问题防止翻车,一般会用心跳( Ping/Pong)保活来维持中间网络的连接状态。至于浏览器兼容问题,现在基本可以忽略不计了,IE11也可以支持WebSocket。
应用
全量API网址:WebSocket - Web APIs | MDN
一般也不会直接用API,只需了解几个主要事件
readyState
readyState
属性返回实例对象的当前状态,共有四种:
-
CONNECTING:值为0,表示正在连接。
-
OPEN:值为1,表示连接成功,可以通信了。
-
CLOSING:值为2,表示连接正在关闭。
-
CLOSED:值为3,表示连接已经关闭,或者打开连接失败。
onopen
连接成功后的回调函数
onclose
关闭成功后的回调函数
onmessage
收到服务器推送后的回调函数
send
向服务器推送
onerror
出现错误时的回调函数
socket.io
超好用的库,官方网址:Socket.IO
-
JavaScript:Socket.IO · GitHub
socketIO支持 WebSocket 协议,但要注意的是它**不是真正的WebSocket 实现,WebSocket 客户端无法连接SocketIO 服务器, SocketIO 客户端也无法连接普通 WebSocket 服务器!**双方都需要使用SocketIO 。
另外,SocketIO默认支持两种连接方式,优先使用polling,如果想使用websocket需要指定transports参数。
// 以下格式无法连接,默认使用polling连接
const socket = io("ws://echo.websocket.org");
// 指定websocket的连接方式
var socket = io('ws://localhost:4000', {transports: ['websocket']}); // 只用websocket
var socket = io("ws://localhost:4000", {
transports: ["websocket", "polling"] // websocket优先
});
socketIO的特点
-
自动回退:如果无法建立 WebSocket 连接,连接将回退到 HTTP 长轮询。
-
自动重连:内含心跳保活,定期检查连接状态,意外中断时自动发起重连
-
自动缓冲:当连接中断时,所有事件都会被延迟直到重连(不过可能会导致重连时发生大量事件,socketIO提供
volatile
解决这个问题) -
多路复用:允许通过命名空间区分拆分应用程序的逻辑
事件
发送(emit)和监听(on)
SocketIO模仿Node.js的EventEmitter,将发送事件称为emit
。emit的第一个参数指定事件名称,其余参数指定事件参数,接收端通过on监听事件。它支持大部分数据类型,自动进行JSON.stringify()
转换。只有部分类型需要手动序列化,具体参考发送事件 | Socket.IO
emit
还可以传递回调函数,在SocketIO称为确认。只需将回调函数作为emit的参数传递即可。
// 服务器
socket.emit("update item", "1", { name: "updated" }, (response) => {});
// 客户端
socket.on("update item", (arg1, arg2, callback)
可以为emit指定超时时间,如socket.timeout(5000).emit
。对于不想缓冲到重连成功时推送,而是仅在服务器连接时发送的数据(比如实时上报位置等),使用socket.volatile.emit
广播(broadcast)和房间(room)
一般的emit是同时推送给所有客户端,broadcast可以推送给除了发送端的其余客户端。
socket.broadcast.emit("hello", "world");
Room 是一个服务器端概念,允许将数据广播到客户端的子集。
总结
基于 HTTP 的技术往往在服务器上占用更多资源,而 WebSocket 在服务器上的占用空间非常小。在实时、持续通信的场景,WebSocket 是更好的选择。
总的来说,WebSocket 和HTTP的选择依赖于业务需要的更新频率,如果需要以秒为单位更新,WebSocket更好。如果更新频率延长到半小时,则建议用短轮询。