You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

异步装饰器返回NoneType错误求助:树莓派Socket客户端代码修正

解决你的Socket客户端异步装饰器NoneType错误问题

看起来你在异步装饰器的实现上踩了几个常见的坑,我帮你一步步定位并修正问题:

核心错误原因:装饰器未返回包装函数

你的connection方法定义了内部的wrapper1,但没有返回这个函数,所以当你调用client.connection(("192.168.0.11", 56500))时,方法默认返回None,这就导致了@None的错误——你试图用None作为装饰器,自然会抛出'NoneType' object is not callable。

除了这个核心问题,还有几个异步逻辑的问题需要修正,下面是完整的修正代码和解释:

修正后的Client类代码

import socket, asyncio
class Client:
    def __init__(self):
        self.s = None
        self.data = None
        self.addr = None  # 提前初始化addr,避免close时可能的错误

    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):
                await self.connect(addr)  # connect是async方法,需要await
                await func(self)
                await self.close_conn()  # close_conn也是async方法,需要await
                # 移除重复的func调用,避免执行两次业务逻辑
            return wrapper2
        # 关键:返回wrapper1,让装饰器能正常工作
        return wrapper1

    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."""
        if self.s:
            self.s.close()
            print(f"Disconnected from {self.addr}")
            self.addr = None  # 重置而不是del,避免后续属性不存在的问题

    async def send_bytes(self, bytes_data):
        """Sends bytedata through a pipe to connected address"""
        if self.s:
            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"""
        if self.s:
            self.data = self.s.recv(buf_size)
            return self.data  # 返回数据,方便调用者直接使用

修正后的main.py代码

import asyncserver
import asyncio  # 需要导入asyncio来运行异步函数

client = asyncserver.Client()

@client.connection(("192.168.0.11", 56500))
async def mainloop(cli):  # 必须定义为async函数,因为装饰器里会await它
    await cli.send_bytes(b'Hello!!')  # 异步方法需要await
    received_data = await cli.recv(1024)  # 接收数据并获取返回值
    if received_data:
        print(received_data.decode())

# 运行异步主函数
asyncio.run(mainloop())

关键修改点说明

  1. 装饰器返回包装函数:在connection方法末尾添加了return wrapper1,这是装饰器能正常工作的核心。
  2. 异步方法调用加await:connect、close_conn、send_bytes、recv都是异步方法,调用时必须加await才能实际执行。
  3. 修复重复执行逻辑:移除了wrapper2中重复的return await func(self),避免你的业务逻辑被执行两次。
  4. mainloop改为async函数:因为装饰器内部会用await调用它,所以必须定义为异步函数。
  5. 添加异步运行入口:用asyncio.run(mainloop())来启动你的异步主逻辑,否则异步函数不会被执行。
  6. 增加空值检查:在close_conn、send_bytes、recv中添加了self.s的空值检查,避免连接异常时的报错。
  7. recv返回数据:让recv返回接收到的数据,方便调用者直接使用,同时保留self.data的赋值。

现在你可以运行修正后的代码,应该能正常连接服务器并收发数据了。

内容的提问来源于stack exchange,提问作者codernaut1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:14:18