zoukankan      html  css  js  c++  java
  • WebSocket实现

    Websocket

    概述

    HTML5 开始提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议。它基于 TCP 传输协议,并复用 HTTP 的握手通道。

    WebSocket 复用了 HTTP 的握手通道。具体指的是,客户端通过 HTTP 请求与 WebSocket 服务端协商升级协议。协议升级完成后,后续的数据交换则遵照 WebSocket 的协议。

    优点:

    1.支持双向通信,实时性更强。
    2.更好的二进制支持。
    3.较少的控制开销。连接创建后,ws 客户端、服务端进行数据交换时,协议控制的数据包头部较小。在不包含头部的情况下,服务端到客户端的包头只有 2~10 字节(取决于数据包长度),客户端到服务端的的话,需要加上额外的 4 字节的掩码。而 HTTP 协议每次通信都需要携带完整的头部。
    4.支持扩展。ws 协议定义了扩展,用户可以扩展协议,或者实现自定义的子协议。(比如支持自定义压缩算法等)
    

    请求报文

    采用的是标准的 HTTP 报文格式,且只支持GET方法

    GET / HTTP/1.1
    Host: localhost:8080
    Origin: http://127.0.0.1:3000
    Connection: Upgrade  		//表示要升级协议
    Upgrade: websocket			//表示要升级到websocket协议	
    Sec-WebSocket-Version: 13	//表示 websocket 的版本。如果服务端不支持该版本,需要返回一个Sec-WebSocket-Versionheader,里面包含服务端支持的版本号。
    Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw==  //浏览器随机生成,与后面服务端响应首部的Sec-WebSocket-Accept是配套的,提供基本的防护,比如恶意的连接,或者无意的连接
    

    响应报文

    备注:每个 header 都以 结尾,并且最后一行加上一个额外的空行 。此外,服务端回应的 HTTP 状态码只能在握手阶段使用。过了握手阶段后,就只能采用特定的错误码。

    注意: Sec-WebSocket-Key/ Sec-WebSocket-Accept 的换算,只能带来基本的保障,但连接是否安全、数据是否安全、客户端 / 服务端是否合法的 ws 客户端、ws 服务端,其实并没有实际性的保证。

    HTTP/1.1 101 Switching Protocols  //101状态码表示服务器已经理解了客户端的请求,表示协议切换。
    Connection:Upgrade  //通过Upgrade消息头通知客户端采用不同的协议来完成这个请求
    Upgrade: websocket
    Sec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU=  //经过服务器确认,并且加密过后的 Sec-WebSocket-Key
    Sec-WebSocket-Protocol: chat  // 表示最终使用的协议
    

    Sec-WebSocket-Accept计算方法

    // 1.将 Sec-WebSocket-Key 跟 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 拼接;
    // 2.通过 SHA1 计算出摘要,并转成 base64 字符串
    const crypto = require('crypto');
    const magic = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
    const secWebSocketKey = 'w4v7O6xFTi36lq3RNcgctw==';
    
    let secWebSocketAccept = crypto.createHash('sha1')
    	.update(secWebSocketKey + magic)
    	.digest('base64');
    
    console.log(secWebSocketAccept);
    // Oy4NRAQ13jhfONC7bP8dTKb4PTU=
    

    实现

    客户端

    向 8080 端口发起 WebSocket 连接。连接建立后,打印日志,同时向服务端发送消息。接收到来自服务端的消息后,同样打印日志

    <html>
    <head>
        <title>ws demo</title>
        <meta charset="utf8">
    </head>
    <body>
      
    </body>
    <script>
      // 创建WebSocket对象,ws表示Websocket协议,后面接地址及端口
      var ws = new WebSocket('ws://localhost:8080');
      ws.onopen = function (msg) {
        console.log('client: ws connection is open');
        ws.send('hello');
      };
      ws.onmessage = function (message) {
        console.log('client: received %s', message.data);
      };
      ws.onerror = function(error){
          console.log('Error: ' + error.name + error.number)
      };
      ws.onclose = function(){
          console.log('Websocket closed!')
      }
      window.onbeforeunload = function(){
          ws.onclose = function(){}  // 首先关闭WebSocket
          ws.close()
      }
    </script>
    </html>
    

    服务端

    监听 8080 端口。当有新的连接请求到达时,打印日志,同时向客户端发送消息。当收到到来自客户端的消息时,同样打印日志。

    var app = require('express')();
    var server = require('http').Server(app);
    var WebSocket = require('ws');
    
    var wss = new WebSocket.Server({ port: 8080 });
    
    wss.on('connection', function connection(ws) {
        console.log('server: receive connection.');
        
        ws.on('message', function incoming(message) {
            console.log('server: received %s', message);
            ws.send('server: reply');
        });
    
        ws.on('pong', () => {
            console.log('server: received pong from client');
        });
    
        ws.send('world');
        
        // setInterval(() => {
        //     ws.ping('', false, true);
        // }, 2000);
    });
    
    app.get('/', function (req, res) {
      res.sendfile(__dirname + '/index.html');
    });
    
    app.listen(3000);
    
  • 相关阅读:
    树莓派ZeroW的Python中使用SQLite数据库
    树莓派Python读写配置文件--configparser库
    信号量示例——“生产者——消费者”实验
    互斥锁示例——模拟银行卡取钱
    管道通信(上)
    命名管道——进程通信案例
    文件I/O
    链表习题
    蓝桥杯ACM训练Day4——算法2-8~2-11:链表的基本操作
    C++——类模板几种常见的情况
  • 原文地址:https://www.cnblogs.com/fhkankan/p/13538935.html
Copyright © 2011-2022 走看看