如何在python中编写协议缓冲区以通过套接字发送消息

时间:2013-12-10 04:21:45

标签: python sockets protocol-buffers

我正在尝试在python中使用协议缓冲区从一台计算机向另一台计算机发送消息。我从一些在线示例中了解到,并尝试用它们做出最小的例子。但是,在我的代码中,服务器不会打印出客户端发送的变量的正确值。非常感谢任何帮助。

首先,test_msg.proto如下:

message test_msg {
    required int32 param1 = 1;
    required int32 param2 = 2;
}

其次,客户端代码(test_client.py)

from test_msg_pb2 import test_msg
import socket
import sys
import struct

address = ('localhost', 6005)
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client_socket.connect(address)
data = test_msg()

num_retransmits = 0
while(num_retransmits < 10): # send the same message 10 times
    num_retransmits = num_retransmits + 1

    data.param1 = 100
    data.param2 = 5
    s = data.SerializeToString()

    totallen = 4 + len(s) 
    pack1 = struct.pack('>I', totallen) # the first part of the message is length
    client_socket.sendall(pack1 + s)

    print "[client] param1: ", data.param1, " param2: ", data.param2

第三,服务器代码(test_server.py)

from test_msg_pb2 import test_msg
import socket
import sys
import struct

address = ('localhost', 6005)
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.bind(address)

while(1):
    print "Listening"

    totallen = server_socket.recv(4)
    totallenRecv = struct.unpack('>I', totallen)[0]
    messagelen = totallenRecv - 4 
    message = server_socket.recv(messagelen)

    msg = test_msg()
    msg.ParseFromString(message)

    print "[server] param1:", msg.param1, "param2:", msg.param2

然后,在启动服务器之后,我执行客户端并打印出以下10行,因为它发送了10次参数

[client] param1: 100 param2: 5
[client] param1: 100 param2: 5
[client] param1: 100 param2: 5
[client] param1: 100 param2: 5
[client] param1: 100 param2: 5
[client] param1: 100 param2: 5
[client] param1: 100 param2: 5
[client] param1: 100 param2: 5
[client] param1: 100 param2: 5
[client] param1: 100 param2: 5

但是,在服务器端,它打印出来

Listening
[server] param1: 0 param2: 0
Listening
[server] param1: 0 param2: 0
Listening
[server] param1: 0 param2: 0
Listening
[server] param1: 0 param2: 0
Listening
[server] param1: 0 param2: 0
Listening

因此,解压缩的数字param1和param2不正确,并且它只收到消息的一半时间。非常感谢你的帮助。

2 个答案:

答案 0 :(得分:2)

第一条建议:简化。让我们首先看看我们是否能够在没有协议缓冲区的情况下使用

client.py

import socket
import sys
import struct

address = ('localhost', 6005)
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client_socket.connect(address)

messages = ["foobar", "barbaz", "bazquxfad", "Jimmy Carter"]

for s in messages:

    totallen = 4 + len(s) 
    pack1 = struct.pack('>I', totallen) # the first part of the message is length
    client_socket.sendall(s)

server.py

import socket
import sys
import struct

address = ('localhost', 6005)
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.bind(address)

while True:
    print "Listening"

    # totallen = server_socket.recv(4)
    # totallenRecv = struct.unpack('>I', totallen)[0]
    # messagelen = totallenRecv - 4 
    message = server_socket.recv(1000)

    print message

现在,我不是套接字的专家(这可能是我回答过他们的第一个问题),但是如果你运行这个例子,你会获得预期的输出。这告诉我的是每个发送对应一个.recv。据推测,sockets库正在处理 length 的详细信息。当然,在接收方面,很可能你希望知道长度,以便你可以通过一个合适的maxlen。如果是这种情况,那么我认为您可能需要发送2条消息。第一条消息的长度为4,为整数长度。第二条消息应该有数据。 e.g:

client2.py

import socket
import sys
import struct

address = ('localhost', 6005)
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client_socket.connect(address)

messages = ["foobar", "barbaz", "bazquxfad", "Jimmy Carter"]

for s in messages:

    totallen = len(s) 
    pack1 = struct.pack('>I', totallen) # the first part of the message is length
    client_socket.sendall(pack1)
    client_socket.sendall(s)

server2.py

import socket
import sys
import struct

address = ('localhost', 6005)
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.bind(address)

while True:
    print "Listening"

    totallen = server_socket.recv(4)
    totallenRecv = struct.unpack('>I', totallen)[0]

    message = server_socket.recv(totallenRecv)

    print message

现在序列化应该很简单。毕竟,proto缓冲区序列化为字符串就好了。

答案 1 :(得分:1)

协议缓冲区允许您为远程过程调用(RPC)定义services。这允许您以声明性,语言无关的方式在.proto文件中定义服务器和客户端之间的接口。一旦定义了服务并且您已经编译了protos,就可以使用RPC库来抽象出所有序列化/网络样板代码。看一下gRPC,它是Google内部RPC库的开源版本,提供了一种非常干净的方式来定义服务器和客户端。