当前位置: 首页 > news >正文

【python socket】实现websocket服务端

一、获取握手信息

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

import socketsock = 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 = Noneif not info:breakpayload_len = info[1] & 127if 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 = Nonemask = 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,threadingdef 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_dictdef send_msg(conn, msg_bytes):"""WebSocket服务端向客户端发送消息:param conn: 客户端连接到服务器端的socket对象,即: conn,address = socket.accept():param msg_bytes: 向客户端发送的字节:return:"""import structtoken = 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_bytesconn.send(msg)return Truedef recv_msg(conn):while True:try:info = conn.recv(8096)except Exception as e:info = Noneif not info:breakpayload_len = info[1] & 127if 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 = Nonemask = 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 bodybody = str(bytes_list, encoding='utf-8')print(body)if body == "exit":conn.close()breaksock = 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.lryc.cn/news/31991.html

相关文章:

  • PANGO的CFG那些事
  • 路由协议(OSPF、ISIS、BGP)实验配置
  • Python可变对象与不可变对象的浅拷贝与深拷贝
  • 滑模控制(Sliding mode control)快速入门
  • golang的垃圾回收详解
  • 线上负载过高排查(top/vmstat/ifstat/free/df)
  • Java的注解(Annotation)
  • 信息系统项目管理师:配置管理
  • web餐饮开源程序
  • 28个案例问题分析---027---单表的11个Update接口--MyBatis
  • 大数据开发治理平台 DataWorks
  • Xshell的下载、使用、配置【ssh、telnet、串口】
  • C++回顾(七)—— 面向对象模型
  • 开源监控服务uptime-kuma
  • JavaScript混淆技术:了解其核心原理和常用手段
  • 大型医院云HIS系统:采用前后端分离架构,前端由Angular语言、JavaScript开发;后端使用Java语言开发 融合B/S版电子病历系统
  • SAP UI5 Upload/Download file through NetWeaver Gateway
  • opencv校正图像
  • JavaScript:函数与箭头函数的区别
  • 八股文(四)
  • XSS挑战赛(xsslabs)1~10关通关解析
  • 什么是以太网供电POE
  • 【JUC2022】第七章 AQS、ReentrantReadWriteLock 和 StampedLock
  • Spark 磁盘作用
  • 三、Spark 内存管理
  • Java 面试常见项目问题回答
  • 文件上传和下载(原生JS + SpringBoot实现)
  • 【C语言学习笔记】:安全性
  • Linux - 磁盘存储管理 磁盘引入
  • 分割std::string成多个string