TCP头
Source Port:源端口号(bind函数绑定的,若没有调用bind则随便绑定一个)
Destination Port:目的端口号(listen函数监听的端口号)
Sequence Number:记录字节数(初始值是随机值,溢出之后,从1开始)
Acknowledgment Number:ack num
header length:头的长度的header length*4
URG:紧急传输标志
Urgent Pointer:URG置1有效,指向后面的紧急传输数据
一个包由head和body组成,body长度为后一次发送的seq num-当前发送的seq num
最后一次发送的时候,对端接收到,会返回acknum,acknum = 本端发送的seq num + 对端解析的长度,若本端接收到acknum与实际的不同,会重新发送
ACK:确认标志位。告诉对端收到数据
PSH:
发送方:一个数据包(长度大于mss)分成多个tcp包发送,最后一个包psh位置1
接收方:立即将缓冲区中的所有数据推送给应用进程
RST:有数据不合法或超时,则回发一个RST,让对端重发数据
SYN:置1告诉对端开始发送一个包
FIN:终止
tcp发送
慢启动与拥塞避免
第一次发送1个包,之后以指数增长,当包的数量到达16的时候,包增长的速度变成线性。
指数增长为慢启动,线性增长为拥塞避免。
如何判断数据包数量超出网络负载
rtt:Round Trip Time 往返时延。发送端发出信息到发送端接收到来自接收端的确认信息总共经历的时延,由发送延迟+传播延迟+排队延迟+处理延迟构成
rtt = 0.1rtt(new) + 0.9rtt(old).
超过rtt时则为超出网络负载,超出以后则减少数据包的发送数量
减少数据包是用降半的方式,即除以2,然后继续线性增长。
具体传输
假设本端发送4k文件,sendbuffer为2k,mss为512,mtu为1024
1、调用send(),当sendbuffer满了之后,send()返回负一,需要调用2次send(),4K/2K
2、sendbuffer分4个包发送,sendbuffer/min(mss,mtu)
3、第一次发送是一个包,返回一个ack和剩余窗口大小(字节数 在window size中);
若第一次的rtt没有超时则第二次发送两个包,返回一个ack和剩余窗口大小;
若第二次的rtt没有超时则第三次发送四个包,返回一个ack和剩余窗口大小;
。。。。
4、当返回的窗口大小为0时,则停止发送数据给对端,有以下两种方式重新启动:
方法一:对端主动告知窗口有空间了。缺点:对端发送时丢包,或者本端下线了,就gg了,这个缺点可以通过定时器来弥补,但是对端是服务器,如果对接的每个客户端都这么做,就很浪费资源
方法二:本端不断地发送探测包,轮询对端窗口大小。tcp是用方法二做的
滑动窗口
可以理解为两个指针指向接受窗口,一个指向已返回ack的位置,一个指向最后一个允许接受数据的位置
上图滑动窗口大小为12,即接收端的5~16
上图滑动窗口大小为9,即接收端的8~16,图中的8未收到数据,虽然9/10收到了,但发送端接收到的acknum为8,所以滑动窗口还是8 ~ 16
上图滑动窗口大小为5,即接收端的12~16
技术参考
视频技术参考:https://ke.qq.com/course/417774?flowToken=1041651