【无标题】

Websocket+protobuf怎么与服务器通信

Websocket:WebSocket是⼀种⽹络传输协议,可在单个TCP连接上进⾏双全⼯通信,位于OSI模型的
应⽤层
【无标题】_第1张图片

实现⼀个简单的客⼾端和服务端

安装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⽂档
【无标题】_第2张图片
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⻓度是多少?
【无标题】_第3张图片
A:加上包头,根据包头的信息读取包的⻓度。
【无标题】_第4张图片
在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协议规定:把接收到的第⼀个字节当作⾼位字节看待,这就要求发送端发送的第⼀个字节是⾼位字节;⽽在发送端发送数据时,发送的第⼀个字节是该数值在内存中的起始地址处对应的那个字节,也就是说,该数值在内存中的起始地址处对应的那个节就是要发送的第⼀个⾼位字节所以:⽹络字节序就是⼤端字节序, 有些系统的本机字节序是⼩端字节序, 有些则是⼤端字节序, 为了保证传送顺序的⼀致性, 所以⽹际协议使⽤⼤端字节序来传送数据。
【无标题】_第5张图片

# 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的通信了

你可能感兴趣的:(websocket,网络协议,网络)