在我们的服务器需要更新低资源传感器/跟踪设备的固件的特殊应用中,我们遇到了这样的问题:有时在接收新固件的数据包的远程设备(客户端)中数据丢失。该连接是通过GPRS网络的TCP/IP。该装置采用SIM900移动通信芯片作为网络接口。
出现问题的原因可能是设备接收的数据太多。我们尝试通过更少地发送包来减少流量,但有时仍然会出现错误。
我们联系了当地的SIM900芯片零售商,他们也负责提供技术支持,并可能与芯片的中国制造商(simcom)联系。他们说,首先我们应该尝试减少连接的TCP MSS (最大分段大小)。
在我们的服务器中,我执行了以下操作:
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;
}运行上面的代码会导致:
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正如您可能看到的,输出的第二行中的问题: setsockopt返回"Invalid argument“。
这一切为什么要发生?我读到了关于设置TCP_MAXSEG的一些限制,但我没有遇到任何关于这样的行为的报告。
谢谢,丹尼斯
发布于 2013-08-08 06:23:26
但您使用的是u_int16。我没有看到任何说明这个参数不是int的东西。
编辑:是的,here是源代码,你可以看到:
637 if (optlen < sizeof(int))
638 return -EINVAL;发布于 2018-03-20 19:04:40
除了xaxxon的回答之外,我还想记录一下我尝试强制我的Linux仅发送特定大小(低于正常大小)的最大TCP段的经验:
sudo iptables -A INPUT -p tcp --tcp-flags SYN,RST SYN --destination 1.1.1.1 -j TCPMSS --set-mss 200
这将覆盖出站连接上的远程传入SYN/ACK数据包,并强制MSS为特定值。
Note1:你在wireshark中看不到这一点,因为wireshark会在这种情况发生之前捕获它。
注2: Iptables不允许增加MSS,只允许降低MSS
我还试着像丹尼斯那样设置套接字选项TCP_MAXSEG。在接受了xaxxon的修复后,这也起到了作用。
注意:您应该在建立连接后读取MSS值。否则,它将返回默认值,这会使我(和dennis)走上错误的轨道。
最后,我还遇到了其他一些事情:
我遇到了TCP-offloading问题,尽管我的
sudo ethtool -K eth0 tx off sg off tso off。我花了很长时间才弄明白这一点。中,我没有遇到任何问题。
希望这能在某一天帮助那些想做同样事情的人。
https://stackoverflow.com/questions/18107728
复制相似问题