54

クライアント接続ごとに新しいスレッドを作成する単純なマルチスレッド ゲーム サーバーを Python で作成しました。ときどき、壊れたパイプ/SIGPIPE エラーが原因でサーバーがクラッシュすることがわかりました。プログラムが存在しなくなったクライアントに応答を返そうとしたときに、それが起こっていると確信しています。

これに対処する良い方法は何ですか? 私の好みの解決策は、プログラム全体を終了するのではなく、クライアントへのサーバー側接続を閉じて先に進むことです。

PS:この質問/回答は、一般的な方法で問題を扱っています。具体的にどのように解決すればよいですか?

4

5 に答える 5

55

標準のソケットモジュールを使用していると仮定すると、socket.error: (32, 'Broken pipe')例外をキャッチする必要があります(他の人が示唆しているように IOError ではありません)。これは、説明した場合、つまり、リモート側が切断されたソケットへの送信/書き込みの場合に発生します。

import socket, errno, time

# setup socket to listen for incoming connections
s = socket.socket()
s.bind(('localhost', 1234))
s.listen(1)
remote, address = s.accept()

print "Got connection from: ", address

while 1:
    try:
        remote.send("message to peer\n")
        time.sleep(1)
    except socket.error, e:
        if isinstance(e.args, tuple):
            print "errno is %d" % e[0]
            if e[0] == errno.EPIPE:
               # remote peer disconnected
               print "Detected remote disconnect"
            else:
               # determine and handle different error
               pass
        else:
            print "socket error ", e
        remote.close()
        break
    except IOError, e:
        # Hmmm, Can IOError actually be raised by the socket module?
        print "Got IOError: ", e
        break

この例外は、閉じたソケットへの最初の書き込みで常に発生するとは限らないことに注意してください。通常は 2 回目の書き込みで発生します (最初の書き込みで書き込まれたバイト数がソケットのバッファー サイズよりも大きい場合を除きます)。リモート エンドが既に切断されている可能性があるときに、最初の書き込みからデータを受信したとアプリケーションが判断した場合に備えて、この点に留意する必要があります。

select.select()(または)を使用すると、この発生率を減らすことができます (ただし、完全になくすことはできませんpoll)。書き込みを試みる前に、ピアから読み取る準備ができているデータを確認してください。selectピア ソケットから読み取ることができるデータがあると報告された場合は、 を使用して読み取りますsocket.recv()。これが空の文字列を返す場合、リモート ピアは接続を閉じています。ここにはまだ競合状態があるため、例外をキャッチして処理する必要があります。

Twisted はこの種の作業には最適ですが、すでにかなりの量のコードを作成しているように思えます。

于 2008-10-08T00:14:18.967 に答える
41

try: ステートメントを読んでください。

try:
    # do something
except socket.error, e:
    # A socket error
except IOError, e:
    if e.errno == errno.EPIPE:
        # EPIPE error
    else:
        # Other error
于 2008-10-07T20:07:27.263 に答える
3

SIGPIPE(たぶんあなたのことだと思いますがEPIPE?)ソケットをシャットダウンしてからデータを送信すると、ソケットで発生します。簡単な解決策は、データを送信する前にソケットをシャットダウンしないことです。これはパイプでも発生する可能性がありますが、ネットワーク サーバーであるため、発生しているようには思えません。

また、各スレッドの最上位ハンドラーで例外をキャッチするという応急処置を適用することもできます。

もちろん、クライアント接続ごとに新しいスレッドを生成するのではなく、Twistedを使用した場合は、おそらくこの問題は発生しません。複数のスレッドが同じ I/O チャネルを処理している場合、閉じる操作と書き込み操作の順序を正しくするのは非常に困難です (アプリケーションによっては不可能かもしれません)。

于 2008-10-07T20:55:36.327 に答える
-4

私の答えは S.Lott の答えに非常に近いですが、さらに具体的に言うと、次のようになります。

try:
    # do something
except IOError, e:
    # ooops, check the attributes of e to see precisely what happened.
    if e.errno != 23:
        # I don't know how to handle this
        raise

ここで、「23」は EPIPE から取得したエラー番号です。こうすることで、パーミッション エラーやその他の準備ができていないことを処理しようとすることはありません。

于 2008-10-07T21:05:54.400 に答える