Pythonで壊れたパイプ(SIGPIPE)を処理する方法は?
-
05-07-2019 - |
質問
Pythonでクライアント接続ごとに新しいスレッドを作成するシンプルなマルチスレッドゲームサーバーを作成しました。私は時々、パイプ破損/ SIGPIPEエラーのためにサーバーがクラッシュすることを発見しています。プログラムが既に存在しないクライアントに応答を返そうとするときに、それが起こっていると確信しています。
これに対処する良い方法は何ですか?私が推奨する解決策は、プログラム全体を終了するのではなく、クライアントへのサーバー側の接続を閉じて先に進むことです。
PS:これの質問/回答は一般的な方法での問題。どのように具体的に解決すべきですか?
解決
try:ステートメントを読んでください。
try:
# do something
except socket.error, e:
# A socket error
except IOError, e:
if e.errno == errno.EPIPE:
# EPIPE error
else:
# Other error
他のヒント
標準のソケットモジュールを使用していると仮定すると、 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はこの種のことには適していますが、かなりの量のコードをすでに作成しているようです。
SIGPIPE
(おそらく EPIPE
を意味すると思いますか?)は、ソケットをシャットダウンしてからデータを送信すると、ソケットで発生します。簡単な解決策は、データを送信する前にソケットをシャットダウンしないことです。これはパイプでも発生する可能性がありますが、ネットワークサーバーであるため、実際に発生しているようには聞こえません。
各スレッドのトップレベルハンドラーで例外をキャッチするバンドエイドを適用することもできます。
もちろん、クライアント接続ごとに新しいスレッドを生成するのではなく、ツイストを使用した場合、おそらくこの問題はありません。複数のスレッドが同じI / Oチャネルを処理している場合、閉じる操作と書き込み操作の順序を正しくするのは非常に困難です(アプリケーションによっては不可能な場合があります)。
同じ質問に直面しています。しかし、次回同じコードを送信すると、動作します。 初めて壊れたとき:
$ packet_write_wait: Connection to 10.. port 22: Broken pipe
2回目の動作:
[1] Done nohup python -u add_asc_dec.py > add2.log 2>&1
理由は、現在のサーバー環境に関するものと思われます。
私の答えは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
where" 23" EPIPEから取得するエラー番号です。これにより、アクセス権エラーやその他の装備されていないものを処理しようとしません。