使用UDP时,我在理解Linux下如何管理套接字缓冲区和超时时遇到一些问题。我正在使用内核版本为4.14.63的OpenWrt嵌入式Linux发行版。
为了更好地理解这些概念,我试图分析iPerf开源网络测量程序的客户端在发送UDP数据包以测试诸如可达吞吐量之类的参数时所使用的代码。它是用C和C ++编写的。
特别是,我尝试将提供的流量值设置得比网络(以及接收者)可以提供的流量高得多,从而获得预期的某些数据包丢失。
在这种情况下,由于iPerf使用时间戳计算了每个数据包传输之后的循环时间,因此我能够估计应用程序将每个数据包写入UDP缓冲区所花费的时间。 这些数据包实际上是在while()循环内写入的,该循环在套接字上为每个数据包调用write()。
通过调用以下命令在套接字上也设置一次超时:
setsockopt(mSettings->mSock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout))
这应该在写入套接字时设置发送超时,这当然是阻塞超时。
当缓冲区已满时,write()调用被阻塞,我可以看到循环时间增加了很多;问题是我无法真正理解此调用会阻塞应用程序多少时间。 通常,当write()被阻止时,它是否会像存在新数据包的空间一样解除阻止?还是等待更多(似乎发生;据我所知,尝试设置“大” UDP缓冲区值(800 KB,当发送带有1470 B有效负载的UDP数据报时),它似乎在等待持续约700毫秒,让连续发送数据的网络堆栈清空缓冲区的时间超过单个数据包所需的空间)?为什么?
我还有另一个与超时有关的疑问:为了记录每个write()调用的返回值,我尝试对源代码进行一些修改,甚至观察到没有错误发生,甚至设置超时时间为300毫秒或600毫秒,这比之前观察到的700毫秒小。
由于记录了缓冲区的演变(以及每次数据包传输时的循环时间),这要归功于ioctl:
ioctl(mSettings->mSock,TIOCOUTQ,&bufsize);
但是,我能够观察到,将超时设置为300 ms或600 ms实际上会有所不同,并且使阻塞write()在第一种情况下等待大约300 ms,在第一种情况下等待600 ms。第二种情况,因为检测到满缓冲区。 因此,即使未检测到错误,超时似乎实际上也已到期。但是,这似乎在所有情况下都可以导致正确的写操作。
这可能吗?发生这种情况是因为write()阻塞了应用程序足够的时间,以使其在超时到期时完全写入数据吗?
对此我有点困惑。
非常感谢您。