0


传输层协议TCP详解(上篇)

目录


一. TCP协议

1.1 什么是TCP协议

  • TCP 是面向连接的运输层协议。应用程序在使用 TCP 协议之前,必须先建立 TCP 连接。在传送数据完毕后,必须释放已经建立的 TCP 连接
  • 每一条 TCP 连接只能有两个端点,每一条 TCP 连接只能是点对点的(一对一)
  • TCP 提供可靠交付的服务。通过 TCP 连接传送的数据,无差错、不丢失、不重复,并且按序到达
  • TCP 提供全双工通信。TCP 允许通信双方的应用进程在任何时候都能发送数据。TCP 连接的两端都设有发送缓存和接受缓存,用来临时存放双向通信的数据
  • 面向字节流。TCP 中的“流”指的是流入到进程或从进程流出的字节序列

1.2 TCP为什么叫传输控制协议

前面我们已经讲过为什么read、write、recv、send 和 tcp 支持全双工,这其中涉及到发送缓冲区和接收缓冲区(点此查看。我们创建的TCP套接字,实际上sockfd会指向一个操作系统给分配好的socket file control block(socket文件控制块),而这个socket文件控制块就是维护发送缓冲区和接收缓冲区的。

我们调用的所有的网络发送函数,write send sendto等实际就是将数据从应用层缓冲区拷贝到TCP协议层,也就是操作系统内部的发送缓冲区而所有的网络接收函数,read recv recvfrom等实际上就是将数据从TCP协议层的接收缓冲区拷贝到用户层缓冲区

而实际上双方主机的TCP协议层之间的数据发送完全是由TCP自主决定的,什么时候发?发多少?出错了怎么办?

这些全都是由TCP协议自主控制决定的,这是操作系统内部的事情,和我们用户层没有任何联系,这也就是为什么TCP叫做传输控制协议的原因,因为这个过程是由TCP自主控制的。

9605ca74b3a14419845725bf93945697.png

二. TCP协议段格式

ec0beb269b5c49cbbdf7816837fa74c9.png

图中,每一行有4个字节,而前20个字节是固定长度的标准报头,解包步骤如下:

  • 提取标准报头:根据20个字节的固定长度提取标准报头。
  • 提取选项:根据4位数据偏移获取整体报头的长度,减去20字节的标准报头,得到选项。如果没有选项的话就能直接得到有效载荷。
  • 提取有效载荷:****有效载荷=报文-报头(-选项)

解析各字段含义:

  • 16位源端口:发送方主机的应用程序的端口号
  • 16位目的端口:目的主机的应用程序的端口号
  • 32位TCP序号:表示本报文段所发送数据的第一个字节的编号。
  • 32位TCP确认序号:接收方期望收到发送方下一个报文段的第一个字节数据的编号。
  • 4位数据偏移:指的是数据段中的“数据”部分起始处距TCP报文段起始处的字节偏移量。
  • 6位保留字段:为TCP将来的发展预留空间,目前必须全部为0.
  • 6位标志位:共有6个标志位,每个标志位占一个bit。
  • 16位窗口大小:表示发送该TCP报文的接收窗口还可以接受多少字节的数据量。该字段用于流量控制。
  • 16位检验和字段:用于确认传输的数据有无损坏。发送端基于数据内容校验生成一个数值,接收端根据接受的数据校验生成一个值。两个值相同代表数据有效,反之无效,丢弃该数据包。校验和根据 伪报头 + TCP头 + TCP数据 三部分进行计算。
  • 16位紧急指针字段: 仅当标志位字段的URG标志位为1时才有意义。指出有效载荷中为紧急数据的字节数。当所有紧急数据处理完后,TCP就会告诉应用程序恢复到正常操作。即使接收方窗口大小为0,也可以发送紧急数据,因为紧急数据无须缓存。
  • 选项字段:长度不定,但长度必须是32bits的整数倍。内容可变,因此必须使用首部长度来区分选项的具体长度。

TCP的报头是变长的,包括固定的20字节和变长的选项。其中,“数据偏移”也叫做“首部长度”,它占固定4位,作用是保存报头整体的长度,以便接收端能够正确解析报文中的字段。

7269b479711b480ca7edbabd33e3483c.png

值得注意的是,虽然首部长度占4个比特位,4个比特位能表示的范围0~15.但是它的单位并不是1字节,而是4字节,所以它实际能表示的范围就是[0,60]字节。

而我们看到,选项上面的报头是固定字节大小,也就是20字节,那么说明选项最大可以有40字节。但是我们今天不谈选项,***因为固定首部20字节,所以选项字段最长40字节。当没有选项字段时,首部长度字段为5(20 = 54),即0101。

首先,在深入学习之前,我们应该掌握这两个问题:

  1. 协议报头如何与有效载荷有效分离?
  2. 有效载荷如何向上交付?

协议报头如何与有效载荷有效分离?

ec0beb269b5c49cbbdf7816837fa74c9.png

提取报头:除了选项之外的报头叫做标准报头,一共 20 字节。
提取选项:根据 4 位首部长度获取报头的整体大小,减去 20 字节的标准报头(固定报头),得到选项。如果没有选项的话就能直接得到有效载荷。
提取有效载荷:有效载荷 = 报文-报头 (-选项)

这样子就 通过首部长度,我们就可以将TCP首部和有效载荷分离。

有效载荷如何向上交付?

TCP是传输层的,上层是应用层。而应用层程序会绑定端口号,TCP首部中有16位目的端口号,根据端口号做到向上交付。

9abfbe84404f45dd93edc6a490709336.png

三. 确认应答(ACK)机制

3.1 什么是确认应答机制

TCP保证数据安全传输的时候最基本的一个特点就是确认应答机制。

实际上为什么网络传输中会存在不可靠问题呢?

本质原因还是因为传输距离过长。

    比如我在重庆给北京的网友发送消息,那数据包其实是要经过很多的路由器结点进行数据包转发,穿过很多的局域网,在局域网内部经过双绞线(以太网技术常用的物理介质)传输,还要经过运营商的基站,数据包在如此之长的传输距离中很有可能会丢失,数据里面的比特位翻转,又或是数据包中的字节乱序,又或是数据包重复发送给我的北京网友(发送方可能以为数据包丢失了)。

TCP应该如何解决网络传输中的不可靠问题呢?

就需要确认应答(ACK)机制

    主机之间进行通信的时候,发完一条消息之后,不会马上接着发送下一条消息,而是等待对方主机传来的应答。确认对方主机收到了发送的消息之后,再发送数据。这样就能避免传输中不可靠的问题。

    虽然可能在传输过程中面临着很多网络问题,但是只要我发送给对方的消息有回复应答,那么证明对方一定是收到了的。

    但是其实可以发现,这样不就会发生无限套娃的情况吗,你发给我消息需要等待我的确认回应,我给你发消息需要等待你的确认回应,这样确认过去确认过来,无穷无尽。

  **  所以可以知道,TCP没有绝对的可靠性,只有相对的!事实上,不只是TCP,所有的协议都没有绝对的可靠性!**

    所以TCP可靠性永远不谈最新的消息,只谈历史的消息,这样看的话就一定存在互相回应的消息。

3.2 推导确认应答机制

首先根据前面的确认应答机制,我们知道是互相应答,那么我们可以设想:

c2625de9808946958ab008eb3d97079d.png

这样子看来,可靠性是保证了,但是好像效率变低了。难道平时我们交流的时候,必须要回应一句“我收到了”,然后才说你想说的话吗?明显不是,我们可以将应答和想回复的消息一起发送回去。

06577caa35f6423dbdecd0c03c373c2f.png

这样显然效率高多了。但是上图中,好像一直都是一条一条消息发送的,难道我们谈话都是一句一句说的吗?

显然不是,实际情况是一次性发送多条消息,并且对方也给出多条应答 。但是这样又有一个问题了:我们咋知道哪条应答对应哪条消息呢?因为消息存在“后发先至”的情况,我们面临着一个顺序被打乱的情况。

针对这种情况,TCP的解决方法就是为每个消息与应答都编上独一无二的编号,这样就不会混乱了。


序列号与确认号

** 客户端在接收到响应之前,还是会把数据存在缓冲区里。**

   首先,我们客户端要发送的数据,已经存在TCP的发送缓冲区(内核里面的那个)中了,因为TCP是面向字节流的,这个缓冲区我们可以看作是char类型的大数组,那么每一个空间就是一个字节,并且还有对应的下标,那么也就是说,**每一个字节天然就有自己的编号。我们拷贝在缓冲区里的数据是按顺序存储的。**

我们只需要记住在缓冲区里的数据是按顺序存储的即可!!!

序列号:

cc0f9bc2e02840938467edcde0861825.png

  1. 含义:序列号是指一个TCP报文段中第一个字节的数据序列标识。它表示在一个TCP连接中,该报文段所携带的数据的开始位置。序号是用来保证数据传输的顺序性和完整性的。
  2. 作用:在TCP连接建立时,双方各自随机选择一个初始序列号(ISN)。随后传输的每个报文段的序号将基于这个初始值递增,其增量为该报文段所携带的数据量(字节数)。通过这种方式,接收方可以根据序号重组乱序到达的数据片段,确保数据的正确顺序和完整性。如果接收到的报文段不连续,接收方可以通过TCP的重传机制请求发送方重新发送缺失的数据。

例如下图,如果一个字段被赋予了序号1,并且他包含1000字节的数据,那么这个报文就代表了从序号1到1000的数据。随后的报文将继续这个序列。比如下一个报文段开始于序号1001,而他包含500的数据,那么就代表了1001到1500的数据。

f6ad210381cb41ea89a50fde610c6b65.png

我们可以简单理解为:数据中每个字节都有唯一的标识 --- 序列号。

  • 在TCP中,当发送端的数据达到接收主机时,接收端主机会返回一个已收到消息的通知,这个消息叫做ACK(确认应答,PositiveAcknowlegement)
  • 每一个 ACK(Acknowledge应答) 都带有对应的确认序列号,意思是告诉发送者,我已经收到了确认号之前的所有数据,下一次你从确认号开始发.
  • 服务器和客户端之间需要有办法能区分出, 当前这个报文是普通报文, 还是确认应答报文。 标志位中的 ACK 就可以解决, ACK 为 0 时, 表示这是一个普通的报文, 此时只有 32 位序号是有效的, 当 ACK 为 1 时, 表示这是一个应答报文, 这个报文的序号和确认序号都是有效的。

** 确认号:**

9d6febb0e86244f2b447842ba3143352.png

确认序号的定义是这样的:确认序号的值代表接收方收到了确认序号之前的所有报文,而且是连续的报文。比如确认序号的值为1001,那么代表接收方收到了1000号及之前所有序号的报文,发送端下次从1001序号开始发送报文即可。所以确认序号的值从发送方的角度来理解,可以理解为发送方下一次发送报文时,报文的序列号。

  1. 含义:确认应答号是接收方期望从发送方接收到的下一个报文段的序号。它实质上是接收方告诉发送方:“我已经成功接收到了哪个序号之前的所有数据,请从这个序号开始发送后续的数据。”
  2. 作用:确认应答号用于实现可靠性传输。当一个报文段被接收方正确接收时,接收方会发送一个ACK报文,其中包含的确认应答号是接收到的数据加上1(即接收方期望接收的下一个数据的序号)。通过检查这个确认应答号,发送方能够知道其发送的数据是否已被接收方正确接收,并据此决定是否需要重传某些数据段。

a33e544aabc64decbbf4ede3b603315e.png

有同学可能会有这样的疑问,既然这样,我们为什么不设置一个32位序号就行了呀,没有必要设置两个序号。那这是为什么呢?

a4b05b93ae9849a79849ab20e453991b.png

可以从两个场景来理解:

  1. 有时候,一个报文可能有两重身份,比如服务器除了返回应答,它还想给客户端传送数据,这种既是应答又是数据的情况,叫做捎带应答。那么此时确认序号就是为了告诉客户确认序号的前面的数据都已经收到了,序列号就是告诉客户端,从服务器发来的数据的第一个字节的序列号是多少的。
  2. 有时候,并不是总是客户端给服务器发消息,服务器也会给客户端发消息,双方地位是对等的,如果只用一个序号位,就搞不清谁发谁收,因此还是需要两种序号位。

此外,我们深知在服务端,数据通常以批量形式连续发送。若采取逐个数据发送并等待服务器应答后再继续的方式,将会极大地降低效率。

然而,这里存在一个挑战:尽管这些数据原本是按照特定顺序发送的,但服务器接收时却可能并不遵循这一顺序,这便是数据乱序问题。对于UDP协议而言,这个问题是无解的;而TCP协议则凭借其报头中的32位序列号,不仅实现了应答确认,还确保了数据能够按照发送顺序准确到达。

总结一下,序列号作用:

  • 标识数据顺序
  • 解决丢包
  • 拥塞控制和流量控制
  • 数据组装和分片

确认序号的作用:

  • 实现可靠性传输
  • 追踪和确认数据

四. 超时重传机制

我们通过上面的知识知道,一条消息发送出去之后,他自己是不知道有没有发送成功的,需要等到收到对方发送过来的确认应答消息才能确认。

前面的确认应答机制是建立在每条数据都能成功发送的基础上的,实际情况中,不可能这么顺利。那么在有丢包可能的情况下该如何应对呢?就是依靠超时重传的机制(隔一段时间没有收到应答,则重新发送)。

丢包的情况有两种,第一是发的数据丢包了,第二是返回的ACK丢包了。不管是哪种情况,只要过了一段时间没有收到ACK,就会进行重发。

丢包的两种情况

ca4db019d6a64f00bfbba38c06390733.png

  • 丢包是小概率事件

数据丢包是一个概率事件,假设一条数据在传输的过程中丢包的概率是5%,传输成功的概率是95%,那么第一次传输丢包,第二次重传也丢包的概率是5%*5%=0.25%,第三次重传也丢包的概率可以忽略不计,在实际情况中,丢包的概率是一个非常小的数字,而上述假设的5%已经是一个很大的数字了,如果连续多次重传还是丢包的情况下,那么就要考虑是否是网线断了或是其他情况了。

  • 自己这里认为发出,但是没有收到对方回复的消息会先保存到滑动窗口

我们知道发送的数据段是有可能没有收到ACK的,所以被发出的数据不应该立马被移除(计算机上的移除其实就是数据覆盖),应该先保存一段时间,如果发送的数据丢包了,则可以将保存的数据再重新发送,而像这样已经发送但没有收到ACK的数据,其实是存放在滑动窗口里面的,这个后面会讲,现在先提一下。

  • ACK丢包的情况下,其实接收端已经收到发送端传输过来的数据,发送端再次发送相同的数据过来该如何处理呢?

其实TCP有一个特殊的处理功能 --- 去重,TCP存在一个“接收缓冲区”的存储空间,接收端会将读到的数据放到对应的缓冲区,根据数据的序号,TCP就能识别是否有两条重复的数据,如果重复就把后面的这条数据给丢弃了。

cc0f9bc2e02840938467edcde0861825.png

  • 控制重传的时间怎么确定?TCP是否会进行无限次的重传?

其实这个时间应该是随着网络情况动态变化的,如果网络情况好,超时时间设定的非常长,这其实就会影响网络传输的效率,因为数据包发送的速度非常快,可能数据包来回一次共需要50ms,但你将超时时间设定为500ms,那中间的450ms的时间就会被平白无故浪费掉,如果网络情况特别差,超时时间设定的非常短,那更离谱了,数据包正在传输的过程当中就被判定为丢包了,这同样也会影响数据传输的效率。所以不同的网络环境具有不同的延迟特性。例如,局域网(LAN)的延迟通常很低且稳定,而广域网(WAN)或互联网则可能具有更高的延迟和更大的延迟变化(抖动)。

所以一个理想的情况就是,找出一个居中的时间,保证在绝大部分网络状态下,数据包能在这个时间内发送到对方手中,同时ACK报文也能发送回来。

Linux 中(Unix 和 Windows 也是如此), 超时以 500ms 为一个单位进行控制, 每次判定超时重发的超时时间都是 500ms 的整数倍。

• 如果重发一次之后, 仍然得不到应答, 等待 2*500ms 后再进行重传。

• 如果仍然得不到应答, 等待 4*500ms 进行重传. 依次类推, 以指数形式递增。

TCP的重传机制并不是无限制的。如果数据包一直无法成功传输,重传次数会不断增加。为了避免因无限重传而导致的资源浪费和网络拥塞,TCP协议通常会对重传次数进行限制。当重传次数超过某个阈值时,TCP连接可能会被关闭,并报告一个错误

此外,TCP的拥塞控制机制也会对重传过程产生影响。当网络出现拥塞时,TCP会降低发送速率,以减少数据包的丢失率。这可能会导致重传时间的增加和重传次数的减少。

五. 连接管理机制

5.1 六位标志位

标志位就在:

574e256dc4c043109c492bcee7721a47.png

标志位字段共 6bit,有6个标志位,每个标志位占 1bit ,每个标志位只有 1 和 0 两种状态。

如下:
TCP标志位中文意思作用URG紧急标志用于指示紧急数据ACK确认标志用于确认收到数据PSH催促标志用于立即传输数据RST重连标志用于重置连接SYN同步标志用于建立连接FIN结束标志用于关闭连接


ACK:表示本报文前面的确认字段是否有效

只有当ACK为1时,前面的确认字段才有效。

在TCP确认回复机制中,客户端和服务端任意一方发送数据后,另一方都需要给予应答以表明自己收到数据。在应答的报文中该标记位需要置1,同时应答的报文也可以携带数据。

TCP规定,建立连接后,ACK必须为1。

9d6febb0e86244f2b447842ba3143352.png


SYN:请求建立连接。携带SYN标识的称为同步报文段

作为服务端,如果我们想要知道客户端是想发消息还是想与我建立连接,就需要依靠这个标志位。

TCP是面向连接的协议,双方在通信之前需要建立连接。建立连接的过程是通过三次握手实现的。为什么说是三次握手呢?

  • 第一次:Client 向 Server 发送请求连接,报文中携带 SYN 标志位来表明当前报文是想与Server 建立连接。
  • 第二次:Server 接受 Client 的连接请求,并询问何时建立连接,报文中也会携带 SYN 标志位,同时会携带ACK来回复上一条请求。
  • 第三次:Client 回复 Server,建立连接,这里只是单纯的回复,所以只需设置 ACK 即可。

800d133e175749a6aee2f48c00d51a92.png

  • 站在 Client 的角度,是在第二次握手,即收到 Server 的回复的时候就认为连接已经建立起来了。
  • 而站在 Server 的角度,建立连接是在第三次握手的时候,因为第二次握手只是单纯的 Server 方面同意了,还不确定 Client 是否同意。
  • client向server请求建立连接:当SYN=1,ACK=0时,表示该报文为请求建立连接的报文;
  • server向client请求建立连接:当SYN=1,ACK=1时,表示同意建立连接;

TCP虽然能保证可靠性,但是TCP是允许建立连接失败的。

只有建立连接的前两次请求中 SYN 才为1。这两次的报文称为同步报文段。


FIN:表示断开连接

断开连接是一次四次挥手的过程:

  • 第一次挥手:Client 给 Server 发消息自己要断开连接,Client 发送的报文中就会携带 FIN 标志位。(注意:此时只是断开了 Client 向 Server 发消息的通道,而 Server 仍然能向 Client 发送消息。)
  • 第二次挥手:Server 收到 Client 的请求,并向 Client 发送应答报文,报文中ACK标志位被设置。

cedb847d818c44108f1d5cde12980e9f.png

  • 第三次挥手:Server 通知 Client 自己要断开连接,Server 发送的报文中会携带 FIN 标志位。
  • 第四次挥手:Client 收到 Server 的请求,并向 Client 发送应答报文,报文中携带 ACK 标志位。

c8e6f3b261ee4aaca6b776b09fa09d60.png


RST:代表重新建立连接。

三次握手建立连接并不一定能成功建立连接,谁也无法保证过程中发生什么,同样四次挥手也一样。就算连接建立成功,也可能会因为某种特殊原因被断开。比如你在家正在峡谷激战呢,你爸突然把你们家网线拔了,这连接不就断开了嘛。

此时,你仍然一直点着你的技能。这时游戏服务器就会感到奇怪,你小子不是被断网了吗,为什么还在一直请求。

所以此时服务器就给客户端发送一个复位报文段,其报头中的 RST 标志位被设置,告诉客户端说,你别再给我发消息了,我们之间的连接早就断了,你再重新发起三次握手,重新和我建立连接吧。

所以复位标志位用于通信双方中,任何一方认为建立连接不一致时,认为连接异常的一方会发送复位报文段,告知对方我们需要重新建立连接。

下面来看几个例子:

  • 服务端过载导致重连

客户端与服务器通过三次握手成功建立连接,但是正常通信时服务器端的操作系统资源满载,导致服务器无法对客户端做出应答,由于服务端建立连接后也要管理连接,操作系统描述管理这些连接数据结构,服务端OS为了解决资源满载的问题可能会释放掉建立的连接,服务端端必须重新发送RST标识为1的报文给对应客户端请求重新建立连接才可以进行通信。

就像下面这样子。

b13b50edec1c4714b12a0574fc92d4a1.png

  • 三次握手时第三次握手失败导致重连

Client 在三次握手的时候,认为只要把三次握手中第三次报文发出,连接就建立好了。但是要是第三次握手失败的时候,就会是下面的情况:

dc48fa09582947d0a9596dbf81c43f96.png


PSH:PSH表示催促标记位,可以理解为PUSH

Client 在不断发消息,Server 在不断接收数据,同时给 Client 发送应答,应答报文中包含了16位窗口大小的字段。所谓的窗口大小字段其实就是在告诉 Client 自己接收缓冲区剩余空间的大小,以便于 Client 调整发送策略。

但是其实,当 Server 接收缓冲区快满了或者说已经满了的时候,此时 Client 会在发送的报文中设置 PSH 标记位来催促对方尽快处理缓冲区的数据。(让对方上层应用程序尽快把 Server 接收缓冲区的数据读取走,以便让 Client 能够继续向 Server 缓冲区写数据。)

  • 为什么缓冲区会满呢?

这是一个相对的过程,就像跑步比赛,位于前面的人跑得慢了,而后面的人跑得快了,那么他们的距离就会缩短,直至反超。缓冲区也一样,当读出数据的数据慢于写入数据的速度时,在时间的积累下,缓冲区会被慢慢写满。或者换一种说法是 Server 应用程序还在处理上一条数据,导致没有时间调用read接口函数取走缓冲区的数据。

  • 如何理解“OS催促上层尽快取走数据”

其实缓冲区有一个低水位与高水位标记,用于控制数据的读取。

(1)低水位:当缓冲区中的数据量低于低水位时,表示当前数据太少,OS可能不会立即催促上层应用读取数据。

(2)高水位:当缓冲区中的数据量达到或超过高水位时,表示缓冲区中的数据已经较多,OS会催促上层应用尽快读取数据,以防止缓冲区溢出和数据丢失。

当接收缓冲区中的数据量达到或超过高水位时,OS会通过某种机制催促上层应用尽快读取数据。这种机制可能涉及中断、轮询或其他方式,具体取决于操作系统的实现。

  • 中断方式:当缓冲区中的数据量达到高水位时,OS可能会触发一个中断,通知上层应用有数据需要读取。
  • 轮询方式:OS可能会周期性地检查接收缓冲区的数据量,当发现数据量达到高水位时,会通知上层应用。

URG:表示紧急标志位。表示本报文中发送的数据是否包含紧急数据。

URG为1时表示有紧急数据,并且只有当URG为1时后续的16位紧急指针字段才有效。

当发送方想要对方尽快拿到一些数据时,就会设置这个标志位。URG标志位通常需要搭配紧急指针使用。紧急指针是一个正的偏移量,这个偏移量与TCP首部中的序号字段的值相加,可以表示紧急数据最后一个字节的序号。因此,紧急指针实际上是指向紧急数据最后一个字节的下一字节。

紧急指针字段如下:

5b0c72ed85244cb0b2949b3732eb599d.png

网上看到一个说法:紧急数据只能有1字节。

这个说法是错误的。紧急数据显然并不是1字节的容量就能满足的。虽然紧急数据并不适用于大量的数据传输,但是实际的长度是由发送端和接收端之间的协商以及TCP协议的实现来决定的。

紧急指针的使用并不常见,它需要双方协商和支持。在实际应用中,紧急指针通常用于传递一些重要的控制信息或紧急指令,而不是用于大量的数据传输。例如,在交互式通信中,一端的应用进程可能在键入一个命令后立即希望收到对方的响应。在这种情况下,TCP就可以使用紧急指针来指示接收端尽快处理这些紧急数据。

这里引入一个名词:带外数据

带外数据是一种特殊的数据类型,它通常与带内数据(即正常的数据流)分开处理。带外数据具有更高的优先级,应该尽快被接收和处理。在TCP中,带外数据通常通过紧急模式进行传输,但并非所有TCP实现都支持这一功能。

带外数据需要与紧急数据区分开来:

  1. 优先级:紧急数据和带外数据都具有较高的优先级,需要尽快被接收和处理。
  2. 传输机制:在TCP中,紧急数据通过设置URG标志和紧急指针来传输,而带外数据则通常与紧急模式相关联。然而,并非所有紧急数据都是带外数据,也并非所有带外数据都通过紧急模式传输。
  3. 应用场景:紧急数据通常用于传递重要的控制信息或通知,而带外数据则可能用于更广泛的目的,如快速传输关键数据、避免拥塞等。
  4. 支持情况:并非所有的TCP实现都支持紧急数据和带外数据。因此,在使用这些功能之前,应该确保发送端和接收端的TCP实现都支持这些功能。

下面来看一下recv函数中的MSG_OOB标志位:

6ffb4db65202478f8d4e5cfe72a9bc4b.png

在recv函数中,MSG_OOB标志位用于接收带外数据。

当recv函数使用MSG_OOB标志位时,它会尝试从TCP连接中接收带外数据。带外数据不会进入接收缓冲区,而是直接交给上层进程处理。这使得带外数据能够绕过正常的数据流处理机制,以便尽快被接收端的应用程序所响应。

5.2 如何理解TCP连接

原文段在(点此查看) 。

  • 如何理解TCP连接?

事实上,我们说的TCP的三次握手建立的这个连接,其实是端到端的,也就是说客户端的应用层到服务端的应用层的连接。更详细说是客户端的进程与服务端的进程之间的连接。只要我客户端的应用层没和服务端的应用层连接上,我们就可以说这个TCP连接是失败的。

当上层(如应用层)调用

connect

函数时,它实际上是请求传输层(TCP协议层)来建立TCP连接。这个连接是逻辑上的,他建立在两个端点的TCP协议栈之间。TCP协议层随后会进行三次握手过程,以在客户端和服务端之间建立连接。在这个过程中,SYN报文是由客户端的TCP协议层(传输层)发出的,用于发起连接请求。

需要注意的是,虽然我们说TCP连接是端到端的,但在实际的网络传输过程中,数据会经过多个网络设备和协议层的处理,如路由器、交换机、防火墙等。然而,这些处理对上层应用来说是透明的,它们只需要关注TCP连接是否成功建立,以及数据是否能够在应用层之间可靠地传输。

当一方和另一方的应用层断开连接时,传输层(TCP协议层)可能仍然保持某种状态,并继续发送SYN报头。

  1. 应用层断开连接:当应用层决定断开连接时,它通常会通过调用相应的系统函数(如close)来通知传输层。这个调用会触发传输层开始断开连接的过程。
  2. 传输层断开连接:传输层在收到应用层的断开连接请求后,会开始执行TCP断开连接的标准过程,即四次挥手。在这个过程中,传输层可能会继续发送和接收SYN、ACK、FIN等报文段,以确保连接的可靠断开。

这些低层协议的状态和操作对上层应用来说是透明的,上层应用只需要关注应用层之间的连接和数据传输即可。

  • TCP连接是要维护的,会消耗资源的

我们知道,TCP在【端对端】之间建立的信道,为上层【端】对应的进程提供服务,它由客户端与服务端的套接字(socket)以及它们之间交换的数据包组成。TCP连接的建立、维持和终止都需要遵循一定的协议和状态机制。

另外,现实模式下一台 Server 不可避免的需要与多台 Client 连接,所以 Server 需要对这些 来源不同的 Client 进行管理。

从数据结构的角度知道:TCP是位于传输层的协议,也就是说,TCP的各种逻辑由操作系统--特指Linux维护,那么这些数据就得按照操作系统的规则组织,即先描述再组织。

当连接建立成功时,内存中会创建对应的连接对象。而管理这些连接实际上就是对这些连接对象的增删查改等操作。

既然这些连接对象需要操作系统的维护,而维护是需要消耗成本的,因为每个连接都需要跟踪其状态、参数和传输的数据,而这些消耗主要是一些CPU与内存资源。这些消耗也是一些网络攻击的切入点。例如:SYN洪水攻击就是通过向服务器发送大量的半连接请求来耗尽其资源。

而TCP连接需要维护的那些状态信息和参数等,会被存储在一个称为传输控制块(TCB)的数据结构中。TCB是TCP连接的核心数据结构,它包含了连接的所有重要信息。每个TCP连接都有一个唯一的TCB与之对应。操作系统通常使用一张表(如TCB表)来存储所有的TCB。

  • ** 为什么说学习网络之前一定要先学好操作系统?**

最重要的原因就如刚才所说,两个具有代表性的协议:TCP 和 UDP 都是传输层的协议,而传输层由操作系统内核维护,那么协议的实现必须符合操作系统中的规则。

另外,在Linux中,传输控制块(Transmission Control Block,TCB)和线程控制块(Thread Control Block,TCB)或者进程控制块(Process Control Block,PCB)之间的关系是不同的,它们分别属于不同的层次(前者是传输层,后两者是内核),他们之间的关系是:

  1. 一个进程可以创建多个线程,这些线程共享进程的资源,如内存空间、文件描述符等等。因此,线程控制块中有一个指针指向所属进程的进程控制块。
  2. 一个进程或者线程可以创建多个套接字,这些套接字用于与其他进程或者线程通信。因此,进程控制块或者线程控制块中有一个文件描述符表,其中包含了指向套接字对应传输控制快的指针。

总结:

好了,到这里今天的知识就讲完了,大家有错误一点要在评论指出,我怕我一人搁这瞎bb,没人告诉我错误就寄了。

祝大家越来越好,不用关注我(疯狂暗示)

7cadd57a9ab1245ed3fe5e181cf2ea00.png


本文转载自: https://blog.csdn.net/weixin_75172965/article/details/143158533
版权归原作者 小灵蛇 所有, 如有侵权,请联系我们删除。

“传输层协议TCP详解(上篇)”的评论:

还没有评论