Обработка SIGINT или KeyboardInterrupt в Python 3 для корректного выхода с помощью блока «with socket» - PullRequest
0 голосов
/ 05 июля 2018

Для проекта, в котором я сбрасываю данные через tcp через защищенную сеть, я пытаюсь написать тестовый сервер разработки, чтобы упростить процесс тестирования. У меня есть сервер, который в основном просто принимает данные и печатает их в терминал.

Однако я недоволен моей текущей реализацией обработки sigint.

Я пытался использовать обработчики сигналов и пробовать / исключать / окончательно блокировать в разных конфигурациях безрезультатно.

Вот код для тестового сервера

#!/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()

Эта версия завершается без трассировки стека ... и ничего больше.

Вот что я пробовал, и проблемы:

  • использовать глобальную переменную «прослушивания» (во время прослушивания) вместо «True», и в этом случае обработчик sigint установит для переменной значение «false». Это означало, что если сокет tcp никогда не принимал какие-либо данные, он вообще не заканчивался бы с sigint, а sigquit или sigkill были необходимы

  • использовать блок try catch в блоке while True или непосредственно перед ним. Еще раз, это на самом деле ничего не помогало, пока соединение не было принято.

  • использовать пробный улов для всего основного с блоком. Это значит, что я могу только выйти и подделать изящное отключение, а на самом деле это не так. Кроме того, я считаю, что это противоречит принципу использования блока with. (и я не уверен, что этот подход закрывает сокет, или если это даже необходимо)

1 Ответ

0 голосов
/ 12 июля 2018

Здесь есть несколько вариантов, в зависимости от того, что именно вас беспокоит. Как написано сейчас ... Нет, если бы ваш 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

Если единственное, что вы гарантируете, это то, что файловые дескрипторы закрыты и избегают трассировки стека, то то, что у вас есть сейчас, в порядке.

...