====测试代码:javascript
==index.htmlhtml
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div>
abc
</div>
</body>
<script type="text/javascript" src="js/websocket.js"></script>
<script src="js/client.js"></script>
</html>
==websocket.js前端
var lockReconnect = false; //避免ws重复链接 var ws = null; // 判断当前浏览器是否支持WebSocket var wsUrl = null; var config = {}; function socketLink(set) { config = set; wsUrl = config.url; createWebSocket(wsUrl); //链接ws } function createWebSocket(url) { try { if ('WebSocket' in window) { ws = new WebSocket(url, 'echo-protocol'); } else if ('MozWebSocket' in window) { ws = new MozWebSocket(url, 'echo-protocol'); } else { alert("您的浏览器不支持websocket") } initEventHandle(); } catch (e) { reconnect(url); console.log(e); } } function initEventHandle() { ws.onclose = function() { reconnect(wsUrl); console.log("llws链接关闭!" + new Date().toUTCString()); }; ws.onerror = function() { reconnect(wsUrl); console.log("llws链接错误!"); }; ws.onopen = function() { heartCheck.reset().start(); //心跳检测重置 console.log("llws链接成功!" + new Date().toUTCString()); config.open(ws) }; ws.onmessage = function(event) { //若是获取到消息,心跳检测重置 heartCheck.reset().start(); //拿到任何消息都说明当前链接是正常的 config.msg(event.data,ws) }; } // 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket链接,防止链接还没断开就关闭窗口,server端会抛异常。 window.onbeforeunload = function() { ws.close(); } function reconnect(url) { if (lockReconnect) return; lockReconnect = true; setTimeout(function() { //没链接上会一直重连,设置延迟避免请求过多 createWebSocket(url); lockReconnect = false; }, 2000); } //心跳检测 var heartCheck = { timeout: 10000, //9分钟发一次心跳 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() { //若是超过必定时间还没重置,说明后端主动断开了 console.log("try=close") ws.close(); //这里为何要在send检测消息后,倒计时执行这个代码呢,由于这个代码的目的时为了触发onclose方法,这样才能实现onclose里面的重连方法
//因此这个代码也很重要,没有这个方法,有些时候发了定时检测消息给后端,后端超时(咱们本身设定的时间)后,不会自动触发onclose方法。咱们只有执行ws.close()代码,让ws触发onclose方法
//的执行。若是没有这个代码,链接没有断线的状况下然后端没有正常检测响应,那么浏览器时不会自动超时关闭的(好比谷歌浏览器),谷歌浏览器会自动触发onclose
//是在断网的状况下,在没有断线的状况下,也就是后端响应不正常的状况下,浏览器不会自动触发onclose,因此须要咱们本身设定超时自动触发onclose,这也是这个代码的
//的做用。
}, self.timeout) }, this.timeout) } }
心跳检测的目的时什么呢?java
一个是为了定时发送消息,使链接不超时自动断线,可能后端设了超时时间就会自动断线,因此须要定时发送消息给后端,让后端服务器知道链接还在通消息不能断。node
二来是为了检测在正常还链接着的状况下,判断后端是否正常,若是咱们发了一个定时检测给后端,后端按照约定要下发一个检测消息给前端,这样才是正常的。web
但是若是后端没有正常下发呢,咱们就要设定一下超时要重连了,咱们把重连写在了onclose里面,当时正常链接的状况下,超时未能响应检测消息的状况下,浏览器不会自动断掉触发onclose,因此须要咱们手动触发,也就是在超时里写一个ws.close()让ws关闭触发onclose方法,实现重连。后端
==浏览器会触发onclose的状况是断网断线的状况下,当时正常链接还未断线,浏览器时不会自动触发onclose的,因此就有超时手动触发onclose的必要。浏览器
self.serverTimeoutObj = setTimeout(function() { //若是超过必定时间还没重置,说明后端主动断开了 console.log("try=close") ws.close(); //这里为何要在send检测消息后,倒计时执行这个代码呢,由于这个代码的目的时为了触发onclose方法,这样才能实现onclose里面的重连方法 //因此这个代码也很重要,没有这个方法,有些时候发了定时检测消息给后端,后端超时(咱们本身设定的时间)后,不会自动触发onclose方法。咱们只有执行ws.close()代码,让ws触发onclose方法 //的执行。若是没有这个代码,链接没有断线的状况下然后端没有正常检测响应,那么浏览器时不会自动超时关闭的(好比谷歌浏览器),谷歌浏览器会自动触发onclose //是在断网的状况下,在没有断线的状况下,也就是后端响应不正常的状况下,浏览器不会自动触发onclose,因此须要咱们本身设定超时自动触发onclose,这也是这个代码的 //的做用。 }, self.timeout)
ws.onopen = function() { heartCheck.reset().start(); //心跳检测重置 在open的时候触发心跳检测 console.log("llws链接成功!" + new Date().toUTCString()); config.open(ws) }; ws.onmessage = function(event) { //若是获取到消息,心跳检测重置 heartCheck.reset().start(); //拿到任何消息都说明当前链接是正常的 //若是后端有下发消息,那么就会重置初始化心跳检测,除非超时没下发,那么就会触发onclose
//而后触发重连
config.msg(event.data,ws) };
==client.js服务器
var number=0; var config = { url: 'ws://localhost:8080/', open: (ws) => { function sendNumber() { if (ws.readyState === ws.OPEN) { number = number+1; ws.send(number.toString()); setTimeout(sendNumber, 1000); } } sendNumber() }, msg: (data) => { console.log(data, "msg") } } socketLink(config)
==node 后端测试代码:websocket
#!/usr/bin/env node var WebSocketServer = require('websocket').server; var http = require('http'); var server = http.createServer(function(request, response) { console.log((new Date()) + ' Received request for ' + request.url); response.writeHead(404); response.end(); }); server.listen(8080, function() { console.log((new Date()) + ' Server is listening on port 8080'); }); wsServer = new WebSocketServer({ httpServer: server, // You should not use autoAcceptConnections for production // applications, as it defeats all standard cross-origin protection // facilities built into the protocol and the browser. You should // *always* verify the connection's origin and decide whether or not // to accept it. autoAcceptConnections: false }); function originIsAllowed(origin) { console.log(origin,"origin") // put logic here to detect whether the specified origin is allowed. return true; } wsServer.on('request', function(request) { if (!originIsAllowed(request.origin)) { // Make sure we only accept requests from an allowed origin request.reject(); console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.'); return; } var connection = request.accept('echo-protocol', request.origin); console.log((new Date()) + ' Connection accepted.'); connection.on('message', function(message) { if (message.type === 'utf8') { console.log('Received Message: ' + message.utf8Data); //connection.sendUTF(message.utf8Data); //注释掉是为了测试前端在没后端响应却链接着的状况下,是什么反应。 } else if (message.type === 'binary') { console.log('Received Binary Message of ' + message.binaryData.length + ' bytes'); connection.sendBytes(message.binaryData); } }); connection.on('close', function(reasonCode, description) { console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.'); }); });