- WebSocket介绍
- 构造函数
- 实例属性
- 实例方法
- 事件
- (en-US)">
[close](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close_event)
(en-US) [error](https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket/error_event)
[message](https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket/message_event)
- (en-US)">
[open](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/open_event)
(en-US)
- (en-US)">
- 参考
- 使用示例、问题
WebSocket介绍
1、什么是WebSocket
WebSocket 协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于[服务器推送技术]的一种。
WebScoket是一种让客户端和服务器之间能进行双向实时通信的技术。它是HTML最新标准HTML5的一个协议规范,本质上是个基于TCP的协议,它通过HTTP/HTTPS协议发送一条特殊的请求进行握手后创建了一个TCP连接,此后浏览器/客户端和服务器之间便可以通过此连接来进行双向实时通信。
2、为什么要用WebSocket?
1)一直以来,HTTP协议是无状态、单向通信的,即客户端请求一次,服务器回复一次。如果想让服务器消息及时下发到客户端,需要采用类似于轮询的机制,即客户端定时频繁的向服务器发出请求,这样效率很低,而且HTTP数据包头本身的字节量较大,浪费了大量带宽和服务器资源;最典型的场景就是聊天室。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)
2)为提高效率,出现了AJAX/Comet技术,它实现了双向通信且节省了一定带宽,但仍然需要发出请求,本质上仍然是轮询;
3)新一代HTML标准HTML5推出了WebSocket技术,它使客户端和服务器之间能通过HTTP协议建立TCP连接,之后便可以随时随地进行双向通信,且交换的数据包头信息量很小;
3、如何使用WebSocket?
在支持WebSocket的浏览器中,创建Socket之后,通过onopen、onmessage、onclose、onerror四个事件的实现来处理Socket的响应;
4、WebSocket与HTTP、TCP的关系
WebSocket和HTTP都属于应用层协议,且都是基于TCP的,它们的send函数最终也是通过TCP系统接口来做数据传输。那么WebSocket和HTTP的关系呢?WebSocket在建立握手连接时,数据是通过HTTP协议传输的,但是在连接建立后,真正的数据传输阶段则不需要HTTP协议的参与。它们之间的关系如下图:
5、什么情况下使用WebSocket?
如果游戏需要同时支持手机端、Web端,那毫无疑问应该使用WebSocket,现在各个平台都提供了相应的WebSocket实现。如果游戏不需要支持Web端,且对实时性要求比较高,如多人射击、MMORPG之类,那么使用TCP/UDP结合的原生Socket会比较好。
6、SocketIO
WebSocket是HTML5最新提出的规范,虽然主流浏览器都已经支持,但仍然可能有不兼容的情况,为了兼容所有浏览器,给程序员提供一致的编程体验,SocketIO将WebSocket、AJAX和其它的通信方式全部封装成了统一的通信接口,也就是说,我们在使用SocketIO时,不用担心兼容问题,底层会自动选用最佳的通信方式。因此说,WebSocket是SocketIO的一个子集。
WebSocket 是浏览器提供的对象,同时也是一个构造函数。用于创建和管理 WebSocket 连接,以及可以通过该连接发送和接收数据的 API。
构造函数
**WebSocket()**
构造函数会返回一个 WebSocket
对象。
语法
var aWebSocket = new WebSocket(url [, protocols]);
参数
url
要连接的URL;这应该是WebSocket服务器将响应的URL。
protocols
可选 一个协议字符串或者一个包含协议字符串的数组。这些字符串用于指定子协议,这样单个服务器可以实现多个WebSocket子协议(例如,您可能希望一台服务器能够根据指定的协议(protocol
)处理不同类型的交互)。如果不指定协议字符串,则假定为空字符串。
Demo 创建实例
<script type="text/javascript">
// 浏览器提供 WebSocket 对象
var ws = new WebSocket('ws://localhost:3000')
// 发送
ws.onopen = function () {
ws.send('hello world')
}
// 接收
ws.onmessage = function (mes) {
alert(mes.data)
if (mes.data === 'hello world') {
ws.close()
}
}
</script>
实例属性
onopen 连接成功时
用于指定连接成功后的回调函数。
ws.onopen = function () {
ws.send('hello world')
}
onerror 连接失败时
onmessage 接收信息时
指定从服务器接收信息时的回调函数。
ws.onmessage = function (mes) {
alert(mes.data)
if (mes.data === 'hello world') {
ws.close()
}
}
onclose 连接关闭时
用于指定连接关闭后的回调函数。
实例方法
close() 关闭连接
send() 发送数据
事件
使用 addEventListener()
或将一个事件监听器赋值给本接口的 on_eventname_
属性,来监听下面的事件。
[close](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close_event)
(en-US)
当一个 WebSocket
连接被关闭时触发。也可以通过 [onclose](https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket/onclose)
属性来设置。
[error](https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket/error_event)
当一个 WebSocket
连接因错误而关闭时触发,例如无法发送数据时。也可以通过 [onerror](https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket/onerror)
属性来设置.
[message](https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket/message_event)
当通过 WebSocket
收到数据时触发。也可以通过 [onmessage](https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket/onmessage)
属性来设置。
[open](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/open_event)
(en-US)
当一个 WebSocket
连接成功时触发。也可以通过 [onopen](https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket/onopen)
属性来设置。
// Create WebSocket connection.
const socket = new WebSocket('ws://localhost:8080');
// Connection opened
socket.addEventListener('open', function (event) {
socket.send('Hello Server!');
});
// Listen for messages
socket.addEventListener('message', function (event) {
console.log('Message from server ', event.data);
});
WebSocket协议本身不要求同源策略(Same-origin Policy),也就是某个地址为[http://a.com](http://a.com)
的网页可以通过WebSocket连接到ws://b.com
。但是,浏览器会发送Origin
的HTTP头给服务器,服务器可以根据Origin
拒绝这个WebSocket请求。所以,是否要求同源要看服务器端如何检查。
参考
https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket
使用示例、问题
WebSocket重连
心跳就是客户端定时的给服务端发送消息,证明客户端是在线的, 如果超过一定的时间没有发送则就是离线了。
它像心跳一样每隔固定时间发一次,以此来告诉服务器,这个客户端还活着。事实上这是为了保持长连接,至于这个包的内容,是没有什么特别规定的,不过一般都是很小的包,或者只包含包头的一个空包。
在 TCP 的机制里面,本身是存在有心跳包的机制的,也就是 TCP 的选项:SO_KEEPALIVE 。系统默认是设置的2小时的心跳频率。但是它检查不到机器断电、网线拔出、防火墙这些断线。而且逻辑层处理断线可能也不是那么好处理。一般,如果只是用于保活还是可以的。
心跳包一般来说都是在逻辑层发送空的 echo 包来实现的。下一个定时器,在一定时间间隔下发送一个空包给客户端,然后客户端反馈一个同样的空包回来,服务器如果在一定时间内收不到客户端发送过来的反馈包,那就只有认定说掉线了。
在长连接下,有可能很长一段时间都没有数据往来。理论上说,这个连接是一直保持连接的,但是实际情况中,如果中间节点出现什么故障是难以知道的。更要命的是,有的节点(防火墙)会自动把一定时间之内没有数据交互的连接给断掉。在这个时候,就需要我们的心跳包了,用于维持长连接,保活。
心跳检测步骤:
- 客户端每隔一个时间间隔发生一个探测包给服务器
- 客户端发包时启动一个超时定时器
- 服务器端接收到检测包,应该回应一个包
- 如果客户机收到服务器的应答包,则说明服务器正常,删除超时定时器
- 如果客户端的超时定时器超时,依然没有收到应答包,则说明服务器挂了
// 前端解决方案:心跳检测
var heartCheck = {
timeout: 30000, //30秒发一次心跳
timeoutObj: null,
serverTimeoutObj: null,
reset: function(){
clearTimeout(this.timeoutObj);
clearTimeout(this.serverTimeoutObj);
return this;
},
start: function(){
var self = this;
this.timeoutObj = setTimeout(function(){
//这里发送一个心跳,后端收到后,返回一个心跳消息,
//onmessage拿到返回的心跳就说明连接正常
ws.send("ping");
console.log("ping!")
self.serverTimeoutObj = setTimeout(function(){//如果超过一定时间还没重置,说明后端主动断开了
ws.close(); //如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
}, self.timeout);
}, this.timeout);
}
}
使用 reconnecting-websocket.min.js
库进行重连