websocket原理
内容:
1.websocket介绍
2.websocket原理
3.websocket实例
参考:https://www.cnblogs.com/jingmoxukong/p/7755643.html
1.websocket介绍
(1)什么是WebSocket
WebSocket 是一种网络通信协议。RFC6455 定义了它的通信标准。
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
另外基于多线程或多进程的服务器无法适用于 WebSockets,因为它旨在打开连接,尽可能快地处理请求,然后关闭连接。任何实际的 WebSockets 服务器端实现都需要一个异步服务器
(2)websocket的特点
- 双向
- 性能高
2.websocket原理
(1)websocket在服务端和客户端
在客户端,没有必要为 WebSockets 使用 JavaScript 库。实现 WebSockets 的 Web 浏览器将通过 WebSockets 对象公开所有必需的客户端功能(主要指支持 Html5 的浏览器)
而WebSocket 在服务端的实现非常丰富。Node.js、Java、C++、Python 等多种语言都有自己的解决方案
node中:
而Java 的 web 一般都依托于 servlet 容器(Tomcat、Jetty、Resin),此外Spring框架对 WebSocket 也提供了支持
(2)websocket核心原理
关于websocket:
- 建立在 TCP 协议之上,服务器端的实现比较容易。
- 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
- 数据格式比较轻量,性能开销小,通信高效。
- 可以发送文本,也可以发送二进制数据。
- 没有同源限制,客户端可以与任意服务器通信。
- 协议标识符是
ws
(如果加密,则为wss
),服务器网址就是 URL
底层协议包装:
(3)HTTP 和 WebSocket 有什么关系?
Websocket 其实是一个新协议,跟 HTTP 协议基本没有关系,只是为了兼容现有浏览器的握手规范而已,也就是说它是 HTTP 协议上的一种补充
而HTTP2.0(未来推行,现在还未推行)中其实要实现全双工通信,故也有人说websocket最终将被HTTP2.0替代
(4)WebSocket属性
WebSocket是前台的东西,是HTML5带的一种东西:
- 只有前台有WebSocket这个东西
- 后台没有,后台有Socket
3.websocket实例
(1)原生websocket
虽然原生的websocket实现起来麻烦,写起来麻烦,但是我们还是要对其有所了解
1 原生WebSocket: 2 i.net模块 3 ii.流程 4 a.握手 5 C:version:13、sec-websocket-key:xxxxx、sha1(key+mask)=>base64 6 S:101 Switching Protocols、sec-websocket-accept: base64 7 C <-> S 8 9 Client: 10 onopen 11 onmessage 12 onclose 13 14 Server: 15 net.createServer(sock=>{}); 16 sock.once('data', 握手); 17 sock.on('data', 数据请求); 18 sock.on('end'); 19 20 b.数据帧解析
原生websocket使用实例如下:
1 // 服务端 2 const http = require('http'); // HTTP 非原生Socket 3 const net = require('net'); // TCP 原生Socket 4 const crypto = require('crypto'); 5 6 /* 7 let server=http.createServer((req, res)=>{ 8 console.log('连接'); 9 }); 10 server.listen(8080); 11 */ 12 13 let server = net.createServer(sock => { 14 console.log('连接'); 15 16 //数据过来 — 握手只有一次 17 sock.once('data', function(data){ 18 console.log('hand shake start...'); 19 20 // 下面都是解析HTTP头 21 let str = data.toString(); 22 let lines = str.split('\r\n'); 23 24 //舍弃第一行和最后两行 25 lines = lines.slice(1, lines.length - 2); 26 //切开headers中的每一项key-value数据 27 let headers = {}; 28 lines.forEach(function(line) { 29 let [key, val] = line.split(': '); 30 headers[key.toLowerCase()] = val; 31 }); 32 console.log(headers); 33 34 if (headers['upgrade'] !== 'websocket') { 35 console.log('其他协议: ', headers['upgrade']); 36 sock.end(); 37 } else if (headers['sec-websocket-version'] !== '13') { 38 console.log('版本不对', headers['sec-websocket-version']); 39 sock.end(); 40 } else { 41 let key = headers['sec-websocket-key']; 42 let mask = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; 43 44 //sha1(key+mask)->base64=>client 45 let hash = crypto.createHash('sha1'); 46 hash.update(key + mask); 47 let key2 = hash.digest('base64'); 48 sock.write(`HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ${key2}\r\n\r\n`); 49 50 console.log('hand shake end'); 51 52 //真正的数据 53 sock.on('data', data => { 54 console.log('有数据'); 55 console.log(data); 56 }); 57 } 58 }); 59 60 //断开了 61 sock.on('end', () => { 62 console.log('客户端已断开'); 63 }); 64 }); 65 server.listen(8080);
1 // 客户端 2 <!DOCTYPE html> 3 <html> 4 <head> 5 <meta charset="utf-8"> 6 <title></title> 7 <script> 8 let sock=new WebSocket('ws://localhost:8080/'); 9 10 // 原生websocket中没有sock.emit 自己用sock.send封装一个sock.emit: 11 sock.emit=function (name, ...args){ 12 alert(JSON.stringify({name, data: [...args]})); 13 sock.send(JSON.stringify({name, data: [...args]})); 14 }; 15 16 // 连上了 17 sock.onopen=function (){ 18 alert('连接上了'); 19 20 sock.emit('msg', 12, 5); 21 }; 22 23 // 有数据 24 sock.onmessage=function (){ 25 alert('有消息过来'); 26 }; 27 28 // 断开了 29 sock.onclose=function (){ 30 alert('断了'); 31 }; 32 </script> 33 </head> 34 <body> 35 36 </body> 37 </html>
(2)socket.io实现websocket
实现套路:
1 // 服务端: 2 const http = require('http') 3 const io = require('socket.io') 4 5 let httpServer = http.createServer(function (req, res) { 6 7 }) 8 httpServer.listen(8080) 9 10 let wsServer = io.listen(httpServer) 11 12 wsServer.on('connection', function (sock) { 13 // sock.emit() --> 发送 14 // sock.on() --> 接收 15 // sock.on('connection') --> 开始连接 16 // sock.on('disconnect') --> 断开连接 17 })
1 // 客户端: 2 let sock = io.connect('ws://localhost:8080/') 3 4 sock.emit() // 发送 5 sock.on() // 接收 6 sock.on('connect') // 开始连接 7 sock.on('disconnect') // 断开连接
(3)socket.io实现聊天室
1 // 前端代码: 2 <!-- author: wyb --> 3 <!DOCTYPE html> 4 <html lang="en"> 5 <head> 6 <meta charset="UTF-8"> 7 <meta name="viewport" content="width=device-width, initial-scale=1"> 8 <title>聊天室</title> 9 <style> 10 *{ 11 margin: 0; 12 padding: 0; 13 } 14 .container{ 15 width: 93%; 16 margin: 0 auto; 17 padding: 15px; 18 } 19 .err_box{ 20 width: 100%; 21 height: 20px; 22 line-height: 20px; 23 text-align: center; 24 color: red; 25 display: none; 26 } 27 #u1{ 28 width: 100%; 29 height: 360px; 30 border: 1px solid black; 31 overflow: auto; 32 margin: 15px 0; 33 } 34 #u1 li.me{ 35 color: green; 36 } 37 #form{ 38 width: 100%; 39 text-align: center; 40 margin: 15px 0; 41 } 42 #form textarea{ 43 width: 100%; 44 min-height: 130px; 45 margin: 10px 0; 46 } 47 </style> 48 <link rel="stylesheet" href="https://unpkg.com/purecss@1.0.0/build/pure-min.css" 49 integrity="sha384-nn4HPE8lTHyVtfCBi5yW9d20FjT8BJwUXyWZT9InLYax14RDjBj46LmSztkmNP9w" crossorigin="anonymous"> 50 <script src="http://localhost:8080/socket.io/socket.io.js"></script> 51 <script> 52 let sock = io.connect('ws://localhost:8080/') 53 54 sock.on('connect', function () { 55 console.log('已连接') 56 document.querySelector('.err_box').style.display = "none" 57 }) 58 sock.on('disconnect', function () { 59 console.log('已断开') 60 document.querySelector('.err_box').style.display = "block" 61 }) 62 63 window.onload = function(){ 64 let oTxt = document.querySelector('#txt1') 65 let oBtn = document.querySelector('#btn1') 66 let oUl = document.querySelector('#u1') 67 68 oBtn.onclick = function () { 69 sock.emit('msg', oTxt.value) 70 71 // 把自己输入的信息添加到自己页面中并将颜色设置为绿色 72 let oLi = document.createElement('li') 73 oLi.innerHTML = oTxt.value 74 oLi.className = 'me' 75 76 oTxt.value = '' 77 oUl.appendChild(oLi) 78 } 79 80 sock.on('msg', function (str) { 81 let oLi = document.createElement('li') 82 oLi.innerHTML = str 83 oUl.appendChild(oLi) 84 }) 85 86 } 87 88 // 聊天室 89 // sock.emit() 90 // sock.on() 91 92 </script> 93 94 </head> 95 <body> 96 97 <div class="container"> 98 <div class="err_box">无法连接到服务器,请检查网络</div> 99 <ul id="u1"></ul> 100 <form class="pure-form" id="form"> 101 <textarea class="pure-input-1" id="txt1" name="" placeholder="请输入聊天内容"></textarea> <br> 102 <input type="button" value="发送" id="btn1" class="pure-button pure-button-primary"> 103 </form> 104 </div> 105 </body> 106 </html>
1 // 后端代码 2 const http = require('http') 3 const io = require('socket.io') 4 5 let httpServer = http.createServer(function (req, res) { 6 7 }) 8 httpServer.listen(8080) 9 10 let wsServer = io.listen(httpServer) 11 12 let aSock = [] 13 wsServer.on('connection', function (sock) { 14 aSock.push(sock) 15 console.log(aSock.length) 16 17 // sock.emit() 18 sock.on('msg', function (str) { 19 aSock.forEach(function (s) { 20 if(s!==sock){ 21 s.emit('msg', str) 22 } 23 }) 24 }) 25 26 // 断开连接 27 sock.on('disconnect', function () { 28 let n = aSock.indexOf(sock) 29 30 if(n!==-1){ 31 aSock.splice(n, 1) 32 } 33 }) 34 }) 35 36 // 每隔0.5秒打印连接数 37 setInterval(function () { 38 console.log(aSock.length) 39 }, 500)
使用方法:启动服务器后,在浏览器中打开多个前端页面即可开始使用