Como definir o tempo limite no método de recv de soquete do python?


Respostas:


126

A abordagem típica é usar select () para esperar até que os dados estejam disponíveis ou até que ocorra o tempo limite. Chame apenas recv()quando os dados estiverem realmente disponíveis. Para ser seguro, também configuramos o soquete para o modo sem bloqueio para garantir que recv()nunca será bloqueado indefinidamente. select()também pode ser usado para aguardar em mais de um soquete de cada vez.

import select

mysocket.setblocking(0)

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

Se você tiver muitos descritores de arquivo abertos, poll () é uma alternativa mais eficiente ao select().

Outra opção é definir um tempo limite para todas as operações no soquete usando socket.settimeout(), mas vejo que você rejeitou explicitamente essa solução em outra resposta.


16
usar selecté bom, mas a parte em que você diz "você não pode" é enganosa, pois existe socket.settimeout().
nosklo

1
Está melhor agora, mas não vejo onde a resposta foi "rejeitada explicitamente".
nosklo

7
Um cuidado ao usar select- se você estiver executando em uma máquina Windows, use selecta biblioteca WinSock, que tem o hábito de retornar assim que alguns dados chegam, mas não necessariamente todos . Portanto, você precisa incorporar um loop para continuar chamando select.select()até que todos os dados sejam recebidos. Como você sabe que obteve todos os dados (infelizmente) depende de você descobrir - pode significar procurar por uma string de terminador, um certo número de bytes ou apenas esperar por um tempo limite definido.
JDM de

4
Por que é necessário definir o soquete sem bloqueio? Não acho que isso importe para a chamada select (e ela bloqueia até que um descritor possa ser lido ou o tempo limite expire, neste caso) e o recv () não bloqueará se o select for satisfeito. Eu tentei usando recvfrom () e parece funcionar corretamente sem setblocking (0).
HankB

1
Será que ready[0]só será falsa se não há nenhum corpo na resposta do servidor?
matanster

60

16
Ele não atinge o tempo limite de recepção (pelo menos quando eu tentei). Apenas o aceitar () expirou.
Oren S

9
O socket.recv () parece expirar para mim muito bem depois de definir socket.settimeout (), exatamente como pretendido. Eu estou inventando isso? Mais alguém pode confirmar isso?
Aeonaut

3
@Aeonaut Eu acho que isso atinge o tempo limite recv () na maioria das vezes, mas há uma condição de corrida. Em socket.recv () Python (2.6) chama select / poll internamente com o tempo limite e então recv () é chamado logo depois. Portanto, se você usar um soquete de bloqueio e entre essas 2 chamadas o outro ponto de extremidade travar, você pode acabar pendurado indefinidamente no recv (). Se você usar um soquete sem bloqueio, o python não chamará select.select internamente, então acho que a resposta de Daniel Stutzbach é a maneira correta.
emil.p.stanchev

4
Na verdade, eu provavelmente não entendi quando select () retorna, portanto, raspe o comentário anterior. O Guia de Beej diz que o acima é uma maneira válida de implementar um tempo limite no recv: beej.us/guide/bgnet/output/html/singlepage/… então, confio em uma fonte autorizada.
emil.p.stanchev

2
Não sei por que a solução que usa selecté preferida quando esta solução é um liner (mais fácil de manter, menos risco na implementação incorreta) e usa select under the capô (implementação é a mesma que a resposta @DanielStuzbach).
Trevor Boyd Smith

33

Conforme mencionado, ambos select.select()e socket.settimeout()irão funcionar.

Observe que você pode precisar ligar settimeoutduas vezes para atender às suas necessidades, por exemplo

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
Acho que ele está entrando na mesma coisa que eu, onde não importa como você cutuque e cutuque essa função, ela trava. Tentei 2 ou 4 tempos limite agora e ainda trava. settimeout trava também.
Casey Daniel

1
Ao chamar .settimeout()mais de uma vez, você pode chamar o setdefaulttimeout()método em primeiro lugar.
mvarge

12

Você pode definir o tempo limite antes de receber a resposta e, depois de receber a resposta, defini-lo novamente para Nenhum:

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

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

5

O tempo limite que você está procurando é o tempo limite do soquete de conexão e não o do soquete primário, se você implementar o lado do servidor. Em outras palavras, há outro tempo limite para o objeto de soquete de conexão, que é a saída do socket.accept()método. Portanto:

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 você implementar o lado do cliente, seria simples.

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

2

Conforme mencionado nas respostas anteriores, você pode usar algo como: .settimeout() Por exemplo:

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

Eu espero que isso ajude!!


2

Você pode usar o socket.settimeout()qual aceita um argumento inteiro que representa o número de segundos. Por exemplo, socket.settimeout(1)definirá o tempo limite para 1 segundo


2

tente fazer isso, ele usa o C.

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

Isso é ótimo, pois permite definir valores diferentes para o tempo limite de envio e recebimento usando SO_RCVTIMEOe SO_SNDTIMEO.
jtpereyda

Por que 2e por quê 100? Qual é o valor do tempo limite? Em qual unidade?
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

Grite para: https://boltons.readthedocs.io/en/latest/socketutils.html

Ele fornece um soquete com buffer, que fornece muitas funcionalidades muito úteis, 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)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.