You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:50:22