Come impostare il timeout sul metodo recv socket di python?


Risposte:


126

L'approccio tipico consiste nell'usare select () per attendere che i dati siano disponibili o fino a quando non si verifica il timeout. Chiama solo recv()quando i dati sono effettivamente disponibili. Per sicurezza, impostiamo anche il socket in modalità non bloccante per garantire che recv()non si bloccherà mai indefinitamente. select()può anche essere utilizzato per attendere su più di un socket alla volta.

import select

mysocket.setblocking(0)

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

Se hai molti descrittori di file aperti, poll () è un'alternativa più efficiente a select().

Un'altra opzione è impostare un timeout per tutte le operazioni sul socket utilizzando socket.settimeout(), ma vedo che hai esplicitamente rifiutato quella soluzione in un'altra risposta.


16
l'uso selectè buono, ma la parte in cui dici "non puoi" è fuorviante, poiché c'è socket.settimeout().
nosklo

1
Adesso va meglio, ma non vedo dove sia stata la risposta "esplicitamente rifiutata".
nosklo

7
Un avvertimento sull'utilizzo select: se si esegue su una macchina Windows, selectsi fa affidamento sulla libreria WinSock, che ha l'abitudine di tornare non appena sono arrivati alcuni dati, ma non necessariamente tutti . Quindi è necessario incorporare un ciclo per continuare a chiamare select.select()fino a quando non vengono ricevuti tutti i dati. Il modo in cui sai di aver ottenuto tutti i dati spetta (sfortunatamente) a te capirlo: potrebbe significare cercare una stringa di terminazione, un certo numero di byte o semplicemente aspettare un timeout definito.
JDM

4
Perché è necessario impostare il socket senza blocco? Non penso che sia importante per la chiamata di selezione (e si blocca fino a quando un descrittore non può essere letto o il timeout scade in questo caso) e recv () non si bloccherà se la selezione è soddisfatta. L'ho provato usando recvfrom () e sembra funzionare correttamente senza setblocking (0).
HankB

1
Sarebbe ready[0]falso solo se non c'è il corpo nella risposta del server?
matanster

60

16
Non va in timeout il recv (almeno quando l'ho provato). Solo il accept () è scaduto.
Oren S

9
Il socket.recv () sembra scadere per me perfettamente dopo aver impostato socket.settimeout (), esattamente come previsto. Lo sto inventando? Qualcun altro può confermarlo?
Aeonaut

3
@Aeonaut Penso che questo time out recv () per la maggior parte del tempo, ma c'è una condizione di gara. In socket.recv () Python (2.6) chiama select / poll internamente con il timeout e poi recv () viene chiamato subito dopo. Quindi, se usi un socket di blocco e tra queste 2 chiamate l'altro endpoint si blocca, puoi finire per rimanere sospeso indefinitamente su recv (). Se usi socket non bloccanti, python non chiama select.select internamente, quindi penso che la risposta di Daniel Stutzbach sia il modo corretto.
emil.p.stanchev

4
In realtà, probabilmente ho frainteso quando select () ritorna, quindi per favore cancella il commento precedente. La Guida di Beej dice che quanto sopra è un modo valido per implementare un timeout su recv: beej.us/guide/bgnet/output/html/singlepage/… quindi mi fiderò che sia una fonte autorevole.
emil.p.stanchev

2
Non sono sicuro del motivo per cui la soluzione che utilizza selectè preferita quando questa soluzione è una linea (più facile da mantenere, meno rischi nell'implementazione sbagliata) e utilizza la selezione sotto il cofano (l'implementazione è la stessa della risposta @DanielStuzbach).
Trevor Boyd Smith

33

Come accennato entrambi select.select()e socket.settimeout()funzioneranno.

Nota che potresti dover chiamare settimeoutdue volte per le tue esigenze, ad es

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
Penso che stia entrando nella stessa cosa in cui sono io, non importa quanto tu colpisca e spinga questa funzione, si blocca. Ho provato 2 o 4 timeout ora e si blocca ancora. anche settimeout si blocca.
Casey Daniel

1
Poiché chiami .settimeout()più di una volta, potresti chiamare il setdefaulttimeout()metodo in primo luogo.
mvarge

12

È possibile impostare il timeout prima di ricevere la risposta e dopo aver ricevuto la risposta impostarlo di nuovo su Nessuno:

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

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

5

Il timeout che stai cercando è il timeout del socket di connessione e non quello del socket primario, se implementi il ​​lato server. In altre parole, c'è un altro timeout per l'oggetto socket di connessione, che è l'output di socket.accept()metodo. Perciò:

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.

Se implementi il ​​lato client, sarebbe semplice.

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

2

Come accennato nelle risposte precedenti, puoi usare qualcosa come: .settimeout() Ad esempio:

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

Spero che aiuti!!


2

È possibile utilizzare socket.settimeout()che accetta un argomento intero che rappresenta il numero di secondi. Ad esempio, socket.settimeout(1)imposterà il timeout a 1 secondo


2

prova questo utilizza il sottostante C.

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

Questo è ottimo, in quanto consente di impostare valori diversi per il timeout di invio e ricezione utilizzando SO_RCVTIMEOe SO_SNDTIMEO.
jtpereyda

Perché 2e perché 100? Qual è il valore di timeout? In quale unità?
Alfe

timeval = struct.pack('ll', sec, usec) s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeval)usec = 10000 significa 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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.