计算机网络-小林coding-TCP篇
本系列笔记为作者在跟随小林coding学习的时候做的笔记。感谢小林大大。加上自己的笔记
TCP 三次握手与四次挥手面试题
TCP 基本认识
TCP 头格式
为什么需要 TCP 协议? TCP 工作在哪一层?
IP无连接不可靠,TCP实现有连接可靠传输
传输层
什么是 TCP ?
TCP 是面向连接的、可靠的、基于字节流的传输层通信协议
- 面向连接:「一对一」
- 可靠的:保证报文到达接收端
- 字节流:应用层消息可能会被操作系统「分组」成多个的 TCP 报文,需要应用层协议规定消息的边界
什么是 TCP 连接?
- Socket:由 IP 地址和端口号组成
- 序列号:用来解决乱序问题等
- 窗口大小:用来做流量控制
如何唯一确定一个 TCP 连接呢?
TCP 四元组可以唯一的确定一个连接:源地址,源端口,目的地址,目的端口
TCP 连接建立
TCP 三次握手过程是怎样的
如何在 Linux 系统中查看 TCP 状态?
netstat -napt
为什么是三次握手?不是两次、四次?
- 避免历史连接(首要原因)
- 同步双方初始序列号
- 避免资源浪费
什么是 SYN 攻击?如何避免 SYN 攻击?
短时间伪造不同 IP 地址的 SYN 报文占满服务端的半连接队列,后续再在收到 SYN 报文就会丢弃
TCP 三次握手的时候,Linux 内核会维护两个队列
- 半连接队列,也称 SYN 队列
- 全连接队列,也称 accept 队列
TCP 连接断开
TCP 四次挥手过程是怎样的?
图里面先关闭连接的是客户端,后关闭的是服务端,这顺序可以反过来
主动关闭连接的,才有 TIME_WAIT 状态
为什么挥手需要四次?
tcp是全双工通信
为什么 TIME_WAIT 等待的时间是 2MSL?
MSL 是 Maximum Segment Lifetime,报文最大生存时间,任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。
发送过去FIN还要接收ACK,一来一回需要等待 2 倍的时间,至少允许报文丢失一次触发超时重传
为什么需要 TIME_WAIT 状态?
- 保证「被动关闭连接」的一方,能被正确的关闭(主要)
- 防止历史连接中的数据,被后面相同四元组的连接错误的接收
Socket 编程
accept 发生在三次握手的哪一步
客户端调用 close 了,连接是断开的流程是什么?
TCP 重传、滑动窗口、流量控制、拥塞控制
重传机制
超时重传
RTT 指的是数据发送时刻到接收到确认的时刻的差值,也就是包的往返时间。
超时重传时间 RTO 的值应该略大于报文往返 RTT 的值
快速重传
SACK 方法
SACK( Selective Acknowledgment), 选择性确认,SACK字段记录在ACK后面的已收到的字节
Duplicate SACK
SACK 字段标记重复接收字节
滑动窗口
Window字段告诉发送端自己还有多少缓冲区可以接收数据。
发送方的滑动窗口
- SND.WND:表示发送窗口的大小(大小是由接收方指定的)
- SND.UNA(Send Unacknoleged):指向已发送但未收到确认的第一个字节的序列号
- SND.NXT:指向未发送但可发送范围的第一个字节的序列号
可用窗口大小 = SND.WND -(SND.NXT - SND.UNA)
接收方的滑动窗口
- RCV.WND:表示接收窗口的大小
- RCV.NXT:指向期望从发送方发送来的下一个数据字节的序列号
流量控制
操作系统缓冲区与滑动窗口的关系
先收缩窗口,过段时间再减少缓存
窗口关闭
窗口大小为 0 阻止发送方给接收方传递数据,直到窗口变为非 0 为止,这就是窗口关闭
TCP 连接一方收到对方的零窗口通知时启动持续计时器,计时器超时发送窗口探测 ( Window probe ) 报文获取接收窗口大小,如果接收窗口仍然为 0,那么收到这个报文的一方就会重新启动持续计时器
窗口探测的次数一般为 3 次,每次大约 30-60 秒(不同的实现可能会不一样)。如果 3 次过后接收窗口还是 0 的话,有的 TCP 实现就会发 RST 报文来中断连接。
糊涂窗口综合症
接收方腾出几个字节并告诉发送方现在有几个字节的窗口,而发送方会义无反顾地发送这几个字节,这就是糊涂窗口综合症
解决办法:
- 让接收方不通告小窗口:「窗口大小」小于 min( MSS,缓存空间/2 ) ,也就是小于 MSS 与 1/2 缓存大小中的最小值时,就会向发送方通告窗口为 0
- 让发送方避免发送小数据: Nagle 算法(延时处理),只在下面两个条件下发送数据:1.窗口大小 >= MSS 并且 数据大小 >= MSS。2.收到之前发送数据的 ack 回包。
两个都用上才能避免糊涂窗口综合症
拥塞控制
目的:避免「发送方」的数据填满整个网络
拥塞窗口 cwnd是发送方维护的一个的状态变量,根据网络的拥塞程度动态变化的。
接收窗口 rwnd
发送窗口的值是swnd = min(cwnd, rwnd)
慢启动
发送方每收到一个 ACK,拥塞窗口 cwnd 的大小就会加 1 (指数性的增长),图中纵坐标的单位是MSS,横坐标是传输轮数
慢启动门限 ssthresh (slow start threshold)
- cwnd < ssthresh 时,使用慢启动算法
- cwnd >= ssthresh 时,就会使用「拥塞避免算法」
拥塞避免算法
每当收到一个 ACK 时,cwnd 增加 1/cwnd
拥塞发生
- 超时重传
- ssthresh 设为 cwnd/2
- cwnd 重置为 1 (是恢复为 cwnd 初始化值,我这里假定 cwnd 初始化值 1)
重新开始慢启动
- 快速重传
收到3个一样的ACK
- cwnd = cwnd/2 ;
- ssthresh = cwnd;
接着快速恢复算法(但是先加3,因为快速重传收到了3个ACK)
服务端挂了,客户端的 TCP 连接还在吗
- 服务端进程挂,发生4次握手
- 服务端宕机,超时重传一定次数断开,开启了keepalive则在探测报文检测到服务端宕机并断开
拔掉网线后, 原本的 TCP 连接还存在吗?
不会直接影响TCP连接
TCP校验和和UDP校验和的计算
都需要添加伪首部(共有12字节(前96Bits),包含如下信息:源IP地址4字节、目的IP地址4字节、保留字节(置0)1字节、传输层协议号(TCP是6)1字节、TCP报文长度(报头+数据)2字节)
- 首先,把伪首部、TCP报头、TCP数据分为16位的字,如果总长度为奇数个字节,则在最后增添一个位都为0的字节
- 把TCP报头中的校验和字段置为0
- 反码相加法累加所有的16位字(进位也要累加)
- 对计算结果取反,作为TCP的校验和
发送RST的场景
- 被connect不存在的端口
- 已经关闭或崩溃的连接被发送数据
- close(sockfd)
- 重启后收到保活探针
发送RST的主机连接立刻进入CLOSED状态,收到RST的立刻终止连接
PSH,URG位作用
PUSH推
让接收缓冲区收到包后直接上推到应用层处理
URGENT紧急
接收区方必须通知应用层