1. ホーム
  2. python

Pythonのsocket.recv()は、ノンブロッキングソケットの場合、タイムアウトが発生するまでデータを受信しなかった場合、何を返すのでしょうか?

2023-09-26 18:54:14

質問

基本的に、私はいくつかの場所で次のように読みました。 socket.recv() は読み取れるものなら何でも返すか、相手側がシャットダウンしたことを示す空の文字列を返すということです (公式ドキュメントでは、接続がシャットダウンしたときに何を返すかについて触れていません...素晴らしい!)。これはブロッキングソケットの場合、すべてうまくいき、ダンディです。 recv() は実際に受信するものがあるときだけ返すと知っているので、 空文字列を返したとき、それは を返さなければなりません。 は相手側が接続を閉じたことを意味しますよね?

わかりました、しかし、私のソケットがノンブロッキングである場合はどうなるのでしょうか?私は少し探しましたが(十分ではないかもしれませんが、誰が知っていますか)、ノンブロッキングソケットを使用して相手側が接続を閉じたときにそれを伝える方法を見つけることができません。これを教えてくれるメソッドや属性はないようです。 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 ソケット をご覧ください。