Как установить тайм-аут для метода recv сокета python?

110

Мне нужно установить тайм-аут для метода recv сокета python. Как это сделать?

Thoreller
источник
2
К вашему сведению, если вы решите использовать тайм-ауты ... вам нужно знать, как обрабатывать тайм-аут. в этом вопросе SO говорится об обработке, когда происходит тайм-аут: stackoverflow.com/questions/16745409
Trevor Boyd Smith

Ответы:

126

Типичный подход - использовать select (), чтобы дождаться, пока данные станут доступны или пока не истечет время ожидания. Звоните только recv()тогда, когда данные действительно доступны. На всякий случай мы также установили сокет в неблокирующий режим, чтобы гарантировать, что recv()он никогда не будет блокироваться бесконечно. select()также может использоваться для ожидания более чем одного сокета одновременно.

import select

mysocket.setblocking(0)

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

Если у вас много дескрипторов открытых файлов, poll () является более эффективной альтернативой select().

Другой вариант - установить тайм-аут для всех операций с использованием сокета socket.settimeout(), но я вижу, что вы явно отклонили это решение в другом ответе.

Даниэль Штутцбах
источник
16
Использование select- это хорошо, но та часть, где вы говорите «вы не можете», вводит в заблуждение, поскольку есть socket.settimeout().
носкло
1
Сейчас лучше, но я не вижу, где ответ был «явно отклонен».
nosklo
7
Одно предостережение при использовании select- если вы работаете на машине с Windows, selectполагается на библиотеку WinSock, которая имеет привычку возвращаться, как только будут получены некоторые данные, но не обязательно все . Таким образом, вам нужно включить цикл, чтобы продолжать звонить, select.select()пока не будут получены все данные. Как вы узнаете, что получили все данные (к сожалению), решать вам - это может означать поиск строки терминатора, определенное количество байтов или просто ожидание определенного тайм-аута.
JDM
4
Почему необходимо установить сокет без блокировки? Я не думаю, что это имеет значение для вызова select (и он блокируется до тех пор, пока дескриптор не может быть прочитан или пока не истечет время ожидания в этом случае), и recv () не будет блокироваться, если выбор будет удовлетворен. Я пробовал использовать recvfrom (), и, похоже, он работает правильно без setblocking (0).
HankB
1
Было ready[0]бы неверно, только если бы в ответе сервера нет тела?
Matanster
60

есть socket.settimeout()

nosklo
источник
16
Это не время ожидания recv (по крайней мере, когда я это пробовал). Истекло время ожидания только для accept ().
Oren S
9
Кажется, что socket.recv () истекает для меня нормально после установки socket.settimeout (), точно так, как задумано. Я это выдумываю? Кто-нибудь еще может это подтвердить?
Aeonaut
3
@Aeonaut Я думаю, что в большинстве случаев у recv () истекает время ожидания, но есть состояние гонки. В socket.recv () Python (2.6) вызывает внутри себя select / poll с тайм-аутом, а затем сразу после этого вызывается recv (). Поэтому, если вы используете блокирующий сокет и между этими двумя вызовами происходит сбой другой конечной точки, вы можете бесконечно зависать на recv (). Если вы используете неблокирующий сокет, python не вызывает select.select внутри, поэтому я думаю, что ответ Даниэля Штутцбаха правильный.
emil.p.stanchev 04
4
На самом деле, я, вероятно, неправильно понял, когда возвращается select (), поэтому, пожалуйста, сотрите предыдущий комментарий. В руководстве Beej говорится, что приведенное выше является допустимым способом реализации тайм-аута для recv: beej.us/guide/bgnet/output/html/singlepage/… так что я доверяю этому источнику.
emil.p.stanchev 04
2
Я не уверен, почему решение, которое использует, selectявляется предпочтительным, когда это решение является однострочным (проще в обслуживании, меньше рисков при неправильной реализации) и использует select под капотом (реализация такая же, как и ответ @DanielStuzbach).
Trevor Boyd Smith
33

Как уже упоминалось, select.select() и socket.settimeout()будут работать.

Обратите внимание, что вам может потребоваться settimeoutдважды позвонить для ваших нужд, например

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
источник
3
Я думаю, что он попадает в то же самое, что и я, где неважно как тыкаешь и толкаешь эту функцию, она зависает. Я пробовал 2 или 4 таймаута, но он все еще зависает. settimeout тоже зависает.
Casey Daniel
1
Когда вы вызываете .settimeout()более одного раза, вы можете setdefaulttimeout()в первую очередь вызвать метод.
mvarge
12

Вы можете установить тайм-аут до получения ответа, а после получения ответа вернуть значение None:

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

sock.settimeout(5.0)
data = sock.recv(1024)
sock.settimeout(None)
Норико
источник
5

Тайм-аут, который вы ищете, - это тайм-аут сокета подключения, а не основного сокета, если вы реализуете серверную часть. Другими словами, существует еще один тайм-аут для объекта сокета подключения, который является выходом socket.accept()метода. Следовательно:

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.

Если вы реализуете клиентскую сторону, это будет просто.

sock.connect(server_address)
sock.settimeout(3)
Вала
источник
2

Как упоминалось в предыдущих ответах, вы можете использовать что-то вроде: .settimeout() Например:

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

Надеюсь, это поможет!!

миск
источник
2

Вы можете использовать socket.settimeout()which принимает целочисленный аргумент, представляющий количество секунд. Например, socket.settimeout(1)установит тайм-аут на 1 секунду.

Брайан Чжэн
источник
2

попробуйте это, он использует базовый C.

timeval = struct.pack('ll', 2, 100)
s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeval)
Мохаммад Альхалди
источник
Это замечательно, так как позволяет устанавливать разные значения для тайм-аута отправки и получения, используя SO_RCVTIMEOи SO_SNDTIMEO.
jtpereyda
Почему 2и почему 100? Какое значение тайм-аута? В каком агрегате?
Alfe
timeval = struct.pack('ll', sec, usec) s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeval)usec = 10000 означает 10 мс
Tavy 02
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

Кричать, чтобы: https://boltons.readthedocs.io/en/latest/socketutils.html

Он предоставляет буферизованный сокет, который предоставляет множество очень полезных функций, таких как:

.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)
Цезур
источник