Как закрыть клиентские потоки после их отключения? - PullRequest
0 голосов
/ 30 сентября 2019

есть простая программа чата, использующая библиотеку asyncio Python. Клиенты могут общаться без каких-либо операций. Также сервер выводит информацию в окно консоли всякий раз, когда клиент подключен / отключен от сервера. Кроме того, все действия клиентов записываются сервером в файл журнала.

Нет явных проблем, когда я смотрю на окна консоли. Спустя одну секунду после отключения клиента информация об этом клиенте записывается в консольное окно сервера. Однако, когда я смотрю на файл журнала, видно, что сервер пытается отправить пустые сообщения всем подключенным клиентам. Но эти сообщения не отображаются в клиентских окнах.

Так что, похоже, сервер неправильно закрывает отключенных клиентов. Потоки чтения и записи отключенного клиента кажутся активными, даже если клиент отключен.

Как решить эту проблему в соответствии с кодами, которыми я поделился? Кстати, я тоже делюсь файлом журнала. Вот коды:

server.py

#!/usr/bin/env python3.7
# -*- coding: utf-8 -*-

import asyncio
import logging

logging.basicConfig(
    filename="server.log",
    format="- %(levelname)s - %(asctime)s - %(message)s", 
    level=logging.DEBUG,
    datefmt="%d.%m.%Y %H:%M:%S"
    )    


class Server:

    def __init__(self):
        self.clients = []

    def run(self):
        asyncio.run(self.main())

    async def client_connected(self, reader, writer):
        client = f"{writer.get_extra_info('peername')}"
        print(f"{client} is connected.")
        logging.info(f"{client} is connected.")
        self.clients.append((writer, reader))
        while True:
            try:
                data = await reader.readline()
            except (BrokenPipeError, ConnectionResetError):
                data = "".encode()
                await asyncio.sleep(1)
            for i in self.clients:
                msg = f"{client}: {data.decode()}"
                try:
                    i[0].write(msg.encode())
                    logging.debug(msg[:-1])
                    await i[0].drain()
                except (BrokenPipeError, ConnectionResetError):
                    i[0].close()
                    self.clients.remove(i)
                    print(f"{client} is disconnected.")
                    logging.info(f"{client} is disconnected.")
                    await asyncio.sleep(2)
                    break

    async def main(self):
        server = await asyncio.start_server(
            client_connected_cb=self.client_connected,
            host="127.0.0.1",
            port=12345
        )
        print(f"Server started on {server.sockets[0].getsockname()}")
        logging.info(f"Server started on {server.sockets[0].getsockname()}")
        async with server:
            await server.wait_closed()


if __name__ == "__main__":
    Server().run()

client.py

#!/usr/bin/env python3.7
# -*- coding: utf-8 -*-

import sys
import asyncio
import threading


class Client:

    def __init__(self):
        self.nick = input("/nick ")
        while not self.nick:
            self.nick = input("/nick ")

    def run(self):
        asyncio.run(self.main())

    async def read(self, reader, writer):
        data = await reader.readline()
        socket = writer.get_extra_info('socket').getsockname()
        if str(socket) not in data.decode() or \
                self.nick not in data.decode():
            if data.decode().count("('") >= 1:
                data = data.decode().split(": ")[-2:]
                print(": ".join(data)[:-1])

    async def write(self, writer):
        t = threading.Thread(
            target=lambda: writer.write(
                f"{self.nick}: {sys.stdin.readline()}".encode()
            )
        )
        t.daemon = True
        t.start()
        t.join(0.1)
        await writer.drain()

    async def main(self):
        reader, writer = await asyncio.open_connection("127.0.0.1", 12345)
        print(f"Connected to {writer.get_extra_info('peername')}")
        while reader and writer:
            await asyncio.gather(
                self.read(reader, writer), 
                self.write(writer)
            )


if __name__ == "__main__":
    Client().run()

server.log

- DEBUG - 30.09.2019 15:17:50 - Using selector: EpollSelector
- INFO - 30.09.2019 15:17:50 - Server started on ('127.0.0.1', 12345)
- INFO - 30.09.2019 15:17:56 - ('127.0.0.1', 45562) is connected.
- INFO - 30.09.2019 15:18:05 - ('127.0.0.1', 45564) is connected.
- INFO - 30.09.2019 15:18:21 - ('127.0.0.1', 45566) is connected.
- DEBUG - 30.09.2019 15:18:48 - ('127.0.0.1', 45566): Client3: Hello Clients!
- DEBUG - 30.09.2019 15:18:48 - ('127.0.0.1', 45566): Client3: Hello Clients!
- DEBUG - 30.09.2019 15:18:48 - ('127.0.0.1', 45566): Client3: Hello Clients!
- DEBUG - 30.09.2019 15:18:51 - ('127.0.0.1', 45566): Client3: How are you?
- DEBUG - 30.09.2019 15:18:51 - ('127.0.0.1', 45566): Client3: How are you?
- DEBUG - 30.09.2019 15:18:51 - ('127.0.0.1', 45566): Client3: How are you?
- DEBUG - 30.09.2019 15:18:53 - ('127.0.0.1', 45566):
- DEBUG - 30.09.2019 15:18:53 - ('127.0.0.1', 45566):
- DEBUG - 30.09.2019 15:18:53 - ('127.0.0.1', 45566):
- DEBUG - 30.09.2019 15:18:53 - ('127.0.0.1', 45566):
- DEBUG - 30.09.2019 15:18:53 - ('127.0.0.1', 45566):
- DEBUG - 30.09.2019 15:18:53 - ('127.0.0.1', 45566):
- INFO - 30.09.2019 15:18:53 - ('127.0.0.1', 45566) is disconnected.
- DEBUG - 30.09.2019 15:18:55 - ('127.0.0.1', 45564):
- DEBUG - 30.09.2019 15:18:55 - ('127.0.0.1', 45564):
- DEBUG - 30.09.2019 15:18:55 - ('127.0.0.1', 45564):
- DEBUG - 30.09.2019 15:18:55 - ('127.0.0.1', 45564):
- INFO - 30.09.2019 15:18:55 - ('127.0.0.1', 45564) is disconnected.
- DEBUG - 30.09.2019 15:18:56 - ('127.0.0.1', 45566):
- DEBUG - 30.09.2019 15:18:57 - ('127.0.0.1', 45562):
- DEBUG - 30.09.2019 15:18:57 - ('127.0.0.1', 45562):
- INFO - 30.09.2019 15:18:57 - ('127.0.0.1', 45562) is disconnected.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...