zoukankan      html  css  js  c++  java
  • WebSocket小记

    what's the WebSocket

      WebSocket是一种通信协议,区别于HTTP协议,HTTP协议只能实现客户端请求,服务端响应的这种单项通信。而WebSocket可以实现客户端与服务端的双向通讯,最大也是最明显的区别就是可以做到服务端主动将消息推送给客户端。

    WebSocket 和 普通HTTP 请求不同点

    WebSocket 由服务端主动推送数据到客户端,普通 HTTP 请求需要客户端每次项服务端发送请求后才能得到响应

    1. 长连接:只需要建立一次握手请求后就可以一直得到服务端推送的数据
    2. 数据格式轻量,性能开销小。客户端与服务端进行数据交换时,服务端到客户端的数据包头只有2到10字节,客户端到服务端需要加上另外4字节的掩码。HTTP每次都需要携带完整头部。
    3. 更好的二进制支持,可以发送文本,和二进制数据
    4. 没有同源限制,客户端可以与任意服务器通信
    5. 协议标识符是ws(如果加密,则是wss),请求的地址就是后端支持websocket的API。

    实现 WebSocket

    WebSocket 连接过程

      客户端发起 HTTP 握手,告诉服务端进行 WebSocket 协议通讯,并告知 WebSocket 协议版本。服务端确认协议版本,升级为 WebSocket 协议。之后如果有数据需要推送,会主动推送给客户端。

    WebSocket 握手时的请求头和响应头

    Accept-Encoding: gzip, deflate, br
    Accept-Language: zh,zh-TW;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6
    Cache-Control: no-cache
    Connection: Upgrade  # 表示要升级协议
    Host: 127.0.0.1:3000
    Origin: http://localhost:3000
    Pragma: no-cache
    Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
    Sec-WebSocket-Key: bwb9SFiJONXhQ/A4pLaXIg==  # 对应服务端响应头的Sec-WebSocket-Accept,由于没有同源限制...客户端随机生成
    Sec-WebSocket-Version: 13  # 表示websocket的版本。如果服务端不支持该版本,需要返回一个Sec-WebSocket-Versionheader,里面包含服务端支持的版本号。
    Upgrade: websocket  # 要升级协议到websocket协议
    请求头字段
    Connection: Upgrade
    Sec-WebSocket-Accept: 2jrbCWSCPlzPtxarlGTp4Y8XD20= # 用来告知服务器愿意发起一个websocket连接, 值根据客户端请求头的Sec-WebSocket-Key计算出来
    Upgrade: websocket
    响应头字段

    WebSocket 连接过程参数

    • WebSocket.onopen: 连接成功后的回调
    • WebSocket.onclose: 连接关闭后的回调
    • WebSocket.onerror: 连接失败后的回调
    • WebSocket.onmessage: 客户端接收到服务端数据的回调
    • webSocket.bufferedAmount: 未发送至服务器的二进制字节数
    • WebSocket.binaryType: 使用二进制的数据类型连接
    • WebSocket.protocol : 服务器选择的下属协议
    • WebSocket.url : WebSocket 的绝对路径
    • WebSocket.readyState: 当前连接状态,对应的四个常量

    Python 简单实现 WebSocket 服务端

    # -*- coding: utf-8 -*-
    import base64
    import copy
    import hashlib
    import socket
    import struct
    import time
    
    from threading import Thread
    
    
    
    class WsServer:
        def __init__(self):
            self.users = set() # 用于存放连接的客户端
    
        def get_headers(self, data):
            '''将请求头转换为字典'''
            header_dict = {}
            data = str(data, encoding="utf-8")
            header, body = data.split("
    
    ", 1)
            header_list = header.split("
    ")
            # print("---" * 22, body)
            for idx, v in enumerate(header_list):
                if idx == 0:
                    if len(v.split(" ")) == 3:
                        header_dict['method'], header_dict['url'], header_dict['protocol'] = v.split(" ")
                else:
                    key, value = v.split(":", 1)
                    header_dict[key] = value.strip()
            return header_dict
    
        # 等待用户连接
        def acce(self):
            conn, addr = sock.accept()
            # print("conn from ", conn, addr)
            self.users.add(conn)
            # 获取握手消息,magic string ,sha1加密
            # 发送给客户端
            data = conn.recv(1024)
            print("websocket client data: %s" % data)
            headers = self.get_headers(data)
    
            # 对请求头中的sec-websocket-key进行加密
            response_tpl = "HTTP/1.1 101 Switching Protocols
    " 
                           "Upgrade:websocket
    " 
                           "Connection: Upgrade
    " 
                           "Sec-WebSocket-Accept: %s
    " 
                           "WebSocket-Location: ws://%s%s
    
    "
    
            magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
            value = headers['Sec-WebSocket-Key'] + magic_string
            ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
            response_str = response_tpl % (ac.decode('utf-8'), headers['Host'], headers['url'])
    
            # 此处可加入校验
            
            conn.send(bytes(response_str, encoding='utf-8'), )
            logger.info('Websocket shake hand success: %s' % conn)
    
    
        def send_msg(self, msg_bytes):
            """
            WebSocket服务端向客户端发送消息
            :param conn: 客户端连接到服务器端的socket对象,即: conn,address = socket.accept()
            :param msg_bytes: 向客户端发送的字节
            :return:
            """
            token = b"x81"  # 接收的第一字节,一般都是x81不变
            length = len(msg_bytes)
            if length < 126:
                token += struct.pack("B", length)
            elif length <= 0xFFFF:
                token += struct.pack("!BH", 126, length)
            else:
                token += struct.pack("!BQ", 127, length)
    
            msg = token + msg_bytes
    
            for conn in self.users:
                # 如果出错就是客户端断开连接
                try:
                    conn.send(msg)
                except Exception as e:
                    print("%s 连接关闭: %s" % (conn, e))
                    # 删除断开连接的记录
                    self.users.remove(conn)
    
    
        # 循环等待客户端建立连接
        def th(self):
            while True:
                self.acce()
    
    
    
    sock = socket.socket()
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(("0.0.0.0", 3002)) # 监听3002端口
    sock.listen(5)
    
    WS = WsServer()
    
    Thread(target=WS.th).start()
    View Code

    HTML 中使用 JS 实现 WebSocket 客户端

    <!DOCTYPE HTML>
    <html>
       <head>
       <meta charset="utf-8">
       <title>websocket通信客户端</title>
           <script type="text/javascript">
             function WebSocketTest()
             {
                if ("WebSocket" in window)
                {
                   // 打开一个 web socket
                   var ws = new WebSocket("ws://10.10.6.91:5678");
                   
                   // 连接建立后的回调函数
                   ws.onopen = function()
                   {
                   // Web Socket 已连接上,使用 send() 方法发送数据
                      ws.send("admin:123456");
                      alert("正在发送:admin:123456");
                   };
                   
                   // 接收到服务器消息后的回调函数
                   ws.onmessage = function (evt) 
                   { 
                      var received_msg = evt.data;
                      if (received_msg.indexOf("sorry") == -1) {
                        alert("收到消息:"+received_msg);
                      }
                      
                   };
                   
                   // 连接关闭后的回调函数
                   ws.onclose = function()
                   { 
                      // 关闭 websocket
                      alert("连接已关闭..."); 
                   };
                }
                else
                {
                   // 浏览器不支持 WebSocket
                   alert("您的浏览器不支持 WebSocket!");
                }
             }
             
          </script>
       </head>
    
       <body onload="WebSocketTest()">
    
       </body>
    </html>
    View Code

    WebSocket 添加握手自定义参数

      由于 WebSocket 没有修改请求头的方法,所以可在 url 后面接问号然后添加自定义参数

    举个例子

    var token='dcvuahsdnfajw12kjfasfsdf34'
    var  ws = new WebSocket("ws://" + url?token + "/webSocketServer");

    心跳

      待更新...

                    

  • 相关阅读:
    logdump命令使用
    centos 添加用户并赋予sudo权限
    ogg进程解析
    xxl-job-executor2.2.0添加为默认执行器(docker方式)
    mysql设置数据库默认编码和表名不区分大小写
    linux-curl工具使用
    docker通过dockerfile打java项目镜像
    通过shell检查服务并发送mail告警(shell监控脚本)
    esxi6.5安装教程
    Vmware Vcenter6.5 配置集群和主机
  • 原文地址:https://www.cnblogs.com/zhuminghui/p/12747226.html
Copyright © 2011-2022 走看看