简体   繁体   English

在套接字上的Linux中减少TCP最大段大小(MSS)

[英]Reduce TCP maximum segment size (MSS) in Linux on a socket

In a special application in which our server needs to update firmware of low-on-resource sensor/tracking devices we encountered a problem in which sometimes data is lost in the remote devices (clients) receiving packets of the new firmware. 在一个特殊的应用程序中,我们的服务器需要更新资源不足的传感器/跟踪设备的固件,我们遇到了一个问题,即有时会在接收新固件数据包的远程设备(客户端)中丢失数据。 The connection is TCP/IP over GPRS network. 连接是GPRS网络上的TCP / IP。 The devices use SIM900 GSM chip as a network interface. 这些设备使用SIM900 GSM芯片作为网络接口。

The problems possibly come because of the device receiving too much data. 这些问题可能是由于设备接收的数据过多所致。 We tried reducing the traffic by sending packages more rarely but sometimes the error still occured. 我们尝试通过很少发送包裹来减少流量,但有时仍然会发生错误。

We contacted the local retailer of the SIM900 chip who is also responsible for giving technical support and possibly contacting the chinese manufacturer (simcom) of the chip. 我们联系了SIM900芯片的本地零售商,后者还负责提供技术支持,并可能与该芯片的中国制造商(simcom)联系。 They said that at first we should try to reduce the TCP MSS (Maximum Segment Size) of our connection. 他们说,起初我们应该尝试减少连接的TCP MSS(最大段大小)。

In our server I did the following: 在我们的服务器中,我执行了以下操作:

static int
create_master_socket(unsigned short master_port) {

    static struct sockaddr_in master_address;
    int master_socket = socket(AF_INET,SOCK_STREAM,0);
    if(!master_socket) {
            perror("socket");
            throw runtime_error("Failed to create master socket.");
    }

    int tr=1;
    if(setsockopt(master_socket,SOL_SOCKET,SO_REUSEADDR,&tr,sizeof(int))==-1) {
            perror("setsockopt");
            throw runtime_error("Failed to set SO_REUSEADDR on master socket");
    }

    master_address.sin_family = AF_INET;
    master_address.sin_addr.s_addr = INADDR_ANY;
    master_address.sin_port = htons(master_port);
    uint16_t tcp_maxseg;
    socklen_t tcp_maxseg_len = sizeof(tcp_maxseg);
    if(getsockopt(master_socket, IPPROTO_TCP, TCP_MAXSEG, &tcp_maxseg, &tcp_maxseg_len)) {
            log_error << "Failed to get TCP_MAXSEG for master socket. Reason: " << errno;
            perror("getsockopt");
    } else {
            log_info << "TCP_MAXSEG: " << tcp_maxseg;
    }
    tcp_maxseg = 256;
    if(setsockopt(master_socket, IPPROTO_TCP, TCP_MAXSEG, &tcp_maxseg, tcp_maxseg_len)) {
            log_error << "Failed to set TCP_MAXSEG for master socket. Reason: " << errno;
            perror("setsockopt");
    } else {
            log_info << "TCP_MAXSEG: " << tcp_maxseg;
    }
    if(getsockopt(master_socket, IPPROTO_TCP, TCP_MAXSEG, &tcp_maxseg, &tcp_maxseg_len)) {
            log_error << "Failed to get TCP_MAXSEG for master socket. Reason: " << errno;
            perror("getsockopt");
    } else {
            log_info << "TCP_MAXSEG: " << tcp_maxseg;
    }
    if(bind(master_socket, (struct sockaddr*)&master_address,
                            sizeof(master_address))) {
            perror("bind");
            close(master_socket);
            throw runtime_error("Failed to bind master_socket to port");

    }

    return master_socket;
}

Running the above code results in: 运行上面的代码将导致:

I0807 ... main.cpp:267] TCP_MAXSEG: 536
E0807 ... main.cpp:271] Failed to set TCP_MAXSEG for master socket. Reason: 22 setsockopt: Invalid argument
I0807 ... main.cpp:280] TCP_MAXSEG: 536

As you may see, the problem in the second line of the output: setsockopt returns "Invalid argument". 您可能会看到,输出第二行中的问题:setsockopt返回“无效参数”。

Why does this happen? 为什么会这样? I read about some constraints in setting TCP_MAXSEG but I did not encounter any report on such a behaviour as this. 我阅读了有关设置TCP_MAXSEG的一些限制,但是我没有遇到有关这种行为的任何报告。

Thanks, Dennis 谢谢,丹尼斯

Unless otherwise noted, optval is a pointer to an int. 除非另有说明,否则optval是指向int的指针。

but you're using a u_int16. 但您使用的是u_int16。 I don't see anything saying that this parameter isn't an int. 我什么也没说这个参数不是整数。

edit: Yeah, here is the source code and you can see: 编辑:是的, 是源代码,您可以看到:

637         if (optlen < sizeof(int))
638                 return -EINVAL;

In addition to xaxxon's answer, just wanted to note my experience with trying to force my Linux to send only maximum TCP segments of a certain size (lower than what they normally are): 除了xaxxon的答案外,我只是想说明一下我试图强迫我的Linux仅发送一定大小(比正常大小要低)的最大TCP段的经验:

  • The easiest way I found to do so, was to use iptables: 我发现这样做的最简单方法是使用iptables:

sudo iptables -A INPUT -p tcp --tcp-flags SYN,RST SYN --destination 1.1.1.1 -j TCPMSS --set-mss 200

This overwrites the remote incoming SYN/ACK packet on an outbound connection, and forces the MSS to a specific value. 这将覆盖出站连接上的远程传入SYN / ACK数据包,并将MSS强制为特定值。

Note1: You do not see this in wireshark, since wireshark capture before this happens. 注意1:您在wireshark中看不到此消息,因为wireshark会在此之前捕获。

Note 2: Iptables does not allow you to -increase- the MSS, just lower it 注意2:iptables不允许您增加MSS,而只需降低它

  • Alternatively, I also tried setting the socket option TCP_MAXSEG, like dennis had done. 另外,我也尝试设置套接字选项TCP_MAXSEG,就像dennis所做的那样。 After taking the fix from xaxxon, this also worked. 从xaxxon修复后,此方法也起作用。

Note: You should read the MSS value after the connection has been set up. 注意:建立连接后,您应该读取MSS值。 Otherwise it returns the default value, which put me (and dennis) on the wrong track. 否则,它将返回默认值,这会使我(和丹尼斯)走错路。

Now finally, I also ran into a number of other things: 现在最后,我还遇到了其他一些问题:

  • I ran into TCP-offloading issues, where despite my MSS being set correctly, the frames being sent were still shown by wireshark as too big. 我遇到了TCP卸载问题,尽管我的MSS设置正确,但wireshark仍然显示发送的帧太大。 You can disable this feature by : sudo ethtool -K eth0 tx off sg off tso off . 您可以通过以下方式禁用此功能: sudo ethtool -K eth0 tx off sg off tso off This took me a long time to figure out. 这花了我很长时间才弄清楚。

  • TCP has lots of fancy things like MTU path discovery, which actually try to dynamically increase the MSS. TCP有很多奇特的东西,例如MTU路径发现,它实际上试图动态增加MSS。 Fun and cool, but confusing obviously. 有趣而酷,但显然令人困惑。 I did not have issues with it though in my tests 我在测试中没有任何问题

Hope this helps someone trying to do the same thing one day. 希望这可以帮助某人一天尝试做同样的事情。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM