Как обрабатывать KeyboardException, возникающие в поточном модуле? - PullRequest
0 голосов
/ 06 марта 2019

Я создаю сервер с использованием модулей socket и threading. Сервер позволяет выполнять несколько соединений. Я занимался обработкой ошибок (как показано в коде ниже). Однако есть одна ошибка, которую я не могу понять, как ее обработать.

Код (server.py):

import socket
import threading
# import pyaudio
# import json
from os import system

system('clear')

host = socket.gethostbyname(socket.gethostname())
port = 5000
buffer_size = 2048
connected_clients = {}


# Thread for listening and accepting new connections
def client_listener():
    while True:
        try:
            # Accepts new client
            connection, address = server_socket.accept() 

            # Creates a new thread for each connected client
            handle_client_thread = threading.Thread(target=handle_client, args=(connection, address))
            handle_client_thread.start()
        except ConnectionAbortedError:
            pass
        except OSError:
            pass
        except KeyboardInterrupt:
            pass


# Handles each connected client in a separate thread
def handle_client(client, client_address):
    try:
        # Receives room number
        room_number = client.recv(buffer_size).decode('utf-8')
        print(f'\nConnection from {client_address[0]}:{client_address[1]} ({room_number})')

        # Adds new client to 'connected_clients'
        connected_clients[room_number] = client
        print(connected_clients)

        while True:
            data = client.recv(buffer_size).decode('utf-8')
            # If no data is received, close the socket
            if not data:
                print(f'{client_address[0]}:{client_address[1]} ({room_number}) Disconnected')
                connected_clients.pop(room_number)
                break
            # Print received data
            else:
                print(f'{client_address[0]} ({room_number}): {data}')
    except KeyboardInterrupt:
        client.close()
        connected_clients.pop(room_number)


with socket.socket() as server_socket:
    try:
        # Prevents [Errno 48: Address already in use]
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # Binds socket to host and port
        server_socket.bind((host, port))
        server_socket.listen()
        print(f'Server hosted on {host}:{port}')

        # Starts new thread for listening for new clients
        client_listener_thread = threading.Thread(target=client_listener, name=client_listener)
        client_listener_thread.start()

        # Prevents main thread from closing
        while True:
            pass
    except socket.error as error_message:
        print(str(error_message))
    except KeyboardInterrupt:
        print(': Closing server...\n')
        pass

Сообщение об ошибке появляется, когда я закрываю сервер с помощью прерывания клавиатуры (^ C). Когда я впервые нажимаю ^ C, сервер закрыт. Однако второй ^ C, похоже, вызывает исключение в модуле threading.

Сообщение об ошибке:

Server hosted on 10.108.249.108:5000
^C: Closing server...

^CException ignored in: <module 'threading' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py'>
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 1273, in _shutdown
    t.join()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 1032, in join
    self._wait_for_tstate_lock()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 1048, in _wait_for_tstate_lock
    elif lock.acquire(block, timeout):
KeyboardInterrupt

Как мне поступить с обработкой этого KeyboardInterrupt без проблем, чтобы программа закрывалась без ошибок на одном ^ C? Желаемым результатом будет то, что сервер закроет соединения с каждым подключенным клиентом, закроет потоки и, наконец, завершит работу самого сервера.

1 Ответ

0 голосов
/ 06 марта 2019

Когда вы нажимаете Ctrl-C на клавиатуре во время выполнения сценария на консоли, вы отправляете SIGINT процессу сценария.Вы можете использовать модуль Python signal для обработки таких сигналов:

import signal
import sys
def my_sig_handler(sig, frame):
        print('Received interrupt signal')
        sys.exit(0)
signal.signal(signal.SIGINT, my_sig_handler) # Register your signal handler for SIGINT

После того, как вы это сделаете, каждый раз, когда ваш исполняемый скрипт получает сигнал прерывания, будет вызываться my_sig_handler.В Linux определено несколько видов сигналов, и, как правило, рекомендуется добавить в ваш код обработчики сигналов для некоторых из них, чтобы он мог корректно очиститься и выйти.

Дополнительная информация осигналы здесь .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...