1.TCP概念
TCP是一种保障可靠性的传输层协议.
这个报文我画的其实有点蠢,书上的更标准,因为TCP底层是位图实现的,一行一个uint更合理.
报文解析:
源端口,目的端口: 标识唯一进程,socket五元组其二,进程网络通信必备. 0~1023是经典端口,一般不用.1024~65535自己用.
32位序号,32位确认序号 : 接收方根据序号整理包序,去重和计算ack序号,发送方根据ack序号知道发送的数据已接收,未接收的会重传.实现全双工通信.
4位首部长度: TCP报文长度 = 首部长度*4 单位:字节 0~60字节
6位标志位: 标记特殊请求. SYN,FIN,ACK连接管理请求. PSH催促接收缓冲区处理数据请求. RST重新连接请求(连接异常情况). URG表示有紧急数据需要处理.
16位窗口大小: 自己的接收缓冲区剩余空间大小
16位紧急指针: 标识紧急数据
16位校验和: TCP报文校验
2.TCP特点
(1)连接: 在知道对方的ip和port之后,需要建立连接
(2)可靠: 能感知到对方是否收到消息
(3)面向字节流: 有读写缓冲区,读写自由
3.可靠性
(1)连接管理机制 --- 保证双方连接
三次握手: 服务器进入LISTEN状态后,客户端向服务器发送SYN连接请求,服务器收到后回应ACK并发送SYN连接请求,等客户端回应ACK后则连接建立成功。
四次挥手: 当有一方主动断开连接后,会发送FIN断开请求,被动方则回应ACK并进入CLOSE_WAIT状态,等被动方关闭文件描述符后,会发送FIN断开请求,此时主动断开方处于TIME_WAIT状态并回应
ACK,2MSL后,TCP连接断开。
(2)确认应答(ACK) --- 明确对方是否收到
(3)超时重传 --- 解决丢包问题
如果丢包了,在一定时间间隔内没有收到ACK,就会重发数据
如果ACK丢了,会收到重复的数据,那么可以利用序列号去重
(4)流量控制 --- 根据接收方的缓冲区大小决定发送窗口大小
1.通过ACK将接受端缓冲区空闲大小放入TCP首部窗口大小字段中;
2.根据空闲大小决定窗口大小,发送端根据窗口大小决定发送速度;
3.满了,发送端不发,但定期会发一个窗口探测,接收端回应窗口大小
(5)拥塞控制 --- 根据网络拥堵情况决定发送窗口大小
在不清楚网络状态情况下,贸然发送大量数据可能会雪上加霜
慢启动机制: 指数增长--> 加法增大 -->(出现丢包) 乘法减小 (轮询)
1.发送开始时,拥塞窗口为1,每有一个ACK到达,拥塞窗口+1
2.实际窗口大小=min(拥塞窗口,流量控制窗口)
3.慢启动中,拥塞窗口指数增长 --- 拥塞避免算法
4.可靠性基础上的效率提升
(1)滑动窗口 --- 16位窗口大小--->接收端缓冲区大小 不回复ACK允许发送的最大数据量
发一次数据回一次ACK,效率低.
一次发送多条数据,能大大提高性能.
窗口大小就是无需等待ACK就可以发送数据的最大值.
发送窗口: (1)没收到ack (2)允许发送还未发送
接收窗口: 允许接收的
快重传机制: 接收窗口未按序收到
1.部分ACK丢了 --- 可以通过后续ACK确认
2.数据包丢了 --- 不必等待超时时间再重传,ACK重复确认三次,告诉发送方,他要的M3,等待发送方重传 --- 之后执行快恢复算法
此时如果收到M3,那么会ACK(下一个是M8),因为之前的数据都被内核放到了接收缓冲区
(2)延迟应答 --- 不急着回复ack,等一会一次性多读点数据,让接收窗口更大
(3)捎带应答 --- ack和数据一起发
5.粘包问题
出现原因: tcp协议每次只会取出小于等于mss大小的数据进行传输 --- 防止了ip分包
解决方法: 制造数据边界 --- (1)报文结尾使用分隔符 (2)指定报文长度
6.TCP保活机制 --- 连接异常
出现原因: 停电断网,电脑关了,对端还在连接,浪费资源
解决方法: 长时间没收到响应的时候,发送心跳包,多次发送后,对方没响应,则断开连接.
心跳包实现: (1)应用层自己实现 (2)启用tcp的keepalive保活机制
7.优雅的关闭TCP连接
双方使用shutdown()关闭写端.