Linux网络编程(四-TCP协议)

目录

一、TCP概念

二、TCP的首部格式

三、TCP可靠传输机制

[3.1 确认应答机制](#3.1 确认应答机制)

[3.2 超时重传机制](#3.2 超时重传机制)

[3.3 连接管理](#3.3 连接管理)

[3.3.1 三次握手](#3.3.1 三次握手)

[3.3.2 四次挥手](#3.3.2 四次挥手)

[3.4 流量控制](#3.4 流量控制)

[3.5 拥塞控制](#3.5 拥塞控制)

四、TCP效率机制

[4.1 滑动窗口](#4.1 滑动窗口)

[4.2 重发控制](#4.2 重发控制)

[4.3 延迟应答](#4.3 延迟应答)

[4.4 捎带应答](#4.4 捎带应答)

五、TCP的异常情况处理

六、TCP协议特点总结


一、TCP概念

TCP(Transmission Control Protocol,传输控制协议)是一种面向连接(连接导向)的、可靠的、 基于IP的传输层协议。TCP协议属于OSI七层模型中的传输层

二、TCP的首部格式

TCP协议段分为首部用户数据部分,TCP协议段结构如图所示:

TCP首部不包括选项为固定20字节:

  • **源端口号:**表示发送端端口号,字段长16位。
  • **目的端口号:**表示接收端端口号,字段长16位。
  • **序号:**表示发送数据的位置,每发送一次数据,就累加一次该数据字节数的大小。注意:序号不会从0或1开始,而是在建立连接时由计算机生成的随机数作为其初始值,通过SYN包传给接收端主机。然后再将每转发过去的字节数累加到初始值上表示数据的位置。此外,在建立连接和断开连接时发送的SYN包和FIN包虽然并不携带数据,但是也会作为一个字节增加对应的序号,字段长32位。
  • **确认序号:**用于给对方的响应,值为收到TCP报文段的序号值加1(表示当前的应答报文针对的是哪个消息进行的确认应答),发送端收到这个确认序号以后可以认为在这个序号之前的所有数据都已经被正常接收,字段长32位。
  • **4位首部长度:**表示TCP首部的长度,字段长4位,单位为4字节,所以该字段能表示的长度范围为[0,60]字节,而不包括选项的首部长度固定为20字节,所以该字段真实的取值范围为[5,15] × 4字节为[20,60]字节,二进制表示为[0101,1111]。
  • 6个标志位:
    • **URG:**紧急标志位,配合16为紧急指针使用。
    • **ACK:**确认应答标志位,凡是报文具有应答特性,该标志位就会被设置为1。
    • **PSH:**用于提示接收端应用程序立刻从TCP缓冲区将数据取走。
    • **RST:**用于重新建立连接,RST为1时,TCP必须强制断开连接,在重新建立连接。
    • **SYN:**用于建立连接,SYN为1时,表示希望建立连接。
    • **FIN:**用于断开连接,FIN为1时,表示今后不会再有数据发送,希望断开连接。
  • **窗口大小:**进行流量窗口控制,字段长16位。
  • **校验和:**发送端填充,CRC校验,接收端校验不通过,则认为数据有问题,此处的检验和不光包含TCP首部,也包含TCP数据部分,字段长16位。
  • **紧急指针:**标识那部分数据时紧急数据,字段长16位。

三、TCP可靠传输机制

3.1 确认应答机制

在TCP中,当发送端的数据到达接收主机时,接收端主机会返回一个已收到消息的通知,这个消息叫做确认应答(ACK)
TCP通过确认应答(ACK)机制实现可靠的数据传输,当发送端将数据发出之后会等待对端的确认应答,如果有确认应答则说明数据已经成功达到对端,反之,则数据大概率丢失

TCP将每个字节的数据都进行了编号,即为序列号(序号)

每一个确认应答(ACK)都带有对应的确认序列号,意思告诉发送者,我已经收到哪些数据,下一次你从哪里开始发送。

3.2 超时重传机制

发送端在一定时间内没有收到确认应答,发送端就认为数据已经丢失,并进行重发。由此,即使产生了丢失,仍然能够保证数据能够到达对端,实现可靠传输,这就是超时重传机制

主机A发送数据给主机B之后,可能因为网络拥堵等原因导致数据丢失无法到达主机B。此时,如果主机A在一个特定的时间间隔内都未收到主机B发来的确认应答(ACK),则会将该数据进行重发。

未收到确认应答并不意味着数据一定丢失,也有可能是确认应答(ACK)丢失,这种情况也会导致发送端因没有收到确认应答(ACK)而进行重发。上图中,主机A因未收到主机B的确认应答(ACK),而对数据进行了重发,主机B其实已经收到了两次1~1000的数据,再收到第二次1~1000的数据时,主机B会根据序列号来进行去重。(接收的数据会放在操作系统内核的接收缓冲区中,接收缓冲区可以视为是一个阻塞队列,对于收到的数据,TCP会根据序号检查这个数据是不是在缓冲区中已经存在,如果存在则丢弃,如果不存在则放进去)

3.3 连接管理

在正常情况下,TCP要经过三次握手建立连接,四次挥手断开连接

3.3.1 三次握手

过程:

  1. 客户端向服务器端发送连接请求(SYN),申请建立客户端到服务器端的连接。
  2. 服务器端返回确认应答(ACK)(第一次SYN的应答)和连接请求(SYN),申请建立服务器端到客户端的连接。
  3. 客户端收到数据,状态置为ESTABLISHED,表示客户端到服务器端连接建立完成,并且发送确认应答(ACK)(第二次SYN的应答),服务器端收到数据,状态置为ESTABLISHED,表示服务器端到客户端的连接建立完成。

服务器端状态转化:

  • [CLOSED -> LISTEN]:服务器端调用listen函数后进入监听状态,等待客户端连接。
  • [LISTEN -> SYN_RCVD]:一旦监听到连接请求(SYN),就将该连接放入内核等待队列中,并向客户端发送ACK+SYN,应答并请求建立连接。
  • [SYN_RCVD -> ESTABLISHED]:服务器端一旦收到客户端的确认应答(ACK),就进入ESTABLISHED状态,表示连接建立完成,可以进行数据传输。

客户端状态转化:

  • [CLOSE -> SYN_SENT]:客户端调用connet函数,向服务器端发起连接请求(SYN)。
  • [SYN_SENT -> ESTABLISHED]:收到服务器端的确认应答(ACK),connect函数调用成功,进入ESTABLISHED状态,表示连接建立完成,可以进行数据传输。
3.3.2 四次挥手

过程:

  1. 客户端发送FIN到服务器端,申请关闭客户端到服务器端的连接。
  2. 服务器端收到FIN状态置为CLOSE_WAIT,并返回确认应答(ACK)。(这个动作是系统实现TCP协议栈默认执行的,不需要程序来调用代码)
  3. 服务器端发送FIN到客户端,申请关闭服务端到客户端的连接。(程序手动调用close函数)
  4. 客户端收到FIN返回确认应答(ACK),并进入TIME_WAIT时间等待状态,客户端等待一段时间后,状态置为CLOSED,表示已经关闭连接。服务器端收到确认应答(ACK)后,状态置为CLOSED,表示已经关闭连接。

服务器端状态转化:

  • [ESTABLISHED -> CLOSE_WAIT]:当客户端主动关闭连接(调用close),服务器端会收到结束报文段(FIN),服务器返回确认应答(ACK),进入CLOSE_WAIT状态。
  • [CLOSE_WAIT -> LAST_ACK]:进入CLOSE_WAIT后说明服务器准备关闭连接(需要处理完之前的数据)。当服务器端真正调用close关闭连接时,会向客户端发送FIN,此时服务器进入LAST_ACK状态,等待最后一个ACK到来(这个ACK是客户端确认收到了FIN)。
  • [LAST_ACK -> CLOSE]:服务器端收到FIN的确认应答(ACK),进入CLOSE状态,彻底关闭连接。

客户端状态转化:

  • [ESTABLISHED -> FIN_WAIT_1]:客户端主动调用close时, 向服务器端发送结束报文段(FIN), 同时进入FIN_WAIT_1;

  • [FIN_WAIT_1 -> FIN_WAIT_2]:客户端收到服务器对结束报文段的确认应答(ACK), 则进入FIN_WAIT_2,开始等待服务器的结束报文段(FIN)。

  • [FIN_WAIT_2 -> TIME_WAIT]:客户端收到服务器发来的结束报文段(FIN), 进入TIME_WAIT状态,向服务器端发送结束报文段(FIN)的确认应答(ACK)。

  • [TIME_WAIT -> CLOSED]:客户端要等待一个2MSL(Max Segment Life,报文最大生存时间)的时间,才会进入CLOSED状态,此时彻底关闭连接。
    常见问题:

  • 为什么服务端不将ACK和FIN合并一起发送,形成三次挥手呢?

答:ACK和FIN的发送时机不同,ACK是操作系统内核响应的(立即执行),FIN是需要由用户程序调用close函数才会发送,用户调用close函数的时间和内核响应的时间不同。

  • 为什么客户端要等待一段时间状态才置为CLOSED,而不直接将状态置为CLOSED?

答:如果客户端发给服务器端最后一个确认应答(ACK)丢失,此时,服务器端会重新给客户端发送结束报文段(FIN),如果直接置为CLOSE状态,则接收不到客户端重新发送结束报文段(FIN),从而导致无法重发确认应答(ACK),使服务器端无法进入CLOSED状态。

3.4 流量控制

接收端处理数据的速度是有限的,如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发送端继续发送,就会造成丢包,继而引起丢包重传等等一系列连锁反应。

因此TCP支持根据接收端的处理能力,来决定发送端的发送速度,这个机制就叫做流量控制(Flow Control)

  • 接收端将自己剩余缓冲区大小存入TCP协议段首部中的"16位窗口大小"字段 ,通过确认应答(ACK)通知发送端,窗口大小越大,说明接收端的接收能力越强。
  • 发送端根据接收到这个窗口的大小,控制自己的发送速度。
  • 如果接收缓冲区满了,就会将窗口大小设置为0,这时,发送端不再发送数据,而是定期的发送一个窗口探测报文(只是为了知道窗口的大小),让接收端将窗口大小告诉发送端。

3.5 拥塞控制

TCP通过滑动窗口能够高效可靠的发送大量的数据,但是如果在刚开始阶段就发送大量的数据,可能会引发其他问题,一般来说,计算机网络都处在一个共享的环境,在网络出现拥堵时,突然发送一个较大量的数据,极有可能导致整个网络的瘫痪。
TCP为了防止该问题的出现,引入慢启动机制,对发送数据量进行控制。 这里引入拥塞窗口,刚开始时,拥塞窗口设置为1,每收到一个确认应答(ACK)时,拥塞窗口加1,每次发送数据的时候,拥塞窗口和流量窗口的较小的值作为实际发送的窗口,即滑动窗口的大小。
拥塞窗口的增长速度是指数级别的,增长速度非常的快,为了控制增长速度,引入了一个叫做慢启动的阈值,当拥塞窗口超过这个阈值时,不再按照指数方式增长,而变为线性增长。

  • 当TCP开始启动的时候, 慢启动阈值等于窗口最大值。
  • 在每次超时重发的时候, 慢启动阈值会变成原来的一半, 同时拥塞窗口置回1。

四、TCP效率机制

4.1 滑动窗口

由于TCP的确认应答机制存在,导致对每一个发送的数据段都要返回确认应答(ACK),收到确认应答(ACK)后,再发送下一个数据段,这样的传输方式产生了一个缺点,那就是,数据往返时间越长通信性能就越低。

为解决这个问题,TCP引入了窗口这个概念,即使再往返时间较长的情况下,它也能够控制网络性能的下降,如上图所示,确认应答不再是单个数据段进行应答,而是以多个数据段进行应答,这个多个数据段的值由窗口大小控制,图中的窗口大小为4000个字节(四个数据段),发送前四个数据段的时候,不需要等待任何确认应答(ACK),直接发送。

滑动窗口存在于发送端的发送缓冲区中,属于发送端的发送缓冲区的一部分

  • 滑动窗口中的数据段因其某种原因已在传输中丢失,发送端未收到此数据段的确认应答(ACK),此时滑动窗口保持不变,滑动窗口中数据段进行超时重传。
  • 滑动窗口以外的左边是已发送且收到应答的数据,右边是尚未发送的数据。
  • 滑动窗口中的数据段发送后,若如期收到确认应答(ACK),滑动窗口将会滑动到确认应答(ACK)中的序列号的位置,原先的数据段就可以不用进行重发,此时数据段就可以从滑动窗口中清除,滑动窗口整体向右滑动。
  • 滑动窗口的大小为min(流量窗口的大小,拥塞窗口的大小)
  • **滑动窗口本质为:**指针或数组下标(暂且认为),int win_start、int win_end
  • **滑动窗口可以为0吗?**答:可以,win_end由确认应答(ACK)中的16为窗口大小决定,当接收缓冲区慢的时候,窗口大小就会被设置为0,此时滑动窗口大小就为0。
  • **如果没有收到开始的报文的确认应答,而收到中间的影响吗?**答:不影响
  • **滑动窗口如果一直向右移动会越界吗?**答:不会,发送缓冲区为环形

4.2 重发控制

滑动窗口传输数据过程中出现丢包,如何进行重传?
情况一:数据包已经抵达,确认应答(ACK)丢失

这种情况下,不是确认应答(ACK)丢失不要紧,可以通过后续的确认应答(ACK)进行确认,确认应答(ACK)中确认序号的含义为确认序号前的所有序号都已经全部收到。
情况二:数据包丢了

  • 当1001~2000这段报文丢失后,发送端一直会收到1001这样的确认应答(ACK)。
  • 如果发送端主机连续三次收到相同的确认应答(ACK)如1001应答,那发送端主机就会重新发送1001~2000数据,此时,接收端收到1001~2000数据后,再次返回的确认应答(ACK)就是7001了,因为2001~7000数据都已经接收到了,被放到接收端操作系统内核的接收缓冲区中。

这种机制,即时不超时也会发生重传,称作**" 高速重发控制 "也叫" 快重传机制 "**。

4.3 延迟应答

接收数据的主机如果每次都立刻回复确认应答(ACK)的话,可能会返回一个较小的流量窗口,但是流量窗口越大,网络吞吐量越大,传输效率就越高,所以等待一部分时间,待接收端处理完一部分数据 ,就可以将流量窗口设置为大一点的值,这样网咯吞吐量大,效率高。
延迟是为了高吞吐量,但是也不能无限延迟,

  • 数量限制,每隔n个包就应答一次
  • 时间限制,超过最大延迟时间,就应答一次

具体的数量和时间,不同操作系统有差异,一般n取2,超时时间取200ms。

4.4 捎带应答

在延迟应答的基础上,我们发现,很多情况下,客户端服务器在应用层也是 "一发一收" 的,这意味着,TCP的确认应答(ACK)和回执数据可以通过一个包发送,这种方法就是捎带应答,通过这种机制可以使收发的数据量减少。需要注意:捎带应答需要依赖延迟应答

五、TCP的异常情况处理

  • **进程终止:**进程终止会释放文件描述符,仍然可以发送FIN,和正常关闭没有什么区别。
  • **机器重启:**和进程终止的情况相同
  • **机器掉电/网线断开:**接收端认为连接还在,一旦接收端有写入操作,接收端发现连接已经不在了就会进行reset,即使没有写入操作,TCP自己也内置了一个保活定时器,会定期询问对方是否还在,如果对方不在,也会把连接释放。

六、TCP协议特点总结

  • **有连接:**通过三次握手建立连接后才可接发数据。
  • **可靠传输:**网络数据传输是一跳一跳的,经过路途中的设备可能发生数据丢失,可靠传输是可能发生数据丢失但有机制保证对方能接收到。
  • **面向字节流:**可以多次的收发数据(连接没有关闭时,可以多次的接收和发送数据)
  • **有接收缓冲区和发送缓冲区:**发送数据时,是先写到发送缓冲区,再刷新缓冲区(flush)
  • **大小不受限制:**多次的收发数据,每次的数据可以很大
相关推荐
打鱼又晒网5 分钟前
linux网络套接字 | 深度解析守护进程 | 实现tcp服务守护进程化
linux·网络协议·计算机网络·tcp
m0_748240028 分钟前
Chromium 中chrome.webRequest扩展接口定义c++
网络·c++·chrome
終不似少年遊*16 分钟前
华为云计算HCIE笔记05
网络·华为云·云计算·学习笔记·hcie·认证·hcs
良许Linux19 分钟前
0.96寸OLED显示屏详解
linux·服务器·后端·互联网
蜜獾云30 分钟前
docker 安装雷池WAF防火墙 守护Web服务器
linux·运维·服务器·网络·网络安全·docker·容器
小屁不止是运维31 分钟前
麒麟操作系统服务架构保姆级教程(五)NGINX中间件详解
linux·运维·服务器·nginx·中间件·架构
bitcsljl44 分钟前
Linux 命令行快捷键
linux·运维·服务器
ac.char1 小时前
在 Ubuntu 下使用 Tauri 打包 EXE 应用
linux·运维·ubuntu
Cachel wood1 小时前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
Youkiup1 小时前
【linux 常用命令】
linux·运维·服务器