TCP可调参数不会在数据传输速率中显示预期的结果

我有用C语言编写的客户端服务器程序。目的是看看大数据可以通过TCP传输多快。 接收端操作系统(Ubuntu Linux 14. *)进行了调整,以提高TCP性能,根据周围的TCP /套接字/ Windows缩放等文件如下:

net.ipv4.tcp_window_scaling = 1 net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.ipv4.tcp_rmem = 4096 87380 16777216 net.ipv4.tcp_wmem = 4096 16384 16777216 

这aprt,我也通过setsockopt调用增加了单独的套接字缓冲区大小。

但是我没有看到程序对这些变化做出反应 – 总体吞吐量有时甚至是平坦的,甚至有所下降。 当我在接收端使用tcpdump的时候,在大多数(99%)的情况下,我看到一个长度为1368的tcp数据包的单调模式。

 19:26:06.531968 IP <SRC> > <DEST>: Flags [.], seq 25993:27361, ack 63, win 57, options [nop,nop,TS val 196975830 ecr 488095483], length 1368 

根据文档,tcp窗口缩放选项增加了接收帧大小的比例和容量的比例 – 但我所看到的只是“赢57” – 接收缓冲区中剩余的字节很less,与预期不匹配。

因此,我开始怀疑我对调整自己的假设,并有这些问题:

  1. 发送方是否需要特定的可调参数来改善客户端接收? 确保你(程序)一次写入整个数据块是不够的?

  2. 在上面提到的客户端可调参数是否必要和充足? 系统中的默认值太低,但是我看不到在/etc/sysctl.conf中应用的更改有任何影响。 正在运行sysctl – 系统更改足以使更改生效? 还是我们需要重启系统?

  3. 如果操作系统是虚拟机,那么这些可调参数是否会在完整性上有意义,或者在真实的物理机器上是否还有额外的步骤?

如果有帮助,我可以分享源代码,但是我可以保证这只是一个简单的代码。

这里是代码:

 #cat client.c #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <unistd.h> #include <arpa/inet.h> #include <string.h> #define size 1024 * 1024 * 32 int main(){ int s; char buffer[size]; struct sockaddr_in sa; socklen_t addr_size; s = socket(PF_INET, SOCK_STREAM, 0); sa.sin_family = AF_INET; sa.sin_port = htons(25000); sa.sin_addr.s_addr = inet_addr("<SERVERIP"); memset(sa.sin_zero, '\0', sizeof sa.sin_zero); addr_size = sizeof sa; connect(s, (struct sockaddr *) &sa, addr_size); int rbl = 1048576; int g = setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rbl, sizeof(rbl)); while(1) { int ret = read(s, buffer, size); if(ret <= 0) break; } return 0; } 

和服务器代码:

 bash-4.1$ cat server.c #include <sys/types.h> #include <sys/mman.h> #include <memory.h> #include <stdlib.h> #include <fcntl.h> #include <sys/stat.h> #include <netinet/in.h> #include <errno.h> #include <stdio.h> #include <sys/socket.h> extern int errno; #define size 32 * 1024 * 1024 int main() { int fdsocket; struct sockaddr_in sock; fdsocket = socket(AF_INET,SOCK_STREAM, 0); int rbl = 1048576; int g = setsockopt(fdsocket, SOL_SOCKET, SO_SNDBUF, &rbl, sizeof(rbl)); sock.sin_family = AF_INET; sock.sin_addr.s_addr = inet_addr("<SERVERIP"); sock.sin_port = htons(25000); memset(sock.sin_zero, '\0', sizeof sock.sin_zero); g = bind(fdsocket, (struct sockaddr *) &sock, sizeof(sock)); if(g == -1) { fprintf(stderr, "bind error: %d\n", errno); exit(1); } int p = listen(fdsocket, 1); char *buffer = (char *) mmap(NULL, size, PROT_WRITE|PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if(buffer == -1) { fprintf(stderr, "%d\n", errno); exit(-1); } memset(buffer, 0xc, size); int connfd = accept(fdsocket, (struct sockaddr*)NULL, NULL); rbl = 1048576; g = setsockopt(connfd, SOL_SOCKET, SO_SNDBUF, &rbl, sizeof(rbl)); int wr = write(connfd, buffer, size); close(connfd); } 

  1. 有很多可调的,但是否有效果,效果是正面还是负面,还取决于情况。 可调参数的默认值是什么? 您设置的值可能实际上低于您的操作系统的默认值,从而降低性能。 但更大的缓冲区有时也可能是有害的,因为使用更多的RAM,并且可能不再适合缓存存储器。 这也取决于你的网络本身。 它是有线的,无线的,有多少跳,哪种路由器介于两者之间? 但是尽可能以大块的方式发送数据通常是正确的。

    您错过的一个可调参数是拥塞控制算法 ,您可以调整net.ipv4.tcp_congestion_control 。 哪些是可用的取决于你的内核,哪一个最好取决于你的网络和你发送的流量的种类。

    另一件事是TCP有两个端点,双方的可调参数都很重要。

  2. 对于新的TCP连接, sysctl所做的更改将立即生效。

  3. TCP参数仅对TCP连接的端点有影响。 所以您不必在VM主机上更改它们。 但是,在客户端运行意味着它发送的数据包仍然需要由主机以某种方式处理(如果只是将它们转发到真实的物理网络接口)。 从虚拟机内运行测试总是比在物理机上运行测试慢。

我缺少的是任何基准数字,你可以比较实际的网络速度。 是否还有改进的空间? 也许你已经达到了可能的最高速度? 在这种情况下,没有任何调整会有帮助。 请注意,默认值通常非常合理。