Linux上TCP丢失小包不可见的解决
Linux上TCP丢失小包不可见的解决
作者:千里孤行(http://blog.csdn.net/yanghehong)
有网友在做一个Linux上的客户端碰到这样的问题:
我们在Linux下开发一个网络客户端程序(服务器是不可修改的),不断向服务器不定期发送一些很小的包(一般只有几十字节),现在出现这种情况:大部分包正常发送,但是当无线Modem断开的时候,如果这个时候刚好有一个小包,就可能丢失,但在程序中却显示已经发送成功,导致丢包。
我们分析原因是这样:
1:主程序创建Socket,TCP/IP方式,并采用Stream方式
2:主程序调用Write,写入小包到系统的Socket缓冲区,并返回成功写入,由于字节数很小,所以一般都立即返回写入成功!
3:Linux TCP/IP协议栈把Socket缓冲区数据发送到服务器
如果第二步完成,刚好在第三步出现无线Modem断开的情况,就会导致主程序以为已经发送成功了,但服务器收不到的情况。
我们在网络上搜索了大量的资料,但是没有找到最终的解决方案,一般都是要求修改协议,加上对小包的ACK处理,但我们这边无法控制服务器。
我们也试着控制KeepAlive和NoDelay,但还是没效果:
//对sock_cli设置KEEPALIVE和NODELAY
len = sizeof(unsigned int);
setsockopt(sock_cli, SOL_SOCKET, SO_KEEPALIVE, &optval, len);//使用KEEPALIVE
setsockopt(sock_cli, IPPROTO_TCP, TCP_NODELAY, &optval, len);//禁用NAGLE算法
请问有什么方式可以解决这种情况?
如果是Windows的,可以把内核的发送buffer设为0,也就是socket的SO_SNDBUF选项。那么直到服务器TCP收到数据并ack了,客户端的写入才返回成功。
不过这种设SO_SNDBUF的方法在Linux上是行不通的。Linux不让把发送buffer设为0。
Linux内核中的代码是这样的:
Socket.c
int sock_setsockopt(struct socket *sock, int level, int optname,
char __user *optval, int optlen)
{
case SO_SNDBUF:
/* Don't error on this BSD doesn't and if you think
about it this is right. Otherwise apps have to
play 'guess the biggest size' games. RCVBUF/SNDBUF
are treated in BSD as hints */
if (val > sysctl_wmem_max)
val = sysctl_wmem_max;
set_sndbuf:
sk->sk_userlocks |= SOCK_SNDBUF_LOCK;
if ((val * 2) < SOCK_MIN_SNDBUF)
sk->sk_sndbuf = SOCK_MIN_SNDBUF;
else
sk->sk_sndbuf = val
相关文档:
linux socket 优化
2008-06-12 16:40
使用 Sockets API,我们可以开发客户机和服务器应用程序,它们可以在本地网络上进行通信,也可以通过 Internet 在全球范围内进行通信。与其他 API 一样,您可以通过一些方法使用 Sockets API,从而提高 Socket 的性能,或者限制 Socket 的性能。本文探索了 4 种使用 Sockets API 来 ......
几个简单的应用。
1、批量图像格式转换
如果想将某目录下的所有jpg文件转换为png文件,只要在命令行模式下输入:
for %f in (*.jpg) do convert “%f” “%~nf.png”
2、对所有图像进行同一操作
譬如,批量生成某目录下所有PNG图像文件的缩略图(大小为80×40):
......
1,-c 编译目标代码
gcc -o hello.o -c hello.c //-o 只是指定生成目标文件名
或
gcc -c hello.c
都生成hello.o文件,不可以执行。
gcc -o hello hello.o
用目标文件生成hell ......
一、头文件
gcc 在编译时寻找所需要的头文件 :
※搜寻会从-I开始
※然后找gcc的环境变量 C_INCLUDE_PATH,CPLUS_INCLUDE_PATH,OBJC_INCLUDE_PATH
※再找内定目录
/usr/include
/usr/local/include
/usr/lib/gcc-lib/i386-linux/2.95.2/include
/usr/lib/gcc-lib/i386-l ......
在前面的准备工作完成之后,先实验一下,谈不上真正的移植 ,因为代码都没有改的。
首先修改顶层的Makefile,修改ARCH,CROSS_COMPLIE变量。
#ARCH ?= $(SUBARCH)
ARCH ?= arm
CROSS_COMPILE ?= arm-linux-
执行make smd ......