Windows下Python实现telnetlib3同步版本时AttributeError的解决方法
问题描述
在Windows 10系统中使用Python 3.10.11编写telnetlib3的同步封装版本时,创建连接、读取数据正常,但调用Telnet3类的write方法时抛出以下错误:
Traceback (most recent call last): session.write("test") File "C:\Users\Test\example_telnet3_sync.py", line 66, in write response = loop.run_until_complete(_write(self.writer, command)) File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\lib\asyncio\base_events.py", line 649, in run_until_complete return future.result() File "C:\Users\Test\example_telnet3_sync.py", line 41, in _write writer.write(command + "\r\n") File "C:\Users\Test\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\telnetlib3\stream_writer.py", line 2614, in write self._write(self.encode(string, errors)) File "C:\Users\Test\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\telnetlib3\stream_writer.py", line 1735, in _write self._transport.write(buf) File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\lib\asyncio\proactor_events.py", line 365, in write self._loop_writing(data=bytes(data)) File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\lib\asyncio\proactor_events.py", line 401, in _loop_writing self._write_fut = self._loop._proactor.send(self._sock, data) AttributeError: 'NoneType' object has no attribute 'send'
完整代码如下:
import asyncio import telnetlib3 async def _open(host, port): reader, writer = await telnetlib3.open_connection(host, port) data = await asyncio.wait_for(reader.read(4096), timeout=2) return reader, writer, data async def _read(reader, expected="myprompt >>"): reply = "" while True: data = await reader.read(4096) if data: reply += data if expected in reply: break return reply async def _read_timeout(reader, timeout=2): try: return await asyncio.wait_for(_read(reader), timeout=timeout) except (asyncio.exceptions.TimeoutError, RuntimeError): print("TimeoutError while reading from telnet!") return None async def _write(writer, command): writer.write(command + "\r\n") class Telnet3: def __init__(self, host, port): self.host = host self.port = port self.reader = None self.writer = None self.message = "" def connect(self): loop = asyncio.new_event_loop() self.reader, self.writer, self.message = loop.run_until_complete(_open(self.host, self.port)) loop.close() def read(self): loop = asyncio.new_event_loop() response = loop.run_until_complete(_read_timeout(self.reader)) loop.close() return response def write(self, command): loop = asyncio.new_event_loop() loop.run_until_complete(_write(self.writer, command)) loop.close() def write_read(self, command): self.write(command) return self.read() if __name__ == "__main__": session = Telnet3("100.200.10.10", 9000) session.connect() session.write("test")
修复方案
错误原因
每次调用connect、read、write方法时都创建新事件循环并立即关闭,导致connect关闭循环后,writer关联的底层传输对象(依赖原循环的proactor)被销毁,后续用新循环操作已失效的传输对象,触发AttributeError。同时_write函数未等待缓冲区刷新,可能出现数据未发送的情况。
修改步骤
- 复用事件循环:初始化时创建并保存事件循环,所有异步操作共用该循环,连接结束时再销毁。
- 确保写入完成:在
_write中添加await writer.drain(),等待缓冲区数据发送完成。
修改后的完整代码:
import asyncio import telnetlib3 async def _open(host, port): reader, writer = await telnetlib3.open_connection(host, port) data = await asyncio.wait_for(reader.read(4096), timeout=2) return reader, writer, data async def _read(reader, expected="myprompt >>"): reply = "" while True: data = await reader.read(4096) if data: reply += data if expected in reply: break return reply async def _read_timeout(reader, timeout=2): try: return await asyncio.wait_for(_read(reader), timeout=timeout) except (asyncio.exceptions.TimeoutError, RuntimeError): print("TimeoutError while reading from telnet!") return None async def _write(writer, command): writer.write(command + "\r\n") await writer.drain() # 等待数据发送完成 class Telnet3: def __init__(self, host, port): self.host = host self.port = port self.reader = None self.writer = None self.message = "" self.loop = asyncio.new_event_loop() # 初始化时创建并保存循环 def connect(self): self.reader, self.writer, self.message = self.loop.run_until_complete(_open(self.host, self.port)) def read(self): response = self.loop.run_until_complete(_read_timeout(self.reader)) return response def write(self, command): self.loop.run_until_complete(_write(self.writer, command)) def write_read(self, command): self.write(command) return self.read() def close(self): # 新增关闭方法,清理资源 if self.writer: self.writer.close() self.loop.run_until_complete(self.writer.wait_closed()) self.loop.close() if __name__ == "__main__": session = Telnet3("100.200.10.10", 9000) try: session.connect() session.write("test") # print(session.read()) # 可按需添加读取操作 finally: session.close()
额外说明
- Windows下asyncio默认使用
ProactorEventLoop,传输对象与创建它的循环强绑定,不能跨循环使用。 - 新增的
close方法用于正确关闭连接和事件循环,避免资源泄漏。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

