Python3.8+Twisted22.10.0连接FreeSWITCH遇字节类型TypeError
使用Python3.8和Twisted22.10.0,通过Reactor与事件套接字连接FreeSWITCH并处理事件,已成功建立TCP连接,但运行Reactor线程后出现以下错误:
Unhandled Error
Traceback (most recent call last):
File "/home/.local/lib/python3.8/site-packages/twisted/python/log.py", line
96, in callWithLogger
return callWithContext({"system": lp}, func, *args, **kw)
File "/home/.local/lib/python3.8/site-packages/twisted/python/log.py", line
80, in callWithContext
return context.call({ILogContext: newCtx}, func, *args, **kw)
File "/home/.local/lib/python3.8/site-packages/twisted/python/context.py",
line 117, in callWithContext
return self.currentContext().callWithContext(ctx, func, *args, **kw)
File "/home/.local/lib/python3.8/site-packages/twisted/python/context.py",
line 82, in callWithContext
return func(*args, **kw)
------
File "/home/.local/lib/python3.8/site-packages/twisted/internet/posixbase.py",
line 487, in _doReadOrWrite
why = selectable.doRead()
File "/home/.local/lib/python3.8/site-packages/twisted/internet/tcp.py", line
249, in doRead
return self._dataReceived(data)
File "/home/.local/lib/python3.8/site-packages/twisted/internet/tcp.py", line
256, in _dataReceived
rval = self.protocol.dataReceived(data)
File "/home/.local/lib/python3.8/site-packages/twisted/protocols/basic.py",
line 542, in dataReceived
line, self._buffer = self._buffer.split(self.delimiter, 1)
builtins.TypeError: a bytes-like object is required, not 'str'
代码片段:
from twisted.internet import protocol, reactor factory = MyFactory() reactor.connectTCP('127.0.0.1', 8021, factory) reactor.run(installSignalHandlers=0) class MyFactory(protocol.ReconnectingClientFactory): def __init__(self): self.password = '----' MyFactory.stopDeferred = defer.Deferred() def buildProtocol(): some code here to build protocol return protocol
排查发现basic.py中self._buffer和self.delimiter均为字节类型,不想修改该文件,求解决方法。
这个错误的核心是字符串与字节类型不匹配,Twisted的TCP传输默认传递字节数据,但协议处理逻辑中混入了字符串操作,或使用了错误的协议模式,解决方法如下:
使用字节版基础协议类
若使用LineReceiver这类基础协议,Python3下Twisted默认处理字节数据,需确保继承的是twisted.protocols.basic.LineReceiver,且将所有分隔符设置为字节类型,比如把delimiter = '\n'改为delimiter = b'\n'。统一数据类型处理
所有与传输相关的操作必须保持字节一致:发送数据时用self.transport.write(b"your_command"),不能直接传字符串;处理dataReceived传入的字节数据时,如需转文本操作,先解码为字符串,处理完成后再编码回字节发送。修正
buildProtocol方法
原方法缺少必要参数,且返回的协议实例需保证属性类型正确,示例修正如下:from twisted.internet import protocol, reactor, defer class MyProtocol(protocol.Protocol): def connectionMade(self): # 发送认证及订阅事件命令,均用字节类型 self.transport.write(b"auth your_password_here\n") self.transport.write(b"event plain ALL\n") def dataReceived(self, data): # 字节转字符串处理 response = data.decode('utf-8') # 后续业务逻辑 print(response) class MyFactory(protocol.ReconnectingClientFactory): def __init__(self): self.password = b'----' # 密码改为字节类型 self.stopDeferred = defer.Deferred() def buildProtocol(self, addr): # 补全self和addr参数 self.resetDelay() return MyProtocol() factory = MyFactory() reactor.connectTCP('127.0.0.1', 8021, factory) reactor.run(installSignalHandlers=0)排查混合操作
检查协议代码中是否存在字符串与字节直接拼接、赋值的情况,统一转换为同一类型后再操作。
内容的提问来源于stack exchange,提问作者Eranki

