web-dev-qa-db-ja.com

python socket.settimeout()を適切に使用する方法

私が知る限り、socket.settimeout(value)を呼び出し、0.0より大きい浮動小数点値を設定すると、たとえば_socket.recv_への呼び出しが待機する必要がある場合、そのソケットはscocket.timeoutを発生させます指定された値より長い。

しかし、大量のデータを受信する必要があり、recv()を数回呼び出さなければならない場合を想像してください。そうすると、settimeoutはどのように影響しますか?

次のコードがあるとします:

_to_receive = # an integer representing the bytes we want to receive
socket = # a connected socket

socket.settimeout(20)
received = 0
received_data = b""

while received < to_receive:
    tmp = socket.recv(4096)
    if len(tmp) == 0:
        raise Exception()
    received += len(tmp)
    received_data += tmp

socket.settimeout(None)
_

コードの3行目は、ソケットのタイムアウトを20秒に設定します。そのタイムアウトはすべての反復をリセットしますか?これらの反復の1つに20秒以上かかる場合にのみ、タイムアウトが発生しますか?

A)予想されるすべてのデータを受信するのに20秒以上かかる場合に例外を発生させるように、それを再コーディングするにはどうすればよいですか?

B)すべてのデータを読み取った後でタイムアウトを[なし]に設定しないと、何か問題が発生する可能性がありますか? (接続はキープアライブであり、将来さらに多くのデータが要求される可能性があります)。

10
Jorky10

タイムアウトは、ソケットの読み取り/書き込み操作の1回の呼び出しに適用されます。したがって、次の呼び出しは再び20秒になります。

A)いくつかの重要な呼び出しで共有されるタイムアウトを設定するには、手動でタイムアウトを追跡する必要があります。これらの線に沿った何か:

deadline = time.time() + 20.0
while not data_received:
    if time.time() >= deadline:
        raise Exception() # ...
    socket.settimeout(deadline - time.time())
    socket.read() # ...

B)タイムアウト付きのソケットを使用していて、socket.timeout例外を処理する準備ができていないコードは失敗する可能性があります。操作を開始する前にソケットのタイムアウト値を記憶しておき、完了したらそれを復元する方が信頼性が高くなります。

def my_socket_function(socket, ...):
    # some initialization and stuff
    old_timeout = socket.gettimeout() # Save
    # do your stuff with socket
    socket.settimeout(old_timeout) # Restore
    # etc

このように、ソケットのタイムアウトでどちらが実行されても、関数はそれを呼び出しているコードの機能に影響を与えません。

4
Lav

タイムアウトは、recv()の各呼び出しに適用されます。

A)既存のタイムアウトを使用して、recv(to_receive)を呼び出すだけです。 1回のrecv呼び出しですべてのデータを受信するようにしてください-実際には、これをデフォルトの動作方法として使用してはならない理由がわかりません

B)悪いことは何も起こりませんが、そのソケットを使用する他のコードは、タイムアウトの処理に注意する必要があります。

既存のコードでは、recv()呼び出しをrecv(max(4096、to_receive-received))にしないでください。これにより、to_receiveバイトの後に続くデータが誤って消費されなくなります。

1
barny

私のサーバースクリプトを参照してください。正しく使用するためのアイデアが得られます。

import socket
import sys
fragments = []
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("192.168.1.4",9001))
s.listen(5)
while True:
    c,a = s.accept()
    c.settimeout(10.0)
    print "Someone came in Server from %s and port %s" %(a[0],a[1])
    c.send("Welcome to system")
    while True:
        chunk = c.recv(2048)
        if not chunk.strip():
            break
        else:
            fragments.append(chunk)
            continue
    combiner = "".join(fragments)
    print combiner
    shutdown = str(raw_input("Wanna Quit(Y/y) or (N/n): "))
    if shutdown == 'Y' or shutdown == 'y':
        c.close()
        sys.exit()
    else:
        continue

このスクリプトは、socket.settimeout()についてのアイデアを提供するためのものです。

0