如何让Twisted的dataReceived方法一次性接收完整输入数据?
问题描述
我编写了如下基于Twisted框架的Python代码:
class TestProtocol(Protocol): def connectionMade(self): print 'Got connection from', self.transport.client def dataReceived(self, data): print(data) class Test(Factory, HoneypotService): NAME = 'test' protocol = TestProtocol def __init__(self, config=None, logger=None): HoneypotService.__init__(self, config=config, logger=logger) self.port = 8888 def getService(self): return internet.TCPServer(self.port, self)
使用telnet 127.0.0.1 8888连接服务并输入abcdef时,dataReceived方法被触发两次,无法一次性获取完整的abcdef数据。
解决方案
这是TCP流式协议的特性导致的:TCP没有内置消息边界,操作系统会根据网络情况将数据拆分为多个TCP段发送,因此Protocol.dataReceived会在每次收到TCP段时触发,无法保证一次拿到完整输入。
以下是两种可行的解决思路:
1. 用Twisted内置的LineReceiver(适配telnet按行输入场景)
LineReceiver已内置处理行分隔符(telnet回车默认发送\r\n),可直接获取整行数据:
from twisted.protocols.basic import LineReceiver class TestProtocol(LineReceiver): def connectionMade(self): print 'Got connection from', self.transport.client def lineReceived(self, line): # line为去掉换行符的完整输入行 print(line) # 如需自定义分隔符,可修改delimiter属性 # self.delimiter = '\n' class Test(Factory, HoneypotService): NAME = 'test' protocol = TestProtocol def __init__(self, config=None, logger=None): HoneypotService.__init__(self, config=config, logger=logger) self.port = 8888 def getService(self): return internet.TCPServer(self.port, self)
2. 手动维护缓冲区处理自定义消息边界
如果需要非按行的消息格式(如固定长度、特定结束符),可在Protocol中维护缓冲区,直到凑齐完整消息:
class TestProtocol(Protocol): def __init__(self): self.buffer = '' # 自定义消息结束符,这里用telnet默认的\r\n self.end_delimiter = '\r\n' def connectionMade(self): print 'Got connection from', self.transport.client def dataReceived(self, data): self.buffer += data # 循环检查缓冲区是否包含完整消息 while self.end_delimiter in self.buffer: # 拆分完整消息与剩余缓冲区 full_msg, self.buffer = self.buffer.split(self.end_delimiter, 1) # 处理完整消息 print(full_msg) # Test类代码保持不变
核心原理
TCP本身不提供消息边界,必须在应用层约定消息分割规则,常见方式包括:
- 按行分隔(如HTTP、telnet)
- 固定长度消息
- 带长度前缀的消息
- 特定结束标记
你的telnet场景下,使用LineReceiver是最简洁的方案。
内容的提问来源于stack exchange,提问作者ljs
相关产品推荐
相关产品推荐

