我在Windows 8.1 Enterprise 64位上使用Python 2.7.9版。
(win 2.7上的Python 2.7.9(默认,2014年12月10日,12:28:03)[MSC v.1500 64位(AMD64)]
所以我正在编写一个python IRC bot,一切都在脚本中运行。
我遇到的问题是,如果我发送一个KeyboardInterrupt,控制台窗口中的脚本显示它仍在运行,直到机器人收到数据。
情况:
执行脚本以连接到IRC服务器
登录服务器没问题
在控制台窗口中,我发送了一个CTRL + C
控制台窗口挂起,看起来好像脚本正在运行
发送机器人查询消息/消息被发送到频道
控制台显示我在异常
中指定的中断和退出消息
一旦将CTRL + C发送到控制台,该脚本是否应该立即退出?如果我发送一个退出消息,我确实在脚本中有一个部分可以让它优雅地关闭,但是这部分让我烦恼。
这是我的代码,我认为它可能有问题:
class Animal
{
// ...
}
class Cat : public Animal
{
// ...
}
class Dog : public Animal
{
// ...
}
// + Several other derived classes
答案 0 :(得分:1)
在Windows上,使用Python 2.x,Ctrl-C通常不会中断套接字调用。
在某些情况下,Ctrl-Break有效。如果确实如此,如果这对您来说足够好,那么您就完成了。
但是如果Ctrl-Break不起作用,或者作为解决方法不可接受,唯一的选择是设置你自己的控制台ctrl-key处理程序,[SetConsoleControlHandler
] (
https://msdn.microsoft.com/en-us/library/windows/desktop/ms686016%28v=vs.85%29.aspx)。
对此on the PyZMQ issue tracker进行了很好的讨论,包括some sample code的链接,以便解决此问题。
如果您可以使用win32api
中的PyWin32
模块,代码可能会更简单,但假设您不能,我认为这是您想要的代码:
from ctypes import WINFUNCTYPE, windll
from ctypes.wintypes import BOOL, DWORD
kernel32 = windll.LoadLibrary('kernel32')
PHANDLER_ROUTINE = WINFUNCTYPE(BOOL, DWORD)
SetConsoleCtrlHandler = kernel32.SetConsoleCtrlHandler
SetConsoleCtrlHandler.argtypes = (PHANDLER_ROUTINE, BOOL)
SetConsoleCtrlHandler.restype = BOOL
CTRL_C_EVENT = 0
CTRL_BREAK_EVENT = 1
@PHANDLER_ROUTINE
def console_handler(ctrl_type):
if ctrl_type in (CTRL_C_EVENT, CTRL_BREAK_EVENT):
# do something here
return True
return False
if __name__ == '__main__':
if not SetConsoleCtrlHandler(console_handler, True):
raise RuntimeError('SetConsoleCtrlHandler failed.')
问题是如何放入# do something here
。如果有一个简单的答案,Python就已经在做了。 :)
根据文档,HandlerRoutine
函数实际上在不同的线程上运行。并且,IIRC,从主线程下关闭套接字将始终导致其recv
唤醒并引发异常。 (不是你想要的那个,但仍然是你可以处理的东西。)
但是,我找不到文档来证明 - 它绝对不推荐(并且WinSock2似乎正式允许close
失败WSAEINPROGRESS
,即使没有微软实施的WinSock2实际上就是这样......),但是你只是试图拯救并退出这里。
所以,我相信只需在main
中定义和安装处理程序,这样就可以编写s.close()
,因为# do something here
可以正常工作。
如果您希望以保证保证安全有效的方式执行此操作,即使使用了一些奇怪的WinSock2实现,您也从未听说过,您需要什么要做的事情有点复杂。您需要使recv
异步,并使用Windows异步I / O(这是非常痛苦的Python 2.x,除非您使用像Twisted这样的重型库),或者编写跨平台{{基于1}}的代码(它不是Windows-y,但只要您使用额外的select
而不是socket
的常用Unix解决方案,就可以工作。像这样(未经测试!)示例:
pipe
如果这对您没有意义,Sockets HOWTO实际上对如何使用# ctypes setup code from above
def main():
extrasock = socket.socket(socket.SOCK_DGRAM)
extrasock.bind(('127.0.0.1', 0))
extrasock.setblocking(False)
@PHANDLER_ROUTINE
def console_handler(ctrl_type):
if ctrl_type in (CTRL_C_EVENT, CTRL_BREAK_EVENT):
killsock = socket.socket(socket.SOCK_DGRAM)
killsock.sendto('DIE', extrasock.getsockname())
killsock.close()
return True
return False
if not SetConsoleCtrlHandler(console_handler, True):
raise RuntimeError('SetConsoleCtrlHandler failed.')
# your existing main code here, up to the try
# except that you need s.setblocking(False) too
try:
r, _, _ = select.select([s, extrasock], [], [])
if extrasock in r:
raise KeyboardInterrupt
if s in r:
readbuffer = readbuffer + s.recv(4096)
except KeyboardInterrupt:
# rest of your code here
有很好的解释,以及在Windows上使用它的缺陷。