Як встановити тайм-аут методом recv socket python?


110

Мені потрібно встановити тайм-аут для методу recv socket python. Як це зробити?


2
FYI, якщо ви вирішили використовувати тайм-аути ... вам потрібно знати, як обробляти час очікування. це питання ТА говорить про обробку, коли трапиться час очікування: stackoverflow.com/questions/16745409
Тревор Бойд Сміт

Відповіді:


125

Типовий підхід полягає у використанні select (), щоб чекати, поки дані стануть доступними або до настання тайм-ауту. Телефонуйте лише recv()тоді, коли дані фактично доступні. В цілях безпеки ми також встановлюємо сокет в незаблокувальний режим, щоб гарантувати, що recv()він ніколи не блокується безстроково. select()може також використовуватися для очікування на більш ніж одній розетці одночасно.

import select

mysocket.setblocking(0)

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

Якщо у вас є багато відкритих дескрипторів файлів, опитування () є більш ефективною альтернативою select().

Інший варіант - встановити тайм-аут для всіх операцій на сокеті, використовуючи socket.settimeout(), але я бачу, що ви явно відхилили це рішення в іншій відповіді.


16
використання selectдобре, але частина, де ви говорите "не можете", вводить в оману, оскільки є socket.settimeout().
nosklo

1
Зараз краще, але я не бачу, де відповідь була "явно відхилена".
nosklo

7
Одна обережність у використанні select- якщо ви працюєте на машині Windows, selectпокладається на бібліотеку WinSock, яка має звичку повертатися, як тільки надходять деякі дані, але не обов'язково всі . Тому вам потрібно включити цикл, щоб продовжувати дзвінки, select.select()поки всі дані не будуть отримані. Як ви знаєте, що ви отримали всі дані, на жаль, вирішувати - це може означати пошук рядка термінатора, певної кількості байтів або просто очікування певного часу очікування.
JDM

4
Чому потрібно встановити розетку без блокування? Я не думаю, що це має значення для вибору вибору (і він блокується, поки дескриптор не зможе прочитати або в цьому випадку закінчиться час очікування), а rekv () не заблокується, якщо вибір буде задоволений. Я спробував це за допомогою recvfrom () і, здається, працює правильно, без встановлення блокування (0).
HankB

1
Чи ready[0]помилковим буде лише те, що у відповіді сервера немає тіла?
matanster

60

16
Він не вичерпує реквізити (принаймні, коли я його спробував). Вичерпано лише час accept ().
Орен С

9
Мені здається, що socket.recv () очікує час після встановлення socket.settimeout (), точно за призначенням. Чи я це складаю? Хтось ще може це підтвердити?
Енавт

3
@Aeonaut Я думаю, що цей час випадає recv () більшу частину часу, але є умова гонки. У socket.recv () Python (2.6) виклики вибирають / опитують внутрішньо з тимчасовим очікуванням, а потім recv () викликається відразу після. Тож якщо ви використовуєте блокуючий гніздо і між цими двома викликами виходить з ладу інша кінцева точка, ви зможете безперервно висіти на rekv (). Якщо ви використовуєте неблокуючий сокет, python не викликає select.select внутрішньо, тому я думаю, що відповідь Даніеля Штуцбаха - це правильний шлях.
emil.p.stanchev

4
Насправді я, мабуть, неправильно зрозумів, коли select () повертається, тому, будь ласка, подряпайте попередній коментар. Посібник Beej говорить, що вищезазначене є коректним способом впровадження тайм-ауту на recv: beej.us/guide/bgnet/output/html/singlepage/…, тому я буду довіряти, що це авторське джерело.
emil.p.stanchev

2
Я не впевнений, чому рішення, яке використовується, selectє кращим, коли це рішення є одним вкладишем (простіший у обслуговуванні, менший ризик при неправильному впровадженні) та використовує функцію select під кришкою (реалізація така сама, як відповідь @DanielStuzbach).
Тревор Бойд Сміт

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)

3
Я думаю, що він потрапляє в те саме, що я - там, де незалежно від того, як ти пхаєш і виконуєш цю функцію. Я спробував 2 або 4 тайм-аути зараз, і він все ще висить. settimeout також висить.
Кейсі Даніель

1
Коли ви телефонуєте .settimeout()не один раз, ви можете викликати setdefaulttimeout()метод в першу чергу.
мінаж

12

Ви можете встановити тайм-аут перед отриманням відповіді, а після отримання відповіді встановіть його назад на "Немає":

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()який приймає цілий аргумент, що представляє кількість секунд. Наприклад, socket.settimeout(1)встановить час очікування на 1 секунду


2

спробуйте це, він використовує основний С.

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

Це чудово, оскільки дозволяє встановити різні значення для таймауту відправки та відновлення за допомогою SO_RCVTIMEOта SO_SNDTIMEO.
jtpereyda

Чому 2і чому 100? Яке значення тайм-аута? У якій одиниці?
Альфа

timeval = struct.pack('ll', sec, usec) s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeval)usec = 10000 означає 10 мс
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

Вигукніть на адресу: 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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.