Jak ustawić limit czasu w metodzie recv gniazda Pythona?


Odpowiedzi:


126

Typowym podejściem jest użycie funkcji select () do czekania, aż dane będą dostępne lub do upływu limitu czasu. Dzwoń tylko recv()wtedy, gdy dane są rzeczywiście dostępne. Aby być bezpiecznym, ustawiliśmy również gniazdo w trybie nieblokującym, aby zagwarantować, że recv()nigdy nie będzie blokować się w nieskończoność. select()może również służyć do oczekiwania na więcej niż jednym gnieździe naraz.

import select

mysocket.setblocking(0)

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

Jeśli masz dużo otwartych deskryptorów plików, poll () jest bardziej wydajną alternatywą dla select().

Inną opcją jest ustawienie limitu czasu dla wszystkich operacji na gnieździe przy użyciu socket.settimeout(), ale widzę, że jawnie odrzuciłeś to rozwiązanie w innej odpowiedzi.


16
użycie selectjest dobre, ale część, w której mówisz „nie możesz”, jest myląca, ponieważ tak jest socket.settimeout().
nosklo

1
Teraz jest lepiej, ale nie widzę, gdzie odpowiedź została „wyraźnie odrzucona”.
nosklo

7
Jedno ostrzeżenie dotyczące używania select- jeśli pracujesz na komputerze z systemem Windows, selectpolega na bibliotece WinSock, która ma zwyczaj zwracania danych, gdy tylko nadejdą niektóre dane, ale niekoniecznie wszystkie . Musisz więc włączyć pętlę, aby dzwonić select.select()do momentu odebrania wszystkich danych. Skąd wiesz, że uzyskałeś wszystkie dane, zależy (niestety) od Ciebie - może to oznaczać szukanie łańcucha terminatora, określonej liczby bajtów lub po prostu czekanie na określony limit czasu.
JDM,

4
Dlaczego konieczne jest ustawienie gniazda na nieblokujące? Nie sądzę, aby miało to znaczenie dla wywołania select (i blokuje się do momentu odczytania deskryptora lub wygaśnięcia limitu czasu w tym przypadku), a recv () nie zostanie zablokowany, jeśli wybór zostanie spełniony. Wypróbowałem to za pomocą recvfrom () i wydaje się, że działa poprawnie bez setblocking (0).
HankB

1
Czy ready[0]byłaby fałszywa tylko wtedy, gdyby w odpowiedzi serwera nie było treści?
matanster

60

16
Nie przekracza limitu czasu recv (przynajmniej kiedy próbowałem). Przekroczono limit czasu tylko funkcji accept ().
Oren S

9
Wydaje się, że socket.recv () przekroczył limit czasu po ustawieniu funkcji socket.settimeout (), dokładnie tak, jak zamierzano. Czy ja to zmyślam? Czy ktoś inny może to potwierdzić?
Aeonaut

3
@Aeonaut Myślę, że to przez większość czasu przekracza recv (), ale istnieje warunek wyścigu. W socket.recv () Python (2.6) wywołuje wewnętrznie funkcję select / poll z upływem czasu, a zaraz po tym wywoływana jest funkcja recv (). Więc jeśli używasz gniazda blokującego i między tymi dwoma wywołaniami drugi punkt końcowy ulega awarii, możesz zawiesić się na czas nieokreślony na recv (). Jeśli używasz gniazda nieblokującego, Python nie wywołuje funkcji select.select wewnętrznie, więc myślę, że odpowiedź Daniela Stutzbacha jest poprawna.
emil.p.stanchev

4
Właściwie prawdopodobnie źle zrozumiałem, kiedy funkcja select () zwraca, więc zdrap poprzedni komentarz. Przewodnik Beeja mówi, że powyższe jest prawidłowym sposobem na zaimplementowanie limitu czasu na recv: beej.us/guide/bgnet/output/html/singlepage/, więc ufam, że jest autorytatywnym źródłem.
emil.p.stanchev

2
Nie jestem pewien, dlaczego selectpreferowane jest rozwiązanie, które wykorzystuje, gdy jest to rozwiązanie one liner (łatwiejsze w utrzymaniu, mniejsze ryzyko błędnego wdrożenia) i używa select pod maską (implementacja jest taka sama jak odpowiedź @DanielStuzbach).
Trevor Boyd Smith

33

Jak wspomniano, oba select.select()i socket.settimeout()będą działać.

Pamiętaj, że możesz potrzebować zadzwonić settimeoutdwa razy, np

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
Myślę, że dostaje to samo co ja, gdzie bez względu na to, jak szturchasz i naciskasz, ta funkcja się zawiesza. Wypróbowałem teraz 2 lub 4 limity czasu i nadal się zawiesza. settimeout również się zawiesza.
Casey Daniel,

1
Ponieważ wywołujesz .settimeout()więcej niż raz, możesz najpierw wywołać setdefaulttimeout()metodę.
mvarge

12

Możesz ustawić limit czasu przed otrzymaniem odpowiedzi i po otrzymaniu odpowiedzi ustawić go z powrotem na Brak:

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

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

5

Limit czasu, którego szukasz, to limit czasu gniazda połączeniowego, a nie gniazda podstawowego, jeśli implementujesz po stronie serwera. Innymi słowy, istnieje inny limit czasu dla obiektu gniazda połączeniowego, który jest wyjściem socket.accept()metody method. W związku z tym:

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.

Jeśli wdrożysz stronę klienta, byłoby to proste.

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

2

Jak wspomniano w poprzednich odpowiedziach, możesz użyć czegoś takiego: .settimeout() Na przykład:

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

Mam nadzieję, że to pomoże!!


2

Możesz użyć, socket.settimeout()który akceptuje argument będący liczbą całkowitą reprezentującą liczbę sekund. Na przykład socket.settimeout(1)ustawi limit czasu na 1 sekundę


2

spróbuj tego, używa podstawowego C.

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

Jest to świetne, ponieważ pozwala ustawić różne wartości limitu czasu wysyłania i odbierania za pomocą SO_RCVTIMEOi SO_SNDTIMEO.
jtpereyda

Dlaczego 2i dlaczego 100? Jaka jest wartość limitu czasu? W jakiej jednostce?
Alfe

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.