TCP知识总结
1. 基本了解
- 是什么:面向连接的、可靠的、基于字节流的传输层通信协议
- 在哪一层:传输层的可靠数据传输的服务,能确保接收端接收的网络包是无损坏、无间隔、非冗余且按序的
- 什么是TCP连接:
- 用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗口大小成为连接
- 建立TCP连接需要客户端和 服务器端达成三个共识:Socket、序列号、窗口大小
- 头格式
- 头格式如图所示
![屏幕截图.png TCP头格式]()
- 序列号
- 作用:解决网络包乱序问题
- 建立连接时计算机生成的随机数作为其初始值,通过SYN包传给接收端主机,每发送一次数据就累加一次该数据字节数的大小
- 确认应答号
- 解决丢包的问题
- 发送端收到这个确认应答号以后可以认为在这个序号之前的数据都已经被正常接收
- 控制位
- ACK:为1时确认应答字段变为有效,除最初建立连接时的SYNC包之外该为值必须为1
- RST:为1时表示TCP连接出现异常必须强制断开连接
- SYN:为1时表示希望建立连接,并在其序列号字段进行序列号初始值的设定
- FIN:为1时表示希望断开连接
- 头格式如图所示
- 如何唯一确定一个TCP连接:源地址、目的地址、源端口、目的端口
- TCP最大连接数
- 客户端IP数*客户端端口数:IPv4理论最多为232*216=2^48
- 实际并发达不到理论上限:文件描述符限制和内存限制
- UDP和TCP区别
- UDP利用IP提供面向无连接的通信服务
- UDP头部只有8个字节,非常精简:源端口、目的端口、包长度、校验和
- 和TCP区别
- 不面向连接
- 服务对象可以一对一、一对多、多对多
- 可靠性:不保证可靠
- 无拥塞控制、流量控制
- 首部开销小
- UDP和TCP应用场景
- TCP:FTP、HTTP/HTTPS
- UDP:DNS、SNMP;视频、音频等多媒体通信;广播通信
2. 连接建立
-
三次握手过程
![屏幕截图.png TCP三次握手]()
- 客户端和服务端都CLOSED状态,然后服务端主动监听某个端口,处于LISTEN状态
- 客户端随机初始化序列号,放入TCP首部,SYN置为1,表示SYNC报文;然后把这第一个SYN报文发给服务端发起链接,该报文不包含应用层数据,之后客户端处于SYN-SENT状态
- 服务端收到客户端的SYN报文后先随机初始化自己的序列号放入首部序列号,然后把客户端的序列号+1放入确认应答号,把SYN和ACK置为1,把报文发给客户端,也不包含应用层数据,服务器端开始处于SYN-RCVD状态
- 客户端收到服务端报文后,回应最后一个应答报文,ACK标志位为1,确认应答号是服务器的序列号+1,这次可以携带应用层数据,处于ESTABLISHED状态
-
为什么是三次握手
- 阻止历史重复连接的初始化:网络不好,会有很老的连接申请,通过SYN+ACK客户端知道这个是老连接申请,直接RST
- 同步双方的初始序列号
- 避免资源浪费,没有双向握手可能建立多个冗余的无效链接
-
Linux中TCP连接状态查看:netstat -napt
-
初始序列号ISN怎么生成:计时器+Hash(Socket)+加密
-
为什么分片在TCP而不是IP:在TCP层分片,一个TCP分片丢失,重发以MSS为单位,不重传所有分片,大大提高重传的效率;IP没有重传机制,一个分片丢了,整个IP报文都要重传
-
SYN攻击
- 攻击者短时间伪造不同IP地址的SYN报文发送,服务端SYN接受队列被占满
- 避免方式
- 修改Linux内核参数,控制队列大小和队列满应怎么办:
- 保存数据包队列最大值
- 状态链接最大个数
- 超出处理能力时对新的SYN直接返回RST
- SYN cookie设置
- 修改Linux内核参数,控制队列大小和队列满应怎么办:
3. 连接断开
![屏幕截图.png TCP四次挥手]()
- TCP 四次挥手过程
- 客户端要关闭连接,发送一个FIN为1的报文,客户端进入FIN_WAIT_1状态
- 服务端收到报文发送ACK应答报文,进入CLOSED_WAIT状态
- 客户端收到服务端的ACK应答报文后进入FIN_WAIT_2状态
- 待服务端处理完数据向客户端发送FIN,之后服务端进入LAST_ACK状态
- 客户端收到服务端的FIN报文后,回一个ACK应答报文,进入TIME_WAIT状态
- 服务器收到ACK应答之后进入CLOSE状态
- 客户端经过2MSL时间自动进入CLOSE状态
- 为什么挥手四次
- 服务端收到客户端的FIN先回一个ACK,但是还有可能自己还要发送等自己不发送再发FIN
- 服务端的ACK和FIN一般会分开发送
- 为什么需要TIME_WAIT状态
- 让网络中此次连接的数据包消失,防止具有相同的四元组的旧数据包被收到
- 保证被动关闭连接的一方能被正确关闭,最后的ACK没收到会重发FIN,接到了会重发ACK
- 为什么TIME_WAIT是2MSL
- Maximum Segment Lifetime,报文最大生存时间
- 网络中可能存在来自发送方的数据包,当这些发送方的数据包被接收方处理后又会向对方发送响应,所以保险起见需要等待 2 倍的时间。
- Linux里MSL为30s
- TIME_WAIT过多的危害:内存资源占用、端口资源占用
- 优化TIME_WAIT(慎重)
- 打开net.ipv4.tcp_tw_reuse和tcp_timestamps:需要时间戳支持;当客户端和服务端主机时间不同步时,客户端发送的消息会被直接拒绝
- net.ipv4.tcp_max_tw_buckets:默认18000,超过这个值系统将会吧所有TIME_WAIT连接状态重置
- SO_LINGER:跳过四次挥手
- 客户端突然出现故障:保活机制
- 默认7200后连发9个,相隔75s
- 一段时间没消息就发送探测报文
- 崩溃并重启后的客户端会返回一个RST,TCP连接被重置
- 客户端崩溃,TCP连接死亡
4. Socket编程
-
![屏幕截图.png Socket编程]()
![屏幕截图.png connect和accept所处位置]()
- 服务端调用accept时是两个socket,一个是监听socket,一个是已完成连接socket
- listen参数backlog目前多指Accept队列
5. 重传机制
- 超时重传
- 超过一定时间没收到对方的ACK就会重发该数据
- 发生超时重传的两种情况:数据包丢失;确认应答丢失
- 超时时间设置
- RTT Round-Trip Time 往返时延
- RTO Retransmission Timeout 超时重传时间
- RTO应略大于RTT
- Linux如何计算RTO
- 采样RTT,加权平均得出一个平滑的RTT,网络状况不断变化,这个值也是不断变化;还要采样RTT波动范围,避免RTT有一个很大波动但是不被发现
- 如果超时重发的数据,再次超时的时候,又需要重传的时候,TCP 的策略是超时间隔加倍。
- 快速重传
- 发送端收到三个一样的ack就认为有包没收到,就会在定时器过期之前直接重传,收到之后服务端发送的ACK会更新为最新的
- 问题:不知道重传几个
- SACK
- Selective Acknowledgment 选择性确认
- 在TCP首部选项字段加一个SACK,可以将缓存区有的区域发送给发送方,发送方就知道收到了哪些,没收到哪些
- D-SACK——Duplicate SACK
- 使用SACK告诉发送方哪些数据被重复接受了,以及因为什么才要重发
- 首先ACK大于包说明是D-SACK,如果已经收到过这个ACK则说明是数据包延迟,否则则是服务端ACK延迟
6. 滑动窗口
- 发送方的滑动窗口
![屏幕截图.png 发送方滑动窗口]()
- window字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。
于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。
7. 流量控制
- 发送方根据接收方的实际接收能力控制发送的数据量
- 如果发生了先减少缓存,再收缩窗口,就会出现丢包的现象
- 为了防止这种情况发生,TCP 规定是不允许同时减少缓存又收缩窗口的,而是采用先收缩窗口,过段时间在减少缓存,这样就可以避免了丢包情况。
- 窗口为0会出现窗口关闭:持续计时器收到窗口0时启动,发送窗口探测报文
- 糊涂窗口综合症
- 避免接收方通知小窗口:接收方窗口大小小于min(MSS,缓存空间/2)就发送通告窗口为0
- 避免发送方发送小数据:Nagle算法
- 窗口大小》=MSS或数据大小》=MSS
- 受到之前发送数据的ack回包
- 都满足可以发送
- 默认打开
8. 拥塞控制
- 拥塞控制是避免发送方数据填满整个网络
- 拥塞窗口:cwnd
- 发送窗口swnd,接收窗口rwnd -> swnd=min(cwnd,rwnd)
- 变化规则:网络没拥塞cwnd增大,网络有拥塞,cwnd减少
- 拥塞控制算法
- 慢启动
- 发送方每收到一个ACK就把拥塞窗口cwnd大小加一
- 慢启动门限,cwnd<ssthresh使用慢启动,之后就用拥塞避免
- 拥塞避免
- 每收到一个ACK,cwnd增加1/cwnd
- 拥塞发生
- 超时重传的拥塞发生算法
- ssthresh设为cwnd/2,cwnd设为1;
- 重新慢启动,会网络卡顿
- 快速重传的拥塞发生算法:
- cwnd/=0.5,ssthresh=cwnd;进入快速恢复算法
- 认为快速重传时网络拥堵不严重,所以可以快恢复
- 超时重传的拥塞发生算法
- 快速恢复
- cwnd=sthresh+3(3是确认有3个数据包被收到了
- 重传丢失的数据包
- 如果收到重复的ACK(那个丢包的ACK一直没到),那么cwnd加1
- 收到新的ACK则把cwnd变为ssthresh的值
- 慢启动






浙公网安备 33010602011771号