Comment définir le délai d'expiration sur la méthode socket recv de python?


Réponses:


126

L'approche typique consiste à utiliser select () pour attendre que les données soient disponibles ou que le délai d'expiration se produise. Appelez uniquement recv()lorsque les données sont réellement disponibles. Pour être sûr, nous avons également mis le socket en mode non bloquant pour garantir qu'il recv()ne se bloquera jamais indéfiniment. select()peut également être utilisé pour attendre sur plus d'un socket à la fois.

import select

mysocket.setblocking(0)

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

Si vous avez beaucoup de descripteurs de fichiers ouverts, poll () est une alternative plus efficace à select().

Une autre option consiste à définir un délai d'expiration pour toutes les opérations sur le socket à l'aide socket.settimeout(), mais je vois que vous avez explicitement rejeté cette solution dans une autre réponse.


16
l'utilisation selectest bonne, mais la partie où vous dites "vous ne pouvez pas" est trompeuse, car il y en a socket.settimeout().
nosklo

1
C'est mieux maintenant, mais je ne vois pas où la réponse a été «explicitement rejetée».
nosklo

7
Une mise en garde sur l'utilisation select- si vous exécutez sur une machine Windows, selects'appuie sur la bibliothèque WinSock, qui a l'habitude de revenir dès que certaines données sont arrivées, mais pas nécessairement toutes . Vous devez donc incorporer une boucle pour continuer à appeler select.select()jusqu'à ce que toutes les données soient reçues. Il vous appartient (malheureusement) de savoir comment vous savez que vous avez obtenu toutes les données - cela peut signifier rechercher une chaîne de terminaison, un certain nombre d'octets ou simplement attendre un délai d'expiration défini.
JDM

4
Pourquoi est-il nécessaire de définir le socket non non bloquant? Je ne pense pas que ce soit important pour l'appel de sélection (et il se bloque jusqu'à ce qu'un descripteur puisse être lu ou que le délai expire dans ce cas) et que recv () ne se bloquera pas si la sélection est satisfaite. Je l'ai essayé en utilisant recvfrom () et il semble fonctionner correctement sans setblocking (0).
HankB

1
Ne ready[0]serait-il faux que s'il n'y a pas de corps dans la réponse du serveur?
matanster le

60

16
Il n'expire pas le recv (du moins quand je l'ai essayé). Seul le accept () a expiré.
Oren S

9
Le socket.recv () semble expirer pour moi très bien après avoir défini socket.settimeout (), exactement comme prévu. Est-ce que j'invente ça? Quelqu'un d'autre peut-il le confirmer?
Aeonaut

3
@Aeonaut Je pense que cela expire la plupart du temps recv (), mais il y a une condition de course. Dans socket.recv (), Python (2.6) appelle select / poll en interne avec le timeout, puis recv () est appelé juste après. Donc, si vous utilisez une socket bloquante et entre ces 2 appels, l'autre point final se bloque, vous pouvez finir par vous suspendre indéfiniment sur recv (). Si vous utilisez un socket non bloquant, python n'appelle pas select.select en interne, donc je pense que la réponse de Daniel Stutzbach est la bonne manière.
emil.p.stanchev

4
En fait, j'ai probablement mal compris lorsque select () revient, alors veuillez gratter le commentaire précédent. Le guide de Beej dit que ce qui précède est un moyen valide d'implémenter un délai d'expiration sur recv: beej.us/guide/bgnet/output/html/singlepage/… donc je vais faire confiance est une source auteur.
emil.p.stanchev

2
Je ne sais pas pourquoi la solution qui utilise selectest préférée lorsque cette solution est une ligne unique (plus facile à entretenir, moins de risque de mise en œuvre incorrecte) et utilise select sous le capot (la mise en œuvre est la même que la réponse @DanielStuzbach).
Trevor Boyd Smith

33

Comme mentionné à la fois select.select()et socket.settimeout()fonctionnera.

Notez que vous devrez peut-être appeler settimeoutdeux fois pour vos besoins, par exemple

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
Je pense qu'il entre dans la même chose où je suis, peu importe comment vous piquez et produisez cette fonction, elle se bloque. J'ai essayé 2 ou 4 timeouts maintenant et ça se bloque toujours. Settimeout se bloque également.
Casey Daniel

1
Lorsque vous appelez .settimeout()plus d'une fois, vous pouvez appeler la setdefaulttimeout()méthode en premier lieu.
mvarge

12

Vous pouvez définir un délai avant de recevoir la réponse et après avoir reçu la réponse, le remettre à Aucun:

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

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

5

Le délai d'expiration que vous recherchez est le délai d'expiration du socket de connexion et non celui du socket principal, si vous implémentez le côté serveur. En d'autres termes, il existe un autre délai d'expiration pour l'objet socket de connexion, qui est la sortie de socket.accept()method. Par conséquent:

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 vous implémentez le côté client, ce serait simple.

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

2

Comme mentionné dans les réponses précédentes, vous pouvez utiliser quelque chose comme: .settimeout() Par exemple:

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

J'espère que ça aide!!


2

Vous pouvez utiliser socket.settimeout()qui accepte un argument entier représentant le nombre de secondes. Par exemple, socket.settimeout(1)définira le délai d'expiration à 1 seconde


2

essayez ceci, il utilise le C.

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

C'est génial, car cela permet de définir différentes valeurs pour les délais d'envoi et de réception en utilisant SO_RCVTIMEOet SO_SNDTIMEO.
jtpereyda

Pourquoi 2et pourquoi 100? Quelle est la valeur du délai d'expiration? Dans quelle unité?
Alfe

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

Criez à: https://boltons.readthedocs.io/en/latest/socketutils.html

Il fournit un socket tamponné, cela fournit de nombreuses fonctionnalités très utiles telles que:

.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)
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.