使用“ with socket”块处理Python 3中的SIGINT或KeyboardInterrupt以正常退出

时间:2018-07-05 12:10:47

标签: python sockets tcp sigint

对于一个我要通过安全网络通过tcp转储数据的项目,我试图编写一个开发测试服务器以简化测试过程。我有服务器,它基本上只接收数据并将其打印到终端。

但是,我对当前的Sigint处理实现感到不满意。

我尝试使用信号处理程序,并且尝试/ except / finally阻止各种配置,但无济于事。

这是测试服务器的代码

#!/usr/bin/env python3
import socket
import sys

host = ''
port = 5000
try:
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        s.bind((host, port))
        s.listen(1)
        conn, addr = s.accept()
        with conn:
            print("Connected by", str(addr))
            while True:
                data = conn.recv(1024).decode()
                if not data:
                    break
                print(str(data))
except KeyboardInterrupt:
    print("Quitting...")
finally:
    sys.exit()

此版本退出时没有堆栈跟踪...那么,别无其他。

这是我尝试过的事情和问题:

  • (在收听时)使用全局“监听”变量(而不是while)为True,在这种情况下,sigint处理程序会将变量设置为false。这意味着,如果tcp套接字从未接受任何数据,它将根本不会以sigint终止,并且有必要使用sigquit或sigkill

  • 在while True块中或之前使用try catch块。再一次,在接受连接之前,这实际上并没有任何帮助。

  • 使用try catch捕获整个带块的主数据库。这意味着我只能退出并伪造正常关机,而实际上却不能。另外,我认为这与使用with块开头的原则背道而驰。 (而且我不确定这种方法是否会关闭套接字,或者是否有必要)

1 个答案:

答案 0 :(得分:1)

这里有很多选择,具体取决于您的关注点。就像现在写的那样...不,如果您的print只是一长串操作,那么如果您按Ctrl-C,它不一定完成。

您可以做的一件事是用SIGINT暂时阻止signal.pthread_sigmask信号,例如,如果您不想中断关键操作,然后再屏蔽它(此时,SIGINT将照常流向KeyboardInterrupt)。也可以通过上下文管理器来实现,如下所示:

#!/usr/bin/env python3
import signal

class SignalMasker:
    """ Temporarily block signals. """
    def __init__(self, sigs):
        self.sigs = set(sigs)
    def __enter__(self):
        signal.pthread_sigmask(signal.SIG_BLOCK, self.sigs)
        return self
    def __exit__(self, *args):
        signal.pthread_sigmask(signal.SIG_UNBLOCK, self.sigs)
        return None

if __name__ == '__main__':
    import time
    interrupted = True
    try:
        with SignalMasker([signal.SIGINT]):
            # Should not be able to interrupt this before 5 seconds expired
            time.sleep(5)
            interrupted = False
    except KeyboardInterrupt:
        print("\nGot keyboard interrupt")
        assert not interrupted

如果您唯一要确保的是关闭文件描述符并避免堆栈跟踪,那么现在您可以使用了。