最近在项目里发现自己两台机发包的时候,有一台收得比较慢,想捉个包来看看,以前只是简单看下捉包下来的data。
这一次真的关心起窗口。一看,觉得比较奇怪。我采用了以下捉包命令:
tcpdump -i eth0 tcp and port 9981 -w /data/steven/tcpdump/tcpdump.cap -C 1 -s 0&
主要是-C,限制文件大小为1M就切文件。由于两个程序是长连接的,所以中途捉下来的包已经是没有SYN握手过程的了。
看了一下数据,觉得挺奇怪的。
99机器告诉100机器,win=36但紧接着100既然给99发了个len=180的数据,这不符合滑动窗口的实现啊!
查了好久没有找到相关资料,因为我之前查的方法不对,后来让同事将程序重启了一下,再捉一个有握手包
的SYN包的,一看,好像正常了些:
这个包win看起来就有点正常了,虽然这里还有个问题,99的win比100小很多。为什么我觉得第一个包不正常呢,
因为RFC有说MSS以太网是1460字节(1500 -40),而最小MSS好像是536字节(576最小重组缓冲区字节数 - 40)。
点开这个SYN包,看到以下两个字段引起了我的注意:
google了一下wireshark window size value calculated window size发现了参考链接中的两个文章,由于第二个
被墙了,只能看第一个,这里面确实找到了答案:
由于TCP的头部窗口字段只有16bit,最多表示64k,为了表示更大的窗口,使用了可选的放大倍数。
1.在TCP三次握手的时候在SYN或SYN,ACK包中,通知options可选信息,告知对方将使用放大倍数。
2.SYN本身不放大
3.所以window size value表示报文的值,calculated window size表示放大后的值,也就是实际可用的值,
这个值应该是wireshark为了友好,自己算出来了。
回到一开始说的问题,我tcpdump的时候使用了-C参数,也就是当文件超过1M的时候会切成多个文件,当你的
文件里不包含三次握手包(SYN)的时候,wireshark就不知道你的放大倍数,所以显示出来的数值就会有我的疑问:
为什么win只有36但可以发len=180。
可以看到,在不包含SYN包的tcpdump文件中,wireshark是不知道你的倍数的,所以显示-1(unknown),而且
乖乖在你的包外,显示这个值 win = 37,如果算128来修正的话(实际上也是128)那calculated window size正确
应该显示 37 * 128 = 4736 这个就跟下面的差不了多少了,比180大好多了,当然可以发180了。
下面这个包,是在有包含SYN里捉出来的,他就能告诉你,倍数是128,实际是5760
终于搞明白这个事了,接下来其实有两个疑问的:
为什么握手的时候两边窗口差那么多,100至少是99的2.4倍。
TCP什么时候启动这个放大倍数。
A:查一下资料,好像linux有个内核配置参数
[steven@KFJK ~]$ more /proc/sys/net/ipv4/tcp_window_scaling
1
相关详细信息这里有简单说明 [原]TCP接收窗口的调整算法(上)
“如果接收窗口的最大值受限于tcp_rmem[2] = 4194304,那么rcv_wscale = 7,窗口扩大倍数为128。”
而我系统的设置好像刚好(但因子远不只这个):
[steven@KFJK ~]$ cat /proc/sys/net/ipv4/tcp_rmem
4096 87380 4194304
同事推荐的这个文章不错 TCP Windows and Window Scaling
顺便推荐一下同事的文章 wireshark tcp 协议分析 写得也非常不错。
有空还是要去读读TCP/IP详解卷,这里推荐大家看看一个高手写的两个文章,速食:
coolshell的TCP 的那些事儿(上)和TCP 的那些事儿(下)
---
参考链接
TCP传输行为分析、wireshark常见字段解读
www.wireshark.org 这个被墙了,有机会要去看看