websocket
websocket
一、websocket是什么
- websocket是一种网络通信协议,是HTML5开始提供的一种在单个TCP连接上进行全双工通讯的协议。
- 在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接, 并进行双向数据传输。(维基百科)
二、websocket与http网络协议的联系与区别
联系:
- 都是基于TPC的;
- 都是可靠性传输协议;
- 都是应用层协议;
- WebSocket在建立握手时,数据是通过HTTP传输的。但是建立之后,在真正传输时候是不需要HTTP协议的;
区别:
http是一种无状态的、无连接的单向的应用层协议、采用请求/响应模式。即,通信请求只能由客户端发起,服务端对请求做出应答处理。
- 无状态:协议对于事务处理没有记忆能力;在传统的方式上(long pull 与 ajax 轮询),要不断的建立,关闭HTTP协议,由于HTTP是非状态性的,每次都要重新传输
identity info
(鉴别信息),来告诉服务端你是谁。降低了效率,消耗了过多的流量/时间。 - 无连接:每次连接只处理一个请求,服务器处理完客户的请求,并收到客户的应答后,即断开连接。
- 单向:每次请求(request)都要由客户端(如 浏览器)主动发起,服务端进行处理后返回response结果,而服务端不能主动向客户端发送数据。
websocket:最大的特点就是服务端可以主动向客户端推送消息,客户端也可以主动向服务端发送消息。应用场景:
- 即时聊天通信
- 多玩家游戏
- 在线协同编辑/编辑
- 实时数据流的拉取与推送
- 体育/游戏实况
- 实时地图位置
- 游戏应用程序:在游戏应用程序中,你可能会注意到,服务器会持续接收数据,而不会刷新用户界面。屏幕上的用户界面会自动刷新,而且不需要建立新的连接,因此在
WebSocket
游戏应用程序中非常有帮助
三、long pull 与ajax 轮询
从下面可以看出其实这两种方式,都是在不断地建立HTTP连接,然后等待服务端处理,可以体现HTTP协议非常消耗资源与被动性。
ajax轮询:
ajax轮询的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。
情景再现:
客户端:啦啦啦,有没有新信息(Request)
服务端:没有(Response)
客户端:啦啦啦,有没有新信息(Request)
服务端:没有。。(Response)
客户端:啦啦啦,有没有新信息(Request)
服务端:你好烦啊,没有啊。。(Response)
客户端:啦啦啦,有没有新消息(Request)
服务端:好啦好啦,有啦给你。(Response)
客户端:啦啦啦,有没有新消息(Request)
服务端:。。。。。没。。。。没。。。没有(Response) -loop
long pull:
其实原理跟 ajax轮询
差不多,都是采用轮询的方式,不过采取的是阻塞模型(一直打电话,没收到就不挂电话),也就是说,客户端发起连接后,如果没消息,就一直不返回Response给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始。
情景再现:
客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request)
服务端:额。。 等待到有消息的时候。。来 给你(Response)
客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request) -loop
总结:ajax轮询 需要服务器有很快的处理速度和资源。(速度)long poll 需要有很高的并发,也就是说同时接待客户的能力。(场地大小)
websocket
当服务器完成协议升级后(HTTP->Websocket),服务端就可以主动推送信息给客户端啦。
情景再现:
客户端:啦啦啦,我要建立Websocket协议,需要的服务:chat,Websocket协议版本:17(HTTP Request)
服务端:ok,确认,已升级为Websocket协议(HTTP Protocols Switched)
客户端:麻烦你有信息的时候推送给我噢。。
服务端:ok,有的时候会告诉你的。
服务端:balabalabalabala
服务端:balabalabalabala
服务端:哈哈哈哈哈啊哈哈哈哈
服务端:笑死我了哈哈哈哈哈哈哈
总结:websocket只需要经过一次HTTP请求,就可以做到源源不断的信息传送了。(在程序设计中,这种设计叫做回调,即:你有信息了再来通知我,而不是我傻乎乎的每次跑来问你 )
四、websocket的其他特点
- 建立在 TCP 协议之上,服务器端的实现比较容易。
- 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
- 数据格式比较轻量,性能开销小,通信高效。
- 可以发送文本,也可以发送二进制数据。
- 没有同源限制,客户端可以与任意服务器通信。
- 协议标识符是
ws
(如果加密,则为wss
),服务器网址就是 URL。
五、创建websocket对象
1 | // url, 指定连接的 URL |
六、websocket属性
*
七、websocket事件
事件 | 事件处理程序 | 描述 |
---|---|---|
open | Socket.onopen | 连接建立时触发 |
message | Socket.onmessage | 客户端接收服务端数据时触发 |
error | Socket.onerror | 通信发生错误时触发 |
close | Socket.onclose | 连接关闭时触发 |
八、websocket方法
方法 | 描述 |
---|---|
Socket.send() | 使用连接发送数据 |
Socket.close() | 关闭连接 |
九、websocket实例
最基本写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17var ws = new WebSocket(url);
//连接发生错误的回调方法
ws.onerror = function () {
console.log("socket连接失败")
}
// 连接成功建立的回调方法
ws.onopen = function () {
console.log("socket连接已打开")
}
// 接收到消息的回调方法
ws.onmessage = function (e) {
console.log("客户端接收服务端数据时触发");
}
// 连接关闭的回调方法
ws.onclose = function () {
console.log("socket连接已关闭");
}稍加复杂
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36var ws = null; //websocket实例
var wsUrl = "ws://localhost:8888/websocket/name";
//1.第一步页面初始化,先调用createWebSocket函数,目的是创建一个websocket的方法:new WebSocket(wsUrl);因此封装成函数内如下代码:
function createWebsocket(url) {
//判断当前浏览器是否支持WebSocket
if (window.WebSocket) {
ws = new WebSocket(url);
init();
} else {
console.log("浏览器不支持WebSocket!")
}
}
//2.第二步调用init方法,该方法内把一些监听事件封装如下:
function init() {
//连接发生错误的回调方法
ws.onerror = function () {
console.log("socket连接失败")
}
// 连接成功建立的回调方法
ws.onopen = function () {
console.log("socket连接已打开")
}
// 接收到消息的回调方法
ws.onmessage = function (e) {
console.log("客户端接收服务端数据时触发");
}
// 连接关闭的回调方法
ws.onclose = function () {
console.log("socket连接已关闭");
}
// 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
websocket.close();
};
}
createWebsocket();
心跳重连机制
一、概念:
在使用websocket的过程中,有时候会遇到网络断开的情况,比如信号不好,或者网络临时性关闭,但是在网络断开的时候服务器端并没有触发onclose的事件。这样会有:服务器会继续向客户端发送多余的链接,并且这些数据还会丢失。所以就需要一种机制来检测客户端和服务端是否处于正常的链接状态。因此就有了websocket的心跳了。还有心跳,说明还活着,没有心跳说明已经挂掉了。
二、心跳包概念:
它像心跳一样每隔固定时间发一次,以此来告诉服务器,这个客户端还活着。事实上这是为了保持长连接,至于这个包的内容,是没有什么特别规定的,不过一般都是很小的包,或者只包含包头的一个空包。
三、心跳机制概念:
心跳机制是每隔一段时间会向服务器发送一个数据包,告诉服务器自己还活着,同时客户端会确认服务器端是否还活着,如果还活着的话,就会回传一个数据包给客户端来确定服务器端也还活着,否则的话,有可能是网络断开连接了。需要重连~
四、实现心跳检测的思路:
大白话:
每隔一段固定的时间,向服务器端发送一个ping数据,如果在正常的情况下,服务器会返回一个pong给客户端,如果客户端通过
onmessage事件能监听到的话,说明请求正常,这里我们使用了一个定时器,每隔xx秒的情况下,如果是网络断开的情况下,在指定的时间内服务器端并没有返回心跳响应消息,因此服务器端断开了,因此这个时候我们使用ws.close关闭连接,在一段时间后(在不同的浏览器下,时间是不一样的,firefox响应更快),可以通过 onclose事件监听到。因此在onclose事件内,我们可以调用 reconnect事件进行重连操作。
整理一下:
- 客户端每隔一段固定的时间发送一个探测包给服务器。
- 客户端发包时启动一个超时定时器。
- 服务器端接收到检测包,应该回应一个包。
- 如果客户机收到服务器的应答包,则说明服务器正常,删除超时定时器。
- 如果客户端的超时定时器超时,依然没有收到应答包,则说明服务器挂了。
心跳检测代码:
1 | //心跳检测 |
WebSocket心跳重连机制完整代码:
参考文献:自定义事件——Event和CustomEvent、自定义事件对象 CustomEvent
1 | // ws.js |
1 | // index.html |