基本的に、socket.recv()
が読み取ることができるものを返すか、反対側がシャットダウンしたことを通知する空の文字列を返すことをいくつかの場所で読みました(公式ドキュメントでは、シャットダウン...素晴らしい!)。 recv()
は実際に受信するものがある場合にのみ戻るため、空の文字列を返す場合はMUST反対側が接続を閉じたことを意味しますか?
OK私は少し検索しましたが(十分ではないかもしれませんが、誰が知っているのでしょうか?)、非ブロッキングソケットを使用して相手がいつ接続を閉じたかを知る方法がわかりません。これを伝えるメソッドや属性はないようで、recv()
の戻り値を空の文字列と比較することは絶対に役に立たないように思われます。
簡単な例として、ソケットのタイムアウトを1.2342342(ここで好きな負でない数値)秒に設定し、socket.recv(1024)
を呼び出しますが、反対側はその1.2342342秒の間に何も送信しません。 recv()
呼び出しは空の文字列を返しますが、接続がまだ立っているかどうかはわかりません...
使用可能なデータがない非ブロッキングソケットの場合、recvはsocket.error例外をスローし、例外の値にはEAGAINまたはEWOULDBLOCKのいずれかのerrnoが含まれます。例:
import sys
import socket
import fcntl, os
import errno
from time import sleep
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1',9999))
fcntl.fcntl(s, fcntl.F_SETFL, os.O_NONBLOCK)
while True:
try:
msg = s.recv(4096)
except socket.error, e:
err = e.args[0]
if err == errno.EAGAIN or err == errno.EWOULDBLOCK:
sleep(1)
print 'No data available'
continue
else:
# a "real" error occurred
print e
sys.exit(1)
else:
# got a message, do something :)
socket.settimeout(n)
または socket.setblocking(False)
を指定してタイムアウトすることで非ブロック動作を有効にした場合、状況は少し異なります。この場合、socket.errorは引き続き発生しますが、タイムアウトの場合、付随する例外の値は常に「timed out」に設定された文字列です。したがって、このケースを処理するには、次のようにします。
import sys
import socket
from time import sleep
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1',9999))
s.settimeout(2)
while True:
try:
msg = s.recv(4096)
except socket.timeout, e:
err = e.args[0]
# this next if/else is a bit redundant, but illustrates how the
# timeout exception is setup
if err == 'timed out':
sleep(1)
print 'recv timed out, retry later'
continue
else:
print e
sys.exit(1)
except socket.error, e:
# Something else happened, handle error, exit, etc.
print e
sys.exit(1)
else:
if len(msg) == 0:
print 'orderly shutdown on server end'
sys.exit(0)
else:
# got a message do something :)
コメントに示されているように、これは、ソケットを非ブロックモードにするOS固有の機能に依存しないため、より移植性の高いソリューションです。
詳細については、 recv(2) および python socket を参照してください。
簡単です。recv()
が0バイトを返す場合。 この接続でこれ以上データを受信しません。これまで。 送信できる可能性があります。
これは、使用可能なデータがなく、接続がまだ有効な場合(相手側が送信する場合)、非ブロッキングソケットが例外を発生させる必要があることを意味します(システムに依存する可能性があります)。
recv
に関連してselect
を使用する場合、ソケットの読み取り準備はできているが、読み取るデータがない場合は、クライアントが接続を閉じたことを意味します。
これを処理するコードを次に示します。また、whileループでrecv
が2回目に呼び出されたときにスローされる例外にも注意してください。読むものが残っていない場合、この例外がスローされます。これは、クライアントが接続を閉じたことを意味するものではありません。
def listenToSockets(self):
while True:
changed_sockets = self.currentSockets
ready_to_read, ready_to_write, in_error = select.select(changed_sockets, [], [], 0.1)
for s in ready_to_read:
if s == self.serverSocket:
self.acceptNewConnection(s)
else:
self.readDataFromSocket(s)
そして、データを受け取る関数:
def readDataFromSocket(self, socket):
data = ''
buffer = ''
try:
while True:
data = socket.recv(4096)
if not data:
break
buffer += data
except error, (errorCode,message):
# error 10035 is no data available, it is non-fatal
if errorCode != 10035:
print 'socket.error - ('+str(errorCode)+') ' + message
if data:
print 'received '+ buffer
else:
print 'disconnected'
既存の回答を完了するために、 ノンブロッキングソケットの代わりにselectを使用 をお勧めします。ポイントは、ノンブロッキングソケットは(おそらく送信を除く)ものを複雑にするということです。したがって、それらを使用する理由はまったくないと思います。 IOの待機中にアプリがブロックされるという問題が定期的に発生する場合は、バックグラウンドで別のスレッドでIOを実行することも検討します。