есть простая программа чата, использующая библиотеку 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.