• 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('
    ');
    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
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: ${key2}
    
    `);
    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)

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

  • 相关阅读:
    React 父调子 子调父 observer.js 非父子传值
    echarts 折线图
    跳转新页面解决用户重新登录的方法
    微信小程序规范 引入框架 引入外部js css
    js 400
    Angular2 表单校验
    Angular2 登录页进门户页 用户接口正常 从服务平台退出到门户页 登录接口报302
    CSP-S 2020 SC 迷惑行为大赏
    【题解】[CSP-S 2020] 函数调用【拓扑图DP】
    【题解】[CSP-S 2020] 动物园
  • 原文地址:https://www.cnblogs.com/wyb666/p/9733737.html
Copyright © 2020-2023  润新知