【python socket】实现websocket服务端

news2024/9/23 17:15:29

一、获取握手信息

首先通过如下代码,我们使用socket来获取客户端的握手信息

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(("127.0.0.1", 8002))
sock.listen(5)

conn, address = sock.accept()  # 获取客户端的socket对象和地址

msg = conn.recv(1024)  # 获取客户端的握手信息
print(msg)

我们可以通过http://www.websocket-test.com/来作为websocket客户端

当连接上服务端时,服务端打印如下信息

二、格式化websocket请求头

将上述打印信息转为JSON格式:

def get_headers(data):
    """
    将请求头格式化成字典
    :param data:
    :return:
    """
    header_dict = {}
    data = str(data, encoding='utf-8')

    # for i in data.split('\r\n'):
    #     print(i)
    header, body = data.split('\r\n\r\n', 1)
    header_list = header.split('\r\n')
    for i in range(0, len(header_list)):
        if i == 0:
            if len(header_list[i].split(' ')) == 3:
                header_dict['method'], header_dict['url'], header_dict['protocol'] = header_list[i].split(' ')
        else:
            k, v = header_list[i].split(':', 1)
            header_dict[k] = v.strip()
    return header_dict

调用上述方法可以将服务端收到的握手信息转为Json格式:

三、提取key并加密

key = headers_dict["Sec-WebSocket-Key"]  # 提取key

# 对key进行加密
magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
value = key + magic_string
ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
# print(ac)

四、将加密的结果返回客户端

# 将加密的结果返回客户端
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%s\r\n\r\n"
response_str = response_tpl % (ac.decode('utf-8'), headers_dict['Host'], headers_dict['url'])
conn.send(response_str.encode("utf-8"))

五、和客户端建立连接后的操作

这里建立连接后,将客户端发送的信息再返回给客户端

while True:
    try:
        info = conn.recv(8096)
    except Exception as e:
        info = None
    if not info:
        break
    payload_len = info[1] & 127
    if payload_len == 126:
        extend_payload_len = info[2:4]
        mask = info[4:8]
        decoded = info[8:]
    elif payload_len == 127:
        extend_payload_len = info[2:10]
        mask = info[10:14]
        decoded = info[14:]
    else:
        extend_payload_len = None
        mask = info[2:6]
        decoded = info[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')
    send_msg(conn, body.encode('utf-8'))

服务端完整代码

实现了服务端接收与发送的分离;

当服务端接收到指定字符串时("exit"),服务端主动断开连接。

import socket,base64,hashlib,time,threading


def get_headers(data):
    """
    将请求头格式化成字典
    :param data:
    :return:
    """
    header_dict = {}
    data = str(data, encoding='utf-8')

    # for i in data.split('\r\n'):
    #     print(i)
    header, body = data.split('\r\n\r\n', 1)
    header_list = header.split('\r\n')
    for i in range(0, len(header_list)):
        if i == 0:
            if len(header_list[i].split(' ')) == 3:
                header_dict['method'], header_dict['url'], header_dict['protocol'] = header_list[i].split(' ')
        else:
            k, v = header_list[i].split(':', 1)
            header_dict[k] = v.strip()
    return header_dict

def send_msg(conn, msg_bytes):
    """
    WebSocket服务端向客户端发送消息
    :param conn: 客户端连接到服务器端的socket对象,即: conn,address = socket.accept()
    :param msg_bytes: 向客户端发送的字节
    :return:
    """
    import struct

    token = b"\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
    conn.send(msg)
    return True

def recv_msg(conn):
    while True:
        try:
            info = conn.recv(8096)
        except Exception as e:
            info = None
        if not info:
            break
        payload_len = info[1] & 127
        if payload_len == 126:
            extend_payload_len = info[2:4]
            mask = info[4:8]
            decoded = info[8:]
        elif payload_len == 127:
            extend_payload_len = info[2:10]
            mask = info[10:14]
            decoded = info[14:]
        else:
            extend_payload_len = None
            mask = info[2:6]
            decoded = info[6:]

        bytes_list = bytearray()
        for i in range(len(decoded)):
            chunk = decoded[i] ^ mask[i % 4]
            bytes_list.append(chunk)
        global body
        body = str(bytes_list, encoding='utf-8')
        print(body)
        if body == "exit":
            conn.close()
            break

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(("127.0.0.1", 8002))
sock.listen(5)

conn, address = sock.accept()  # 获取客户端的socket对象和地址

data = conn.recv(1024)  # 获取客户端的握手信息
headers_dict = get_headers(data)
key = headers_dict["Sec-WebSocket-Key"]  # 提取key

# 对key进行加密
magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
value = key + magic_string
ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
# print(ac)

# 将加密的结果返回客户端
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%s\r\n\r\n"
response_str = response_tpl % (ac.decode('utf-8'), headers_dict['Host'], headers_dict['url'])
conn.send(response_str.encode("utf-8"))

# 和客户端建立连接后的操作

# 启动接收客户端信息的线程
thread_obj = threading.Thread(target=recv_msg, args=(conn,))
thread_obj.daemon = True
thread_obj.start()

# 不断给客户端发送信息
while True:
    if getattr(conn, "_closed") == False:  # 客户端没有关闭
        send_msg(conn, str("你好").encode('utf-8'))
        time.sleep(1)
    else:  # 客户端关闭
        break

参考视频链接:https://www.bilibili.com/video/BV1tf4y1K7Ww?p=8&spm_id_from=pageDriver&vd_source=36a3e35639c44bb339f59760641390a8

参考文章:https://www.cnblogs.com/wupeiqi/p/6558766.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/392431.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

启动项管理工具Autoruns使用实验(20)

实验目的 &#xff08;1&#xff09;了解注册表的相关知识&#xff1b; &#xff08;2&#xff09;了解程序在开机过程中的自启动&#xff1b; &#xff08;3&#xff09;掌握Autoruns在注册表和启动项方面的功能&#xff1b;预备知识 注册表是windows操作系统中的一个核心数据…

Android Framework-Android启动过程

第一个系统进程&#xff08;init&#xff09; Android设备的启动必须经历3个阶段&#xff0c;即Boot Loader、Linux Kernel和Android系统服务&#xff0c;默认情况下它们都有各自的启动界面。严格来说&#xff0c;Android系统实际上是运行于Linux内核之上的一系列“服务进程”…

元宇宙XR应用,如何迎接大规模普及的时代?

未来&#xff0c;具有互动性、沉浸感的元宇宙/XR应用将逐渐成为主流&#xff0c;这个趋势已毋庸置疑。 然而&#xff0c;在大趋势下&#xff0c;大众终端用户普遍设备能力不足、网络传输时延、GPU算力分配限制等技术挑战&#xff0c;依然是元宇宙/XR应用在大众广泛渗透的瓶颈。…

【vulhub漏洞复现】Fastjson 1.2.24反序列化漏洞

一、漏洞详情Fastjson 是一个 Java 库&#xff0c;可以将 Java 对象转换为 JSON 格式&#xff0c;也可以将 JSON 字符串转换为 Java 对象。漏洞成因&#xff1a;目标网站在解析 json 时&#xff0c;未对 json 内容进行验证&#xff0c;直接将 json 解析成 java 对象并执行&…

国产数字源表在压力传感器电阻测量上的应用

压力传感器分类压力传感器(Pressure Transducer)是能感受压力信号&#xff0c;并能按照一定的规律将压力信号转换成可用的输出的电信号的器件或装置,压力传感器通常由压力敏感元件和信号处理单元组成。常见的压力传感器有四种:应变式压力传感器、压阻式压力传感器、电容式压力传…

OpenMMLab 目标检测

OpenMMLab 目标检测1. 目标检测简介1.1 滑窗2. 基础知识2.1 边界框&#xff08;Bounding Box&#xff09;3. 两阶段目标检测算法3.1 多尺度检测技术4. 单阶段目标检测算法4.1 YOLO: You Only Look Once (2015)4.2 SSD: Single Shot MultiBox Detetor (2016)5. 无锚框目标检测算…

Nginx的搭建与核心配置

目录 一.Nginx是什么&#xff1f; 1.Nginx概述 2.Nginx模块与作用 3.Nginx三大作用&#xff1a;反向代理、负载均衡、动静分离 二.Nginx和Apache的差异 三.安装Nginx 1.编译安装 2.yum安装 四.Nginx的信号使用 五.Nginx的核心配置指令 1.访问状态统计配置 2.基于授…

非华为电脑安装华为电脑管家以及注意事项

非华为电脑安装华为电脑管家前言安装注意事项效果展示前言 非华为电脑是可以安装华为电脑管家的&#xff0c;不过部分功能可能不兼容。值得一提的是&#xff0c;超级终端、多屏协同、文件共享、远程控制等功能大部分电脑是可以使用的&#xff0c;本人在联想ThinkBook 15电脑上…

【CS144】Lab1总结

Lab1Lab汇总概述具体实现Lab汇总 概述 lab1要求实现一个字符串的装配器&#xff0c;用于将TCPTCPTCP接收方接收到的字节流拼接起来&#xff0c;并缓存一定量的乱序到达的字节&#xff0c;便于TCPTCPTCP接收方相关功能的实现。 具体实现 该装配器实现的重点是push_substring…

Java线程池使用与原理解析2(自定义线程池、合适的线程数量、线程池阻塞队列、线程拒绝策略)

在上篇我们学习了线程池各个参数的含义&#xff0c;线程池任务处理流程&#xff0c;使用线程池的好处等内容&#xff0c;本篇我们学习如何创建一个适合我们业务的线程池。为此&#xff0c;我们有必要先学习一下如何大概确定我们线程池核心线程数、怎么设置阻塞队列的类型与大小…

Malware Dev 04 - 隐匿之 ETW(Event Tracing for Windows)Bypass

写在最前 如果你是信息安全爱好者&#xff0c;如果你想考一些证书来提升自己的能力&#xff0c;那么欢迎大家来我的 Discord 频道 Northern Bay。邀请链接在这里&#xff1a; https://discord.gg/9XvvuFq9Wb我拥有 OSCP&#xff0c;OSEP&#xff0c;OSWE&#xff0c;OSED&…

使用leangoo领歌单团队敏捷开发项目管理

概述单团队敏捷开发主要是针对10人以下、只有一个Scrum团队的小型产品或项目的敏捷开发。对于小型团队来说&#xff0c;在Leangoo中创建一个单团队敏捷开发项目就可以很好地支持团队产品或项目的开发。适用场景适用于单个团队进行Scrum敏捷开发协作&#xff0c;Leangoo项目内也…

Linux - 磁盘存储管理 磁盘引入

# 我们要介绍下 磁盘管理&#xff0c; 那不妨先来看一张图来简单 引入 &#xff1a;这张图呢&#xff0c;是我们 Windows 上的磁盘管理的显示 。根据这幅图呢&#xff0c;提出一个问题 &#xff1a;>>> 这幅图磁盘管理所显示的内容&#xff0c;你能判断出 该电脑 有几…

【FMCW 04】测角-Angle FFT

在之前的文章中&#xff0c;我们已经详尽讨论过FMCW雷达测距和测速的原理&#xff0c;现在来讲最后一块内容&#xff0c;测角。测角对于硬件设备具有要求&#xff0c;即要求雷达具有多发多收结构&#xff0c;从而形成多个空间信道&#xff08;channel&#xff09;&#xff0c;我…

css选择器详解

简单选择器&#xff08;根据名称、id、类来选取元素&#xff09;组合器选择器&#xff08;根据它们之间的特定关系来选取元素&#xff09;伪类选择器&#xff08;根据特定状态选取元素&#xff09;伪元素选择器&#xff08;选取元素的一部分并设置其样式&#xff09;属性选择器…

第六讲:ambari-web 模块二次开发

上述图片为 Ambari 部署及操作 hdp 集群相关的部分界面截图。这些页面如果想调整的话,比如汉化,二次开发等,则可以修改 ambari-web 模块的源码来实现。 一、介绍 ambari-web 模块涉及到的界面有: HDP 集群部署向导已安装服务的仪表板、配置界面等主机列表及详细信息告警列…

【Opencv项目实战】图像的像素值反转

文章目录一、项目思路二、算法详解2.1、获取图像信息2.2、新建模板2.3、图像通道顺序三、项目实战&#xff1a;彩图的像素值反转&#xff08;方法一&#xff09;四、项目实战&#xff1a;彩图的像素值反转&#xff08;方法二&#xff09;五、项目实战&#xff1a;彩图转换为灰图…

Java中class文件的格式

常见的class文件格式如下图所示&#xff0c;下面我将对一下格式一一作出解释。 一、magic 该部分主要是对语言类型的规范&#xff0c;只有magic这个部分是CAFEBABE时才能被检测为Java语言&#xff0c;否则则不是。 二、minor version和major version minor version主要表示了…

【微信小程序-原生开发】实用教程16 - 查看详情(含页面跳转的传参方法--简单传参 vs 复杂传参)

需在实现列表的基础上开发 【微信小程序-原生开发】实用教程15 - 列表的排序、搜索&#xff08;含云数据库常用查询条件的使用方法&#xff0c;t-search 组件的使用&#xff09;_朝阳39的博客-CSDN博客 https://sunshinehu.blog.csdn.net/article/details/129356909 效果预览 …

【计算机网络】数据链路层可靠传输机制的三大协议:停止等待协议SW、后退N帧协议GBN、选择重传协议SR

一、可靠传输实现机制 1.停止等待协议SW case1、确认与否认 在发送端发送数据出现误码时&#xff0c;接收端回复一个NAK否认码&#xff0c;并要求发送端再发送一次。 case2、超时重传 接收端接收不到数据分组时&#xff0c;发送端就会一直处于等待接受端回复ACK或NAK的状态…