web-dev-qa-db-ja.com

Pythonで壊れたパイプ(SIGPIPE)を処理する方法は?

pythonで、各クライアント接続に新しいスレッドを作成する単純なマルチスレッドゲームサーバーを作成しました。 -pipe/SIGPIPEエラー:プログラムが、存在しないクライアントに応答を返そうとするときに発生していると確信しています。

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

PS: これ 質問/回答は一般的な方法で問題に対処します。どのように具体的に解決する必要がありますか?

45
Adam Plumb

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

try:
    # do something
except socket.error, e:
    # A socket error
except IOError, e:
    if e.errno == errno.EPIPE:
        # EPIPE error
    else:
        # Other error
37
S.Lott

標準のソケットモジュールを使用していると仮定すると、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はこの種のものには最適ですが、かなりの量のコードをすでに作成しているようです。

51
mhawke

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

また、各スレッドのトップレベルハンドラーで例外をキャッチするバンドエイドを適用することもできます。

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

3
Glyph

私は同じ質問に直面しています。しかし、次回同じコードを送信すると、うまくいきます。初めて壊れたとき:

$ packet_write_wait: Connection to 10.. port 22: Broken pipe

動作する2回目:

[1]   Done                    Nohup python -u add_asc_dec.py > add2.log 2>&1

その理由は、現在のサーバー環境にあるのかもしれません。

0
yuan