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

110

Eu preciso definir o tempo limite no método de recv de soquete do python. Como fazer isso?

Thoreller
fonte
2
Para sua informação, se você escolher usar tempos limite ... você precisa saber como lidar com o tempo limite. esta pergunta do SO fala sobre como lidar quando ocorre um tempo limite: stackoverflow.com/questions/16745409
Trevor Boyd Smith

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.

Daniel Stutzbach
fonte
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

socket.settimeout()

nosklo
fonte
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)
ubershmekel
fonte
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)
Noriko
fonte
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)
Vala
fonte
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!!

meep
fonte
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

Brian Zheng
fonte
2

tente fazer isso, ele usa o C.

timeval = struct.pack('ll', 2, 100)
s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeval)
Mohammad Alkhaldi
fonte
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
sheng
fonte
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)
Caesurus
fonte