Wie stelle ich ein Timeout für die Socket-Recv-Methode von Python ein?

110

Ich muss das Zeitlimit für die Socket-Recv-Methode von Python festlegen. Wie es geht?

Thoreller
quelle
2
Zu Ihrer Information, wenn Sie Timeouts verwenden möchten, müssen Sie wissen, wie Sie mit Timeouts umgehen. Diese SO-Frage befasst sich mit der Behandlung, wenn eine Zeitüberschreitung auftritt
Trevor Boyd Smith

Antworten:

125

Der typische Ansatz besteht darin, mit select () zu warten, bis Daten verfügbar sind oder das Timeout auftritt. Rufen Sie nur an, recv()wenn tatsächlich Daten verfügbar sind. Um sicher zu gehen, setzen wir den Socket auch auf den nicht blockierenden Modus, um sicherzustellen, dass er recv()niemals auf unbestimmte Zeit blockiert. select()kann auch verwendet werden, um auf mehr als einen Socket gleichzeitig zu warten.

import select

mysocket.setblocking(0)

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

Wenn Sie viele offene Dateideskriptoren haben, ist poll () eine effizientere Alternative zu select().

Eine andere Möglichkeit besteht darin, ein Zeitlimit für alle Vorgänge am Socket mit socket.settimeout()festzulegen. Ich sehe jedoch, dass Sie diese Lösung in einer anderen Antwort explizit abgelehnt haben.

Daniel Stutzbach
quelle
16
Verwendung selectist gut, aber der Teil, in dem Sie "Sie können nicht" sagen, ist irreführend, da es gibt socket.settimeout().
Nosklo
1
Es ist jetzt besser, aber ich sehe nicht, wo die Antwort "explizit abgelehnt" wurde.
Nosklo
7
Eine Warnung bei der Verwendung select: Wenn Sie auf einem Windows-Computer ausgeführt werden, sollten Sie selectsich auf die WinSock-Bibliothek verlassen, die die Gewohnheit hat, zurückzukehren, sobald einige Daten eingegangen sind , aber nicht unbedingt alle . Sie müssen also eine Schleife einbauen, um so lange anzurufen, select.select()bis alle Daten empfangen wurden. Wie Sie wissen, dass Sie alle Daten erhalten haben, müssen Sie (leider) herausfinden. Dies kann bedeuten, dass Sie nach einer Abschlusszeichenfolge, einer bestimmten Anzahl von Bytes suchen oder nur auf ein definiertes Zeitlimit warten.
JDM
4
Warum muss der Socket nicht blockiert werden? Ich denke nicht, dass dies für den Auswahlaufruf von Bedeutung ist (und er blockiert, bis ein Deskriptor gelesen werden kann oder das Timeout in diesem Fall abläuft), und recv () wird nicht blockiert, wenn die Auswahl erfüllt ist. Ich habe es mit recvfrom () versucht und es scheint korrekt zu funktionieren, ohne setblocking (0).
HankB
1
Wäre ready[0]nur falsch, wenn die Antwort des Servers keinen Text enthält?
Matanster
60

da ist socket.settimeout()

nosklo
quelle
16
Das Recv läuft nicht ab (zumindest als ich es ausprobiert habe). Nur für accept () ist eine Zeitüberschreitung aufgetreten.
Oren S
9
Die Zeit für socket.recv () scheint für mich in Ordnung zu sein, nachdem socket.settimeout () genau wie beabsichtigt eingestellt wurde. Mache ich das nach? Kann das noch jemand bestätigen?
Aeonaut
3
@Aeonaut Ich denke, dass diesmal die meiste Zeit recv () abläuft, aber es gibt eine Rennbedingung. In socket.recv () ruft Python (2.6) select / poll intern mit dem Timeout auf und recv () wird direkt danach aufgerufen. Wenn Sie also einen blockierenden Socket verwenden und zwischen diesen beiden Aufrufen der andere Endpunkt abstürzt, können Sie unbegrenzt an recv () hängen bleiben. Wenn Sie einen nicht blockierenden Socket verwenden, ruft Python select.select nicht intern auf, daher denke ich, dass Daniel Stutzbachs Antwort der richtige Weg ist.
emil.p.stanchev
4
Eigentlich habe ich wahrscheinlich falsch verstanden, als select () zurückgegeben wurde. Bitte kratzen Sie den vorherigen Kommentar. Im Beej-Handbuch heißt es, dass das oben Gesagte eine gültige Methode ist, um ein Timeout auf recv zu implementieren: beej.us/guide/bgnet/output/html/singlepage/…. Ich vertraue also darauf, dass es sich um eine maßgebliche Quelle handelt.
emil.p.stanchev
2
Ich bin mir nicht sicher, warum die verwendete Lösung selectbevorzugt wird, wenn diese Lösung ein Einzeiler ist (einfacher zu warten, weniger Risiko bei der Implementierung von Fehlern) und select unter der Haube verwendet (die Implementierung entspricht der Antwort von @DanielStuzbach).
Trevor Boyd Smith
33

Wie bereits erwähnt select.select()und socket.settimeout()wird funktionieren.

Beachten Sie, dass Sie möglicherweise settimeoutzweimal anrufen müssen, um Ihre Anforderungen zu erfüllen, z

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)
ubershmekel
quelle
3
Ich denke, er kommt in die gleiche Sache, in der ich bin, egal wie Sie diese Funktion anstoßen und anstoßen, sie hängt. Ich habe jetzt 2 oder 4 Timeouts ausprobiert und es hängt immer noch. Settimeout hängt auch.
Casey Daniel
1
Wenn Sie .settimeout()mehr als einmal aufrufen, können Sie die setdefaulttimeout()Methode an erster Stelle aufrufen .
mvarge
12

Sie können das Zeitlimit vor dem Empfang der Antwort festlegen und nach Erhalt der Antwort auf Keine zurücksetzen:

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

sock.settimeout(5.0)
data = sock.recv(1024)
sock.settimeout(None)
Noriko
quelle
5

Das gesuchte Zeitlimit ist das Zeitlimit des Verbindungssockets und nicht das des primären Sockets, wenn Sie die Serverseite implementieren. Mit anderen Worten, es gibt ein anderes Zeitlimit für das Verbindungssocketobjekt, das die Ausgabe der socket.accept()Methode ist. Deshalb:

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.

Wenn Sie die Client-Seite implementieren, wäre dies einfach.

sock.connect(server_address)
sock.settimeout(3)
Vala
quelle
2

Wie in früheren Antworten erwähnt, können Sie Folgendes verwenden: .settimeout() Zum Beispiel:

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()

Ich hoffe das hilft!!

meep
quelle
2

Sie können verwenden, socket.settimeout()welches ein ganzzahliges Argument akzeptiert, das die Anzahl der Sekunden darstellt. Setzt beispielsweise socket.settimeout(1)das Zeitlimit auf 1 Sekunde

Brian Zheng
quelle
2

Versuchen Sie dies, es verwendet das zugrunde liegende C.

timeval = struct.pack('ll', 2, 100)
s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeval)
Mohammad Alkhaldi
quelle
Das ist großartig, da es eine erlaubt es, unterschiedliche Werte für Sende- und recv Timeout einstellen mit SO_RCVTIMEOund SO_SNDTIMEO.
jtpereyda
Warum 2und warum 100? Welches ist der Timeout-Wert? In welcher Einheit?
Alfe
timeval = struct.pack('ll', sec, usec) s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeval)usec = 10000 bedeutet 10 ms
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
Sheng
quelle
0

Rufen Sie an: https://boltons.readthedocs.io/en/latest/socketutils.html

Es bietet einen gepufferten Socket, der viele sehr nützliche Funktionen bietet, wie zum Beispiel:

.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)
Caesurus
quelle