Websocket+protobuf怎么与服务器通信
Websocket:WebSocket是⼀种⽹络传输协议,可在单个TCP连接上进⾏双全⼯通信,位于OSI模型的
应⽤层
实现⼀个简单的客⼾端和服务端
安装websockets
pip3 install websockets
客户端代码
import asyncio
import websockets
async def hello():
uri = "ws://localhost:8765"
async with websockets.connect(uri) as websocket:
while True:
name = input("What's your name?: ")
await websocket.send(name)
print(f"> {name}")
greeting = await websocket.recv()
print(f"< {greeting}")
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(hello())
服务端代码
import asyncio
import websockets
async def hello(websocket):
while True:
name = await websocket.recv()
print(f"< {name}")
greeting = f"Hello {name}!"
await websocket.send(greeting)
print(f"> {greeting}")
if __name__ == "__main__":
start_server = websockets.serve(hello, "localhost", 8765)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
websockets 是⼀个⽤于在 Python 中构建WebSocket服务器和客⼾端的库,专注于正确性、简单性、健壮性和性能。它建⽴在 Python 的标准异步 I/O 框架之上 asyncio ,提供了⼀个优雅的基于协程的 API。
Protobuf
protobuf全称Google Protocol Buffers,是google开发的的⼀套⽤于数据存储,⽹络通信时⽤于协议编解码的⼯具库。protobuf是⼀种灵活⾼效的独⽴于语⾔平台的结构化数据表⽰⽅法
probuf⽂档
protobuf与语言无关,protobuf转化成Python protoc --python_out=. ./user.proto
定义一个proto文件
syntax = "proto3";
message User {
string name = 1;
}
执行protoc --python_out=. ./user.proto,转化成Python结构体后将Python结构体转化成字节流,进行通信
user = user_pb2.User()
user.name = "张三"
data = user.SerializeToString()
粘包问题
拿我们的举例的PB⽂件来说:
syntax = "proto3";
message User {
string name = 1;
}
Q: 怎么知道你传过来的name⻓度是多少?
A:加上包头,根据包头的信息读取包的⻓度。
在Python中没有结构体的概念,所以⽤struct操作字节流
import struct
print("len(hello)=", len(bytes("hello", encoding="utf8")))
print("pack=", struct.pack('>I5s', 12341234, bytes("hello", encoding="utf8")))
print("unpack=", struct.unpack('>I5s', b'\x00\xbcO\xf2hello'))
- I 表⽰接下来是⼀个四个字节的int 12341234
- 5S表⽰接下来是五个Char
-
表⽰⼤端,⽹络端
UDP/TCP/IP协议规定:把接收到的第⼀个字节当作⾼位字节看待,这就要求发送端发送的第⼀个字节是⾼位字节;⽽在发送端发送数据时,发送的第⼀个字节是该数值在内存中的起始地址处对应的那个字节,也就是说,该数值在内存中的起始地址处对应的那个节就是要发送的第⼀个⾼位字节所以:⽹络字节序就是⼤端字节序, 有些系统的本机字节序是⼩端字节序, 有些则是⼤端字节序, 为了保证传送顺序的⼀致性, 所以⽹际协议使⽤⼤端字节序来传送数据。
# client
import asyncio
import websockets
import user_pb2
import struct
async def hello():
uri = "ws://localhost:8765"
async with websockets.connect(uri) as websocket:
user = user_pb2.User()
user.name = "张三"
data = user.SerializeToString()
l = len(data)
format = ">I%us" % l
print("format=", format)
buf = struct.pack(format, l, data)
await websocket.send(buf)
print(f"> {user.name}")
ret = await websocket.recv()
print(f"< {ret}")
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(hello())
# server
import asyncio
import websockets
import struct
import user_pb2
async def hello(websocket, path):
buf = await websocket.recv()
l = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3]
print('l=', l)
format = ">%us" % l
data = struct.unpack(format, buf[4:])
print(data)
user = user_pb2.User()
user.ParseFromString(buf[4:])
print("服务器收到名字, name=", user.name)
greeting = f"I received!"
await websocket.send(greeting)
print(f"> {greeting}")
if __name__ == "__main__":
start_server = websockets.serve(hello, "localhost", 8765)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
以上就可以实现protobuf+websocket的通信了