前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >【Linux网络编程】传输层协议TCP:ACK机制 | 超时重传机制 | 三次握手四次挥手 | 流量控制 | 滑动窗口 | 拥塞控制 | 面向字节流 | 粘包问题

【Linux网络编程】传输层协议TCP:ACK机制 | 超时重传机制 | 三次握手四次挥手 | 流量控制 | 滑动窗口 | 拥塞控制 | 面向字节流 | 粘包问题

作者头像
南桥
发布2024-12-28 10:40:54
发布2024-12-28 10:40:54
34600
代码可运行
举报
文章被收录于专栏:南桥谈编程南桥谈编程
运行总次数:0
代码可运行

TCP协议

TCP全称为传输控制协议,需要对数据进行一个传输控制。

TCP协议格式

宏观上,在发送方的缓冲区中只是数据部分,经过传输层后添加报头。接收方在接收到报文时,需要将报头和数据分离。

  • TCP报头的标准长度是20字节,字节的宽度是4字节。
  • 4位首部长度(单位是4字节)取值范围为[0,15],也就是说有[0,60]字节,所以对应的报文首部长度为20+40字节的选项(可以没有)。 假设当前首部长度为20字节,如何求4位首部当长度? 设4位首部长度为x,这x*4=20,得到x=5,转换成二进制为:0101
  • 报头和有效载荷进行分离:1.读取标准20字节;2.提取首部长度;3.根据首部长度-20,如果为0,报头读完,剩下的是数据;如果不是0,假设是30,接着从报文中读取30个字节,这部分对应的是选项。
  • 源/目的端口号:表示数据是从哪个进程来,到哪个进程去。
  • 对报文进行分用:报文中具有目的端口字段,根据目的端口将报文给对应进程,实现了报文的分用。

确认应答(ACK)机制

TCP为了确保可靠性,有了确认应答机制。

对于一般通信,总有最新的信息没有应答:

上述接收方发送完“可以的”之后,发送方没有回应,因此接收方并不知道发送方是否接收到消息。

在日常生活中,从左往右只要收到了应答就能保证左侧发送方的数据对方一定收到了。

什么叫做可靠性:把事情办成了,对方知道,没有做成,对方也知道。

  • 客户端到服务器的可靠性: TCP协议中,客户端发送一条消息,收到服务器一个应答,客户端不需要对应答再做出应答,因此客户端发送消息给服务器后客户端收到了一条服务器的应答,这样可以保证历史消息被接收到,这种情况称之为可靠性。
  • 服务器到客户端的靠可行:服务器发送给客户端的消息后收到来自客户端的应答,但是服务器不需要再对应答做出应答。

总的来说,双方采用确认应答机制来保证两个朝向上的可靠性。

  • TCP的两种通信模式

为了确保发送方知道接收方应答的是哪一个消息,在报头中有序号 确认序号字段。确认序号原则上是序号+1。 确认序号表示:当前确认序号之前的数据已经全部收到,并不单只确认序号-1的序号被收到。

无论是发送还是应答,发送的都是TCP报头或者TCP报头+数据

为什么报头字段中需要有序号和确认序号? 双方在进行正常通信时,TCP支持全双工通信,客户端给服务器发送消息后客户端需要接收到来自服务端的应答,此时如果服务器需要给客户端发送信息并且需要收到来自客户端的应答。

如何理解序号? 如果将发送缓冲看做是一个数组char sendbuffer[65525]

既然是数组,本身就有序号即下标。假设当前从第1个字节开始,长度为100,那么当前发送序号就是100。因此可以称序号为该发送缓冲区的数组下标。发送端的发送缓冲区有自己的序号,那么接收端的发送缓冲区也有自己的序号,这样双方都可以携带自己的序号来进行互相发送。

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

6个标记位

客户端发给服务器可能有建立连接的请求、断开连接的请求、确认报文、正常数据等等,因此TCP协议要有处理不同类型报文的能力,也就是说,TCP报文有不同的类型。

在TCP报头中,有6个标记位,这6个标记位就是为了区分不同的类型。

应答:ACK+确认序号 捎带应答:ACK+确认序号+数据

超时重传机制

主机A给主机B发送信息,在特定的时间间隔内,主机B没有给主机A应答,此时数据丢包。主机A需要给主机B重新补发数据,这称之为超时重传

规定:主机B收到数据就是主机A收到ACK;主机A没有收到ACK则说明主机B没有收到数据。 在这里,主机A没有收到主机B发来的应答,可能是这个应答丢失,但是主机B可能接收到了数据,这里是不确定的,因此规定了在特定的时间间隔没有收到对应的ACK,主机A就会重新发送数据。

这里可能会出现一个问题 :主机B可能收到两个一样的报文。在一次通信过程中,同一个报文的序号是一样的,可以通过序号进行去重报文。

除此之外,序号可以保证TCP报文的按序到达,按序交付给上层。

因此这里总结一下序号的三个作用:

  1. 构建确认应答中的确认序号
  2. 做收到报文的去重
  3. 确保报文按序到达

这里的特定的时间间隔不能太长也不能太短,网络的状态是会变化的,数据从A到B的时间是变化的,因此特定时间间隔和网络状态相关联,当网络状态好的时候,特定的时间间隔短;当网络状态不好的时候,特定的时间间隔应该长一点,更报文更大的容忍度。因此特定的时间间隔是一个浮动的时间间隔。

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

具体的超时间隔计算,贴上小林coding的介绍。

连接管理机制

三次握手建立连接

  1. listen状态:服务端进入listen状态,随时等待客户端连接
  2. connect本质是要求客户端的TCP发送TCP完整报头,将SYN标记位置为1,服务器一旦都到报文,服务器知道此时没有数据而是和服务器建立连接,所以服务器会给客户端一个裸的应答报头(SYN+ACK),没有数据。
  3. 客户端发送TCP报文,将报头标记位ACK置为1,再进行应答:表示对SYN+ACK做出应答

此时双方完成了建立连接前的协商,这个过程称之为建立前的三次握手

应用层调用connect进行网络连接,实质上只是发起了三次握手

accept不参与三次握手的过程,connect成功后,会返回一个文件描述符给应用层,供用户使用。

总结:三次握手是由其中一方发起,即connect,握手的过程是由双方的TCP协议自动完成的。

三次握手建立连接的目的不是为了一定要建立连接成功,但是一旦三次握手成功,一定认为建立连接成功。

三次握手中,最后一次发送ACK如果丢失会出现什么后果? 客户端对服务器建立好连接后,客户端接下来就会向服务器发送数据。三次握手中,客户端最后一次发送ACK丢失没有被服务器接收到,服务器没有认为此时连接建立好了,但是客户端认为建立好了。此时的客户端就会直接向服务器发送数据,服务器接收到数据。服务器认为服务端误认为连接建立好了,服务器需要对客户端进行应答,在应答的时候将对应的RSTRSTreset连接重置标志位。)标记位置为1。此时的客户端会认为三次握手并没有建立好,服务器要求客户端重新发起三次握手

补充知识:RSTreset连接重置标志位,收到该标志的主机要对异常连接释放,重新发起建立连接。

四次挥手断开连接

天下没有不散的宴席,对于TCP的断开连接需要进行四次挥手,断开连接需要双方都同意。两次FIN是双方发起断开,两次ACK是双方同意。

客户端没有数据发送给服务器时,客户端断开连接,得到服务器的应答,此时的服务器依然没有断开连接,依然在给客户端发送数据,数据发送完了之后上层也调用close,此时发送FIN,然后得到客户端的应答。

客户端断开连接,文件描述符已经关闭了,但是服务器还没有断开,依然在发送数据,客户端的上层怎么读取数据? 在Linux中,关闭文件描述符除了close之外还有shutdown

代码语言:javascript
代码运行次数:0
复制
#include <sys/socket.h>
int shutdown(int sockfd, int how);

这里how可以是关闭写,关闭读,关闭读写。如果howSHUT_RDWR就代表close

四次挥手实质上是双方告知对方都没有数据可以发送给对方。

close本质是进行四次挥手

为什么重启的时候端口绑定失败

在上一个HTTP,我们在访问一次端口后,按下ctrl+c后断开连接,紧接着再次连接服务器发现连接失败:

解释:客户端进程已经退出,可能连接还是存在,正在执行四次挥手。如果此时立即重启,此时的连接还在,对应的端口号被占用,因此绑定可能会失败。

为什么要三次握手

TCP连接建立的过程中,数据需要被组织成报文,每个报文都包含了必要的控制信息,比如:序列号、确认号、标志位、校验和等。这些报文需要按照一定的格式进行描述和组织,在内核中通过数据结构来管理。

在操作系统的TCP/IP协议栈中,内核需要管理多个连接状态,每个连接都有一个对应的 内核数据结构。每当新的连接建立时,内核需要分配内存来创建并维护这些数据结构。这就需要通过 malloc 或其他内存分配机制来分配空间,并在连接关闭时释放这些资源。

因此维护连接是有时间成本和空间成本的。

如果将建立连接变成一次握手: 客户端给服务器发送一个的SYN,服务器就建立了连接。此时如果一个客户端给服务器发送大量的SYN(称之为SYN洪水),客户端几乎没有成本,但是服务器瞬间建立了大量连接,每个连接都有一定的成本,会导致服务器可用资源越来越少

如果将建立连接变成两次握手: 客户端发送一个报文得到服务器的ACK,这个ACK客户端可以不受理,但是此时服务器依然有大量连接,也会造成服务器可用资源越来越少

采用三次握手,在安全角度来看,可能也会存在上述SYN洪水的问题,但是三次握手保证了最后一次报文时客户端给服务器发送的,因此要想三次握手建立好连接,客户端需要先建立好连接

为什么需要三次握手建立连接理由:

  1. 可以验证网络的连通性和全双工,可以发送消息也可以接受消息,客户端和服务器都互相收到对方的ACK。
  2. 建立双方通信的共识意愿。

理解CLOSE_WAIT 状态

客户端调用close()函数接口,客户端向服务器发送FIN,此时服务器给客户端应答ACK,此时的服务器处于CLOSE_WAIT状态。如果服务器一直没有调用close(),即不关闭文件描述符,服务器将一直处于CLOSE_WAIT状态。

在这里插入图片描述
在这里插入图片描述

通过代码查看一下现象:

  • TcpServer.hpp中服务器端的关闭文件描述符操作注释掉:
在这里插入图片描述
在这里插入图片描述
  • 运行代码:
在这里插入图片描述
在这里插入图片描述
  • 此时我们将客户端关闭:
在这里插入图片描述
在这里插入图片描述
  • 通过netstat -natp查看一下当前状态:
在这里插入图片描述
在这里插入图片描述
  • 服务器一直在运行,一直处于CLOSE_WAIT 状态。
  • 将服务器关闭后,不再是CLOSE_WAIT 状态,转换为LAST _ACK状态

如果服务器卡顿,使用netstat -natp命令查看一下是不是有大量的close_wait状态,如果有表示此时有大量文件描述符fd泄漏。

理解TIME_WAIT状态

主动断开连接,自己处于TIME_WAIT状态

在这里插入图片描述
在这里插入图片描述

TIME_WAIT状态一般等待的时间为2*MSLMSL时间具体可以通过cat /proc/sys/net/ipv4/tcp_fin_timeout查看:

在这里插入图片描述
在这里插入图片描述

为什么是 TIME_WAIT 的时间是 2MSL?

MSL 是 TCP 报文的最大生存时间, 因此 TIME_WAIT 持续存在 2MSL 的话,就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服 务器立刻重启, 可能会收到来自上一个进程的迟到的数据, 但是这种数据很可能是错 误的); 同时也是在理论上保证最后一个报文可靠到达(假设最后一个 ACK 丢失, 那么 服务器会再重发一个FIN. 这时虽然客户端的进程不在了, 但是 TCP 连接还在, 仍然 可以重发LAST_ACK)


通过代码查看一下现象:

  • 运行代码:
在这里插入图片描述
在这里插入图片描述
  • 客户端连接:
在这里插入图片描述
在这里插入图片描述
  • 查看此时连接状态:
在这里插入图片描述
在这里插入图片描述
  • 将服务器关闭,此时服务器时主动断开的一方:
在这里插入图片描述
在这里插入图片描述
  • 当前情况下查看连接状态:
在这里插入图片描述
在这里插入图片描述

TIME_WAIT状态不是持续存在的,如果处于TIME_WAIT状态,服务器无法再使用同样的端口去连接,会出现绑定失败。当TIME_WAIT状态不存在了,此时可以继续使用原来的端口号进行连接,不会出现绑定失败的情况。

解决 TIME_WAIT 状态引起的 bind 失败的方法

代码语言:javascript
代码运行次数:0
复制
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
  • sockfd:套接字文件描述符。它是通过 socket() 函数创建的套接字返回值,用于标识要设置选项的目标套接字。
  • level:指定套接字选项的协议层次。常见的 level 值包括:
    • SOL_SOCKET:表示在套接字层面设置选项(例如,设置发送缓冲区大小、关闭Nagle算法等)。
    • IPPROTO_TCP:表示设置与 TCP 相关的选项(如 TCP 的最大重传次数、延迟等)。
    • IPPROTO_IP:表示设置与 IP 相关的选项。
    • SOL_IPV6:表示设置与 IPv6 相关的选项。
  • optname:表示要设置的选项名称。具体选项取决于 level 参数。例如: 当 level 为 SOL_SOCKET 时,可以设置:
    • SO_REUSEADDR:允许地址重用。
    • SO_RCVBUF:设置接收缓冲区大小。
    • SO_RCVBUF:设置接收缓冲区大小。
    • SO_KEEPALIVE:启用 TCP keep-alive 功能等。 当 level 为 IPPROTO_TCP 时,可以设置:
    • TCP_NODELAY:禁用 Nagle 算法,允许小数据包立即发送。
    • TCP_MAXSEG:设置最大分段大小。
  • optval:指向包含选项值的缓冲区的指针。选项值的类型和内容由 optname 和 level 决定。例如,如果设置 SO_RCVBUF,optval 可能是一个包含接收缓冲区大小的整数指针。
  • optlen:optval 缓冲区的长度(以字节为单位)。通常情况下,如果 optval 是一个整数值,则 optlen 为 sizeof(int);如果是结构体或其他复杂类型,则需要指定相应的大小。
代码语言:javascript
代码运行次数:0
复制
virtual void ReUseAddr() override
{
    int opt=1;
    ::setsockopt(_sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
}

此时断开服务器连接紧接着立马重启就会再次连接,不会出现绑定失败的现象:

在这里插入图片描述
在这里插入图片描述

流量控制

发送方在发送数据本质是将发送方的发送缓冲区数据拷贝到接收方的接受缓冲区中,如果接受缓冲区满了,对于新来的报文将会丢弃。也就是说,发送方不能无脑给接收方发送数据,需要根据对方接收能力来控制自己的发送速度,这称之为流量控制

发送放放根据接收缓冲区剩余空间大小来判断接收缓冲区的接收能力。

如何知道剩余缓冲区的大小呢?

  • 接收端将自己可以接收的缓冲区大小放入 TCP 首部中的窗口大小字段, 通过 ACK 端通知发送端。
  • 如果接收端缓冲区满了, 就会将窗口置为 0, 这时发送方不再发送数据,但是需要定期发送一个窗口探测数据段,使接收端把窗口大小告诉发送端。
  • 第一次在发送报文时候是在三次握手期间获取对方的缓冲区剩余大小。
在这里插入图片描述
在这里插入图片描述

如果对方的窗口大小一致不更新,窗口大小一直处于0,发送方如何让接收方尽快向上交付呢?

  • 在TCP报头中PSH标志位,是推送的意思,如果报文中携带了PSH标志位,告知对方操作系统的TCP尽快让缓冲区数据交付给上层处理。
  • 如果想让对方尽快处理数据,都可以设置PSH标志位,让对方尽快交付。
在这里插入图片描述
在这里插入图片描述

16 位数字最大表示 65535, 那么 TCP 窗口最大就是 65535 字节么?

  • 实际上, TCP 首部 40 字节选项中还包含了一个窗口扩大因子 M, 实际窗口大小是 窗口字段的值左移 M 位

16位紧急指针

  • 16 位紧急指针: 标识哪部分数据是紧急数据
  • URG: 紧急指针是否有效
在这里插入图片描述
在这里插入图片描述

16 位紧急指针实际上是紧急数据的偏移量,标记了偏移量,就可以在数据中找出对应数据。

16位紧急指针指示给出紧急数据开始的位置,在TCP中紧急数据只有一个字节。

滑动窗口

在流量控制中,已经知道对方的接收能力,根据对方的接收能力发送数据,发送方具体如何发送?

在超时重传中,将数据发出后在超时时间以内,已经发送的报文不能被丢弃,需要保存起来。报文需要保存在哪里?

主机A在给主机B发送一批数据,要保证主机B能接收到这一批数据:

发送方规定一个概念叫做滑动窗口。在滑动窗口以内的数据可以直接发送,暂时不用收到应答。

滑动窗口的本质是发送缓冲区的一部分:

滑动窗口只能向右滑动,因为滑动窗口之前的数据已经发送给,不需要再去发送。

滑动窗口的大小不是一直不变的,根据对方的接收能力,滑动窗口可以有适当的变化大小。

滑动窗口可以为0,当对方接收能力为0时,滑动窗口可以变为0。


理解滑动窗口的更新: 滑动窗口可以理解为是两个指针维护的一个区域:int win_startint win_end

让滑动窗口向右移动无非是win_start++win_end++;滑动窗口变小就是让win_start加的快一点、win_end加的慢一点。

收到对方的ACK时,发送缓冲区中的滑动窗口如何更新呢? 收到对方的应答ACK,报头中的确认序号为滑动窗口起始位置,滑动窗口结尾是滑动窗口起始位置加上窗口大小。


丢包问题:

  • 当某一段报文段丢失之后, 发送端会一直收到 1001 这样的 ACK, 就像是在提醒发送端 “我想要的是 1001” 一样。
  • 如果发送端主机连续三次收到了同样一个 “1001” 这样的应答, 就会将对应的数据 1001 - 2000 重新发送。
  • 这个时候接收端收到了 1001 之后, 再次返回的 ACK 就是 7001 了(因为 2001-7000)。

这种机制称之为快重传。超时重传是重传策略兜底的,快重传是给重传策略提高策略的。


如果丢包了,滑动窗口如何更新? 丢包问题分为三种情况:

  1. 窗口中的最左侧报文丢失

如果报文没有丢失,收到的应答丢失不会有影响,后面会有应答。 2000的报文丢失了

  • 3000,4000,5000报文的应答ACK一定是1001
  • 滑动窗口win_start不进行向右滑动
  • 发送方意识到1000-2000的报文丢失,根据快重传和超时重传策略进行补发
  • 补发成功,会收到2001或者3001或者4001或者5001的ACK,然后滑动窗口继续向右移动

  1. 窗口中的中间报文丢失

中间报文丢失的问题实际上就是最左侧报文丢失。中间报文前面的报文丢失,滑动窗口会向右移动,直到2001-3001报文为止。因此中间报文转换到了新窗口的最左侧报文丢失问题。

  1. 窗口中的最右侧报文丢失 最右侧报文丢失本质是也是转换到了新窗口的最左侧报文丢失问题

因此,在滑动窗口中丢包问题都可以转换成最左侧报文丢失问题

对于已经发送且已经确认部分的数据,在缓冲区是否需要清零? 不需要再清零,已发送已确认的数据代表已经废弃的数据。缓冲区可以看成一个环形的缓冲区,对于已发送已确认的数据下次滑动窗口到这里的时候,再次拷贝新数据到这里即可。

拥塞控制

在重传策略以及滑动窗口中考虑的都是发送方和接受放的能力来取决发送数据量和接收数据量。但是没有考虑网络的状况。

因为网络上有很多的计算机, 可能当前的网络状态就已经比较拥堵. 在不清楚当前网络 状态下, 贸然发送大量的数据, 是很有可能引起雪上加霜的。

网络是被所有人共享的,造成网络拥堵不是一个用户造成的,一旦网络拥堵,造成的是所有用户都网络拥堵。

TCP如何识别出来网络的问题? 作为发送方,发送数据时发现大量丢包,此时认为是网络的问题。这种情况下不能使用重传策略,因为此时网络处于拥堵状态,再去快速重传只会使得网络更堵。而是使用慢开始策略。

慢开始

TCP 引入 慢启动 机制, 先发少量的数据, 探探路, 摸清当前的网络拥堵状态, 再决定按照多大的速度传输数据。

这里新增了一个窗口,叫做拥塞窗口。因此此时的滑动窗口 = min(应答窗口,拥塞窗口)

网络的状况是浮动的,拥塞窗口的大小也是浮动的。主机要想直到拥塞窗口的大小,需要通过多轮尝试才能获取拥塞窗口的大小

  • 发送开始的时候, 定义拥塞窗口大小为 1;
  • 每次收到一个 ACK 应答, 拥塞窗口加 1;
  • 每次发送数据包的时候, 将拥塞窗口和接收端主机反馈的窗口大小做比较, 取较小的值作为实际发送的窗口。

像上面这样的拥塞窗口增长速度, 是指数级别的.慢启动只是指初使时慢, 但是增长速度非常快。 初始时慢,可以慢慢减少网路发送,让网络恢复。网络恢复后,通信过程也需要恢复,中后期增长快。

为了不增长的那么快,因此不能使拥塞窗口单纯的加倍,因此需要设置一个阈值,当拥塞窗口超过这个阈值的时候,不再按照指数方式增长,而是按照线性方式增长。

  • 当 TCP 开始启动的时候, 慢启动阈值等于窗口最大值
  • 在每次超时重发的时候, 慢启动阈值会变成原来的一半, 同时拥塞窗口置回 1;
  • 少量的丢包, 我们仅仅是触发超时重传; 大量的丢包, 我们就认为网络拥塞

当 TCP 通信开始后,网络吞吐量会逐渐上升;随着网络发生拥堵,吞吐量会立刻下降;拥塞控制,归根结底是 TCP 协议想尽可能快的把数据传输给对方,但是又要避免给网络造成太大压力的折中方案。

延迟应答

如果接收数据的主机立刻返回 ACK 应答, 这时候返回的窗口可能比较小。

窗口越大, 网络吞吐量就越大, 传输效率就越高. 我们的目标是在保证网络不拥塞的情况下尽量提高传输效率。

延迟应答策略:

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

捎带应答

在延迟应答的基础上, 我们发现, 很多情况下, 客户端服务器在应用层也是 "一发一收"的. 意味着客户端给服务器说了 “How are you”, 服务器也会给客户端回一个 “Fine,thank you”;

面向字节流

创建一个 TCP 的 socket, 同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区

双方在进行通信的时候,调用write数据会先写入发送缓冲区。如果发送的字节数太长,会被拆分成多个TCP的数据包;如果字节太短,会先在缓冲区等待长度适当或者其他合适的机会再发送出去。

由于缓冲区的存在,TCP程序的读和写不需要一一匹配: 写 100 个字节数据时, 可以调用一次 write 写 100 个字节, 也可以调用 100 次write, 每次写一个字节。 读 100 个字节数据时, 也完全不需要考虑写的时候是怎么写的, 既可以一次read 100 个字节, 也可以一次 read 一个字节, 重复 100 次。

这样可能就会出现粘包问题

粘包问题

粘包问题中的 “包” , 是指的应用层的数据包

站在传输层的角度, TCP 是一个一个报文过来的. 按照序号排好序放在缓冲区中;站在应用层的角度, 看到的只是一串连续的字节数据。应用程序看到了这么一连串的字节数据, 就不知道从哪个部分开始到哪个部分, 是一个完整的应用层数据包。

如何避免粘包问题?明确两个包之间的边界。对于定长的包, 保证每次都按固定大小读取即可; 对于变长的包, 可以在包头的位置, 约定一个包总长度的字段, 从而就知道了包的结束位置,还可以在包和包之间使用明确的分隔符。

在UDP中,有一个16位的UDP长度以及定长报头,果还没有上层交付数据, UDP 的报文长度仍然在. 同时, UDP 是一个一个把数据交付给应用层. 就有很明确的数据边界。站在应用层的站在应用层的角度, 使用 UDP 的时候, 要么收到完整的 UDP 报文, 要么不收. 不会出现"半个"的情况

TCP异常

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

基于TCP的应用层协议

  • HTTP
  • HTTPS
  • SSH
  • Telnet
  • FTP
  • SMTP
  • 包括你自己写 TCP 程序时自定义的应用层协议

TCP/UDP 对比

  • TCP 用于可靠传输的情况, 应用于文件传输, 重要状态更新等场景
  • UDP 用于对高速传输和实时性要求较高的通信领域, 例如, 早期的 QQ, 视频传输等. 另外 UDP 可以用于广播
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-12-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • TCP协议
  • TCP协议格式
  • 确认应答(ACK)机制
  • 6个标记位
  • 超时重传机制
  • 连接管理机制
    • 三次握手建立连接
    • 四次挥手断开连接
    • 为什么重启的时候端口绑定失败
    • 为什么要三次握手
  • 理解CLOSE_WAIT 状态
  • 理解TIME_WAIT状态
  • 解决 TIME_WAIT 状态引起的 bind 失败的方法
  • 流量控制
  • 16位紧急指针
  • 滑动窗口
  • 拥塞控制
    • 慢开始
  • 延迟应答
  • 捎带应答
  • 面向字节流
  • 粘包问题
  • TCP异常
  • 基于TCP的应用层协议
  • TCP/UDP 对比
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档