twisted: 捕获键盘中断并正确关闭

18 投票
2 回答
9313 浏览
提问于 2025-04-16 02:36

更新:为了让阅读更简单,这里是如何在反应器关闭之前添加一个回调函数的方法:

reactor.addSystemEventTrigger('before', 'shutdown', callable)

原始问题如下。


如果我有一个客户端连接到服务器,并且它在反应器的主循环中等待事件,当我按下CTRL-C时,我会看到“与另一方的连接以不干净的方式丢失:连接丢失。”我该如何设置,以便在发生键盘中断时知道,这样我就可以进行适当的清理并干净地断开连接?或者,如果可能的话,我该如何实现一种更干净的关闭方式,而不涉及CTRL-C?

2 个回答

2

我不太确定你是在说你写的客户端还是服务器。

不过,使用'CTRL-C'是没问题的。

如果你在写一个服务器应用,可以从 twisted.application.service.Service 这个类继承,并定义 startServicestopService 这两个方法。你需要维护一个活跃的协议实例列表。然后用 stopService 方法来遍历这些实例,优雅地关闭它们。

如果你写的是客户端,也可以继承 Service,但其实用 reactor.addSystemEventTrigger('before','shutdown',myCleanUpFunction) 可能更简单。在这个函数里,你可以优雅地关闭连接。

31

如果你真的很想专门捕捉到 C-c 这个操作,可以按照 Python 应用程序的常规方式来做——使用 signal.signal 来安装一个处理器,专门处理 SIGINT 信号,这样你就可以在这个处理器里做你想做的事情。不过,如果你在处理器里调用任何 Twisted 的 API,记得要用 reactor.callFromThread,因为几乎所有其他的 Twisted API 在信号处理器中调用都是不安全的。

不过,如果你只是想在关闭时插入一些清理代码,那么你可能更想用 IService.stopService(或者它的实现机制 reactor.addSystemEventTrigger)来处理。

如果你在使用 twistd,那么使用 IService.stopService 就很简单。你已经有一个 Application 对象,并且至少有一个服务附加在上面。你可以添加另一个服务,并自定义一个 stopService 方法来处理你的关闭工作。这个方法可以返回一个 Deferred 对象。如果返回了这个对象,关闭过程会暂停,直到这个 Deferred 被触发。这让你可以很好地清理连接,即使这涉及到一些网络(或其他异步)操作。

如果你没有使用 twistd,那么直接使用 reactor.addSystemEventTrigger 可能更简单。你可以安装一个 在关闭之前 的触发器,这个触发器会在 IService.stopService 本该被调用的情况下被调用。这个触发器(只要是任何可调用的对象)也可以返回一个 Deferred 来延迟关闭。你可以通过调用 reactor.addSystemEventTrigger('before', 'shutdown', callable) 来实现(在关闭被启动之前的某个时刻注册,这样在关闭发生时它已经被注册了)。

service.tac 提供了创建和使用自定义服务的示例。

wxacceptance.py 提供了使用 addSystemEventTrigger 并延迟关闭三秒的示例。

这两种机制都会在 reactor 停止时给你通知。这个停止可能是因为按下了 C-c,或者是因为有人使用了 kill -INT ...,也可能是因为某个地方调用了 reactor.stop()。这些情况都会导致 reactor 关闭,而 reactor 关闭时总是会处理关闭事件触发器。

撰写回答