Python 使用Protobuf(struct模块)

Protobuf介绍

xml、json是目前常用的数据交换格式,它们直接使用字段名称维护序列化后类实例中字段与数据之间的映射关系,一般用字符串的形式保存在序列化后的字节流中。消息和消息的定义相对独立,可读性较好。但序列化后的数据字节很大,序列化和反序列化的时间较长,数据传输效率不高。

Protobuf和Xml、Json序列化的方式不同,采用了二进制字节的序列化方式,用字段索引和字段类型通过算法计算得到字段之前的关系映射,从而达到更高的时间效率和空间效率,特别适合对数据大小和传输速率比较敏感的场合使用。

Protobuf作为数据交换的格式,广泛应用在网络编程中作为数据交换的格式使用。protobuf作为数据交换的格式,可以简化发送端序列化、接收端发序列的流程,方便将更多精力放在程序逻辑的处理而不是发送接收的数据处理。

因为protobu经常使用在网络编程中,下面就以一个简单的CS程序的处理流程,来简单介绍下在Python下使用Protobuf。

下载Protobuf与生成py文件

程序中使用到是message.proto文件,具体内容如下:(proto文件的具体语法这里不再介绍)

syntax = "proto3"; 

message MsgPlayerLoginRequest
{
	string playerName = 1;
	string playerPass = 2;
	string ip = 3;
	int32 id = 4;
}

要将message.proto文件装换成对应的py文件,需要使用protoc.exe。下载地址如下:
protocolbuffers/protobuf

根据自己需求下载匹配的版本:

在这里插入图片描述
下载完成后解压缩,然后将bin目录中的protoc.exe,放入到message.proto所在目录

然后使用命令:protoc --python_out=./ message.proto,则在当前目录下生成message_pb2.py文件,这就是python程序能够使用的文件。

也可以将protoc.exe所在目录加入到环境变量Path中以便在各处都能使用。

protoc命令中,--python-out指定输出文件的路径,message.proto则是需要转换的目标文件。

工程程序

客户端连接服务端,连接成功后,构造MsgPlayerLoginRequest,然后序列化后发送到服务端。服务端收到连接,然后接收数据,将其反序列化为MsgPlayerLoginRequest并打印其中的字段值。其中用到的常用的序列化和反序列的成员函数:SerializeToString()ParseFromString()

import socket
import struct
from message_pb2 import MsgPlayerLoginRequest

class TestClient:
    def __init__(self, ip_addr, port):
        self.ip_addr = ip_addr
        self.port = port
        self.sock = socket.socket()

    def start(self):
        print('connect to {}:{}'.format(self.ip_addr, self.port))
        self.sock.connect((self.ip_addr, self.port))
        print('connect OK')
		
		# 构造MsgPlayerLoginRequest
        msg_login = MsgPlayerLoginRequest()
        msg_login.playerName = 'hello hi'
        msg_login.playerPass = '123456'
        msg_login.ip = '127.0.0.1'
        msg_login.id = 1
		
		# 将SerializeToString对象序列化成字节流并发送出去
        send_msg = msg_login.SerializeToString()
        self.sock.sendall(send_msg)
        print('send MsgPlayerLoginRequest ok')

if __name__ == '__main__':
    one_client = TestClient('127.0.0.1', 10088)
    one_client.start()

test_server.py

import socket
import struct
from message_pb2 import MsgPlayerLoginRequest

# 接收固定长度data_len的数据
def recv_some(client_sock, data_len):
    recv_data = b''
    remain_len = data_len

    while True:
        try:
            cur_data = client_sock.recv(remain_len)
        except ConnectionResetError as error:
            return False, recv_data

        recv_data += cur_data
        remain_len -= len(cur_data)
        if remain_len == 0:
            break

    return True, recv_data

class TestServer:
    def __init__(self, ip_addr, port):
        self.ip_addr = ip_addr
        self.port = port
        self.sock = socket.socket()

    def start(self):
        self.sock.bind((self.ip_addr, self.port))
        self.sock.listen(5)

        print('server listen on {}:{}'.format(self.ip_addr, self.port))
        client_sock, client_info = self.sock.accept()
        print('recv connection from {}'.format(client_info))

        # 因为是测试程序 所以就直接接收发送时数据的长度
        msg_len = 31
        recv_suc, msg_data = recv_some(client_sock, msg_len)
        if not recv_suc:
             return
		
		# 反序列话收到的数据为MsgPlayerLoginRequest并打印其中数据
        recv_msg = MsgPlayerLoginRequest()
        recv_msg.ParseFromString(msg_data)
        print(f'recv MsgPlayerLoginRequest : playerName = {recv_msg.playerName}, playerPass = {recv_msg.playerPass}, ip = {recv_msg.ip}, id = {recv_msg.id}')

if __name__ == '__main__':
    one_server = TestServer('127.0.0.1', 10088)
    one_server.start()

运行程序,客户端输出:

connect to 127.0.0.1:10088
connect OK
send MsgPlayerLoginRequest ok

服务端输出:

server listen on 127.0.0.1:10088
recv connection from ('127.0.0.1', 49723)
recv MsgPlayerLoginRequest: playerName = hello hi, playerPass = 123456, ip = 127.0.0.1, id = 1
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值