วิธีตั้งค่าการหมดเวลาในวิธีการ Recv ซ็อกเก็ตของ python


110

ฉันต้องการตั้งค่าการหมดเวลาในวิธีการรับซ็อกเก็ตของ python ทำอย่างไร?


2
FYI หากคุณเลือกใช้การหมดเวลา ... คุณต้องรู้วิธีจัดการกับการหมดเวลา คำถาม SO นี้พูดถึงการจัดการเมื่อหมดเวลา: stackoverflow.com/questions/16745409
Trevor Boyd Smith

คำตอบ:


127

แนวทางโดยทั่วไปคือใช้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
เหตุใดจึงจำเป็นต้องตั้งซ็อกเก็ตโดยไม่มีการปิดกั้น ฉันไม่คิดว่าสิ่งนั้นสำคัญกับการเรียกที่เลือก (และจะบล็อกจนกว่าจะสามารถอ่านตัวอธิบายได้หรือหมดเวลาหมดเวลาในกรณีนี้) และ recv () จะไม่ปิดกั้นหากการเลือกนั้นพอใจ ฉันลองใช้ recvfrom () และดูเหมือนว่าจะทำงานได้อย่างถูกต้องโดยไม่ต้องตั้งค่าบล็อก (0)
HankB

1
จะready[0]เป็นเท็จก็ต่อเมื่อไม่มีเนื้อหาในการตอบสนองของเซิร์ฟเวอร์?
matanster

60

16
มันไม่หมดเวลารับการกู้คืน (อย่างน้อยก็เมื่อฉันลอง) เฉพาะการยอมรับ () เท่านั้นที่หมดเวลา
Oren S

9
ดูเหมือนว่า socket.recv () จะหมดเวลาสำหรับฉันหลังจากตั้งค่า socket.settimeout () ตรงตามที่ตั้งใจไว้ ฉันกำลังทำสิ่งนี้อยู่หรือเปล่า? มีใครยืนยันเรื่องนี้ได้อีกไหม
Aeonaut

3
@Aeonaut ฉันคิดว่านี่หมดเวลา recv () เกือบตลอดเวลา แต่มีเงื่อนไขการแข่งขัน ใน socket.recv () Python (2.6) เรียกใช้ select / โพลภายในด้วยการหมดเวลาจากนั้นเรียก recv () ในภายหลัง ดังนั้นหากคุณใช้ซ็อกเก็ตบล็อกและระหว่าง 2 สายนี้ทำให้จุดสิ้นสุดอีกอันขัดข้องคุณสามารถแขวนไว้ที่ recv () ได้อย่างไม่มีกำหนด หากคุณใช้ซ็อกเก็ตที่ไม่ปิดกั้น python จะไม่เรียก select.select ภายในดังนั้นฉันคิดว่าคำตอบของ Daniel Stutzbach เป็นวิธีที่ถูกต้อง
emil.p.stanchev

4
อันที่จริงฉันอาจเข้าใจผิดเมื่อเลือก () ส่งคืนดังนั้นโปรดขีดข่วนความคิดเห็นก่อนหน้านี้ คำแนะนำของ Beej กล่าวว่าข้างต้นเป็นวิธีที่ถูกต้องในการใช้การหมดเวลาใน recv: beej.us/guide/bgnet/output/html/singlepage/…ดังนั้นฉันจะเชื่อว่าเป็นแหล่งข้อมูลเชิงวิเคราะห์
emil.p.stanchev

2
ฉันไม่แน่ใจว่าเหตุใดโซลูชันที่ใช้selectจึงเป็นที่ต้องการเมื่อโซลูชันนี้เป็นแบบซับเดียว (ดูแลรักษาง่ายกว่ามีความเสี่ยงน้อยกว่าในการใช้งานผิดพลาด) และใช้การเลือกภายใต้ประทุน (การใช้งานเหมือนกับคำตอบของ @DanielStuzbach)
Trevor Boyd Smith

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 แฮงค์เช่นกัน
Casey Daniel

1
เมื่อคุณโทร.settimeout()มากกว่าหนึ่งครั้งคุณสามารถเรียกใช้setdefaulttimeout()เมธอดได้ตั้งแต่แรก
mvarge

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

ลองใช้มันใช้พื้นฐาน C.

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

นี้เป็นสิ่งที่ดีที่จะช่วยให้หนึ่งในการตั้งค่าที่แตกต่างกันสำหรับการส่งและ recv หมดเวลาการใช้และSO_RCVTIMEO SO_SNDTIMEO
jtpereyda

ทำไม2และทำไม100? ค่าการหมดเวลาคืออะไร? อยู่ในหน่วยใด?
Alfe

timeval = struct.pack('ll', sec, usec) s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeval)usec = 10000 หมายถึง 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

ติดต่อไปที่: 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)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.