[英]Python, server - client connection coroutine issues asyncio
Been trying to create a server and client script that allows clients to connect to the server and join different rooms to chat.一直在尝试创建一个服务器和客户端脚本,允许客户端连接到服务器并加入不同的房间进行聊天。 As of now everything is terminal based.
到目前为止,一切都是基于终端的。 The issue at hand is the following.
手头的问题如下。
When a new clients connects to a existing room i get the following error:当新客户端连接到现有房间时,出现以下错误:
Task exception was never retrieved
future: <Task finished name='Task-9' coro=<ChatServer.handle_client() done, defined at C:\Users\~~~~\server.py:10> exception=TypeError('a coroutine was expected, got None')>
Traceback (most recent call last):
File "C:\Users\~~~~\server.py", line 41, in handle_client
await self.broadcast(f'{addr} has joined room {room}!\n', room, writer)
File "C:\Users\~~~~\server.py", line 75, in broadcast
task = asyncio.create_task(client.write(message.encode()))
File "C:\Users\~~~~\AppData\Local\Programs\Python\Python39\lib\asyncio\tasks.py", line 361, in create_task
task = loop.create_task(coro)
File "C:\Users\~~~~\AppData\Local\Programs\Python\Python39\lib\asyncio\base_events.py", line 438, in create_task
task = tasks.Task(coro, loop=self, name=name)
TypeError: a coroutine was expected, got None
But, if the clients join separete rooms there is no error.但是,如果客户加入单独的房间,则不会出现错误。 If a client join an exists room with another client it inside, it gets that error.
如果一个客户端加入一个存在的房间,而另一个客户端在它里面,它就会得到那个错误。 I have been trying to understand the error at hand but cant wrap my head around it.
我一直试图理解手头的错误,但无法解决这个问题。
Here is the code for the server:这是服务器的代码:
import asyncio
#available_rooms = {"Room_1"}
class ChatServer:
def __init__(self):
self.clients = {}
self.rooms = {}
async def handle_client(self, reader, writer):
addr = writer.get_extra_info('peername')
print(f'Connected by {addr}')
# Send the list of available rooms to the client
available_rooms = '\n'.join(self.rooms.keys())
print("---->" + available_rooms)
writer.write(f'Available Rooms: {available_rooms}\n'.encode())
await writer.drain()
while True:
message = await reader.readline()
if not message:
break
message = message.decode().strip()
command = message.split()[0]
if command == "LIST":
available_rooms = '\n'.join(self.rooms.keys())
writer.write(f'Available Rooms: {available_rooms}\n'.encode())
await writer.drain()
elif command == "JOIN":
room = message.split()[1]
if room not in self.rooms:
writer.write(f"{room} room not found. creating it\n".encode())
self.rooms[room] = []
await writer.drain()
self.clients[writer] = room
self.rooms[room].append(writer)
writer.write(f'You joined room: {room}!\n'.encode())
await writer.drain()
await self.broadcast(f'{addr} has joined room {room}!\n', room, writer)
elif command == "LEAVE":
if writer not in self.clients:
writer.write(f'You are not currently in a room!\n'.encode())
await writer.drain()
else:
room = self.clients[writer]
writer.write(f'You left room {room}!\n'.encode())
await writer.drain()
self.clients.pop(writer)
self.rooms[room].remove(writer)
await self.broadcast(f'{addr} has left room {room}!\n', room, writer)
# remove the room if it is empty
if not self.rooms[room]:
self.rooms.pop(room)
else:
try:
await self.broadcast(f'{addr}: {message}\n', self.clients[writer], writer)
except KeyError:
writer.write(f'You are not currently in a room!\n'.encode())
await writer.drain()
continue
print(f'Disconnected {addr}')
writer.close()
async def broadcast(self, message, room, sender):
tasks = []
for client in self.rooms[room]:
# exclude sender from broadcast
if client != sender:
task = asyncio.create_task(client.write(message.encode()))
tasks.append(task)
if tasks:
await asyncio.gather(*tasks)
def start(self):
loop = asyncio.get_event_loop()
coro = asyncio.start_server(self.handle_client, '127.0.0.1', 5323, loop=loop)
#coro = asyncio.start_server(self.handle_client, '10.44.33.158', 5000, loop=loop)
server = loop.run_until_complete(coro)
# Serve requests until Ctrl+C is pressed
print('Serving on {}'.format(server.sockets[0].getsockname()))
try:
loop.run_forever()
except KeyboardInterrupt:
pass
# Close the server
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
server = ChatServer()
server.start()
And here is the code for the client:这是客户端的代码:
import asyncio
class ChatClient:
def __init__(self):
self.room = None
async def start(self, host, port):
self.reader, self.writer = await asyncio.open_connection(host, port)
# Get the list of available rooms
available_rooms = await self.reader.readline()
print(available_rooms.decode())
self.writer.write('JOIN myroom_2\n'.encode())
await self.writer.drain()
resp = await self.reader.readline()
if resp:
print(resp.decode())
self.room = "myroom_2"
while True:
message = await self.reader.readline()
print(message.decode())
message_to_send = input()
if message_to_send == "LEAVE":
self.writer.write("LEAVE\n".encode())
await self.writer.drain()
print("left the room")
break
elif message_to_send.startswith("JOIN"):
_, new_room = message_to_send.split()
self.writer.write(f"JOIN {new_room}\n".encode())
await self.writer.drain()
resp = await self.reader.readline()
if resp:
print(resp.decode())
self.room = new_room
continue
elif message_to_send == "LIST":
self.writer.write("LIST\n".encode())
await self.writer.drain()
response = await self.reader.readline()
print(response.decode())
continue
self.writer.write(f"{message_to_send}\n".encode())
await self.writer.drain()
async def list_rooms(self):
self.writer.write("LIST\n".encode())
await self.writer.drain()
try:
response = await asyncio.wait_for(self.reader.readline(), timeout=5)
print(response.decode())
except asyncio.TimeoutError:
print("timeout reached while waiting for a response")
async def change_room(self):
self.writer.write("Enter new room name:\n".encode())
await self.writer.drain()
new_room = input()
self.writer.write(f"JOIN {new_room}\n".encode())
await self.writer.drain()
response = await self.reader.readline()
print(response.decode())
self.room = new_room
async def write(self, message):
self.writer.write(message.encode())
await self.writer.drain()
client = ChatClient()
asyncio.run(client.start('127.0.0.1',5323 ))
I have tried different write methods but nothing works, unable to wrap my head around this issue.我尝试了不同的写入方法但没有任何效果,无法解决这个问题。
The problem is in how you are using asyncio.create_task
.问题在于您如何使用
asyncio.create_task
。
In the Server.broadcast
function you call:在
Server.broadcast
function 你调用:
task = asyncio.create_task(client.write(message.encode()))
asyncio.create_task
expects a coroutine but you are giving it a normal funciton. asyncio.create_task
需要一个协程,但你给它一个正常的功能。 client
in this context will be a asyncio.StreamWriter
, and the write
method is a normal function. you should probably create a task from the client.drain
coroutine:此上下文中的
client
将是asyncio.StreamWriter
, write
方法是正常的 function。您可能应该从client.drain
协程创建一个任务:
client.write(message.encode())
task = asyncio.create_task(client.drain())
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.