Làm cách nào để đặt thời gian chờ trên phương thức recv socket của python?


Câu trả lời:


125

Cách tiếp cận điển hình là sử dụng select () để đợi cho đến khi dữ liệu có sẵn hoặc cho đến khi thời gian chờ xảy ra. Chỉ gọi recv()khi dữ liệu thực sự có sẵn. Để an toàn, chúng tôi cũng đặt ổ cắm ở chế độ không chặn để đảm bảo rằng ổ cắm recv()sẽ không bao giờ chặn vô thời hạn. select()cũng có thể được sử dụng để chờ trên nhiều ổ cắm cùng một lúc.

import select

mysocket.setblocking(0)

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

Nếu bạn có nhiều trình mô tả tệp đang mở, thì thăm dò ý kiến ​​() là một giải pháp thay thế hiệu quả hơn select().

Một tùy chọn khác là đặt thời gian chờ cho tất cả các hoạt động trên ổ cắm bằng cách sử dụng socket.settimeout(), nhưng tôi thấy rằng bạn đã từ chối giải pháp đó một cách rõ ràng trong một câu trả lời khác.


16
sử dụng selectlà tốt, nhưng phần mà bạn nói "bạn không thể" gây hiểu nhầm, vì có socket.settimeout().
nosklo

1
Bây giờ thì tốt hơn, nhưng tôi không thấy câu trả lời là "bị từ chối rõ ràng" ở đâu.
nosklo

7
Một lưu ý khi sử dụng select- nếu bạn đang chạy trên máy Windows, hãy selectdựa vào thư viện WinSock, thư viện này có thói quen trả về ngay sau khi một số dữ liệu được gửi đến, nhưng không nhất thiết phải là tất cả . Vì vậy, bạn cần kết hợp một vòng lặp để tiếp tục gọi select.select()cho đến khi nhận được tất cả dữ liệu. Làm thế nào bạn biết bạn đã nhận được tất cả dữ liệu (không may là) tùy thuộc vào bạn để tìm ra - nó có thể có nghĩa là tìm kiếm một chuỗi kết thúc, một số byte nhất định hoặc chỉ chờ một khoảng thời gian chờ xác định.
JDM

4
Tại sao cần đặt ổ cắm không có khóa? Tôi không nghĩ rằng điều đó quan trọng đối với cuộc gọi đã chọn (và nó sẽ chặn cho đến khi có thể đọc bộ mô tả hoặc hết thời gian chờ trong trường hợp này) và recv () sẽ không chặn nếu lựa chọn được thỏa mãn. Tôi đã thử nó bằng cách sử dụng recvfrom () và nó có vẻ hoạt động chính xác mà không cần setblocking (0).
HankB

1
Sẽ ready[0]chỉ sai nếu không có phần thân trong phản hồi của máy chủ?
matanster 19/07/18

60

16
Nó không hết thời gian chờ recv (ít nhất là khi tôi đã thử nó). Chỉ chấp nhận () là hết thời gian.
Oren S

9
Socket.recv () dường như đã hết thời gian đối với tôi sau khi thiết lập socket.settimeout (), chính xác như dự định. Tôi đang bịa ra điều này? bất cứ ai khác có thể xác nhận điều này?
Aeonaut

3
@Aeonaut Tôi nghĩ rằng điều này xảy ra hầu hết thời gian recv (), nhưng có một điều kiện chủng tộc. Trong socket.recv () Python (2.6) các cuộc gọi select / thăm dò nội bộ với thời gian chờ và sau đó recv () được gọi ngay sau đó. Vì vậy, nếu bạn sử dụng một ổ cắm chặn và giữa 2 cuộc gọi này, điểm cuối kia bị treo, bạn có thể bị treo vô thời hạn trên recv (). Nếu bạn sử dụng ổ cắm không chặn, python không gọi select.select trong nội bộ, vì vậy tôi nghĩ câu trả lời của Daniel Stutzbach là cách chính xác.
emil.p.stanchev

4
Thực ra, có lẽ tôi đã hiểu nhầm khi select () trả về, vì vậy xin vui lòng cào bình luận trước. Beej's Guide cho biết ở trên là một cách hợp lệ để triển khai thời gian chờ trên recv: beej.us/guide/bgnet/output/html/singlepage/… vì vậy tôi tin tưởng đây là nguồn có thẩm quyền.
emil.p.stanchev

2
Tôi không chắc tại sao giải pháp sử dụng lại selectđược ưu tiên khi giải pháp này là một lớp lót (dễ bảo trì hơn, ít rủi ro hơn khi triển khai sai) và sử dụng lựa chọn ẩn (cách triển khai giống như câu trả lời @DanielStuzbach).
Trevor Boyd Smith,

33

Như đã đề cập cả hai select.select()socket.settimeout()sẽ hoạt động.

Lưu ý rằng bạn có thể cần gọi settimeouthai lần nếu cần

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
Tôi nghĩ rằng anh ta đang nhận được cùng một thứ tôi đang ở, bất kể bạn chọc và thúc đẩy chức năng này như thế nào nó bị treo. Tôi đã thử 2 hoặc 4 lần hết giờ và nó vẫn bị treo. settimeout cũng bị treo.
Casey Daniel

1
Khi bạn gọi .settimeout()nhiều lần, bạn có thể gọi setdefaulttimeout()phương thức ngay từ đầu.
mvarge

12

Bạn có thể đặt thời gian chờ trước khi nhận được phản hồi và sau khi nhận được phản hồi, hãy đặt lại thành Không có:

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

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

5

Thời gian chờ mà bạn đang tìm kiếm là thời gian chờ của ổ cắm kết nối không phải của ổ cắm chính, nếu bạn triển khai phía máy chủ. Nói cách khác, có một thời gian chờ khác cho đối tượng socket kết nối, đó là đầu ra của socket.accept()phương thức. Vì thế:

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.

Nếu bạn triển khai phía khách hàng, nó sẽ đơn giản.

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

2

Như đã đề cập trong các câu trả lời trước, bạn có thể sử dụng một cái gì đó như: .settimeout() Ví dụ:

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

Tôi hi vọng cái này giúp được!!


2

Bạn có thể sử dụng socket.settimeout()mà chấp nhận một đối số nguyên đại diện cho số giây. Ví dụ: socket.settimeout(1)sẽ đặt thời gian chờ là 1 giây


2

hãy thử điều này, nó sử dụng C bên dưới.

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

Điều này thật tuyệt, vì nó cho phép người ta đặt các giá trị khác nhau cho thời gian chờ gửi và gửi lại bằng cách sử dụng SO_RCVTIMEOSO_SNDTIMEO.
jtpereyda

Tại sao 2và tại sao 100? Giá trị thời gian chờ nào là? Ở đơn vị nào?
Alfe

timeval = struct.pack('ll', sec, usec) s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeval)usec = 10000 có nghĩa là 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

0

Kêu to: https://boltons.readthedocs.io/en/latest/socketutils.html

Nó cung cấp một ổ cắm được đệm, điều này cung cấp rất nhiều chức năng rất hữu ích như:

.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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.