Python实现WebSocket通信

WebSocket是一种在单个TCP连接上进行全双工通信的协议,位于 OSI 模型的应用层。

与传统的HTTP请求-响应模型不同,WebSocket的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,实现实时性和互动性。

WebSocket关键特点:
  • 双向通信:WebSocket提供了全双工通信,允许服务器和客户端同时发送和接收数据。相比起传统的HTTP请求-响应模型,WebSocket使得服务器可以主动推送数据给客户端,而不需要客户端首先发起请求。
  • 持久连接:与HTTP请求不同,WebSocket连接是持久的,即一旦建立连接,它将保持打开状态,直到其中一方关闭连接或发生错误。
  • 轻量级协议:WebSocket使用简单的消息传递协议,在传输数据时减少了额外的开销。它采用二进制帧格式或文本帧格式来发送数据。
  • 跨域支持:WebSocket支持跨域通信,允许在不同域名或端口之间建立连接和交换数据。

WebSocket在许多场景下非常有用,特别是实时通信和实时数据更新方面。

常见的编程语言和框架提供了对WebSocket的支持,可以使用WebSocket API来建立WebSocket连接,并使用WebSocket的事件和方法来处理连接的打开、关闭、错误和消息等操作。

前端demo.html
html 复制代码
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<ul id="content"></ul>
<form class="form">
    <input type="button" value="连接" id="connect" class="connect"/>
    <br/>
    <input type="text" placeholder="请输入发送的消息" class="message" id="message"/>
    <input type="button" value="发送" id="send" class="connect"/>
</form>
 
<script type="text/javascript">
    var oUl=document.getElementById('content');
    var oConnect=document.getElementById('connect');
    var oSend=document.getElementById('send');
    var websocket=null;
    oConnect.onclick=function(){
        websocket=new WebSocket('ws://127.0.0.1:10083');
 
        <!--客户端链接后触发-->
         websocket.onopen=function(){
             oUl.innerHTML+="<li>客户端已连接</li>";
         }
         <!--收到消息后触发-->
        websocket.onmessage=function(evt){
            oUl.innerHTML+="<li>"+evt.data+"</li>";
        }
        <!--关闭后触发-->
        websocket.onclose=function(){
            oUl.innerHTML+="<li>客户端已断开连接</li>";
        };
        <!--出错后触发-->
        websocket.onerror=function(evt){
            oUl.innerHTML+="<li>"+evt.data+"</li>";
        };
    };
    oSend.onclick=function(){
        if(websocket){
             websocket.send($("#message").val())
        }
    }
</script>
</body>
</html>
后端main.py
python 复制代码
import socket, struct, hashlib, base64
import threading
 
# 获取请求头部数据,并将请求头转换为字典
def get_headers(data):
    headers = {}
    data = str(data, encoding="utf-8")
    header, body = data.split("\r\n\r\n", 1)
    header_list = header.split("\r\n")
    for i in header_list:
        i_list = i.split(":", 1)
        if len(i_list) >= 2:
            headers[i_list[0]] = "".join(i_list[1::]).strip()
        else:
            i_list = i.split(" ", 1)
            if i_list and len(i_list) == 2:
                headers["method"] = i_list[0]
                headers["protocol"] = i_list[1]
                print("请求类型: {} 请求协议: {}".format(i_list[0],i_list[1]))
    return headers
 
# 接收数据时的解码过程
def parse_payload(payload):
    payload_len = payload[1] & 127
    if payload_len == 126:
        mask = payload[4:8]
        decoded = payload[8:]
    elif payload_len == 127:
        mask = payload[10:14]
        decoded = payload[14:]
    else:
        mask = payload[2:6]
        decoded = payload[6:]
 
    # 将所有数据全部收集起来,对所有字符串编码
    bytes_list = bytearray()
    for i in range(len(decoded)):
        chunk = decoded[i] ^ mask[i % 4]
        bytes_list.append(chunk)
    body = str(bytes_list, encoding='utf-8')
    return body
 
# 封装并发送数据到浏览器
def send_msg(conn, msg_bytes):
    # 接收的第一个字节都是x81不变
    first_byte = b"\x81"
    length = len(msg_bytes)
    if length < 126:
        first_byte += struct.pack("B", length)
    elif length <= 0xFFFF:
        first_byte += struct.pack("!BH", 126, length)
    else:
        first_byte += struct.pack("!BQ", 127, length)
    msg = first_byte + msg_bytes
    conn.sendall(msg)
    return True
 
# 从浏览器中接收数据
def recv_msg(conn):
    data_recv = conn.recv(8096)
    if data_recv[0:1] == b"\x81":
        data_parse = parse_payload(data_recv)
        return data_parse
    return False
 
# 建立握手流程并创建 handler_msg 完成数据收发
def handler_accept(sock):
    while True:
        conn, addr = sock.accept()
        data = conn.recv(8096)
        headers = get_headers(data)
 
        # 对请求头中的sec-websocket-key进行加密
        response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \
                       "Upgrade:websocket\r\n" \
                       "Connection: Upgrade\r\n" \
                       "Sec-WebSocket-Accept: %s\r\n" \
                       "WebSocket-Location: ws://%s\r\n\r\n"
        # 加盐操作,此处是H5规范定义好的
        magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
        if headers.get('Sec-WebSocket-Key'):
            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.get("Host"))
        # 相应握手包数据
        conn.sendall(bytes(response_str, encoding="utf-8"))
        t = threading.Thread(target=handler_msg, args=(conn, ))
        t.start()
 
# 主函数,用于实现数据交互
def handler_msg(connect):
    with connect as connect_ptr:
        while True:
            try:
                recv = recv_msg(connect_ptr)
                msg_str = "接收数据: {}".format(recv)
                print(msg_str)
                send_msg(connect_ptr, bytes(msg_str, encoding="utf-8"))
            except Exception:
                exit(0)
 
if __name__ == "__main__":
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(("127.0.0.1", 10083))
    sock.listen(5)
    print("Start Server ws://127.0.0.1:10083")
    t = threading.Thread(target=handler_accept(sock))
    t.start()

主要功能包括:

  • get_headers(data): 从请求数据中获取请求头部信息,并将其转换为字典格式。
  • parse_payload(payload): 对接收到的数据进行解码,还原出原始消息。
  • send_msg(conn, msg_bytes): 封装并发送数据到浏览器。
  • recv_msg(conn): 从浏览器中接收数据,并解析出原始消息。
  • handler_accept(sock): 建立握手流程,处理与客户端的连接和握手过程。在握手完成后,创建一个新的线程来处理与客户端的数据交互。
  • handler_msg(connect): 处理与客户端的数据交互。通过循环不断接收客户端发送的消息,并发送一个固定的回复消息。
相关推荐
普通网友4 分钟前
pytest一些常见的插件
开发语言·python·pytest
时空系13 分钟前
Python 高性能高压缩打包器 —— 基于 JianPy 语义分析引擎
python
cndes35 分钟前
给Miniconda换源,让包下载更迅速
开发语言·python
Metaphor6921 小时前
使用 Python 添加、隐藏和删除 PDF 图层
python·pdf·图层
丨白色风车丨1 小时前
【Python 计算机视觉】基于 Dlib+OpenCV 实现实时人眼疲劳检测(闭眼预警)
python·opencv·计算机视觉
嘘嘘出差2 小时前
Python 爬虫入门实战:爬取豆瓣电影 Top 250
开发语言·爬虫·python
爱写代码的倒霉蛋2 小时前
实现协程的三种方式
开发语言·python
崖边看雾2 小时前
记录Python学习——第一章环境安装下载(Windows)
windows·python·学习·pycharm
用户298698530143 小时前
Python 实现 Excel 到 ODS、XPS、PostScript 及 PDF/A-1b 的格式转换
后端·python·excel
本地化文档3 小时前
xlwings-docs-l10n
python·github·excel·gitcode·sphinx