TCP内部缓冲区填满时的行为及客户端快发场景下的缓冲区问题
Hey there, let's break down your TCP questions one by one:
TCP的内部缓冲区其实分两种:发送缓冲区(属于发送端)和接收缓冲区(属于接收端),两者满了之后的表现不太一样,咱们分别说:
接收缓冲区填满时:
接收端的TCP会在给发送端的ACK报文中把窗口大小设为0,相当于直白告诉发送端:“我这边没地方存数据了,先停一停”。发送端收到这个窗口为0的ACK后,会立刻暂停发送新数据,进入等待状态。
等接收端处理完缓冲区里的数据、腾出空间后,会主动给发送端发一个窗口更新报文,把当前可用的窗口大小同步给对方,发送端收到后就会恢复数据发送。
另外,如果发送端等了很久都没收到窗口更新,还会触发窗口探测报文,用来确认接收端是不是真的还没腾出空间,避免因为报文丢失导致的永久等待。发送缓冲区填满时:
如果是阻塞套接字,调用send()的时候会直接阻塞,直到缓冲区里已有数据被发送出去、腾出空间,send()才会返回成功。
如果是非阻塞套接字,send()会立刻返回错误码EAGAIN(或EWOULDBLOCK),明确告诉你现在缓冲区满了,没法发送,得稍后再尝试。
答案是肯定会,咱们结合你给的伪代码来捋清楚整个过程:
你看服务器的逻辑:每次recv(4096)把数据读到data里,接着就去执行cpu_intensive_task(parsed_data)——这个CPU密集任务会占用大量时间,在这段时间里,服务器完全没有调用recv()去读取TCP接收缓冲区里的新数据。
而客户端是一直在循环调用send(data)发送数据,速度还比服务器处理快得多:
- 一开始,服务器的TCP接收缓冲区会不断接收客户端发来的数据,很快就会被填满。
- 接收缓冲区满了之后,服务器的TCP就会给客户端发窗口为0的ACK,客户端的发送端收到后会暂停发送新数据。
- 但客户端还在继续调用
send(),如果客户端用的是阻塞套接字,send()会直接卡住;如果是非阻塞套接字,send()会返回错误。同时客户端的发送缓冲区也会被“已发送但未收到ACK”的数据填满(因为服务器没处理完数据,没法发ACK确认)。
简单说,服务器因为CPU密集任务拖慢了数据读取的节奏,导致接收缓冲区被占满,进而连锁反应让客户端的发送缓冲区也跟着满起来。
内容的提问来源于stack exchange,提问作者TukeV




