TCP报文格式
1、TCP报文:由TCP首部和TCP数据组成
2、TCP首部:由 20字节的固定长度 + 变长字段(选项)+ 填充 组成成
3、MSS(Maximum Segment Size):占4字节,是每一个TCP报文中数据字段的最大长度,只是数据部分,不包括TCP头部。MSS只出现在SYN包中,如果一方不接受另一方的MSS值,则为默认的536Byte
4、TCP首部长度:由TCP头中的“数据偏移”字段决定。该字段占4bit,取最大的1111时,也就是十进制的15,TCP首部的偏移单位为4byte,那么TCP首部长度最长为15*4=60字节。
5、选项和填充的长度:其最大值 = TCP首部最大长度 - 20字节固定长度,也就是最大40字节,填充是为了使TCP首部为4byte的整数倍。
TCP选项格式
1字节的选项类型,1字节的选项长度(Option Length)和一个可变长的选项数据(Option data)
时间戳选项占10个字节= kind(1字节) + length(1字节) + info (8字节),其中kind=8,length=10,info由timestamp和timestamp echo两个值组成,各4个字节的长度。
TCP时间戳选项
发送方在发送报文段时把当前时钟的时间值放入时间戳字段,接收方在确认该报文段时把时间戳字段值复制到时间戳回送回答字段。
因此,发送方在收到确认报文后,可以准确计算出RTT。
通过例子理解timestamp和timestamp echo字段中存放的内容:假设a主机和b主机之间通信,a主机向b主机发送一个报文段,那么:
1)timestamp字段中存放的内容:a主机向b主机发送报文s1,在s1报文中timestamp存储的是a主机发送s1时的内核时刻ta1。
2)timestamp echo字段中存放的内容:b主机收到s1报文并向a主机发送含有确认ack的报文s2,在s2报文中,timestamp为b主机此时的内核时刻tb,而timestamp echo字段为从s1报文中解析出的ta1.
1)计算往返时延RTT:
当a主机接收到b主机发送过来的确认ack报文s2时,a主机此时内核时刻为ta2.
a主机从s2报文的timestamp echo选项中可以解析出该确认ack确认的报文的发送时刻为ta1.
那么:RTT=接收ack报文的时刻-发送报文的时刻=ta2 -ta1.
ta2和ta1都来自a主机的内核,所以不需要在tcp连接的两端进行任何时钟同步的操作。
2)防止回绕的序号
原文链接:https://blog.csdn.net/mary19920410/java/article/details/77255967
NAT负载后的服务端和客户端访问网站出现丢包现象
源码函数:tcp_v4_conn_request(),该函数是tcp层三次握手syn包的处理函数(服务端);
源码片段:
if (tmp_opt.saw_tstamp &&
tcp_death_row.sysctl_tw_recycle &&
(dst = inet_csk_route_req(sk, req)) != NULL &&
(peer = rt_get_peer((struct rtable *)dst)) != NULL &&
peer->v4daddr == saddr) {
if (get_seconds() < peer->tcp_ts_stamp + TCP_PAWS_MSL &&
(s32)(peer->tcp_ts - req->ts_recent) >
TCP_PAWS_WINDOW) {
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED);
goto drop_and_release;
}
}
tmp_opt.saw_tstamp:该socket支持tcp_timestamp
sysctl_tw_recycle:本机系统开启tcp_tw_recycle选项
TCP_PAWS_MSL:60s,该条件判断表示该源ip的上次tcp通讯发生在60s内
TCP_PAWS_WINDOW:1,该条件判断表示该源ip的上次tcp通讯的timestamp 大于本次tcp
tcp_tw_recycle/tcp_timestamps都开启的条件下,60s内同一源ip主机的socket connect请求中的timestamp必须是递增的
分析:当A、B两台主机通过同一个NAT网关访问服务器,由于timestamp时间为系统启动到当前的时间,所以A和B的timestamp不同,tcp_tw_recycle/tcp_timestamps都开启的条件下,timstamp大的主机可以访问,小的失败。
参数:/proc/sys/net/ipv4/tcp_timestamps - 控制timestamp选项开启/关闭
/proc/sys/net/ipv4/tcp_tw_recycle - 减少timewait socket释放的超时时间
为了解决TIME_WAIT问题的参数优化
提到TIME_WAIT就不得不提MSL:即Maximum Segment Lifetime,最大报文生存时间,和TTL相似,TTL是IP头中的,是一个设置的初始值,每过一个网络节点-1,0的时候数据报被丢弃。
2MSL是为了解决发起连接关闭方回复最后一个fin 的ack,为避免对方ack 收不到、重发的或还在中间路由上的fin 把新连接给干掉了
1. tw_reuse,tw_recycle 必须在客户端和服务端timestamps 开启时才管用(默认打开)
2. tw_reuse 只对客户端起作用,开启后客户端在1s内回收
3. tw_recycle 对客户端和服务器同时起作用
对于客户端
1. 作为客户端因为有端口65535问题,TIME_OUT过多直接影响处理能力,打开tw_reuse 即可解决,不建议同时打开tw_recycle,帮助不大。
2. tw_reuse 帮助客户端1s完成连接回收,基本可实现单机6w/s请求,需要再高就增加IP数量吧。
3. 如果内网压测场景,且客户端不需要接收连接,同时tw_recycle 会有一点点好处。
4. 业务上也可以设计由服务端主动关闭连接
对于服务端
1. 打开tw_reuse无效
2. 线上环境 tw_recycle 不要打开 因为一般的服务器、客户端都在NAT之后
公网服务打开就可能造成部分连接失败,内网的话到时可以视情况打开;
像我所在公司对外服务都放在负载后面,负载会把timestamp 都给清空,好吧,就算你打开也不起作用。
3. 服务器TIME_WAIT 高怎么办
不像客户端有端口限制,处理大量TIME_WAIT Linux已经优化很好了,每个处于TIME_WAIT 状态下连接内存消耗很少,
而且也能通过tcp_max_tw_buckets =100000 解决(这个值根据TIME_WAIT数量定),现代机器一般也不缺这点内存。