TCP工作在哪一层、为什么需要TCP协议?
TCP/IP是一个四层的体系结构,主要包括:应用层、传输层、网络层和网络接口层。
五层协议的体系结构主要包括:应用层、传输层、网络层,数据链路层和物理层。
OSI七层协议模型主要包括是:应用层(Application)、表示层(Presentation)、
会话层(Session)、传输层(Transport)、网络层(Network)、数据链路层(Data Link)、物理层(Physical)。
发送端在层与层之间传输数据时,每经过一层时会被打上一个该层所属的首部信息。反之,接收端在层与层之间传输数据时,每经过一层时会把对应的首部信息去除。
IP 层是不提供可靠性保障的、无状态的,它不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中的数据的完整性。
如果需要保障网络数据包的可靠性,那么就需要由上层(传输层)的 TCP 协议来负责。
因为 TCP 是一个工作在传输层
的可靠数据传输的服务,它能确保接收端接收的网络包是无损坏
、无间隔
、非冗余
和按序的
。
TCP首部
- 每个TCP段都包含源端和目的端的端口号,用于寻找发端和收端应用进程。这两个值加上IP首部中的源端IP地址和目的端IP地址唯一确定一个TCP连接。
- 序列号用来标识从TCP发端向TCP收端发送的数据字节流,它表示在这个报文段中的的第一个数据字节。如果将字节流看作在两个应用程序间的单向流动,则TCP用序列号对每个字节进行计数。序列号是32 bit的无符号数,序列号到达232-1后又从0开始。用来解决网络包乱序问题。
- 确认应答号包含发送确认的一端所期望收到的下一个序号。因此,确认序号应当是上次已成功收到数据字节序号加1. 用来解决不丢包的问题
- 标志比特:
ACK:该位为 1 时,「确认应答」的字段变为有效,TCP 规定除了最初建立连接时的 SYN 包之外该位必须设置为 1 。
RST:该位为 1 时,表示 TCP 连接中出现异常必须强制断开连接。
SYN:该位为 1 时,表示希望建立连接,并在其「序列号」的字段进行序列号初始值的设定。
FIN:该位为 1 时,表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双方的主机之间就可以相互交换 FIN 位为 1 的 TCP 段。
使用WireShark抓包分析三次握手
TCP 建立连接为什么需要三次握手
针对这个问题,首先我们需要知道什么是连接,只有知道连接的定义,我们才能去尝试回答为什么 TCP 建立连接需要三次握手。
什么是TCP连接?
用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗口大小称为连接。
所以,重要的是为什么三次握手才可以初始化Socket、序列号和窗口大小并建立 TCP 连接。
接下来以三个方面分析三次握手的原因:
- 三次握手才可以阻止重复历史连接的初始化(主要原因)
- 三次握手才可以同步双方的初始序列号
-
三次握手才可以避免资源浪费
如果客户端的 SYN 阻塞了,重复发送多次 SYN 报文,那么服务器在收到请求后就会建立多个冗余的无效链接,造成不必要的资源浪费。
TCP 协议的通信双方, 都必须维护一个「序列号」, 序列号是可靠传输的一个关键因素,它的作用:
接收方可以去除重复的数据;
接收方可以根据数据包的序列号按序接收;
可以标识发送出去的数据包中, 哪些是已经被对方收到的;
SYN攻击
我们都知道 TCP 连接建立是需要三次握手,假设攻击者短时间伪造不同 IP 地址的 SYN 报文,服务端每接收到一个 SYN 报文,就进入SYN_RCVD 状态,但服务端发送出去的 ACK + SYN 报文,无法得到未知 IP 主机的 ACK 应答,久而久之就会占满服务端的SYN 接收队列(未连接队列),使得服务器不能为正常用户服务。
TCP连接断开
为什么 TIME_WAIT 等待的时间是 2MSL?
- 为了保证客户端最后一次挥手的报文能够到达服务器,如果第四次挥手的报文段丢失了,服务器会超时重传这个第三次挥手的报文段,
所以客户端不是直接进入CLOSED,而是要保持TIME_WAIT(等待2MSL就是TIME_WAIT)就起到作用了,
当再次收到服务器的超时重传的断开连接的第三次挥手的请求的时候,
客户端会继续给服务器发送一个第四次挥手的报文,能够保证对方(服务器)收到客户端的回应报文,最后客户端和服务器正确的关闭连接。 - 如果Client(客户端)直接CLOSED(关闭),然后又再向Server(服务器端)发起一个新连接,
我们不能保证这个新连接与刚关闭的连接的端口号是不同的。也就是说有可能新连接和老连接的端口号是相同的。
一般来说不会发生什么问题,但是还是有特殊情况出现:假设新连接和已经关闭的老连接端口号是一样的,
如果前一次连接的某些数据仍然滞留在网络中,这些延迟数据在建立新连接之后才到达Server,
由于新连接和老连接的端口号是一样的,于是,TCP协议就认为那个延迟的数据是属于新连接的,这样就和真正的新连接的数据包发生混淆了。
所以TCP连接还要在TIME_WAIT状态等待2倍MSL,这样可以保证本次连接的所有数据都从网络中消失。
超时与重传
超时重传 :在发送数据时,设定一个定时器,当超过指定的时间后,没有收到对方的 ACK 确认应答报文,就会重发该数据
TCP 会在以下两种情况发生超时重传:
- 数据包丢失
-
确认应答丢失
重传超时时间RTO计算
TCP超时与重传中最重要的部分就是对一个给定连接的往返时间(RTT)的测量。由于路由器和网络流量均会变化,因此我们认为这个时间可能经常会发生变化,TCP应该跟踪这些变化并相应地改变其超时时间。
滑动窗口
win这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。起到流量控制的作用
可用窗口的大小为 0 时,表明可用窗口耗尽,在没收到 ACK 确认之前是无法继续发送数据了。
拥塞控制
前面的流量控制是避免「发送方」的数据填满「接收方」的缓存,但我们并不知道当时的网络状况是怎样的,在网络出现拥堵时,如果继续发送大量数据包,可能会导致数据包时延、丢失等,这时 TCP 就会重传数据,但是一重传就会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这个情况就会进入恶性循环被不断地放大....
每一个 TCP 连接都会维护一个拥塞控制窗口(cwnd),它决定了发送方同时能向接收方发送多少数据,其作用主要有两个:
- 防止发送方向接收方发送了太多数据,导致接收方无法处理;
- 防止 TCP 连接的任意一方向网络中发送大量数据,导致网络拥塞崩溃;
使用 TCP 慢启动时,发送方每收到一个响应方的 ACK 消息,拥塞窗口大小就会加一。当拥塞窗口大小大于慢启动阈值时,就会使用拥塞避免算法:
线性增长:每经过一个往返时间 RTT,拥塞窗口大小会加一;
积式减少:当发送方发送的数据包丢包时,慢启动阈值会设置为拥塞窗口大小的一半;
慢启动为发送方的TCP增加了另一个窗口:拥塞窗口(congestion window),记为cwnd。当与另一个网络的主机建立TCP连接时,拥塞窗口被初始化为1个报文段(即另一端通告的报文段大小)。每收到一个ACK,拥塞窗口就增加一个报文段(cwnd以字节为单位,但是慢启动以报文段大小为单位进行增加)。发送方取拥塞窗口与通告窗口中的最小值作为发送上限。拥塞窗口是发送方使用的流量控制,而通告窗口则是接收方使用的流量控制。
当cwnd超过一定的阀值,就会启动拥塞避免算法.
网络就会慢慢进入了拥塞的状况了,于是就会出现丢包现象,这时就需要对丢失的数据包进行重传。
当触发了重传机制,也就进入了「拥塞发生算法」。
当网络出现拥塞,也就是会发生数据包重传,重传机制主要有两种:
- 超时重传
-
快速重传
当发生了「超时重传」,则就会使用拥塞发生算法。
这个时候,ssthresh 和 cwnd 的值会发生变化:
ssthresh 设为 cwnd/2,
cwnd 重置为 1
当接收方发现丢了一个中间包的时候,发送三次前一个包的 ACK,于是发送端就会快速地重传,不必等待超时再重传。
cwnd = cwnd/2 ,也就是设置为原来的一半;
ssthresh = cwnd;