TCP 接続を介して Google Protobuf メッセージを送信するために、プレーン ジェーン、ダム ダウン、同期 Python サーバーとクライアントの組み合わせを使用しています。サーバーは、Python 2.5.4 と Google Protobuf 2.4.1 を使用して Windows XP で実行されています。クライアントは、Python 2.7.3 と Google Protobuf 2.4.1 を使用して Ubuntu 12.04 サーバーで実行されています。
XP マシンでサーバーを実行し、Ubuntu マシンのクライアントを使用してサーバーと通信しようとすると、サーバーは要求メッセージを正常に解析し、応答メッセージを正常に送信しますが、クライアントでgoogle.protobuf.message.DecodeError: Truncated message
エラーが発生します。
XP マシンでクライアントとサーバーの両方を実行してもエラーは発生しません。また、サーバーを Ubuntu マシンで実行し、クライアントを XP マシンで実行してもエラーは発生しません。
実際の Protobuf メッセージ自体と関係があるようです...下の例のような非常に単純な Protobuf メッセージを使用すると、問題なく動作するようです。ただし、本番環境で使用している実際の Protobuf メッセージの使用を開始すると、エラーが表示されます。サーバーがWindows上にあり、クライアントがUbuntu上にある場合にのみエラーが発生するのは、まだ奇妙に思えます。
何が起こっているのでしょうか?いくつかの環境上の制約があるため、Windows マシンでは Python 2.5 を使用し、Google Protobuf 2.4.1 を使用する必要があります。
例 protobuf.proto
message Foo {
required string name = 1;
}
message Bar {
required string name = 1;
}
message Sucka {
required string name = 1;
repeated Foo foos = 2;
repeated Bar bars = 3;
}
message Result {
enum Exception {
NONE = 0;
GENERIC = 1;
}
required Exception exception = 1;
optional string message = 2;
optional Sucka sucka = 3;
}
サーバー.py
import signal, socket, struct
from protobuf import Sucka, Result
class TestServer():
def __init__(self, host = '0.0.0.0', port = 9989):
self.host = host
self.port = port
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.bind((self.host, self.port))
self.socket.listen(1)
self.running = False
self.conn = None
self.run()
def run(self):
def handle_exit(signum, stack):
print 'Shutting down server in response to signal %s' % (str(signum))
self.running = False
signal.signal(signal.SIGINT, handle_exit)
signal.signal(signal.SIGTERM, handle_exit)
print 'Waiting on a connection'
self.running = True
while self.running:
try:
self.conn, addr = self.socket.accept()
print 'Connection from:', addr
size_t = self.conn.recv(4)
size = struct.unpack('!I', size_t)
data = self.conn.recv(size[0])
if not data:
raise Exception('No data... did the client close the connection?')
result = Result()
try:
sucka = Sucka()
sucka.ParseFromString(data)
result.exception = result.NONE
result.sucka.CopyFrom(sucka)
except Exception, ex:
print str(ex)
result.exception = result.GENERIC
result.message = 'Exception: %s' % (str(ex))
finally:
if self.conn: self.conn.close()
self.socket.close()
if __name__ == '__main__':
s = TestServer()
client.py
import socket, struct, time
from protobuf import Sucka, Result
class TestClient():
def __init__(self, sucka, host = '127.0.0.1', port = 9989):
self.addr = (host, port)
self.__call(sucka)
def __call(self, sucka):
size = sucka.ByteSize()
size_t = struct.pack('!I', size)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(self.addr)
sock.send(size_t)
sock.send(sucka.SerializeToString())
size_t = sock.recv(4)
size = struct.unpack('!I', size_t)
data = sock.recv(size[0])
result = Result()
result.ParseFromString(data)
if result.exception != Result.NONE:
raise Exception(result.message)
print str(result.sucka)
if __name__ == '__main__':
import sys, os
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
from protobuf import Sucka
sucka = Sucka()
sucka.name = 'testing'
for i in range(0, 25):
foo = sucka.foos.add()
foo.name = 'foo-%i-tester' % i
for i in range(0, 25):
bar = sucka.bars.add()
bar.name = 'bar-%i-tester' % i
c = TestClient(sucka, '127.0.0.1', 9989)