目前计算 TCP/IP Socket 连接产生的上下行额外流量方法大约有两种
1、RAW_SOCKET、libpcap 捕包方式
2、不要脸的算流字节方法
最精准的办法是RAW_SOCKET,这可以捕获到重发、RST、FIN、ECE、UGR、CWR、SYN等产生的所有流量,如果用于计算实际产生流量是最好的办法。
缺点是不能用到 Android 上面,因为没有ROOT权限,没有办法执行这样的套路,所以我们只能用“第二方案”,即不要脸的算流字节方法。
但该方法并不能真正的统计到所有流量,要丢一部分带宽统计,因为它只是一个算法规则来求解达到多大带宽吞吐量大约需要多少的上下 Kbps 带宽。
TCP_MSS_ 为1440,即MT_则为1492,因为对于普通用户的网络来说,MTU_最大仅为1492而非1500字节,1440=180<<3,1500最大为TCP/IP单片1448=181<<3。
小于1的流量,于TCP/IP上面通常为收到FIN、RST则收到0字节,那么就可以确定至少产生了一个TCP_HDR_SIZE_大小的流量,否则我们仅只能通过当前产生的字节数来求大约本次产生了多少流量,但由于现代TCP/IP协议栈都是使用FAST ACK(快速确认)机制来处理对端扔的大量TCP/IP Segment过来,所以不会是傻瓜的一对一ACK,所以计算流量的方式就得按照大体公式了,在使用系统可变长CWND缓冲区的情况下,Windows、Linux 系统内核协议栈大致产生的上/下额外流量大致可以用以下公式计算出来,根据对网卡的抓包,一个TCP/IP连接大量的从外面下载,该连接产生上行带宽速率,反之亦然。
inline static int TCPLEN(int n) {
static int TCP_MSS_ = 1440;
static int IPP_MTU_ = ITap::Mtu;
static int TCP_HDR_SIZE_ = IPP_MTU_ - TCP_MSS_;
if (n < 1) {
return TCP_HDR_SIZE_;
}
int d = n / TCP_MSS_;
d *= TCP_HDR_SIZE_;
if ((n % TCP_MSS_) != 0) {
d += TCP_HDR_SIZE_;
}
d = (std::max<int>(0, d >> 3) * 750) / 1000;
return d + 1;
}
但 Linux 使用了不同的TCP/IP控制算法,可能最终产生的上行带宽速率(非用户)是不同的,BBR/BBRplus 等等,产生的上行带宽吞吐是 Windows CTCP/DTCP协议栈产生的上行带宽的8倍左右,所以大致我们就要用以下的算法来计算,即 300Mbps 下行带宽吞吐时,上行带宽需要介于10~20Mbps。
inline static int TCPLEN(int n) {
static int TCP_MSS_ = 1440;
static int IPP_MTU_ = ITap::Mtu;
static int TCP_HDR_SIZE_ = IPP_MTU_ - TCP_MSS_;
if (n <= TCP_MSS_) {
return TCP_HDR_SIZE_;
}
int d = n / TCP_MSS_;
d *= TCP_HDR_SIZE_;
if ((n % TCP_MSS_) != 0) {
d += TCP_HDR_SIZE_;
}
return std::max<int>(0, d >> 1);
}
Windows 应用程式被 “Windows32 调试器” 附加的时候,TCP/IP连接产生的上行带宽流量会扩大8倍,其计算方法大致如以下的实现。
#if NETCOREAPP
[MethodImpl()]
#endif
private static int TCPLEN(int n)
{
if (n <= TCP_MSS_)
{
return TCP_HDR_SIZE_;
}
int d = n / TCP_MSS_;
d *= TCP_HDR_SIZE_;
if ((n % TCP_MSS_) != 0)
{
d += TCP_HDR_SIZE_;
}
if ()
{
return (0, d >> 1);
}
else
{
return (0, d >> 7);
}
}
但这一切都建立在单次读入应用缓冲区为16KB,低于该缓冲区大小收取,不敢保证公式计算出来的上行/下行额外带宽是正确的。
以第二类方法,如何正确统计实际产生的流量?
TCP/IP Socket 向外发送数据:则
int TX = TCPLEN(发送字节数)
传出字节数 + 发送字节数 + TX
传入字节数 + TX
TCP/IP Socket 收取入站数据:则
int TX = TCPLEN(收取字节数)
传入字节数 + 收取字节数 + TX
传出字节数 + TX
那么,如何进行验证上述代码的有效性?
Ubuntu16.04 打开 gnome-system-monitor(系统监视器)查看 Network 哪一项
Windows 10 打开任务管理器,查看 Network(网络)哪一项(找你的出入网的物理网卡那张)
然后打开应用上述提到算法的工具,去拉上/下行带宽峰值测速,然后查看 “任务管理器/系统监视器” 上面,网卡流出的实际上/行带宽是不是跟程序上指示的差不多就知道。(很好验证的)