在每个人都将此标记为重复之前,请允许我声明,我了解我的网络编程知识,并且这个问题是我尝试解决的,即使我已经找到了“解决方案”。
设置
过去几周中,我花费大量时间编写代码来将一个大型工业系统整合到我们当前的设置中。该系统由控制Windows XP计算机(PC A)控制,通过向Ubuntu 14.04系统(PC B)发送2000 Hz的UDP数据包进行控制。它会响应包含系统当前状态的UDP数据包。
要确保保持每秒2000次的速率,需要仔细处理,因为超过3ms后就会出现故障并返回到安全状态。这涉及测量和考虑std::this_thread::sleep_for
的不准确性。测量结果显示,与目标速率相比仅有0.1%偏差。
观察结果
当我开始从系统接收状态响应时,问题就开始出现了。在PC B上的控制端看起来大致如下:
forever at 2000Hz {
send current command;
if ( socket.available() >= 0 ) {
receive response;
}
}
编辑 2:或者在实际代码中:
auto cmd_buf = ...
auto rsp_buf = ...
while (true) {
// prepare and send command buffer
cmd_buf = ...
socket.send(cmd_buf, endpoint);
if (socket.available() >= 0) {
socket.receive(rsp_buf);
// the results are then parsed and stored, nothing fancy
}
// time keeping
}
问题在于,只要代码的接收部分存在于PC B上,PC A在尝试分配接收缓冲区时就会在几秒钟内耗尽内存。此外,它还会引发错误,指出超时已经错过,这可能是由于数据包未到达控制软件引起的。
仅为突出奇怪之处:在这种情况下,PC A是发送UDP数据包的电脑。 针对EJP的编辑: 这是(现在)工作的设置。 它最初是这样的:
forever at 2000Hz {
send current command;
receive response;
}
但是当收到响应(阻塞)时,已经错过了截止日期。因此需要进行可用性检查。
尝试的另一件事是在单独的线程中接收:
// thread A
forever at 2000Hz {
send current command;
}
// thread B
forever {
receive response;
}
这个版本的行为与第一个版本相同。
解决方案
解决方案是将PC B上的套接字设置为非阻塞模式。只需要一行代码,所有问题就都解决了。
即使在阻塞模式下,我很确定也可以满足截止时间。当只涉及一个套接字时,阻塞模式和非阻塞模式之间应该没有性能差异。 即使在阻塞模式下,检查套接字是否有可用数据需要比在非阻塞模式下多花费几微秒,但当整体截止时间得到精确满足时,这并不会造成影响。
现在...这是怎么回事?
available() > 0
是完全浪费时间的,你应该删除那个测试并将其保留在阻塞模式下。接下来的read()
或recv()
将一直阻塞,直到有数据可用。如果需要读取超时,请设置读取超时。接收传入数据的缓冲区应该在循环之前已经分配好。 - user207421