如何在Python中捕获SIGINT信号以中断Socket接收操作
解决Socket阻塞接收时响应信号中断的问题
你现在遇到的核心问题是:阻塞的sock.recv()调用会卡住程序,哪怕收到SIGINT/SIGTERM信号把killer.die设为True,程序也得等recv()返回后才会检查循环条件。这是因为recv()是阻塞式系统调用,信号处理完成后,程序会回到这个调用继续等待,不会主动跳出阻塞状态。
下面给你几个实用的解决方案:
方案1:给Socket设置超时时间(最简单直接)
给socket设置一个超时阈值,这样recv()会在超时后抛出socket.timeout异常,我们可以捕获这个异常,趁机检查killer.die的状态,决定是否退出循环。
修改后的完整代码示例:
import signal import socket class Killer: die = False def __init__(self): signal.signal(signal.SIGINT, self.terminate) signal.signal(signal.SIGTERM, self.terminate) def terminate(self, signum, frame): print(f"caught {signum}") self.die = True # 初始化信号处理器 killer = Killer() # Socket初始化(替换成你的实际连接配置) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('localhost', 12345)) # 设置超时时间,比如5秒,可根据需求调整 sock.settimeout(5) while not killer.die: try: # 尝试接收数据,超时会抛出异常 data = sock.recv(1024) # 注意原代码里的receive是Python socket库的recv方法 if not data: # 连接被对方关闭,直接退出循环 print("Connection closed by peer") break # 处理收到的数据(替换成你的业务逻辑) print(f"Received data: {data}") except socket.timeout: # 超时后啥也不用做,回到循环检查退出标记 continue except Exception as e: # 其他异常(比如连接中断),直接退出 print(f"Error occurred: {e}") break # 清理资源 sock.shutdown(socket.SHUT_RDWR) sock.close() print("Program exited gracefully")
方案2:非阻塞Socket + select(更灵活的控制)
如果需要更精准的事件监听,可以把socket设为非阻塞,配合select.select()同时监听socket的可读事件和信号通知(用管道传递信号触发的事件)。这种方式适合复杂场景,但代码稍繁琐:
import signal import socket import select import os class Killer: die = False def __init__(self, pipe_w): self.pipe_w = pipe_w signal.signal(signal.SIGINT, self.terminate) signal.signal(signal.SIGTERM, self.terminate) def terminate(self, signum, frame): print(f"caught {signum}") self.die = True # 往管道写数据,通知select退出阻塞 os.write(self.pipe_w, b'x') # 创建管道用于传递信号触发的事件 pipe_r, pipe_w = os.pipe() killer = Killer(pipe_w) # Socket初始化(替换成你的实际连接配置) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('localhost', 12345)) sock.setblocking(False) while not killer.die: # 同时监听socket和读管道的可读事件 ready, _, _ = select.select([sock, pipe_r], [], []) if pipe_r in ready: # 读取管道内容(避免下次重复触发) os.read(pipe_r, 1) continue if sock in ready: try: data = sock.recv(1024) if not data: print("Connection closed by peer") break print(f"Received data: {data}") except BlockingIOError: # 暂时没有数据可读,继续循环 continue # 清理所有资源 sock.shutdown(socket.SHUT_RDWR) sock.close() os.close(pipe_r) os.close(pipe_w) print("Program exited gracefully")
关键注意事项
- 原代码里的
sock.receive()是笔误,Python socket库的正确接收方法是sock.recv()。 - 如果把接收逻辑放在子线程中,建议用
threading.Event替代布尔值die,保证线程安全。 - 无论哪种方案,退出前一定要关闭socket和相关资源,避免资源泄漏。
内容的提问来源于stack exchange,提问作者Brydon Gibson
相关产品推荐
相关产品推荐

