我有一个用C编写的客户端服务器程序.目的是了解通过TCP传输大数据的速度有多快.调整接收端操作系统(Ubuntu
Linux 14. *)以提高TCP性能,根据tcp / socket / windows scaling等文档,如下所示:
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窗口缩放选项增加接收帧大小以适应需求和容量 – 但我所看到的只是“win 57” – 接收缓冲区中剩余的字节非常少,这与预期不匹配.
因此,我开始怀疑我对调整本身的假设,并提出以下问题:
>发送方是否需要任何特定的可调参数来改善客户端接收?确保您(程序)一次性写入整个数据块是不够的?
>在客户端可调,如上所述必要且充足?系统中的默认设置太低,但我没有看到/etc/sysctl.conf中应用的更改有任何影响.在更改后运行sysctl –system足以使更改生效吗?还是我们需要重启系统?
>如果操作系统是虚拟机,这些可调参数是否会在其完整性方面具有意义,还是在真实物理机器上还有其他步骤?
如果有帮助的话,我可以分享源代码,但我可以保证它只是一个简单的代码.
这是代码:
#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);
}
最佳答案 >有许多可调参数,但它们是否有影响以及效果是正还是负也取决于具体情况.可调参数的默认值是什么?您设置的值实际上可能低于操作系统的默认值,从而降低了性能.但是更大的缓冲区有时也可能是有害的,因为使用了更多的RAM,并且它可能不再适合缓存内存.它还取决于您的网络本身.它是有线的,无线的,有多少跳,中间是什么类型的路由器?但是以尽可能大的数据块发送数据通常是正确的做法.
您错过的一个可调参数是congestion control algorithm,您可以使用net.ipv4.tcp_congestion_control进行调整.哪些可用取决于您的内核,哪一个最好取决于您的网络和您发送的流量类型.
另一件事是TCP有两个端点,两侧的可调参数很重要.
>使用sysctl所做的更改将立即生效,以用于新的TCP连接.
> TCP参数仅对TCP连接的端点有影响.因此您无需在VM主机上更改它们.但是在guest虚拟机中运行意味着它发送的数据包仍然需要以某种方式由主机处理(如果只是将它们转发到真实的物理网络接口).从虚拟机内部运行测试总是比在物理机器上运行测试慢.
我缺少的是任何可以与实际网络速度进行比较的基准数字.有改进的余地吗?也许你已经达到了可能的最大速度?在这种情况下,没有多少调整会有所帮助.请注意,默认值通常非常合理.