Bagaimana cara mengatur timeout pada metode socket recv python?

110

Saya perlu mengatur batas waktu pada metode socket recv python. Bagaimana cara melakukannya?

Thoreller
sumber
2
FYI jika Anda memilih untuk menggunakan waktu tunggu ... Anda perlu tahu cara menangani batas waktu. Pertanyaan SO ini berbicara tentang penanganan ketika batas waktu terjadi: stackoverflow.com/questions/16745409
Trevor Boyd Smith

Jawaban:

126

Pendekatan tipikal adalah menggunakan select () untuk menunggu hingga data tersedia atau hingga batas waktu terjadi. Telepon hanya recv()jika data benar-benar tersedia. Untuk amannya, kami juga menyetel soket ke mode non-pemblokiran untuk menjamin bahwa recv()tidak akan pernah memblokir tanpa batas. select()juga dapat digunakan untuk menunggu lebih dari satu soket dalam satu waktu.

import select

mysocket.setblocking(0)

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

Jika Anda memiliki banyak deskriptor file yang terbuka, poll () adalah alternatif yang lebih efisien untuk select().

Pilihan lain adalah mengatur batas waktu untuk semua operasi pada soket yang digunakan socket.settimeout(), tetapi saya melihat bahwa Anda secara eksplisit menolak solusi itu di jawaban lain.

Daniel Stutzbach
sumber
16
penggunaan selectitu bagus, tetapi bagian di mana Anda mengatakan "Anda tidak bisa" menyesatkan, karena memang ada socket.settimeout().
nosklo
1
Sekarang lebih baik, tetapi saya tidak melihat di mana jawabannya "ditolak secara eksplisit".
nosklo
7
Satu kehati-hatian dalam menggunakan select- jika Anda menjalankan mesin Windows, selectbergantung pada pustaka WinSock, yang memiliki kebiasaan untuk kembali segera setelah beberapa data tiba, tetapi tidak harus semuanya . Jadi, Anda perlu memasukkan loop untuk terus menelepon select.select()hingga semua data diterima. Bagaimana Anda tahu bahwa Anda telah mendapatkan semua data (sayangnya) terserah Anda untuk mengetahuinya - ini mungkin berarti mencari string terminator, sejumlah byte, atau hanya menunggu waktu tunggu yang ditentukan.
JDM
4
Mengapa perlu mengatur soket tanpa pemblokiran? Saya tidak berpikir itu penting untuk panggilan pilih (dan itu memblokir sampai deskriptor dapat dibaca atau batas waktu berakhir dalam kasus ini) dan recv () tidak akan memblokir jika pemilihan puas. Saya mencobanya menggunakan recvfrom () dan tampaknya berfungsi dengan benar tanpa setblocking (0).
HankB
1
Apakah ready[0]hanya salah jika tidak ada bagian dalam respons server?
matanster
60

disana socket.settimeout()

nosklo.dll
sumber
16
Tidak ada waktu tunggu penerimaan (setidaknya ketika saya mencobanya). Hanya accept () yang waktunya habis.
Oren S
9
Socket.recv () tampaknya waktu habis bagi saya setelah menyetel socket.settimeout (), persis seperti yang dimaksudkan. Apakah saya mengada-ada? Adakah yang bisa mengkonfirmasi ini?
Aeonaut
3
@Aeonaut Saya pikir kali ini recv () sebagian besar waktu, tetapi ada kondisi balapan. Dalam socket.recv () Python (2.6) memanggil select / poll secara internal dengan batas waktu dan kemudian recv () dipanggil tepat setelahnya. Jadi jika Anda menggunakan soket pemblokiran dan di antara 2 panggilan ini, titik akhir lainnya lumpuh, Anda bisa berakhir tanpa batas waktu di recv (). Jika Anda menggunakan soket non-pemblokiran, python tidak memanggil select.select secara internal, jadi saya pikir jawaban Daniel Stutzbach adalah cara yang benar.
emil.p.stanchev
4
Sebenarnya, saya mungkin salah paham saat select () kembali, jadi gores komentar sebelumnya. Beej's Guide mengatakan bahwa di atas adalah cara yang valid untuk mengimplementasikan batas waktu di recv: beej.us/guide/bgnet/output/html/singlepage/… jadi saya percaya adalah sumber otoratif.
emil.p.stanchev
2
Saya tidak yakin mengapa solusi yang menggunakan selectlebih disukai ketika solusi ini adalah satu liner (lebih mudah dirawat, lebih sedikit risiko dalam penerapan yang salah) dan menggunakan pilihan di bawah tenda (penerapannya sama dengan jawaban @DanielStuzbach).
Trevor Boyd Smith
33

Seperti yang disebutkan, keduanya select.select()dan socket.settimeout()akan berhasil.

Perhatikan bahwa Anda mungkin perlu menelepon settimeoutdua kali untuk kebutuhan Anda, misalnya

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
sumber
3
Saya pikir dia mendapatkan hal yang sama saya di mana tidak peduli bagaimana Anda menyodok dan mendorong fungsi ini hang. Saya sudah mencoba 2 atau 4 waktu tunggu sekarang dan masih hang. settimeout juga hang.
Casey Daniel
1
Saat Anda memanggil .settimeout()lebih dari sekali, Anda dapat memanggil setdefaulttimeout()metode ini sejak awal.
mvarge
12

Anda dapat menyetel batas waktu sebelum menerima respons dan setelah menerima respons, setel kembali ke Tidak Ada:

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

sock.settimeout(5.0)
data = sock.recv(1024)
sock.settimeout(None)
noriko
sumber
5

Waktu tunggu yang Anda cari adalah batas waktu soket sambungan, bukan waktu tunggu soket utama, jika Anda menerapkan sisi server. Dengan kata lain, ada waktu tunggu lain untuk objek soket sambungan, yang merupakan keluaran socket.accept()metode. Karena itu:

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.

Jika Anda menerapkan sisi klien, itu akan sederhana.

sock.connect(server_address)
sock.settimeout(3)
Vala
sumber
2

Seperti yang disebutkan dalam balasan sebelumnya, Anda dapat menggunakan sesuatu seperti: .settimeout() Misalnya:

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

Saya harap ini membantu!!

meep
sumber
2

Anda dapat menggunakan socket.settimeout()yang menerima argumen integer yang mewakili jumlah detik. Misalnya, socket.settimeout(1)akan menyetel waktu tunggu ke 1 detik

Brian Zheng
sumber
2

coba ini menggunakan C. yang mendasari.

timeval = struct.pack('ll', 2, 100)
s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeval)
Mohammad Alkhaldi
sumber
Ini bagus, karena memungkinkan seseorang menetapkan nilai yang berbeda untuk waktu tunggu kirim dan terima menggunakan SO_RCVTIMEOdan SO_SNDTIMEO.
jtpereyda
Mengapa 2dan mengapa 100? Berapakah nilai batas waktu? Di unit apa?
Alfe
timeval = struct.pack('ll', sec, usec) s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeval)usec = 10000 berarti 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
sumber
0

Berteriak kepada: https://boltons.readthedocs.io/en/latest/socketutils.html

Ini menyediakan soket buffer, ini menyediakan banyak fungsi yang sangat berguna seperti:

.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
sumber