4

我有一个 Python 中的机器控制系统,目前看起来大致像这样

goal = GoalState()
while True:
    current = get_current_state()
    move_toward_goal(current,goal)

现在,我正在尝试添加通过网络控制机器的能力。我想写的代码是这样的:

goal = GoalState()
while True:
    if message_over_network():
        goal = new_goal_from_message()
    current = get_current_state()
    move_toward_goal(current,goal)

将这种网络功能添加到我的应用程序中的最简单和最 Pythonic 的方法是什么?套接字可以工作,以为它们不是特别感觉 Pythonic。我看过 XMLRPC 和 Twisted,但两者似乎都需要对代码进行重大修改。我还查看了 ØMQ,但感觉就像我添加了一个外部依赖项,它没有提供任何我在套接字中没有的东西。

我不反对使用我上面提到的任何系统,因为我认为失败可能是我的误解。我只是对处理这个简单、常见问题的惯用方式感到好奇。

4

2 回答 2

6

您至少需要决定两个问题:

  1. 如何交换消息?
  2. 以什么格式?

关于 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

但这并不能优雅地处理错误(没有尝试确定哪些消息已成功传递)。

于 2012-07-02T22:31:20.327 回答
3

如果您熟悉套接字,我会考虑SocketServer.UDPServer(请参阅http://docs.python.org/library/socketserver.html#socketserver-udpserver-example)。UDP 绝对是最简单的消息传递系统,但显然,您必须处理某些消息可能会丢失、重复或乱序传递的事实。如果您的协议非常简单,则相对容易处理。优点是您不需要任何额外的线程,也不需要外部依赖项。如果您的应用程序没有会话的概念,它也可能是一个很好的选择。

开始可能会很好,但是您的问题中没有包含更多需要考虑的细节。我也不会担心套接字不是非常 Pythonic 的事实。最后,无论如何你都会使用套接字,有人会为你包装它们,你将被迫学习框架,在最好的情况下,这可能会超出你的要求。

(请注意,我的观点非常有偏见,因为我喜欢处理原始套接字。)

于 2012-07-02T22:44:58.797 回答