一. 客户端服务端进行握手

1.客户端向服务端发送握手包

GET ws://localhost:8000/ HTTP/1.1
Host: localhost:8000
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://localhost:63342
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: zh-CN,zh;q=0.8
Sec-WebSocket-Key: 60jj9YDlrvPB+DzLmVJzcg==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits复制代码

2.服务端进行回应,返回给客户端

HTTP/1.1 101 Switching Protocols
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Accept: Cx7gYudkmGmF8rMFP1W1HbLkWRA=复制代码

客户端发送握手请求,然后服务端返回正确的数据,经过客户端的确认完成握手,然后就可以进行通信了;
这里客户端发送的Sec-WebSocket-Key是一个Base64 encode的值,它是浏览器随机生成的,然后服务端通过这个值生成一个Sec-WebSocket-Accept;
方法: key+'258EAFA5-E914-47DA-95CA-C5AB0DC85B11',然后SHA-1加密,再进行base-64加密;
下面是握手代码:

HANDSHAKE_STR = ("HTTP/1.1 101 Switching Protocols\r\n""Upgrade: WebSocket\r\n""Connection: Upgrade\r\n""Sec-WebSocket-Accept: %(token)s\r\n\r\n"
)
GUID_STR = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
def parseHeaders(self, msg):# print(msg)headers = {}msg = str(msg, encoding="utf-8")header, data = msg.split('\r\n\r\n', 1)for x in header.split("\r\n")[1:]:# 这里是 “:+space”key, value = x.split(": ", 1)headers[key] = valuereturn headers
# 握手数据
def handShakeData(self,c_req):sec_key = self.parseHeaders(c_req)['Sec-WebSocket-Key']key = sec_key.encode("ascii") + GUID_STR.encode("ascii")accept_token = base64.b64encode(hashlib.sha1(key).digest()).decode("ascii")s_res = HANDSHAKE_STR % {'token': accept_token}return s_res复制代码

二. 进行通信

大概就是握手完成后,客户端发送消息到服务端,然后服务端将消息发送到每个连接上的客户端;

数据交互协议

  0                   1                   2                   30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+-+-+-+-+-------+-+-------------+-------------------------------+|F|R|R|R| opcode|M| Payload len |    Extended payload length    ||I|S|S|S|  (4)  |A|     (7)     |             (16/64)           ||N|V|V|V|       |S|             |   (if payload len==126/127)   || |1|2|3|       |K|             |                               |+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +|     Extended payload length continued, if payload len == 127  |+ - - - - - - - - - - - - - - - +-------------------------------+|                               |Masking-key, if MASK set to 1  |+-------------------------------+-------------------------------+| Masking-key (continued)       |          Payload Data         |+-------------------------------- - - - - - - - - - - - - - - - +:                     Payload Data continued ...                :+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +|                     Payload Data continued ...                |+---------------------------------------------------------------+复制代码
第一个字节:
  • FIN:描述消息是否结束;占1bit;如果是1表示这是数据包的最后一帧,0则表示后续还有数据;
  • RSV1, RSV2, RSV3:扩展定义,默认0,各占1bit;
  • opcode:表示传输的数据包类型,占4bit,(15种情况)
    • 0x0 表示为附加数据
    • 0x1 表示为text类型数据
    • 0x2 表示binary类型数据
    • 0x3~7 暂未定义
    • 0x8 表示连接关闭
    • 0x9 表示ping类型数据
    • 0xA 表示pong类型数据
    • 0xB~F 暂未定义
第二个字节:
  • MASK:表示是否经过掩码处理;如果是1,表示经过掩码处理,Masking-key的数据为掩码密码
  • Payload length:
    • 如果值为0~125,则就是payload的真实长度;(若MASK=1,则Masking-key为第3~6个字节)
    • 如果值为126,则后面的2个字节(16bit)为payload的真实长度;(若MASK=1,则Masking-key为第5~8个字节)
    • 如果值为127,则后面的8个字节(64bit)为payload的真实长度;(若MASK=1,则Masking-key为第11~14个字节)

1. 将客户端发送的数据解析出真实的数据

方法:将Payload Data数据的每一位x,与Masking-key的第i%4位进行异或运算,i是x在Payload Data中的索引;
下面是解析的代码:

def parseData(self,msg):g_code_length = msg[1] & 127if g_code_length == 126:g_code_length = struct.unpack('!H', msg[2:4])[0]masks = msg[4:8]data = msg[8:]elif g_code_length == 127:g_code_length = struct.unpack('!Q', msg[2:10])[0]masks = msg[10:14]data = msg[14:]else:masks = msg[2:6]data = msg[6:]i = 0raw_by = bytearray()for d in data:raw_by.append( int(d) ^ int(masks[i % 4]) )i += 1print(u"总长度是:%d" % int(g_code_length))raw_str = raw_by.decode()return raw_str复制代码

2. 将数据发送到客户端

发送的数据格式:固定字节+包长度字节+原始数据
例:\x81\x0cHello World!

三. 完整的代码
服务端:


import socket
import base64,hashlib
import threading
import struct
from collections import dequeHANDSHAKE_STR = ("HTTP/1.1 101 Switching Protocols\r\n""Upgrade: WebSocket\r\n""Connection: Upgrade\r\n""Sec-WebSocket-Accept: %(token)s\r\n\r\n"
)
GUID_STR = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
STREAM = 0x0
TEXT = 0x1
BINARY = 0x2
CLOSE = 0x8
PING = 0x9
PONG = 0xAclass WebSocket(threading.Thread):def __init__(self,conn,addr,index):threading.Thread.__init__(self)self.index = indexself.conn = connself.addr = addr#发送的数据缓存self.buffer = bytearray()self.sendToClientData = deque()# 组装header 获取‘Sec-WebSocket-Key’def parseHeaders(self, msg):# print(msg)headers = {}msg = str(msg, encoding="utf-8")header, data = msg.split('\r\n\r\n', 1)for x in header.split("\r\n")[1:]:# 这里是 “:+space”key, value = x.split(": ", 1)headers[key] = valuereturn headers# 握手数据def handShakeData(self,c_req):sec_key = self.parseHeaders(c_req)['Sec-WebSocket-Key']key = sec_key.encode("ascii") + GUID_STR.encode("ascii")accept_token = base64.b64encode(hashlib.sha1(key).digest()).decode("ascii")s_res = HANDSHAKE_STR % {'token': accept_token}return s_res# 得到数据长度 (包含描述字节)def getMsglen(self,msg):g_code_length = msg[1] & 127if g_code_length == 126:g_code_length = struct.unpack('!H', msg[2:4])[0]g_code_length += 8elif g_code_length == 127:g_code_length = struct.unpack('!Q', msg[2:10])[0]g_code_length += 14else:g_code_length += 6g_code_length = int(g_code_length)print(g_code_length)return g_code_length# 解析数据def parseData(self,msg):g_code_length = msg[1] & 127if g_code_length == 126:g_code_length = struct.unpack('!H', msg[2:4])[0]masks = msg[4:8]data = msg[8:]elif g_code_length == 127:g_code_length = struct.unpack('!Q', msg[2:10])[0]masks = msg[10:14]data = msg[14:]else:masks = msg[2:6]data = msg[6:]i = 0raw_by = bytearray()for d in data:raw_by.append( int(d) ^ int(masks[i % 4]) )i += 1print(raw_by)print(u"总长度是:%d" % int(g_code_length))raw_str = raw_by.decode()# raw_str = str(raw_by)return raw_str# 发送消息def sendMessage(self,message):# 遍历连接上的列表for conn in connections.values():# 发送消息到client (除了自己)if conn != self.conn:self._sendMessage(False,TEXT,message)while self.sendToClientData:data = self.sendToClientData.popleft()conn.send(data[1])# FIN 后面是否还有数据;opcode 传输的数据包类型;message 传输的数据(str)def _sendMessage(self,FIN,opcode,message):payload = bytearray()b1 = 0b2 = 0if FIN is False:b1 |= 0x80b1 |= opcode        # 若opcode=TEXT    b'0x81'payload.append(b1)      #msg_utf = message.encode('utf-8')msg_len = len(msg_utf)if msg_len <= 125:b2 |= msg_lenpayload.append(b2)elif msg_len >= 126 and msg_len <= 65535:b2 |= 126payload.append(b2)payload.extend(struct.pack("!H", msg_len))elif msg_len <= (2 ^ 64 - 1):b2 |= 127payload.append(b2)payload.extend(struct.pack("!Q", msg_len))else:print("传输的数据太长了! ——(_sendMessage)")# 拼接上需要发送的数据# 格式大概这样:bytearray(b'\x81\x0cHello World!')   '\x0c'是发送的数据长度if msg_len > 0:payload.extend(msg_utf)self.sendToClientData.append((opcode,payload))# 端开连接def _disConnected(self):self.conn.close()print("断开的连接conn%s" % (self.index))print(connections)del connections['conn%s' % (self.index)]print(connections)# def run(self):# print(self.listeners)self.isHandShake = Falsewhile True:if self.isHandShake == False:print("start handshake %s" % (self.addr[0]))# 接收客户端内容c_req = self.conn.recv(1024)# print(c_req)handData = self.handShakeData(c_req)self.conn.sendall(str.encode(handData))self.isHandShake = Trueelse:message = self.conn.recv(16384)    # 16 * 1024   16384   # bytesprint(message)# 断开连接if message[0]&127 == CLOSE:    # 0x88   136self._disConnected()returnself.buffer.extend(message)  # bytescode_length = self.getMsglen(self.buffer)  # 数据中带的 数据长度(包含描述字节)# 数据完整就发送if code_length == len(self.buffer):buffer_utf = self.parseData(self.buffer)      # strself.sendMessage(buffer_utf)print(message)self.buffer = bytearray()connections = {}
class websocketServer(object):def __init__(self,host, port):self.serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)self.serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)self.serversocket.bind((host, port))self.serversocket.listen(5)def server(self):print(self.serversocket)index =0while True:conn,addr = self.serversocket.accept()print(conn)print(addr)# 新建线程newSocket = WebSocket(conn,addr,index)# 开始线程,执行run函数newSocket.start()connections['conn%s'% (index)] = connindex += 1print(connections)websocketServer = websocketServer("127.0.0.1",8000)
websocketServer.server()复制代码

感谢您的阅读;

Python3 websocket通信相关推荐

  1. Python3+WebSockets实现WebSocket通信

    一.说明 1.1 背景说明 前端时间同事说云平台通信使用了一个websocket的东西,今天抽空来看一下具体是怎么个通信过程. 从形式上看,websocket是一个应用层协议,socket是数据链路层 ...

  2. python websocket_python 模拟websocket通信

    以前,很多网站使用轮询实现推送技术.轮询是在特定的的时间间隔(比如1秒),由浏览器对服务器发出HTTP request,然后由服务器返回最新的数据给浏览器.轮询的缺点很明显,浏览器需要不断的向服务器发 ...

  3. C#(SuperWebSocket)与websocket通信

    原文:C#(SuperWebSocket)与websocket通信 客户端代码 点击可以查看一些关于websocket的介绍 1 <!DOCTYPE html> 2 <html> ...

  4. WebSocket 通信原理和详细使用(十六)

    今天我们详细分析WebSocket 通信原理和使用 一.什么是 WebSocket ? WebSocket --一种在 2011 年被互联网工程任务组( IETF )标准化的协议.WebSocket ...

  5. 《 Socket.IO》 解决 WebSocket 通信

    大家好呀,我是小菜~ 本文主要介绍 Socket.IO 微信公众号已开启,小菜良记,没关注的同学们记得关注哦! 在介绍 Socket.IO 之前, 我们先考虑一个问题, 如果这个时候有个需求, 类似实 ...

  6. 微信小程序 WebSocket 通信 —— 在线聊天

    在Node栏目就讲到了Socket通信的内容,使用Node实现Socke通信,还使用两个流行的WebSocket 库,ws 和 socket.io,在小程序中的WebSocket接口和HTML5的We ...

  7. 请使用netty框架实现高效稳定的websocket通信

    Netty 是一个异步事件驱动的网络应用框架,提供了一种高效稳定的方法来实现 WebSocket 通信. 要使用 Netty 实现 WebSocket 通信,需要执行以下步骤: 创建一个新的 Nett ...

  8. 在vue中webSocket通信

    1.简单介绍 基于webSocket通信的库主要有 socket.io,SockJS,这次用的是 SockJS. 2.前提 这里我们使用sockjs-client.stomjs这两个模块,要实现web ...

  9. 基于asp.netCoreWebApi的webSocket通信示例(net6)

    背景: 在阿里云服务器中搭建了常规的tcp server服务(基于.net framework 4.0).用以实现远程控制家里的鱼缸灯,办公室的电脑开关机等功能.客户端采用PC桌面端和微信小程序端. ...

最新文章

  1. Django框架之Auth模块
  2. [Swift][OC]tableView去掉顶部空白
  3. java linux 时区_java同步/设置Linux系统时间
  4. Oracle之索引、权限
  5. Spring Security3源码分析-UsernamePasswordAuthenticationFilter分析
  6. Nginx-1.9.8推出的切片模块
  7. leetcode 376. Wiggle Subsequence | 376. 摆动序列(动态规划)
  8. 会场安排(nyoj14)
  9. sql计数_SQL计数区分功能概述
  10. kafka性能优化入门
  11. arg,argmin和argmax理解
  12. http抓包实践--(六)-fiddler抓包手机
  13. [简历模板] 英文简历要用到的各种词汇-奖学金/担任职务(很全)
  14. 架构师原来是这样修炼出来的
  15. napi娃娃_第二十九章 干妈
  16. Bugku misc 旋转跳跃wp
  17. 在计算机网络拓扑结构中目前最常用的是,常用的计算机网络拓扑结构.docx
  18. gimp 架构_超级图像处理软件 gimp 2.99.2中文免费版
  19. 黄山自助旅行之注意事项
  20. 程序员必备的软技能-金字塔原理拆解(上)

热门文章

  1. 官方翻译不当导致的PowerShell运行失败一例
  2. 一位39岁程序员的困惑:知道得越多编程越慢怎么办?
  3. android 循环平移动画
  4. puppet中master和agent之间实现通信
  5. Microsoft Remote Desktop移动客户端QA
  6. matlab7.1(ERROR STARTING DESKTOP)解决
  7. Silverlight入门:第三部分 - 数据访问
  8. java fx alert_javaFx:使用弹出对话框 Alert
  9. vconsole插件_手机前端开发调试利器 – vConsole
  10. 发条js调试工具_小工具大帮手,利用 @open-node/antman 实现 node.js 进程线上调试,无须重启...