简体   繁体   English

Python,服务器 - 客户端连接协程问题异步

[英]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.StreamWriterwrite方法是正常的 function。您可能应该从client.drain协程创建一个任务:

client.write(message.encode())
task = asyncio.create_task(client.drain())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM