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)

使用方法:启动服务器后,在浏览器中打开多个前端页面即可开始使用

 

posted @ 2018-09-30 23:26  woz333333  阅读(614)  评论(0编辑  收藏  举报