Pythonのソケットrecvメソッドでタイムアウトを設定するにはどうすればよいですか?


110

Pythonのソケットrecvメソッドにタイムアウトを設定する必要があります。どうやってするの?


2
タイムアウトを使用する場合は、タイムアウトの処理方法を知っておく必要があります。このSOの質問タイムアウトが発生した場合の取り扱いについて協議:stackoverflow.com/questions/16745409
トレバー・ボイド・スミス

回答:


125

典型的なアプローチは、select()を使用して、データが利用可能になるまで、またはタイムアウトが発生するまで待機することです。recv()データが実際に利用可能な場合にのみ呼び出します。安全のために、ソケットを非ブロックモードに設定して、recv()無期限にブロックされないことを保証します。 select()同時に複数のソケットで待機するためにも使用できます。

import select

mysocket.setblocking(0)

ready = select.select([mysocket], [], [], timeout_in_seconds)
if ready[0]:
    data = mysocket.recv(4096)

開いているファイル記述子がたくさんある場合、poll()はのより効率的な代替手段select()です。

別のオプションは、を使用してソケットのすべての操作にタイムアウトを設定するsocket.settimeout()ことですが、別の回答でその解決策を明示的に拒否したことがわかります。


16
使い勝手selectは良いのですが、「できない」と書いてある箇所が誤解を招きsocket.settimeout()ます。
nosklo

1
今はもっと良いですが、答えが「明示的に拒否された」のはどこかわかりません。
nosklo 2010

7
使用に関する1つの注意select-Windowsマシンで実行している場合select、WinSockライブラリを使用してください。WinSockライブラリは、一部のデータが到着するとすぐに戻るという習慣がありますが、必ずしもすべてではありません。したがってselect.select()、すべてのデータが受信されるまで呼び出しを続けるためにループを組み込む必要があります。すべてのデータを取得したかどうかは、(残念ながら)把握する次第です。つまり、ターミネーター文字列、特定のバイト数を探すか、定義されたタイムアウトを待つだけです。
JDM

4
ソケットを非ブロッキングなしに設定する必要があるのはなぜですか?select呼び出しは重要ではないと思います(そして、記述子が読み取れるか、この場合はタイムアウトが期限切れになるまでブロックします)。selectが満たされている場合、recv()はブロックしません。recvfrom()を使用して試しましたが、setblocking(0)がなくても正常に動作するようです。
HankB 2015年

1
だろうready[0]何のボディは、サーバーの応答がない場合にのみfalseに?
matanster

60

あります socket.settimeout()


16
それはrecvをタイムアウトしません(少なくとも私がそれを試みたとき)。accept()のみがタイムアウトになります。
Oren S

9
socket.recv()は、socket.settimeout()を設定した後で、意図したとおりにタイムアウトしたようです。私はこれを作っていますか?他の誰かがこれを確認できますか?
Aeonaut、2011年

3
@Aeonautこれはほとんどの場合recv()をタイムアウトさせると思いますが、競合状態があります。socket.recv()では、Python(2.6)がタイムアウトを指定して内部的にselect / pollを呼び出し、直後にrecv()が呼び出されます。そのため、ブロッキングソケットを使用し、これら2つの呼び出しの間に他のエンドポイントがクラッシュすると、recv()で無期限にハングアップする可能性があります。非ブロッキングソケットを使用する場合、Pythonは内部でselect.selectを呼び出さないため、Daniel Stutzbachの答えが正しい方法だと思います。
emil.p.stanchev

4
実際、select()が戻ったときに誤解していると思いますので、前のコメントをスクラッチしてください。Beejのガイドによると、上記はrecvにタイムアウトを実装する有効な方法です:beej.us/guide/bgnet/output/html/singlepage/…信頼できるソースであると私は信じます。
emil.p.stanchev 2012

2
selectこのソリューションが1つのライナー(保守が容易で、間違った実装のリスクが少ない)であり、内部でselectを使用している(実装は@DanielStuzbachの回答と同じ)場合に、使用するソリューションが推奨される理由がわかりません。
Trevor Boyd Smith

33

両方select.select()socket.settimeout()も述べたように動作します。

必要にsettimeout応じて、2回呼び出す必要がある場合があることに注意してください。

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(("",0))
sock.listen(1)
# accept can throw socket.timeout
sock.settimeout(5.0)
conn, addr = sock.accept()

# recv can throw socket.timeout
conn.settimeout(5.0)
conn.recv(1024)

3
私は彼が私と同じことをしていると思います、あなたがこの機能をぶら下げて、この機能がぶら下がっていても関係ありません。私は今2つまたは4つのタイムアウトを試しましたが、それでもハングします。settimeoutもハングします。
ケーシーダニエル

1
あなたが呼び出しとして.settimeout()複数回あなたが呼び出すことができますsetdefaulttimeout()最初の場所での方法を。
mvarge 2016年

12

応答を受信する前にタイムアウトを設定し、応答を受信した後でタイムアウトをNoneに戻すことができます。

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

sock.settimeout(5.0)
data = sock.recv(1024)
sock.settimeout(None)

5

サーバー側を実装する場合、探しているタイムアウトは、プライマリソケットのタイムアウトではなく、接続ソケットのタイムアウトです。つまり、接続ソケットオブジェクトには別のタイムアウトがあります。これは、socket.accept()メソッド。したがって:

sock.listen(1)
connection, client_address = sock.accept()
connection.settimeout(5)    # This is the one that affects recv() method.
connection.gettimeout()     # This should result 5
sock.gettimeout()           # This outputs None when not set previously, if I remember correctly.

クライアント側を実装する場合、それは簡単です。

sock.connect(server_address)
sock.settimeout(3)

2

以前の返信で述べたように、次のようなものを使用できます.settimeout() 。例:

import socket

s = socket.socket()

s.settimeout(1) # Sets the socket to timeout after 1 second of no activity

host, port = "somehost", 4444
s.connect((host, port))

s.send("Hello World!\r\n")

try:
    rec = s.recv(100) # try to receive 100 bytes
except socket.timeout: # fail after 1 second of no activity
    print("Didn't receive data! [Timeout]")
finally:
    s.close()

これが役に立てば幸いです!!


2

socket.settimeout()秒数を表す整数引数を受け入れるwhichを使用できます。たとえばsocket.settimeout(1)、タイムアウトを1秒に設定します


2

基礎となるCを使用してこれを試してください。

timeval = struct.pack('ll', 2, 100)
s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeval)

それは使用して、タイムアウト送信とrecvのための異なる値を設定する1することができますので、これは素晴らしいですSO_RCVTIMEOSO_SNDTIMEO
jtpereyda

なぜ2そしてなぜ100?タイムアウト値はどれですか?どのユニットで?
Alfe、

timeval = struct.pack('ll', sec, usec) s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeval)usec = 10000は10ミリ秒を意味します
Tavy

1
#! /usr/bin/python3.6

# -*- coding: utf-8 -*-
import socket
import time
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.settimeout(5)
PORT = 10801

s.bind(('', PORT))
print('Listening for broadcast at ', s.getsockname())
BUFFER_SIZE = 4096
while True:
    try:
        data, address = s.recvfrom(BUFFER_SIZE)
    except socket.timeout:
        print("Didn't receive data! [Timeout 5s]")
        continue

0

叫ぶ:https : //boltons.readthedocs.io/en/latest/socketutils.html

バッファ付きソケットを提供します。これにより、次のような非常に便利な機能が数多く提供されます。

.recv_until()    #recv until occurrence of bytes
.recv_closed()   #recv until close
.peek()          #peek at buffer but don't pop values
.settimeout()    #configure timeout (including recv timeout)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.