¿Cómo configurar el tiempo de espera en el método de recepción de socket de Python?


Respuestas:


125

El enfoque típico es usar select () para esperar hasta que los datos estén disponibles o hasta que se agote el tiempo de espera. Solo llame recv()cuando los datos estén realmente disponibles. Para estar seguros, también configuramos el socket en modo sin bloqueo para garantizar que recv()nunca se bloqueará indefinidamente. select()también se puede utilizar para esperar en más de un socket a la vez.

import select

mysocket.setblocking(0)

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

Si tiene muchos descriptores de archivos abiertos, poll () es una alternativa más eficiente a select().

Otra opción es establecer un tiempo de espera para todas las operaciones en el socket que usan socket.settimeout(), pero veo que ha rechazado explícitamente esa solución en otra respuesta.


16
el uso selectes bueno, pero la parte en la que dices "no puedes" es engañosa, ya que la hay socket.settimeout().
nosklo

1
Es mejor ahora, pero no veo dónde la respuesta fue "rechazada explícitamente".
nosklo

7
Una advertencia sobre el uso select: si está ejecutando en una máquina con Windows, selectconfíe en la biblioteca WinSock, que tiene la costumbre de regresar tan pronto como llegan algunos datos, pero no necesariamente todos . Por lo que debe incorporar un bucle para seguir llamando select.select()hasta que se reciban todos los datos. Cómo saber que ha obtenido todos los datos depende de usted (desafortunadamente) de averiguarlo; puede significar buscar una cadena de terminación, una cierta cantidad de bytes o simplemente esperar un tiempo de espera definido.
JDM

4
¿Por qué es necesario configurar el socket sin bloqueo? No creo que eso importe para la llamada de selección (y se bloquea hasta que se puede leer un descriptor o el tiempo de espera expira en este caso) y recv () no se bloqueará si se satisface la selección. Lo probé usando recvfrom () y parece funcionar correctamente sin setblocking (0).
HankB

1
¿ ready[0]Solo sería falso si no hay cuerpo en la respuesta del servidor?
matanster

60

16
No agota el tiempo de recuperación (al menos cuando lo probé). Solo se agota el tiempo de aceptar ().
Oren S

9
El socket.recv () parece agotar el tiempo de espera para mí después de configurar socket.settimeout (), exactamente como se esperaba. ¿Me lo estoy inventando? ¿Alguien más puede confirmar esto?
Aeonaut

3
@Aeonaut Creo que esto se agota recv () la mayor parte del tiempo, pero hay una condición de carrera. En socket.recv () Python (2.6) llama a select / poll internamente con el tiempo de espera y luego recv () se llama inmediatamente después. Entonces, si usa un socket de bloqueo y entre estas 2 llamadas, el otro extremo falla, puede terminar colgando indefinidamente en recv (). Si usa un socket sin bloqueo, Python no llama a select.select internamente, así que creo que la respuesta de Daniel Stutzbach es la forma correcta.
emil.p.stanchev

4
En realidad, probablemente entendí mal cuando select () regresa, así que por favor, raspe el comentario anterior. Beej's Guide dice que lo anterior es una forma válida de implementar un tiempo de espera en recv: beej.us/guide/bgnet/output/html/singlepage/… así que confiaré en que es una fuente autorizada.
emil.p.stanchev

2
No estoy seguro de por qué selectse prefiere la solución que se usa cuando esta solución es de una sola línea (más fácil de mantener, menos riesgo de implementar mal) y usa select bajo el capó (la implementación es la misma que la respuesta de @DanielStuzbach).
Trevor Boyd Smith

33

Como se mencionó, ambos select.select()y socket.settimeout()funcionarán.

Tenga en cuenta que es posible que deba llamar settimeoutdos veces para sus necesidades, por ejemplo

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
Creo que se está metiendo en lo mismo que yo estoy donde no importa cómo empujes y pinches esta función que cuelga. He intentado 2 o 4 tiempos de espera ahora y todavía se cuelga. settimeout también se bloquea.
Casey Daniel

1
Si llama .settimeout()más de una vez, puede llamar al setdefaulttimeout()método en primer lugar.
mvarge

12

Puede establecer el tiempo de espera antes de recibir la respuesta y, después de recibir la respuesta, volver a establecerlo en Ninguno:

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

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

5

El tiempo de espera que está buscando es el tiempo de espera del socket de conexión, no el del socket principal, si implementa el lado del servidor. En otras palabras, hay otro tiempo de espera para el objeto de conector de conexión, que es la salida del socket.accept()método. Por lo tanto:

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.

Si implementa el lado del cliente, sería simple.

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

2

Como se mencionó en respuestas anteriores, puede usar algo como: .settimeout() Por ejemplo:

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

¡¡Espero que esto ayude!!


2

Puede usar socket.settimeout()que acepta un argumento entero que representa el número de segundos. Por ejemplo, socket.settimeout(1)establecerá el tiempo de espera en 1 segundo.


2

intente esto, utiliza el C.

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

Esto es genial, ya que permite establecer diferentes valores para el tiempo de espera de envío y recepción usando SO_RCVTIMEOy SO_SNDTIMEO.
jtpereyda

¿Por qué 2y por qué 100? ¿Cuál es el valor del tiempo de espera? En que unidad
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

0

Grita a: https://boltons.readthedocs.io/en/latest/socketutils.html

Proporciona un zócalo con búfer, esto proporciona una gran cantidad de funciones muy útiles, como:

.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)
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.