web-dev-qa-db-ja.com

タイムアウトが発生するまでデータが受信されない場合、Pythonのsocket.recv()は非ブロッキングソケットに対して何を返しますか?

基本的に、socket.recv()が読み取ることができるものを返すか、反対側がシャットダウンしたことを通知する空の文字列を返すことをいくつかの場所で読みました(公式ドキュメントでは、シャットダウン...素晴らしい!)。 recv()は実際に受信するものがある場合にのみ戻るため、空の文字列を返す場合はMUST反対側が接続を閉じたことを意味しますか?

OK私は少し検索しましたが(十分ではないかもしれませんが、誰が知っているのでしょうか?)、非ブロッキングソケットを使用して相手がいつ接続を閉じたかを知る方法がわかりません。これを伝えるメソッドや属性はないようで、recv()の戻り値を空の文字列と比較することは絶対に役に立たないように思われます。

簡単な例として、ソケットのタイムアウトを1.2342342(ここで好きな負でない数値)秒に設定し、socket.recv(1024)を呼び出しますが、反対側はその1.2342342秒の間に何も送信しません。 recv()呼び出しは空の文字列を返しますが、接続がまだ立っているかどうかはわかりません...

49

使用可能なデータがない非ブロッキングソケットの場合、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 を参照してください。

66
mshildt

簡単です。recv()が0バイトを返す場合。 この接続でこれ以上データを受信しません。これまで。 送信できる可能性があります。

これは、使用可能なデータがなく、接続がまだ有効な場合(相手側が送信する場合)、非ブロッキングソケットが例外を発生させる必要があることを意味します(システムに依存する可能性があります)。

7
jfs

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'
5

既存の回答を完了するために、 ノンブロッキングソケットの代わりにselectを使用 をお勧めします。ポイントは、ノンブロッキングソケットは(おそらく送信を除く)ものを複雑にするということです。したがって、それらを使用する理由はまったくないと思います。 IOの待機中にアプリがブロックされるという問題が定期的に発生する場合は、バックグラウンドで別のスレッドでIOを実行することも検討します。

2
Ulrich Eckhardt