使用Python Twisted进行UDP流控制

3 投票
1 回答
1771 浏览
提问于 2025-04-17 05:03

我有一个类,它是从 twisted.internet.protocol.DatagramProtocol 这个类继承而来的。在我的 startProtocol() 方法里,我调用了 startWriting(),这样每当我可以往套接字写数据而不被阻塞时,它就会收到通知。这里有两个问题:

  1. 当套接字变得可以写入时,twisted 会调用哪个方法?
  2. 如果我需要在特定的时间间隔内调用 startWriting() 方法,以限制每秒发送的 UDP 数据包数量,我应该怎么做?

1 个回答

2

哎呀。我想我可能在另一个讨论中回答了你的问题,让你觉得Twisted对UDP流控制的支持比实际要强大一些。不过,不管怎样,你还是可以完成你需要做的事情……

1. Twisted会在什么情况下调用socket变为可写的方法?

不幸的是,Twisted中的UDP协议并不会监控可写性,因为UDP协议本身就可能随时失败,所以它不应该抛出EWOULDBLOCK这个错误。(实际上,它有时候确实会抛出,而这是Twisted中的一个bug,我在回答这个问题时又重新发现了这个问题。这种情况只会在Twisted以比本地网络速度更快的速度发送UDP时发生,这需要一个非常快的应用和一个非常慢的网络。)

作为一种解决方法,你的应用可以简单地捕获EWOULDBLOCK错误。对于其他协议来说,这种解决方法可能会造成严重问题,但对于UDP来说,你本来就得准备好丢失任何发出的数据包,所以你需要一种内部控制流机制。

帮助我们把这个bug通过审核流程也是一个选项。

如果你想要更复杂一点,可以自己写一个替代udp.Port(通过自己实现IFileDescriptor),而不是写一个UDP协议,重写doReaddoWrite(这两个方法分别在底层socket可读和可写时被调用)。这样你就能实现完美的写入级流控制,但可能并不必要,因为UDP有时会丢失你的数据包,而且在一些无法正确处理“ICMP源抑制”消息的网络上(那些配置为阻止ICMP的简单防火墙会直接阻止这些消息),丢失的数据包就是你唯一的流控制信息。我并不是说你不应该真正修复Twisted中的这个bug,但在UDP的世界里,这种情况可能就是为什么到现在还没有人去修复它的原因。

2. 如果我需要在特定的时间间隔调用startWriting()方法,以限制每秒发送的UDP数据包数量,该怎么做?

由于在第一部分中提到的限制,UDP传输并没有一个有用的startWriting方法。

不过,startWriting/stopWriting其实也不是限制你发送UDP带宽的正确方法。

你只需在适当的时间调用self.transport.write(...),并通过合适的调度机制安排这些调用。例如,LoopingCall就是为了在适当的时间间隔内发送UDP数据而设计的,适合用于RTP媒体流传输。但你也可以自己计算延迟,直接使用callLater。无论如何,你可能需要保留某种队列机制,以防需要重新传输排队等待通过UDP发送的数据,所以只需弹出

如果你需要进行入站流控制,UDP传输在这方面支持得很好,可以使用stopReadingstartReading

希望这个回答对你有帮助,如果之前让我误导了你关于Twisted在这方面的能力,我很抱歉!

撰写回答