Я пытаюсь закодировать сервер сокетов на своем Raspberry Pi. Я пытаюсь использовать декоратор для подключения клиента к серверу (который будет моим Raspberry Pi). Но мой код продолжает выдавать ошибку NoneType. Я немного новичок в асинхронных декораторах, и мне нужна помощь. Не могли бы вы посмотреть, откуда берется моя ошибка, и, пожалуйста, немного поправьте мой код? Я перечислю свой код и сообщения об ошибках ниже.
Мое сообщение об ошибке:
C:\Users\####\AppData\Local\Programs\Python\Python36-32\python.exe C:/Users/#####/Desktop/autorank/server/client.py
Traceback (most recent call last):
File "C:/Users/#####/Desktop/autorank/server/client.py", line 5, in <module>
@client.connection(("192.168.0.11", 56500))
TypeError: 'NoneType' object is not callable
Мой клиентский код для пакета:
import socket, asyncio
class Client:
def __init__(self):
self.s = None
self.data = None
def connection(self, addr):
"""An asynchronous decorator for any server mainloop. Just decorate it with this and add a Client parameter."""
def wrapper1(func):
async def wrapper2(*args):
self.connect(addr)
await func(self)
self.close_conn()
return await func(self)
return wrapper2
async def connect(self, addr):
"""Connects to the desired server. You can make a server mainloop with the connection decorator."""
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.connect(addr)
self.addr = addr
print(f"Connected to {addr}")
async def close_conn(self):
"""Closes connection with the address connected to."""
self.s.close()
print(f"Disconnected from {self.addr}")
del self.addr
async def send_bytes(self, bytes_data):
"""Sends bytedata through a pipe to connected address"""
self.s.sendall(bytes_data)
print(f"Sent {bytes_data.decode()} in bytedata format to {self.addr}")
async def recv(self, buf_size):
"""Recieves data from a server in bytedata format"""
self.data = self.s.recv(buf_size)
И, наконец, мой main.py:
import asyncserver
client = asyncserver.Client()
@client.connection(("192.168.0.11", 56500))
def mainloop(cli):
cli.send_bytes(b'Hello!!')
cli.recv(1024)
print(cli.data.decode())
Пожалуйста, скажите, должен ли я улучшить свой вопрос, и любые ответы приветствуются!
Ничего, если вы можете объяснить больше? Я пытался найти хороший учебник по асинхронному серверу, но не смог его найти! Я был бы признателен, если бы вы прислали ссылку на учебник по асинхронному серверу, но все остальное приветствуется!
Я не знаю учебника по серверу, но пример эхо-сервера в документации — хорошее начало.
Декоратор connection
правильно определяет wrapper1
, но не возвращает его. Он неявно возвращает None
, и это то, что используется как mainloop
на верхнем уровне, что приводит к ошибке.
Обратите внимание, что wrapper2
ожидает func(self)
, что может привести к другой ошибке, о том, что None
не является ожидаемым. Если вы планируете ждать функцию, которую вы декорируете, вы должны либо сделать ее асинхронной (async def mainloop(cli): ...
), либо заставить ее возвращать ожидаемый объект.
Наконец, wrapper2
не должен принимать произвольные *args
, если не будет их использовать. Это приведет к тому, что что-то вроде mainloop(1, 2, 3)
будет работать, игнорируя аргументы. Обычно точка принятия обертки *args
состоит в том, чтобы перенаправить их в обернутую функцию. Если вы не хотите или не нуждаетесь в этом, просто сделайте так, чтобы wrapper2
не принимал аргументов.
Взглянув еще раз на этот код, вы поймете, что он совершенно неверен, поскольку использует блокирующие сокеты внутри цикла событий asyncio. Например, ваша сопрограмма
send_bytes()
заблокирует весь цикл событий, ожидая, пока одноранговый узел примет байты. Это серьезный недостаток, который приведет к сбою в работе другого асинхронного кода из-за невозможности распараллеливания. Вместо этого вы должны использовать asyncio.open_connection, хранить объекты потока, которые он возвращает, и работать с ними.