我已经使用 Twisted 实现了一个服务器程序。我正在使用带有 dataReceived 方法的 basic.lineReceiver 来接收来自多个客户端的数据。
这是一个错误——不幸的是,在 Twisted 的许多协议实现中错误地使用继承作为构建越来越复杂行为的机制而导致的常见错误。当您使用twisted.protocols.basic.LineReceiver 时,dataReceived 回调不适合您。 LineReceiver.dataReceived 是LineReceiver 的实现细节。您的回电是LineReceiver.lineReceived。 LineReceiver.dataReceived 看起来可能适合你——它不是以下划线或任何东西开头的——但事实并非如此。 dataReceived 是 LineReceiver 从其传输中接收信息的方式。它是IProtocol 的公共方法之一 - 传输和解释通过该传输接收的数据的协议之间的接口。是的,我只是在那里说“公共方法”。问题是它是为了其他人的利益而公开的。这是令人困惑的,也许没有像它可能的那样沟通。毫无疑问,这就是为什么它是 Frequently Asked Question。
这种方法被证明是不可靠的。第一个问题是由于使用了 TCP 流,有时消息会合并(我可以为此使用分隔符)。
使用dataReceived 是发生这种情况的原因。 LineReceiver 已经为您实现了基于分隔符的解析。这就是为什么它被称为“行”接收器 - 它接收由分隔符分隔的行。如果您覆盖 lineReceived 而不是 dataReceived,那么无论 TCP 如何拆分或将它们粉碎在一起,都会调用接收到的每一行。
其次,接收到的消息有时没有按正确的顺序排列。
TCP 是一种可靠、有序、面向流的传输。 “有序”意味着字节以与发送相同的顺序到达。换句话说,当您write("x"); write("y") 时,可以保证接收者在收到“y”之前会收到“x”(他们可能会在对recv() 的同一次调用中收到“x”和“y”,但如果他们收到了,数据肯定是“xy”而不是“yx”;或者他们可能会在两次调用recv() 时收到这两个字节,如果这样做,第一个recv() 肯定会是“x”,第二个肯定会是“y”,而不是相反)。
如果字节到达的顺序与您发送它们的顺序不同,则可能是某个地方存在另一个错误,使其 看起来 像发生这种情况 - 但实际上并非如此。您平台的 TCP 堆栈很可能非常接近无错误,特别是它可能没有 TCP 数据重新排序错误。同样,Twisted 的这个区域也经过了非常好的测试,并且可能正常工作。这会在您的应用程序代码中留下错误或对您的观察结果的误解。也许您的代码并不总是将数据附加到列表中,或者数据未按您预期的顺序发送。
另一种可能性是您正在谈论数据通过多个单独的 TCP 连接到达的顺序。 TCP 仅在单个连接上排序。如果您有两个连接,则很少(如果有的话)保证数据通过它们到达的顺序。
第三,网络通信似乎太慢了,因为当服务器最初尝试访问缓冲列表的最后一个元素时,该列表是空的(这表明缓冲区上的最后一条消息可能不是对最后发送的命令)。
“太慢”的定义是什么?网络和网络一样快。如果这对你来说还不够快,那就找一块更厚的铜片。听起来您在这里的真正意思是您的服务器有时希望数据在数据实际到达之前已经到达。这并不意味着网络太慢,但这意味着您的服务器没有正确地事件驱动。如果您正在检查缓冲区但没有找到您期望的信息,那是因为您在通知您该信息到达的事件发生之前检查了它。这就是为什么 Twisted 拥有所有这些回调方法 - dataReceived、lineReceived、connectionLost 等。当调用 lineReceived 时,这是一个事件通知,告诉您现在发生了一些事情这导致一行可用(并且为方便起见,lineReceived 采用一个参数 - 一个表示现在可用的行的对象)。
如果您有一些代码要在一行到达时运行,请考虑将该代码放在lineReceived 方法的实现中。这样,当它运行时(响应接收到的线路),您可以 100% 确定您有一条线路可以操作。您还可以确保它会尽快运行(只要线路到达),但不会更早。