我正在使用带有 epoll 的非阻塞套接字在 linux C++ 上编写一个程序,等待 EPOLLOUT 以便对某些数据执行 send()。
我的问题是:我已经读过,在非阻塞模式下,数据被复制到内核的缓冲区,因此 send() 调用可能会立即返回,表明所有数据都已发送,而实际上它只是复制到内核的缓冲区。
我如何知道远程对等方实际发送和接收数据的时间,以了解实际传输速率?
我正在使用带有 epoll 的非阻塞套接字在 linux C++ 上编写一个程序,等待 EPOLLOUT 以便对某些数据执行 send()。
我的问题是:我已经读过,在非阻塞模式下,数据被复制到内核的缓冲区,因此 send() 调用可能会立即返回,表明所有数据都已发送,而实际上它只是复制到内核的缓冲区。
我如何知道远程对等方实际发送和接收数据的时间,以了解实际传输速率?
您可以使用 IOCTL 获取内核套接字缓冲区中的当前数据量。这将允许您检查实际发送的内容。不过,我不确定这是否重要,除非您有大量缓冲区和少量数据要发送,否则可能不感兴趣。
调查套接字 fd 上的 TIOCOUTQ/TIOCINQ ioctl。
我的问题是:我已经读过在非阻塞模式下数据被复制到内核的缓冲区
这发生在所有模式中,而不仅仅是非阻塞模式。我建议你复习你的阅读材料。
因此,一个 send() 调用可能会立即返回,表明所有数据都已发送,而实际上它只是复制到内核的缓冲区中。
同样,在所有模式下都是如此。
我如何知道远程对等方实际发送和接收数据的时间,以了解实际传输速率?
发送完所有数据后,关闭套接字以进行输出,然后设置阻塞模式并读取,或者继续选择“可读”;然后在任何一种情况下都读取应该产生的 EOS。这起到了对收盘的对等确认的作用。然后停止计时器。
无论是否处于非阻塞模式,只要将数据复制到内核缓冲区,send 就会返回。阻塞和非阻塞模式的区别在于缓冲区满的时候。在缓冲区满的情况下,阻塞模式将暂停当前线程,直到写入发生,而非阻塞模式将立即返回 EAGAIN 或 EWOULDBLOCK。
在 TCP 连接中,内核缓冲区通常等于窗口大小,因此一旦有太多数据仍未确认,连接就会阻塞。这意味着发送方知道远程端接收数据的速度。
对于 UDP,它有点复杂,因为没有确认。这里只有接收端能够测量真实速度,因为发送的数据可能会在途中丢失。
在 TCP 和 UDP 两种情况下,内核都不会尝试发送链路层无法处理的数据。如果网络拥塞,链路层也可以流出数据。
回到你的情况,当使用非阻塞套接字时,如果你正确处理了 EAGAIN 或 EWOULDBLOCK 错误,你可以测量网络速度。这对于 TCP 来说当然是正确的,在这种情况下,您发送的数据比当前窗口大小(可能是 64K 左右)更多,您也可以了解使用 UDP 套接字的链路层速度。
send()
只是将数据放入内核的缓冲区然后退出,让内核在后台执行实际的传输,所以你真正能做的就是测量内核接受你传出数据的速度。除非对等方为收到的每个缓冲区发送确认(并且无法检测何时收到 TCP 自己的确认),否则您无法真正测量实际的传输速度。但是,当有太多数据仍在传输中时,使用可以阻塞的事实send()
可以帮助您了解您的代码将传出数据传递给send()
.
send()
告诉您接受了多少字节。因此,很容易计算出一个近似的接受速度——用接受的字节数除以自上次调用send()
. 因此,当您调用send()
发送X
字节并获取Y
返回的字节时,将时间记录为time1
,send()
再次调用以发送X
字节并获取Y
返回的字节,将时间记录为time2
,您将看到您的代码以大约Y / ((time2-time1) in ms)
每毫秒字节数发送数据,您可以然后根据B/KB/MB/GB per ms/sec/min/hr
需要使用计算。在数据传输的整个生命周期中,这让您可以很好地了解应用程序的一般传输速度。