linux服务器开发中网络数据分析与故障排查( 五 )


  • TIME_WAIT
根据三次握手断开连接规定 , 发起socket主动关闭的一方 socket将进入TIME_WAIT状态 。TIME_WAIT状态将持续2MSL 。TIME_WAIT状态下的socket不能被回收使用 。具体现象是对于一个处理大量短连接的服务器 , 如果是由服务器主动关闭客户端的连接 , 将导致服务器端存在大量的处于TIME_WAIT状态的socket ,  甚至比处于Established状态下的socket多的多 , 严重影响服务器的处理能力 , 甚至耗尽可用的socket , 停止服务 。TIME_WAIT是TCP协议用以保证被重新分配的socket不会受到之前残留的延迟重发报文影响的机制,是必要的逻辑保证 。和TIME_WAIT状态有关的系统参数有一般由3个 , 本机设置如下:
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_fin_timeout , 默认60s , 减小fin_timeout , 减少TIME_WAIT连接数量 。
net.ipv4.tcp_tw_reuse = 1表示开启重用 。允许将TIME-WAIT sockets重新用于新的TCP连接 , 默认为0 , 表示关闭;
net.ipv4.tcp_tw_recycle = 1表示开启TCP连接中TIME-WAIT sockets的快速回收 , 默认为0 , 表示关闭 。
我们这里总结一下这些与tcp状态的选项:
u net.ipv4.tcp_syncookies=1 表示开启SYN Cookies 。当出现SYN等待队列溢出时 , 启用cookie来处理 , 可防范少量的SYN攻击 。默认为0 , 表示关闭 。
u net.ipv4.tcp_tw_reuse=1 表示开启重用 。允许将TIME-WAIT套接字重新用于新的TCP连接 。默认为0 , 表示关闭 。
u net.ipv4.tcp_tw_recycle=1 表示开启TCP连接中TIME-WAIT套接字的快速回收 。默认为0 , 表示关闭 。
u net.ipv4.tcp_fin_timeout=30 表示如果套接字由本端要求关闭 , 这个参数决定了它保持在FIN-WAIT-2状态的时间 。
u net.ipv4.tcp_keepalive_time=1200 表示当keepalive启用时 , TCP发送keepalive消息的频度 。默认是2小时 , 这里改为20分钟 。
u net.ipv4.ip_local_port_range=1024 65000 表示向外连接的端口范围 。默认值很小:32768~61000 , 改为1024~65000 。
u net.ipv4.tcp_max_syn_backlog=8192 表示SYN队列的长度 , 默认为1024 , 加大队列长度为8192 , 可以容纳更多等待连接的网络连接数 。
u net.ipv4.tcp_max_tw_buckets=5000 表示系统同时保持TIME_WAIT套接字的最大数量 , 如果超过这个数 字 , TIME_WAIT套接字将立刻被清除并打印警告信息 。默认为180000 , 改为5000 。
注意
上文中用加粗字体标识出来的两个参数:
net.ipv4.tcp_tw_recyclenet.ipv4.tcp_tw_reuse在实际linux内核参数调优时并不建议开启 。至于原因 , 我会单独用一篇文章来介绍 。
四、 关于跨系统与跨语言之间的网络通信连通问题
如何在JAVA语言中去解析C++的网络数据包 , 如何在C++中解析Java的网络数据包 , 对于很多人来说是一件很困难的事情 , 所以只能变着法子使用第三方的库 。其实使用tcpdump工具可以很容易解决与分析 。
首先 , 我们需要明确字节序列这样一个概念 , 即我们说的大端编码(big endian)和小端编码(little endian) , x86和x64系列的cpu使用小端编码 , 而数据在网络上传输 , 以及Java语言中 , 使用的是大端编码 。那么这是什么意思呢?
我们举个例子 , 看一个x64机器上的32位数值在内存中的存储方式:
linux服务器开发中网络数据分析与故障排查

文章插图
 
【linux服务器开发中网络数据分析与故障排查】i在内存中的地址序列是0x003CF7C4~ 0x003CF7C8 , 值为40 e2 01 00 。
linux服务器开发中网络数据分析与故障排查

文章插图
 
十六进制0001e240正好等于10进制123456 , 也就是说小端编码中权重高的的字节值存储在内存地址高(地址值较大)的位置 , 权重值低的字节值存储在内存地址低(地址值较小)的位置 , 也就是所谓的高高低低 。
相反 , 大端编码的规则应该是高低低高 , 也就是说权值高字节存储在内存地址低的位置 , 权值低的字节存储在内存地址高的位置 。
所以 , 如果我们一个C++程序的int32值123456不作转换地传给Java程序 , 那么Java按照大端编码的形式读出来的值是:十六进制40E20100 = 十进制1088553216 。


推荐阅读