前段时间有位同事抓包分析了报文交互的通告窗口大小,有些不理解。下面是他的描述:
有一台机器(下面称为客户端C)通过TCP与服务器交互(下面称为S),抓包后发现通告窗口(下面简称为窗口)如下变化,C的窗口在变小,而S的窗口一直保持不变。他的疑问是,1、C的收包已经很及时了,为什么窗口还在变小;2;S的窗口为什么不变;3、窗口能为0吗?
实际上,这是一种很典型的客户端与服务器端通信的场景,窗口的变化也是很典型的。
当时我的回答是:
C与S交互,通常S不会主动发送消息,是由C发送请求,S收到请求后,处理这个请求,然后应答处理。那么,这就意味着,S应答处理时(即发包给C),S已经把C发送过来的报文读走了(通过SOCKET的recv接口从TCP的接收队列里把报文读走),S的接受队列是空的,S发送给C的报文中携带的通告窗口当然就没有变化了。
而S发送给C的报文,TCP收到后,会放入C的接收队列,这时候TCP协议会判断是否需要报文发送,如果有报文发送,会进行发送处理,这时候会携带C当前剩余的接收队列大小,所以,看到的当然是C的窗口在变小。C的用户态程序,再怎么及时,也不可能比软中断触发的协议处理快。当S停止发送,或者C的窗口变为0(C一直来不及处理),或者S发送的流量慢于C的处理速度,那么窗口就变大了。
通告窗口当然可以为0,比如C不接收了,那么S发送的报文一直没有上层模块读取,最后会填满C的接收队列。这时,S怎么办呢,S会启动一个坚定定时器,超时发送探测报文给C,或者C主动通告本端的窗口,如果S发现C可以再继续接收报文了,才继续发送。那么,如果C读取的很慢,或者每次只取一个字节,或者几个字节的内容,会怎么办呢?我建议他去搜索下“糊涂窗口综合症”,看看协议是怎么处理的。