您至少需要决定两个问题:
- 如何交换消息?
- 以什么格式?
关于 1. TCP 套接字是最低级别的,您需要处理低级别的事情,例如识别消息边界。此外,TCP 连接可以为您提供可靠的传递,但前提是连接未重置(例如由于临时网络故障)。如果您希望您的应用程序在 TCP 连接重置时正常恢复,您需要实现某种形式的消息确认以跟踪需要通过新连接重新发送的内容。OMQ 为您提供比普通 TCP 连接更高级别的抽象。您不需要处理字节流,而是处理整个消息。它仍然不能为您提供可靠的传递,消息可能会丢失,但它提供了几种可用于确保可靠传递的通信模式。0MQ 也是高性能的,IMO 是一个不错的选择。
关于 2,如果不需要与其他语言的互操作性,Pickle 是一个非常方便和 Pythonic 的选择。如果需要互操作性,您可以考虑 JSON,或者,如果性能是一个问题,则可以考虑二进制格式,例如 Google 协议缓冲区。最后一个选择需要最多的工作(您需要在 .idl 文件中定义消息格式),这绝对不会让人觉得 Pythonic。
看看在普通套接字上交换消息(任何可序列化的 Python 对象)的样子:
def send(sockfd, message):
string_message = cPickle.dumps(message)
write_int(sockfd, len(string_message))
write(sockfd, string_message)
def write_int(sockfd, integer):
integer_buf = struct.pack('>i', integer)
write(sockfd, integer_buf)
def write(sockfd, data):
data_len = len(data)
offset = 0
while offset != data_len:
offset += sockfd.send(data[offset:])
不错,但正如您所见,必须处理消息长度的序列化是相当低的水平。
并收到这样的消息:
def receive(self):
message_size = read_int(self.sockfd)
if message_size == None:
return None
data = read(self.sockfd, message_size)
if data == None:
return None
message = cPickle.loads(data)
return message
def read_int(sockfd):
int_size = struct.calcsize('>i')
intbuf = read(sockfd, int_size)
if intbuf == None:
return None
return struct.unpack('>i', intbuf)[0]
def read(sockfd, size):
data = ""
while len(data) != size:
newdata = sockfd.recv(size - len(data))
if len(newdata) == 0:
return None
data = data + newdata
return data
但这并不能优雅地处理错误(没有尝试确定哪些消息已成功传递)。