Python AsyncIO协议创建报错:存在Transport却提示参数缺失
解决你的asyncio.Proxy错误
让我来帮你拆解这个问题——这个错误看起来有点绕,但核心是两个对asyncio.Protocol工作机制的误解:
错误1:工厂函数返回的是类而非实例
你在创建目标服务器连接时写了:
server_connection = loop.create_connection( lambda: ProxyForwardProtocol, host="127.0.0.1", port=25575)
这里的lambda返回的是ProxyForwardProtocol这个类本身,而不是它的实例。asyncio要求create_connection的第一个参数是一个返回Protocol实例的工厂函数,当asyncio尝试调用这个类的connection_made方法时,会把它当成类方法调用,而不是实例方法:
- 实例方法
connection_made(self, transport)需要两个参数(self和transport) - 但此时调用时只传入了transport作为第一个参数,所以Python会报错说缺少
transport参数(因为它把transport当成了self,然后找不到第二个参数)
错误2:ProxyForwardProtocol的__init__错误地要求transport参数
asyncio的Protocol实例的transport是通过connection_made方法传递的,不是在初始化时传入的。你的ProxyForwardProtocol定义了需要transport的构造函数,这会导致即使你返回实例,也会因为缺少参数而报错。
额外的架构优化建议
你在ClientProxyProtocol的__init__中就创建目标服务器连接,这其实不太合理——因为此时客户端的连接还没建立(connection_made还没触发),更好的时机是在connection_made中创建目标连接,这样你能确保客户端的transport已经就绪。
修正后的代码示例
import asyncio import socket users = {} class UserConnection(object): def __init__(self, client_socket, proxy_socket, peername): self.client_socket = client_socket self.proxy_socket = proxy_socket self.peername = peername class ClientProxyProtocol(asyncio.Protocol): def __init__(self): self.transport = None self.session = None self.proto_forward = None # 保存目标服务器的Protocol实例 def connection_made(self, transport): peername = transport.get_extra_info('peername') client_socket = transport.get_extra_info('socket') self.session = UserConnection(client_socket, None, peername) print('Connection from {}'.format(peername)) self.transport = transport # 移到connection_made中创建目标连接,确保客户端连接已就绪 server_connection = loop.create_connection( lambda: ProxyForwardProtocol(self), # 传递当前ClientProxy实例用于双向通信 host="127.0.0.1", port=25575) task = loop.create_task(server_connection) task.add_done_callback(self.handle_proxy_connection) def handle_proxy_connection(self, task): trans, proto = task.result() proxy_socket = trans.get_extra_info("socket") self.session.proxy_socket = proxy_socket self.proto_forward = proto # 保存目标端的Protocol,方便后续转发数据 users[proxy_socket] = self.session print(f"Connected to target server: {trans}") def data_received(self, data): message = data.decode('utf-8', errors='replace') print(f'Data from client {self.session.peername}: {message!r}') # 转发数据到目标服务器 if self.proto_forward and self.proto_forward.transport: self.proto_forward.transport.write(data) def connection_lost(self, exc): print(f'Close client socket for {self.session.peername}') self.transport.close() # 关闭目标服务器连接 if self.proto_forward and self.proto_forward.transport: self.proto_forward.transport.close() class ProxyForwardProtocol(asyncio.Protocol): def __init__(self, client_proxy): self.transport = None self.client_proxy = client_proxy # 保存客户端的Proxy实例,用于回传数据 def connection_made(self, transport): self.transport = transport proxy_socket = transport.get_extra_info("socket") print(f"Forward connection established: {proxy_socket.getpeername()}") def data_received(self, data): message = data.decode('utf-8', errors='replace') print(f'Data from target server: {message!r}') # 回传数据到客户端 if self.client_proxy and self.client_proxy.transport: self.client_proxy.transport.write(data) def connection_lost(self, exc): print("Closing forward connection") self.transport.close() # 关闭客户端连接 if self.client_proxy and self.client_proxy.transport: self.client_proxy.transport.close() loop = asyncio.get_event_loop() coro = loop.create_server(ClientProxyProtocol, '127.0.0.1', 25565) server = loop.run_until_complete(coro) print('Serving on {}'.format(server.sockets[0].getsockname())) try: loop.run_forever() except KeyboardInterrupt: pass server.close() loop.run_until_complete(server.wait_closed()) loop.close()
关键修改点说明
- 把
lambda: ProxyForwardProtocol改成lambda: ProxyForwardProtocol(self),返回实例同时传递客户端Proxy实例,方便双向数据转发 - 删除了
ProxyForwardProtocol中需要transport的构造函数,改为接收客户端Proxy实例用于通信 - 将目标服务器连接的创建移到
ClientProxyProtocol.connection_made中,确保客户端连接已建立 - 添加了数据转发逻辑,让代理真正实现双向通信(你原来的代码只打印数据,没有转发)
内容的提问来源于stack exchange,提问作者huhnmonster
相关产品推荐
相关产品推荐

