Python'un soket recv yönteminde zaman aşımı nasıl ayarlanır?


110

Python'un soket recv yönteminde zaman aşımı ayarlamam gerekiyor. Nasıl yapılır?


2
Bilginize, zaman aşımlarını kullanmayı seçerseniz ... zaman aşımını nasıl idare edeceğinizi bilmeniz gerekir. bu SO sorusu, bir zaman aşımı meydana geldiğinde
Trevor Boyd Smith

Yanıtlar:


126

Tipik yaklaşım, veri kullanılabilir olana kadar veya zaman aşımı gerçekleşene kadar beklemek için select () kullanmaktır . Yalnızca recv()veriler gerçekten mevcut olduğunda arayın . Güvende olmak için, recv()süresiz olarak asla bloke olmayacağını garanti etmek için soketi engellemesiz moda da ayarladık . select()aynı anda birden fazla soket beklemek için de kullanılabilir.

import select

mysocket.setblocking(0)

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

Çok sayıda açık dosya tanımlayıcınız varsa, anket () 'e göre daha etkili bir alternatiftir select().

Diğer bir seçenek, kullanarak soket üzerindeki tüm işlemler için bir zaman aşımı ayarlamaktır socket.settimeout(), ancak bu çözümü başka bir yanıtta açıkça reddettiğinizi görüyorum.


16
kullanımı selectiyidir, ancak "yapamazsın" dediğiniz kısım yanıltıcıdır, çünkü vardır socket.settimeout().
nosklo

1
Şimdi daha iyi, ancak cevabın nerede "açıkça reddedildiğini" anlamıyorum.
nosklo

7
Kullanmaya ilişkin bir uyarı select- bir Windows makinesinde çalışıyorsanız, bazı veriler gelir selectgelmez geri dönme alışkanlığı olan WinSock kitaplığına güvenir , ancak hepsi zorunlu değildir . Bu nedenle, tüm veriler alınana kadar aramaya devam etmek için bir döngü eklemeniz gerekir . Tüm verileri aldığınızı nasıl bildiğiniz (maalesef) size kalmış - bu bir sonlandırıcı dizge, belirli sayıda bayt aramak veya sadece belirli bir zaman aşımını beklemek anlamına gelebilir. select.select()
JDM

4
Soketi engellemesiz olarak ayarlamak neden gereklidir? Select çağrısı için önemli olduğunu sanmıyorum (ve bir tanımlayıcı okunabilene kadar veya bu durumda zaman aşımı sona erene kadar engelliyor) ve select tatmin olursa recv () engellemeyecek. Bunu recvfrom () kullanarak denedim ve setblocking (0) olmadan düzgün çalışıyor gibi görünüyor.
HankB

1
Misiniz ready[0]sunucunun yanıt olarak ortada ceset yok eğer sadece yanlış?
matanster

60

16
Recv zaman aşımına uğramıyor (en azından denediğimde). Yalnızca accept () zaman aşımına uğrar.
Oren S

9
Socket.recv () tam olarak amaçlandığı gibi soket.settimeout () ayarlandıktan sonra benim için zaman aşımına uğradı. Bunu ben mi uyduruyorum? Bunu başka biri doğrulayabilir mi?
Aeonaut

3
@Aeonaut Bence bu çoğu zaman recv () zaman aşımına uğradı, ancak bir yarış durumu var. Socket.recv () Python (2.6) içinde zaman aşımı ile birlikte select / anket'i çağırır ve hemen ardından recv () çağrılır. Dolayısıyla, bir engelleme soketi kullanırsanız ve bu 2 çağrı arasında diğer uç nokta çökerse, recv () üzerinde süresiz olarak asılı kalabilirsiniz. Engellemeyen soket kullanırsanız, python dahili olarak select.select'i çağırmaz, bu yüzden Daniel Stutzbach'ın cevabının doğru olduğunu düşünüyorum.
emil.p.stanchev

4
Aslında, select () döndüğünde muhtemelen yanlış anladım, bu yüzden lütfen önceki yorumu kazı kazan Beej's Guide, yukarıdakilerin recv'de bir zaman aşımı uygulamak için geçerli bir yol olduğunu söylüyor: beej.us/guide/bgnet/output/html/singlepage/…, bu yüzden yazarlık bir kaynak olduğuna güveneceğim.
emil.p.stanchev

2
selectBu çözüm tek satırlık bir çözüm olduğunda (bakımı daha kolay, yanlış uygulamada daha az risk) ve başlık altında select kullandığında (uygulama @DanielStuzbach yanıtıyla aynıdır) neden kullanılan çözümün tercih edildiğinden emin değilim .
Trevor Boyd Smith

33

As hem söz select.select()ve socket.settimeout()çalışacaktır.

settimeoutİhtiyaçlarınız için iki kez aramanız gerekebileceğini unutmayın , örneğin

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
Sanırım, bu işlevi nasıl dürttüğünüz ve dürttüğünüz önemli değil. Şimdi 2 veya 4 zaman aşımını denedim ve hala takılıyor. settimeout da kilitleniyor.
Casey Daniel

1
Bir .settimeout()kereden fazla aradığınızda, setdefaulttimeout()yöntemi ilk etapta çağırabilirsiniz .
mvarge

12

Yanıtı almadan önce zaman aşımını ayarlayabilir ve yanıtı aldıktan sonra tekrar Yok olarak ayarlayabilirsiniz:

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

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

5

Sunucu tarafını uygularsanız, aradığınız zaman aşımı, bağlantı soketinin zaman aşımıdır, birincil soketler değil. Başka bir deyişle, socket.accept()yöntemin çıktısı olan bağlantı soketi nesnesi için başka bir zaman aşımı vardır . Bu nedenle:

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.

İstemci tarafını uygularsanız, bu basit olacaktır.

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

2

Önceki yanıtlarda belirtildiği gibi, aşağıdaki gibi bir şey kullanabilirsiniz: .settimeout() Örneğin:

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

Umarım bu yardımcı olur!!


2

socket.settimeout()Saniyelerin sayısını temsil eden bir tamsayı bağımsız değişkenini kabul eden seçeneği kullanabilirsiniz . Örneğin socket.settimeout(1), zaman aşımını 1 saniyeye ayarlayacak


2

bunu deneyin, temeldeki C'yi kullanır.

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

O gönderme ve kullanma recv zaman aşımı için farklı değerleri ayarlamak için bir imkan sağladığından, büyük SO_RCVTIMEOve SO_SNDTIMEO.
jtpereyda

Neden 2ve neden 100? Zaman aşımı değeri nedir? Hangi birimde?
Alfe

timeval = struct.pack('ll', sec, usec) s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeval)usec = 10000, 10 ms anlamına gelir
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

Şu kişiye seslenin: https://boltons.readthedocs.io/en/latest/socketutils.html

Arabelleğe alınmış bir soket sağlar, bu, aşağıdakiler gibi birçok yararlı işlevsellik sağlar:

.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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.