我正在嘗試撰寫一個簡單的守護行程來監聽 Unix 套接字上的命令。以下作業,但connection.recv(1024)行阻塞,這意味著我不能優雅地殺死服務器:
import socket, os
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as server:
server.bind("/tmp/sock")
server.listen()
connection, __ = server.accept()
with connection:
while True:
data = connection.recv(1024)
print("Hi!") # This line isn't executed 'til data is sent
if data:
print(data.decode())
理想情況下,我想將所有這些放在Thread每秒鐘檢查一次self.should_stop屬性的a中self.LOOP_TIME,如果該值設定為True,則退出。但是,由于該.recv()行被阻塞,我的程式除了在任何給定時間等待之外沒有辦法做任何事情。
當然有一種正確的方法可以做到這一點,但由于我是套接字的新手,我不知道那是什么。
編輯
Jeremy Friesner 的回答讓我走上了正軌。我意識到我可以允許執行緒阻塞并簡單地設定.should_stop然后將 an 傳遞b""給套接字,以便它解除阻塞,看到它應該停止,然后干凈地退出。這是最終結果:
import os
import socket
from pathlib import Path
from shutil import rmtree
from threading import Thread
class MyThreadThing(Thread):
RUNTIME_DIR = Path(os.getenv("XDG_RUNTIME_DIR", "/tmp")) / "my-project-name"
def __init__(self):
super().__init__(daemon=True)
self.should_stop = False
if self.RUNTIME_DIR.exists():
rmtree(self.RUNTIME_DIR)
self.RUNTIME_DIR.mkdir(0o700)
self.socket_path = self.RUNTIME_DIR / "my-project.sock"
def run(self) -> None:
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
s.bind(self.socket_path.as_posix())
s.listen()
while True:
connection, __ = s.accept()
action = ""
with connection:
while True:
received = connection.recv(1024).decode()
action = received
if not received:
break
# Handle whatever is in `action`
if self.should_stop:
break
self.socket_path.unlink()
def stop(self):
"""
Trigger this when you want to stop the listener.
"""
self.should_stop = True
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
s.connect(self.socket_path.as_posix())
s.send(b"")
uj5u.com熱心網友回復:
使用任意長度的超時總是有點不令人滿意——要么將超時值設定為相對較長的時間,在這種情況下,您的程式對退出請求的反應變得緩慢,因為它毫無意義地等待超時期限到期...或者您將超時值設定為相對較短的時間,在這種情況下,您的程式會不斷喚醒以查看它是否應該退出,從而浪費 CPU 電源 24/7 來檢查可能永遠不會到達的事件。
處理該問題的更優雅的方法是創建一個管道,并在您希望事件回圈退出時在管道上發送一個位元組。您的事件回圈可以同時“觀察”管道的讀取端檔案描述符和您的網路套接字通過select(),并且當該檔案描述符指示它已準備好讀取時,您的事件回圈可以通過退出來回應。這種方法完全是事件驅動的,因此它不需要 CPU 喚醒,除非確實有事情要做。
下面是您的程式的示例版本,它為 SIGINT(也就是按下 Control-C)實作了一個信號處理程式,以在管道上發送請立即退出位元組:
import socket, os
import select
import signal, sys
# Any bytes written to (writePipeFD) will become available for reading on (readPipeFD)
readPipeFD, writePipeFD = os.pipe()
# Set up a signal-handler to handle SIGINT (aka Ctrl C) events by writing a byte to the pipe
def signal_handler(sig, frame):
print("signal_handler() is executing -- SIGINT detected!")
os.write(writePipeFD, b"\0") # doesn't matter what we write; a single 0-byte will do
signal.signal(signal.SIGINT, signal_handler)
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as serverSock:
serverSock.bind("/tmp/sock")
serverSock.listen()
# Wait for incoming connection (or the please-quit signal, whichever comes first)
connection = None
while True:
readReady,writeReady,exceptReady = select.select([readPipeFD,serverSock], [], [])
if (readPipeFD in readReady):
print("accept-loop: Someone wrote a byte to the pipe; time to go away!");
break
if (connection in readReady):
connection, __ = serverSock.accept()
break
# Read data from incoming connection (or the please-quit signal, whichever comes first)
if connection:
with connection:
while True:
readReady,writeReady,exceptReady = select.select([readPipeFD,connection], [], [])
if (readPipeFD in readReady):
print("Connection-loop: Someone wrote a byte to the pipe; time to go away!");
break
if (connection in readReady):
data = connection.recv(1024)
print("Hi!") # This line isn't executed 'til data is sent
if data:
print(data.decode())
print("Bye!")
uj5u.com熱心網友回復:
使用與您相同的超時時間LOOP_TIME:
import socket, os
LOOP_TIME = 10
should_stop = False
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as server:
server.bind("/tmp/sock")
server.listen()
connection, __ = server.accept()
connection.settimeout(LOOP_TIME)
with connection:
while not should_stop:
try:
data = connection.recv(1024)
except socket.timeout:
continue
print("Hi!") # This line isn't executed 'til data is sent
if data:
print(data.decode())
您可以使用select,但如果它只是一個簡單的套接字,這種方式就不太復雜了。
您可以選擇將其放置在不同的執行緒中,self.should_stop或者只是在主執行緒中- 它現在將偵聽 KeyboardInterrupt。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/387198.html
上一篇:EADDRNOTAVAIL即使在使用IP_FREEBIND之后?
下一篇:使用C編程發送UDP結構
