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

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函数未等待缓冲区刷新,可能出现数据未发送的情况。

修改步骤

  1. 复用事件循环:初始化时创建并保存事件循环,所有异步操作共用该循环,连接结束时再销毁。
  2. 确保写入完成:在_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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 13:54:51