TCP全称为“传输控制协议”,传输层协议,TCP是一种有链接的,传输速率慢,可靠性高的协议
TCP协议段格式
- 16位的源端口号: 发送方的端口号
- 16位的目的端口号:接收方的端口号
- 32位的序列号: 本次发送数据段的序列号
- 32位的确认序列号:向对端表明自己接下来想要收到的序列号,且表明该确认序列号以前的数据段都无误接收到了
- 4位首部长度,指最多有15(1111)个32位的比特位(4字节),即TCP头部的最大字节数为
15*4=60字节 - 6位标志位:
- ACK:确认序列号是否有效
- SYN:同步报文段
- FIN:结束报文段
- 16位的窗口大小,主要用在流量控制中,反映了接收方的接受缓存大小。
TCP的连接管理机制
正常情况下TCP要经过三次握手建立连接和四次挥手断开连接
服务器状态变化
- 调用listen,状态从
CLOSED
关闭状态变为LISTEN
监听状态 - 当客户端建立连接发来
SYN
请求时,回应SYN
+ACK
,状态变为SYN_RCVD
- 当收到客户端的
ACK
时,状态变为ESTABLISHED
,此时三次握手结束,连接建立成功 - 当接受到客户端
FIN
请求时,状态变为CLOSE_WAIT
,等待close()
的调用 - 当
close(connfd)
关闭连接时,状态变为LAST_ACK
,等待最后得ACK
- 收到
ACK
,状态变为CLOSED
,此时四次挥手完成,连接断开
- 调用listen,状态从
客户端状态变化
- 调用
connet()
,向服务器发送SYN
,请求建立连接,状态从CLOSE
变为SYN_SENT
- 收到服务器端的相应,并发送
ACK
回应,状态变为ESTABLISHED
,连接成功 - 关闭连接时,向服务器发送
FIN
,状态变为FIN_WAIT_1
,等待对方相应 - 收到相应,状态变为
FIN_WAIT_2
,等待服务器关闭连接 - 收到服务器的
FIN
,发送相应ACK
,状态变为TIME_WAIT
,等待保护
- 调用
比较重要的几个状态:
LISTEN
:服务器监听状态,较为常见ESTABLISHED
: 表示已连接,可以进行通信CLOSS_WAIT
: 服务器端等待调用close
,注意大量CLOSS_WAIT
状态FIN_WAIT_2
:客户端等待服务器断开连接TIME_WAIT
: 谁先发起关闭连接,谁进入TIME_WAIT
状态,确保对端接受到最后一个ACK
TIME_WAIT
的时间是2MSL,MSL就是TCP报文的最大生存时间,保证最后一个报文可靠到达(假设最后一个ACK丢失,那么服务器虎重发一个FIN,这时虽然客户端的进程不在了,但是TCP连接还在,仍可以重发LAST_ACK)
TCP协议的八大特性机制
确认应答(ACK)机制
每一个ACK都带有对应的确认序列号,意思是告诉发送者,我已经收到了那些数据,下次你从哪里开始发。
超时重传机制
如果主机A在在一个特定时间间隔内没有收到B发来的确认应答,就会进行重发
也会是因为B的确认应答ACK丢失,那么B就会接收到很多重复的包,TCP能够识别出重复的包,并丢弃之
超时时间的确定:Linux中一般以500ms为一个单位进行控制,每次判断重发的超时时间都是500ms的整数倍,比如:
第一次等待500ms,仍不应答,那就等待2*500ms,仍不应答,就等待4*500ms,这样不进行网络的抢占。
滑动窗口
由于一发一收的效率太低,那我们一次发送多条数据,同时等待多条数据,大大的提高了性能。
窗口大小指的是无须确认等待而可以继续发送数据的最大值
操作系统内核为维护这个滑动窗口,需要开辟开发缓存区来记录还有那些数据没有应答,只有确认应答的数据才能从缓冲区删掉
快速重传
有滑动窗口和超时重传结合成的快速重传,即使发送端接受前面的应答丢失了,后面的应答也会保证前面的数据无误。
如果发送端主机连续三次收到同一个确认序列号(1001)的应答,就会将对应数据重新发送
这时候接受段收到了数据(1001)后,再次返回的ACK就是7001了,因为中间的已经收到了,被放到接收端操作系统内核的接受缓冲区中。
流量控制
TCP支持根据接收端的处理能力,来决定发送端的发送速度,这个机制叫做流量控制
接收端会将自己接受的缓冲区大小放入TCP首部中的“窗口大小”字段,通过ACK段通知发送端,发送端收到这个窗口后,后减慢自己的发放速度。
如果接收端缓冲区满了,就会将窗口置为0;这时发送端不在发送数据需要定期发送一个窗口探测数据段,使接收端吧窗口大小告诉发送端。
TCP“窗口大小”字段就16位, 两个字节,表示窗口最大有65535字节吗?不是的,其实在TCP40字节选项中还包含了一个窗口扩大因子M,实际窗口大小的值左移M位。
拥塞控制
由于网络状况TCP是不清楚的,因此TCP引入慢启动机制,先发少量的数据,探探路,摸清楚当前网路拥塞状态,再决定按照多大速度传输数据。
- 发送开始时,定义拥塞窗口为1;
- 每次收到一个ACK应答,窗口大小增加。
- 每次发送数据包的时候,将拥塞窗口和接受端主机反馈的窗口大小做比较,取较小值
- 慢启动是指初始时慢,但是增长速度非常快,是指数级别的
- 为了增长的不那么快,因此不能使拥塞窗口单纯的加倍,会设置一个阈值,超过阈值就按线性增长
- 当TCP开始启动的时候,慢启动阈值就等于窗口最大值
- 每次超时重发的时候,慢启动阈值会变为原来的一半,同时拥塞窗口置回1
延时应答
如果接受数据的主机立刻返回ACK应答,这时返回的窗口可能比较小,如:
假设接收端缓冲区为1M. 一次收到了500K的数据; 如果⽴立刻应答, 返回的窗⼝口就是500K;
但实际上可能处理端处理的速度很快, 10ms之内就把500K数据从缓冲区消费掉了;
在这种情况下, 接收端处理还远没有达到⾃自⼰己的极限, 即使窗⼝口再放⼤大⼀些, 也能处理过来;
如果接收端稍微等一会再应答, ⽐比如等待200ms再应答, 那么这个时候返回的窗⼝口⼤大⼩小就是1M;
并不不是所有包都可以延时应答:
- 数量限制:每个N个包就应答一次,N一般取2
- 时间限制:超过最大延迟时间就应答一次,一般取200ms
捎带应答
在延时应答的基础上,可以将两个包一起发送给对端