TCP/IP超全笔记 - TCP篇
什么是 TCP
TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的 、可靠的 、基于字节流 的传输层通信协议。
- 面向连接:一对一,先连接,再传输数据
- 可靠交付:保证数据准确
- 面向字节流:把数据看成一连串的无结构字节流
网络模型
七层模型
- 应用层
- 表示层
- 会话层
- 传输层:TCP/UDP
- 网络层: IP
- 寻址和路由选择
- 路由器,防火墙,多层交换机
- 数据链路层
- 关注数据的传输和流控制、差错检测和纠正等逻辑控制功能
- 数据单元是帧(frame)
- 网卡、交换机
- MAC地址
- 物理层
- 主要是将数字信号转化为模拟信号,并通过物理媒介传输信号
- 关注传输媒介,信号的传输和电气规范等物理特性
- 数据单元是比特
五层模型
- 应用层
- 传输层:TCP/UDP
- 网络层: IP
- 数据链路层
- 物理层
四层模型
- 应用层:HTTP、FTP、SMTP
- 传输层:TCP/UDP,数据包 Segment
- 网络层: 负责数据的包装,寻址和路由,IP、RIP、ICMP,数据包 Packet
- 网络接口层:ARP协议,数据包 Frame
TCP 三次握手
- client -> server: SYN = 1, seq = u,告诉服务器,我准备连接了
- server -> client: SYN = 1, ACK = 1, ack = u + 1, seq = v,告诉客户端,我可以被连接
- client -> server: ACK = 1, ack = v + 1, seq = u + 1,告诉服务器,我准备开始传输数据
为啥要三次握手,只要前两次握手不行么?
因为网络是不可靠的,如果只进行两次握手,那么可能会出现如下情况:
client发送第一个连接的请求报文,但由于网络问题,请求没有立即到服务端,而是在网络节点滞留了,直到某个时间才到达server,但是这个时候,可能已经变成了个失效的报文,但是server还是以为client要连接它,所以server会回复一个连接成功的报文,但是client根本不会理睬,所以server白白浪费了一个连接成功的报文。
为了避免这种情况,所以要进行三次握手。
TCP 四次挥手
- client -> server: FIN = 1, seq = u,告诉服务器,我马上要关闭了
- server -> client: ACK = 1, ack = u + 1, seq = v,告诉客户端,我知道你马上要关闭了
但服务端可能还有数据没发送完成,所以这时候要等待server数据发送完成
- server -> client: FIN = 1, seq = w,server数据终于发送完了,告诉客户端,我马上也要关闭了
- client -> server: ACK = 1, ack = w + 1, seq = u + 1,客户端收到server的FIN,知道server要关闭了
client 处于TIME_WAIT状态,等待2MSL后关闭连接,为啥?
- client最后回的ACK,server可能会没收到,从而导致server再次发送FIN,如果client这时候已经关闭了,那么就会导致server错误
- 如果client发送最后的ACK之后直接进入关闭状态,然后再次连接Server,如果端口恰好相同的话,且前一次的连接有数据滞留在网络中,这个时候最新一次的连接就会收到上一次连接的脏数据,导致数据包混乱。
TCP 状态流转
TCP 头部格式
- 源端口号:16bit
- 目标端口号:16bit
- 序列号:32bit
一次TCP通信(从连接建立到断开)过程中某个传输方向上的字节流的字节在数据流上的索引
- 确认应答号:32bit
- 首部长度:4bit, tcp头有多少个32bit,故TCP头最多60字节
- 保留:6bit
- URG:1bit
- ACK: 1bit,用于确认应答
- PSH: 1bit
- RST: 1bit
- SYN: 1bit,用于建立连接
- FIN: 1bit,用于释放连接
- 窗口大小:16bit,用于TCP流量控制,接收缓冲区还能容纳多少字节的数据,以便于发送方控制发送数据的速度
- 校验和:16bit,用于数据校验
- 紧急指针:16bit
- 选项:可变长度
- 数据
连接建立中的异常
- SYN 攻击
攻击者伪造很多IP地址,对目标服务器发送SYN连接请求,服务器回复确认包,并等待攻击者的ACK,由于伪造IP地址,所以攻击者根本不会回ACK,导致服务器端一直处于等待状态,从而导致服务器端资源耗尽,无法为正常用户提供服务。
服务器没收到第三次握手ACK时,会重发(Linux环境下,重发5次,每次间隔1s、2s、4s、8s、16s、32s),重发耗时很长,短时间大量SYN请求会导致资源耗尽。
解决方案:缩短重试时间间隔、
TCP如何实现可靠传输
停止等待方式
- 设定时间内未收到确认则进行重传
- 发送数据完等待ACK,效率低
流水线传输方式
- 采用滑动窗口协议,允许发送端发送多个数据包,而不需要等待对方确认
- 当发送数据包达到窗口上限时停止发送(窗口大小设置多少合适呢?有没有说法?)
- 接收端收到数据包后,返回ACK,发送端滑动窗口右移,继续发送数据
- 接收端发送的ACK,并不一定是当前接收到的包序号,而是返回已连续接收的最大的包序号+1,比如收到1,2,3这时候返回4(表示我已经收到了4之前的报文),如果后续收到了5,7,8,回复的三个ACK都会是4,然后又收到数据包4,则ACK返回6,发送端这时候就知道1-5都接收成功了,滑动窗口直接右移到6开始
- 超时重传机制:发送端滑动窗口内数据包一定时间没收到ACK,则会启动重发机制,直到收到ACK。
- 快速重传机制:如果发送端收到同一报文的三次冗余确认,就会认为这条报文的下一条丢失,不管是否超时都会进行重发
TCP流量控制
接收端处理数据的能力有限,如果发送太快超过了接收端处理能力,就会把接收端缓冲区打满,这时候就会导致丢包,发送端又得重发。因此,需要根据接收端能力来控制发送速度。
- 接收端发送ACK时,返回窗口大小,即剩余缓冲区大小。
- 发送端根据接收端返回的窗口大小来控制发送速度。
- 如果接收端返回窗口大小为0,则发送端停止发送数据,但仍需要定时发送一个窗口探测数据段,不然发送端不知道啥时候接收端可以再接收数据了。
TCP拥塞控制
在不清楚当前网络状态下,贸然发送大量数据,可能会引起计算机网络的拥塞,导致网络性能下降,严重时甚至会导致网络瘫痪,另外网络情况时刻在变化,网络变得拥堵/空闲,都要及时调整发送速度,一方面避免加剧网络堵塞,一方面最大限度地利用网络资源。因此,TCP需要根据网络拥塞情况来动态调整发送数据量,以避免网络拥塞。
慢启动
- 拥塞窗口先设置为1,后面每次都翻倍,直到出现数据传输超时或者触发了快速重传。
- 如果传输超时,可能网络出现严重堵塞(需要立即减少发送),这时候需要将慢启动阈值设置为拥塞窗口的一半,然后重新开始慢启动过程(拥塞窗口设置为1),直到拥塞窗口增加到慢启动阈值,然后改为拥塞避免模式。
- 如果触发了快速重传,则将慢启动阈值减半,然后将拥塞窗口设置为原先的一半 + 3,触发快速重传,说明发送端还能收到ACK,说明网络没有那么严重的堵塞,这时候减半发送就够了,没必要降为1,这时候进入快速恢复模式
拥塞避免
拥塞避免阶段是个速率增加缓慢且线性增长的过程
- 每收到一个ACK,拥塞窗口+1
- 如果发生了超时,则将慢启动阈值设置为拥塞窗口的一半,然后重新开始慢启动过程(拥塞窗口设置为1)
- 如果触发了快速重传,则将慢启动阈值减半,然后将拥塞窗口设置为原先的一半 + 3,然后进入快速恢复模式
快速恢复
- 每收到一个冗余的确认报文,则拥塞窗口+1
- 如果出现数据传输超时,则将慢启动阈值设置为拥塞窗口的一半,然后重新开始慢启动过程(拥塞窗口设置为1)
- 如果发送方接收到新的确认报文,则拥塞窗口设置为慢启动阈值,然后进入拥塞避免模式
为啥收到冗余确认报文,拥塞窗口还要+1?按理来说,没有收到新的确认报文,这个时候还是拥堵的,为啥还有增长?原因在于,新收到冗余确认报文后,意味着网络中腾出了一条报文的空间,所以可以再发一条,但是这个时候拥塞窗口已经满了,只有再+1,才能再发一条数据。