传输层TCP协议

发布于:2025-03-20 ⋅ 阅读:(17) ⋅ 点赞:(0)

本篇主要讲解TCP协议报头的各个字段,在讲解字段的过程中引出TCP的各种机制。

TCP协议的报文格式

1. 源端口号和目的端口号 

        源端口号:表示报文的发送端口,占16位。源端口和源IP地址组合起来,可以标识报文的发送地址。可以理解表示为数据从哪个进程来的。

        目的端口号:表示报文的接收端口,占16位。目的端口和目的IP地址相结合,可以标识报文的接收地址。可以理解为表示数据要去到哪个进程。

2. 4位首部长度

        4位首部长度是用来表示TCP报头长度的。TCP报头是可变的,一般由选项决定;标准的TCP报头(没有选项)是20字节

        4位首部长度的范围为[0,15],但是这只是数值,不是报头的实际大小,4位首部长度是由单位的,单位是int或者4字节;实际范围为[0,60];

        4位首部长度是多少取决与选项。例如,没有选项,报头的实际大小是20字节,根据4位首部长度单位,推出数值应该为5, 4位首部长度就是0101。

3.序号和确认序号

        确认应答(ACK)机制:

           首先要理解确认应答,客户端给服务端发送报文,只有服务端发送一个回应(报文),才能确认收到上一条报文;又要让客户端确认是否收到,又要再发一个回应(报文),要确认就会一直这样循环下去。

           所以,发送回应报文,只能确认上一步,进一步说,只能确认历史报文的接受,下一步或者现在我们不能确定!

           实际上的通信,是客户端发送了几个请求,服务端一起回应,这个回应有先后顺序,而且怎么确认哪个请求被回应?TCP是通过序号的,每个请求都有一个序号,回应的报文需要把对应的序号+1。这样就解决了请求是否被回应,以及回应多个请求时的辨别问题。

                

       

  捎带应答机制的初识

        

        实际的应答不仅仅是确认上一个报文以及收到,也可能会需要发送数据,与这次应答一起发送给对方。因此,既需要一个序号用来确认对方的报文,自己也要有序号来发送自己的报文。这就是TCP的序号与确认序号。

总结:序号保证报文能够按序到达,确认应答;确认序号和序号保证同时进行应答和发送数据。

        3.TCP面向字节流:

        TCP协议下的发送和接受缓冲区,存储单位是字节;于是把字节的下标当作序号,这样每个字节就有序号了

  4.控制标志

控制标志是用来区分报文类型的。报文类型有建立连接的报文、发送数据的报文和断开连接的报文等。

常见的标志位SYN:表示该报文请求建立连接或者请求接受报文。

ACK:表示接受报文成功,并且该报文包含确认序号。

FIN:表示该报文请求断开连接

其他:

URG: 紧急指针是否有效
PSH: 提示接收端应用程序立刻从 TCP 缓冲区把数据读走
RST: 对方要求重新建立连接 ; 我们把携带 RST 标识的称为 复位报文段

超时重传机制:

发送报文后,可能遇到的情况:报文没送到丢失,甚至报文送的太慢;收到报文,应答丢失,或者应答太慢甚至对方不应答。

为了解决上述情况,TCP定义了 超时重传机制。发送了报文,在特定时间内没有收到应答,一律视为丢包,并进行重新发送

如果出现对方只是应答有问题,报文收到了,由于报头有序号的原因,可以识别重复的数据进行去重。

超时时间的确定:

最理想的情况下, 找到一个最小的时间, 保证 "确认应答一定能在这个时间内返回".
但是这个时间的长短, 随着网络环境的不同, 是有差异的.
如果超时时间设的太长, 会影响整体的重传效率;
如果超时时间设的太短, 有可能会频繁发送重复的包

超时时间是动态的,取决于当前网络状态。例如:Linux的超时时间机制

Linux 中(BSD Unix 和 Windows 也是如此), 超时以 500ms 为一个单位进行控
制, 每次判定超时重发的超时时间都是 500ms 的整数倍.
如果重发一次之后, 仍然得不到应答, 等待 2*500ms 后再进行重传.
如果仍然得不到应答, 等待 4*500ms 进行重传. 依次类推, 以指数形式递增.
累计到一定的重传次数, TCP 认为网络或者对端主机出现异常, 强制关闭连接

连接管理机制: 

在正常情况下 , TCP 要经过三次握手建立连接 , 四次挥手断开连接
三次握手的目的是建立连接
第一次握手:客户端向服务端发送包含SYN的TCP报文,客户端的状态变为SYN_SEND;

第二次握手:服务端收到SYN报文后,进入SYN_RCVD状态;同时发送包含SYN和ACK的报文给客户端,通知客户端已经接受报文,需要进行确认。                                                                      第三次握手:收到SYN和ACK的报文后,客户端先把状态变为ESTABLISHED,认为建立连接成功;然后给服务端发送ACK报文,服务端收到该报文,会进入ESTABLISHED,认为建立连接成功。

至此,双方连接建立成功,可以发送数据了。

四次挥手的目的是断开连接

第一次挥手:主动断开方发送包含FIN(请求结束)的报文,然后进入FIN_WAIT_1状态;

第二次挥手:被动断开方收到FIN报文,然后发送一个ACK报文(应答同意断开连接);然后进入CLOSE_WAIT状态;主动方接受到该报文,进入FIN_WAIT_2状态,此时主动方到被动方的连接已经断开,不能发数据了(应答还可以)。

第三次挥手:在发送完成ACK报文后,被动断开方还可以继续完成业务数据的发送,待剩余数据发送完成后,或者CLOSE-WAIT(关闭等待)截止后,被动断开方发送FIN报文,进入LAST_ACK状态。

第四次挥手:主动方收到FIN报文,进入TIME_WAIT状态,等待超时后最终关闭连接;然后给被动方发送ACK报文。被动方接受到该报文,断开连接。

至此,双方连接断开。

注:在发送报文的过程中,如果连接异常或者收到的报文有问题;就会给对方发一个含有RST的报文。

RST:用于重置一个已经混乱的连接,也可用于拒绝一个无效的数据段或者拒绝一个连接请求。如果数据段被设置了RST位,说明报文发送方有问题发生。

在内核中的过程:

在三次握手中,客户端首先要有套接字,服务端要处于监听状态;客户端通过connect()接口发起三次握手,如果建立连接成功,服务端通过accept()返回新的套接字和客户端通信。

在四次握手中,主动方通过关闭套接字close(),发起四次挥手,期间被动发的read()接口阻塞,等待可能还要发送的数据,read()返回0,被动方也通过关闭套接字close(),继续四次挥手让连接断开。

为什么是三次挥手?

1.保证信道(网络)是健康的。三次握手,双方都进行了一次发送和接受,保证双方都是全双工。

其次,1,2次握手,有安全隐患;服务器要维护连接,需要成本;选择1,2次握手,服务器可能被故意大量访问,使得服务器无法正常工作。

2.确保双方是健康且愿意通信的。三次握手,双方都有接受和请求,表示双方都愿意进行通信,避免不愿意的连接。

四次挥手中的CLOSE_WAIT和TIME_WAIT:

1.CLOSE_WAIT:等待被动方内核调用close()关闭套接字;

对于服务器上出现大量的 CLOSE_WAIT 状态, 原因就是服务器没有正确的关闭 socket, 导致四次挥手没有正确完成. 这是一个 BUG. 只需要加上对应的 close 即可解决问题。

2.TIME_WAIT:让客户到服务端的连接等待,不用调用close();主要是保证第四次挥手,如果ACK报文没发到,对方会触发超时重传再来第四次挥手;其次为了等到历史陈旧报文丢弃以免对下次连接产生影响。

TIME_WAIT期间,没有调用close(),响应的套接字是被占用的,下次绑定这个套接字会失败!

通常使用socketopt函数解决这个问题。

5.16位窗口大小

窗口大小指的是自己 接受缓冲区 的剩余空间大小;此字段用来进行流量控制。 

 6.流量控制

接收端处理数据的速度是有限的. 如果发送端发的太快, 导致接收端的缓冲区被打满, 这
个时候如果发送端继续发送, 就会造成丢包, 继而引起丢包重传等等一系列连锁反应.
因此 TCP 支持根据接收端的处理能力, 来决定发送端的发送速度. 这个机制就叫做流量
控制
例:
         接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 "窗口大小" 字段, 通
过 ACK 端通知发送端;
        窗口大小字段越大, 说明网络的吞吐量越高;
        接收端一旦发现自己的缓冲区快满了, 就会将窗口大小设置成一个更小的值通
知给发送端;
        发送端接受到这个窗口之后, 就会减慢自己的发送速度;
        如果接收端缓冲区满了, 就会将窗口置为 0; 这时发送方不再发送数据, 但是需
要定期发送一个窗口探测数据段, 使接收端把窗口大小告诉发送端。

注: 

如果接受端将窗口大小设置为0,发送方的探测报文会包含PSH控制标志,“催"对方赶紧把数据交给上层。

PSH:接收方在收到数据后立即将数据交给上层,而不是直到整个缓冲区满。

7.滑动窗口

滑动窗口,意思是暂时不需要应答,可以一直发送数据。数据量也就是窗口大小是对方接受缓冲区的剩余大小(暂时,不考虑网络的状态)。

1.滑动窗口的动作

 滑动窗口内的数据发送后,只有收到应答,窗口才会滑动。可以支持超时重传!

 滑动窗口只能向右滑动,窗口左边的数据是已经被接受的。

滑动窗口的大小可以变大变小。窗口本质是一个限制区域,通过两个下标就可以维护。

2.滑动窗口的策略

只有收到应答的数据,才会被滑出窗口,来到窗口的左边;

如果窗口内发送的数据丢包,由于确认序号:只能确认序号之前的数据被接受。所有回应滑动窗口的应答都会只表示未收到的数据之前的序号(比如发送数据1000~5000,其中1001到2001的数据丢失,所有应答的确认序号都是1001)。

如果连续收到三个重复的确认序号,就会触发快重传机制:补发重复确认序号的报文。

如果数据发送的次数少(数据是一批一批发的),ACK也少,导致有丢失但是收到的重复确认序号少于三个,就触发超时重传机制!。

8.拥塞控制

虽然 TCP 有了滑动窗口 , 能够高效可靠的发送大量的数据 . 但是如果在刚开
始阶段就发送大量的数据 , 仍然可能引发问题 .
因为网络上有很多的计算机 , 可能当前的网络状态就已经比较拥堵 . 在不清楚当前网络
状态下 , 贸然发送大量的数据 , 只会更加堵塞。
TCP 引入 慢启动 机制 , 先发少量的数据 , 探探路 , 摸清当前的网络拥堵状态 , 再决定按
照多大的速度传输数据。

此处引入一个概念称为拥塞窗口

发送开始的时候, 定义拥塞窗口大小为 1;

每次收到一个 ACK 应答, 拥塞窗口加 1;

每次发送数据包的时候, 将拥塞窗口和接收端主机反馈的窗口大小做比较, 取较小的值作为实际发送的窗口;

像上面这样的拥塞窗口增长速度, 是指数级别的. "慢启动" 只是指初使时慢, 但是增长速

度非常快.为了不增长的那么快, 因此不能使拥塞窗口单纯的加倍.此处引入一个叫做慢启动的阈值

当拥塞窗口超过这个阈值的时候, 不再按照指数方式增长, 而是按照线性方式增长

TCP 开始启动的时候 , 慢启动阈值等于窗口最大值 ;
在每次超时重发的时候 , 慢启动阈值会变成原来的一半 , 同时拥塞窗口置回 1;
少量的丢包 , 我们仅仅是触发超时重传 ; 大量的丢包 , 我们就认为网络拥塞 ;
TCP 通信开始后 , 网络吞吐量会逐渐上升 ; 随着网络发生拥堵 , 吞吐量会立刻下降 ;
拥塞控制 , 归根结底是 TCP 协议想尽可能快的把数据传输给对方 , 但是又要避免给网络
造成太大压力的折中方案 . 

9.TCP 异常情况
进程终止: 进程终止会释放文件描述符, 仍然可以发送 FIN. 和正常关闭没有什么区别.
机器重启: 和进程终止的情况相同.
机器掉电/网线断开: 接收端认为连接还在, 一旦接收端有写入操作, 接收端发现连接已
经不在了, 就会进行 reset. 即使没有写入操作, TCP 自己也内置了一个保活定时器, 会
定期询问对方是否还在. 如果对方不在, 也会把连接释放.
最后TCP为什么这么复杂?   因为要保证可靠性, 同时又尽可能的提高性能
感谢浏览!!!