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

決定する必要があるのは、少なくとも 2 つの問題です。

  1. メッセージを交換するには?
  2. どの形式で?

1について。TCPソケットは最低レベルであり、メッセージ境界の認識などの低レベルの処理を行う必要があります。また、TCP 接続は信頼性の高い配信を提供しますが、接続がリセットされない限り (たとえば、一時的なネットワーク障害が原因で) のみです。TCP 接続がリセットされたときにアプリケーションを正常に回復させたい場合は、新しい接続を介して再送信する必要があるものを追跡するために、何らかの形式のメッセージ確認応答を実装する必要があります。OMQ は、プレーンな TCP 接続よりも高いレベルの抽象化を提供します。バイト ストリームを処理する必要はありませんが、メッセージ全体を処理する必要があります。それでも信頼性の高い配信は得られず、メッセージが失われる可能性がありますが、信頼性の高い配信を保証するために使用できるいくつかの通信パターンが提供されます。0MQ もパフォーマンスが高く、IMO は良い選択です。

2 に関しては、他の言語との相互運用性が必要ない場合、Pickle は非常に便利で Pythonic の選択肢です。相互運用性が必要な場合は、JSON を検討できます。パフォーマンスが問題になる場合は、Google プロトコル バッファなどのバイナリ形式を検討できます。この最後の選択は、ほとんどの作業を必要とします (.idl ファイルでメッセージ形式を定義する必要があります)。

プレーンなソケットを介したメッセージ (シリアライズ可能な任意の 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 に答える