【问题标题】:TCP tunables does not show expected result in data xfer rateTCP 可调参数未显示数据传输速率的预期结果
【发布时间】:2017-02-01 09:31:45
【问题描述】:

我有一个用 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” - 接收缓冲区中剩余的字节非常少,与期待。

因此我开始怀疑我对调整本身的假设,并有以下问题:

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

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

  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);
}

【问题讨论】:

  • 为了获得有用的答案,您需要提供有关您的设置和遇到的问题的更多详细信息。例如,“我有两台具有 10gbit/s 网卡的机器连接到同一交换机上的两个 10gbit/s 交换机端口,但我似乎无法获得超过 5gbit/s 的吞吐量”与“我有一个客户端和服务器通过 WAN 连接,RTT 为 600 毫秒。我能做些什么来提高吞吐量。”
  • 您似乎正在尝试增加接收器的窗口大小以加快传输速度。这是否甚至导致接收器宣传更大的窗口大小?只有当窗口大小是一个限制因素时,这才会有所帮助。如果您在进行更改之前进行了网络捕获,并使用 Wireshark 过滤器 tcp.analysis.flags 进行了查看,并且您看到零窗口广告,那么,是的,增加窗口大小会有所帮助。您能做的最好的事情是获取网络捕获并查看 Wireshark 中的 IO 图和 tcp.analysis.flags,看看有什么可以改进的。
  • win 57 可能未缩放:参见wireshark and tcpdump -r: strange tcp window sizes
  • 服务器的write() 调用的返回值是多少?它真的发送了整个 32 MB 吗? (还要注意write() 返回ssize_t,而不是int。它们相同。)另外,您如何知道您的setsockopt() 调用实际上可以增加您的套接字缓冲区大小?你忽略返回值。
  • @GireeshPunathil * 我看到 ssize_t 起源于: typedef long int __ssize_t;并且在 32 位系统中 int 和 ssize_t 是相同的。* 我看到您没有编写可移植或 64 位代码的经验。如果将-m64 之类的东西添加到您的完全独立且不相关的 编译器选项和您的代码中断。同样,ssize_tint 不一样。标准指定ssize_t 而不是intlong int 是有原因的。忽略它,你写的最好的情况是不可移植的,最坏的情况是损坏和错误的代码。

标签: c linux performance sockets tcp


【解决方案1】:
  1. 可调参数有很多,但它们是否有效果以及效果是正面还是负面也取决于具体情况。可调参数的默认值是什么?您设置的值实际上可能低于操作系统的默认值,从而降低性能。但是更大的缓冲区有时也可能是有害的,因为使用了更多的 RAM,并且它可能不再适合缓存内存。它还取决于您的网络本身。是有线的,无线的,有多少跳,中间是什么路由器?但是以尽可能大的块发送数据通常是正确的做法。

    您错过的一个可调参数是congestion control algorithm,您可以使用net.ipv4.tcp_congestion_control 对其进行调优。哪些可用取决于您的内核,哪个最好取决于您的网络和您发送的流量类型。

    另外一点是 TCP 有两个端点,两端的可调参数很重要。

  2. 使用sysctl 所做的更改会立即对新的 TCP 连接生效。

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

我缺少的是可以与实际网络速度进行比较的任何基准数据。有没有改进的余地?也许您已经处于可能的最大速度?在这种情况下,再多的调整也无济于事。请注意,默认值通常非常合理。

【讨论】:

  • 感谢 Sliepen 提供有用的信息 - 我会等待几天才能接受您的回答。就基准而言——“win 57”和 tcpdump 中的数据包大小 1368 表明它没有以最佳方式运行。在操作系统的虚拟性方面 - 同意,但我使用和不使用可调参数的比较使用相同的主机,所以应该显示一些差异,但事实并非如此,这是我担心的原因。
  • 我无法仅从窗口和数据包大小判断您的可调参数是否最佳。也许您也可以向我们展示您的代码?
  • 谢谢——正如我告诉你的,它只包含标准的客户端-服务器代码。两个额外的事情是:i)服务器的写入循环和客户端的读取循环,ii)将sockopt设置为客户端套接字以增加缓冲区大小。我的主要担忧仍然是:对于我设置的选项和创建的套接字,数据包大小 1368 看起来“非常小”。
  • 不过,我在问题中添加了我的客户端服务器代码。
猜你喜欢
  • 2022-08-14
  • 1970-01-01
  • 2021-04-14
  • 1970-01-01
  • 2012-01-26
  • 2011-10-02
  • 2019-04-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多