【Hello Network】TCP协议相关理解

这篇具有很好参考价值的文章主要介绍了【Hello Network】TCP协议相关理解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

作者:@小萌新
专栏:@网络
作者简介:大二学生 希望能和大家一起进步
本篇博客简介:补充下对于TCP协议的各种理解

TCP相关试验

理解CLOSE_WAIT状态

我们要理解CLOSE_WAIT状态 首先要知道什么时候会出现CLOSE_WAIT状态

【Hello Network】TCP协议相关理解

当我们的客户端和服务器通信的时候 客户端调用close函数关闭其文件描述符 此时客户端在底层就会像服务器发送FIN报文 服务器给予FIN报文响应的ACK报文之后就会变成CLOSE_WAIT状态 而客户端会在第一次发送FIN报文的时候变为FIN_WAIT1状态 在收到ACK报文后变为FIN_WAIT2状态

【Hello Network】TCP协议相关理解

我们可以编写一个简单的TCP服务器来模拟出该现象 不过事实上我们只需要写出服务器的代码就可以了 客户端的代码我们可以使用一些现成的工具比如说TELNET

服务器代码如下

  #include <iostream>    
  #include <cstring>    
  #include <unistd.h>    
  #include <sys/socket.h>    
  #include <sys/types.h>    
  #include <arpa/inet.h>    
  #include <netinet/in.h>    
  #include <pthread.h>    
  using namespace std;    
      
  const int PORT = 8081;    
  const int NUM = 5;    
      
      
  void* RUN(void* arg)    
  {    
    pthread_detach(pthread_self());    
    int fd = *(int*)arg;    
    delete arg;                                                                                                                                                                                                                                                                                                                                                                                                                                              
    while(1)    
    {    
      cout << "sockfd" << fd << "is severing the client" << endl;    
      sleep(1);    
    }    
      
    return nullptr;    
  }    
      
      
      
      
  int main()    
  {    
    int listen_socket = socket(AF_INET , SOCK_STREAM , 0);    
    if (listen_socket < 0)    
    {    
      cerr << "listen_socket error" << endl;    
      exit(1);    
    }    
      
    struct sockaddr_in local;    
    memset(&local , '\0' , sizeof(local));    
    local.sin_family = AF_INET;    
    local.sin_port = htons(PORT);    
    local.sin_addr.s_addr =  INADDR_ANY;    
      
    if (bind(listen_socket , (struct sockaddr*)&local , sizeof(local)) < 0)    
    {    
      cerr << "bind error" << endl;    
      exit(2);    
    }    
      
    if(listen(listen_socket , NUM) < 0)    
    {    
      cerr << "listen error" << endl;    
      exit(3);    
    }    
      
    // peer     
    struct sockaddr_in peer;    
    memset(&peer , '\0' , sizeof(peer));    
    socklen_t len = sizeof(peer);    
      
    for(;;)    
    {    
      int sockfd = accept(listen_socket , (struct sockaddr*)&peer , &len);    
      if (sockfd < 0)    
      {    
        cerr << "accept error" << endl;    
        continue;    
      }    
      
      cout << "get a new linl:" << endl;    
      int* p = new int(sockfd);    
      
      pthread_t tid;    
      pthread_create(&tid , nullptr , RUN , (void*)p);    
    }    
      
      
    return 0;    
  }  

我们使用netstat查看 可以发现两条连接 一条是客户端的连接 一条是服务器的连接

【Hello Network】TCP协议相关理解

当我们现在将Telnet推出后客户端维护的连接状态会变为FIN_WAIT2 (因为它调用close函数到收到响应这段时间太短了 以至于我们捕捉不到FIN_WAIT1状态) 服务器的状态就会变为CLOSE_WAIT状态 因为我们在内部没有调用close函数 所以说服务器不会发起最后的第三次挥手
【Hello Network】TCP协议相关理解

理解TIME_WAIT状态

当客户端和服务器进行通信的时候 客户端调用close函数关闭对应的文件描述符 如果服务器收到后也调用close函数进行了关闭 那么此时双方将正常完成四次挥手

但是主动发起四次挥手的一方 在四次挥手之后并不会立即进入COLSED状态 而是会短暂的进入TIME_WAIT状态中等待一段时间

我们继续刚刚的试验 由于telnet推出后服务器没有调用close函数 所以说客户端的变为了TIME_WAIT 服务器的状态变为了CLOSE_WAIT

之后如果我们想要见到TIME_WAIT状态 我们就只需要方服务器调用close函数就可以 但是我们在服务器端的源码中并没有调用close函数 所以说我们要想办法关闭服务器打开的文件描述符

我们在前面一篇博客 TCP协议详解中说明了TCP连接异常断开的几种情况 其中我们说过 如果进程崩溃那么其实是和正常完成四次挥手没有区别的 而进程崩溃实际上就是操作系统给进行发送了信号 所以说我只需要我们使用键盘让OS向该进程发送信号就能够模拟服务器正常调用close函数的情况

再刨根问底一点 为什么操作系统向进程发送信号之后就相当于会调用close函数呢? 这里其实是因为文件描述符的生命周期是随进程的 而进程在被操作系统杀死后操作系统会回收资源 这些资源中肯定就包括文件描述符了

所以说此时我们先让telnet退出 紧接着让服务器退出 就能见到TIME_WAIT状态了

【Hello Network】TCP协议相关理解

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

我们在前面介绍过 四次挥手的时候主动发起挥手一方在四次挥手结束的时候会进入TIME_WAIT状态一段时间

如果服务器在有客户端的情况下主动退出了 就相当于是服务器先进行了四次挥手 那么服务器就将要进入TIME_WAIT一段时间

如果我们此时想要重新启动服务器绑定该端口号我们会发现我们绑定失败了

【Hello Network】TCP协议相关理解

这是因为在TIME_WAIT期间 这个连接并没有被完全释放 这也就意味着我们的端口是被占用着的 此时服务器想要继续绑定该端口号就只能等待TIME_WAIT时间结束

当时我们服务器崩溃时最重要的就是让服务器重新启动并且是绑定原来的端口号 (因为一般一些服务的端口号都是固定的 如果你修改了端口号的话在网络中别人可能就不知道你了)如果想要让服务器崩溃后在TIME_WAIT期间也能立马重新启动 需要让服务器在调用socket函数创建套接字后 继续调用setsockopt函数设置端口复用 这也是编写服务器代码时的推荐做法

setsockopt函数

setsockopt函数可以设置端口复用 该函数的函数原型如下:

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

返回值说明:

  • 设置成功返回0 失败返回-1 同时错误码会被设置

参数说明:

  • sockfd:需要设置的套接字对应的文件描述符
  • level:被设置选项的层次 比如在套接字层设置选项对应就是SOL_SOCKET
  • optname:需要设置的选项 该选项的可取值与设置的level参数有关
  • optval:指向存放选项待设置的新值的指针
  • optlen:待设置的新值的长度

我们这里要设置的就是监听套接字 将监听套接字在套接字层设置端口复用选项SO_REUSEADDR 该选项设置为非零值表示开启端口复用

int opt = 1;
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

【Hello Network】TCP协议相关理解

当我们主动发送信号让服务器崩溃之后我们立刻再次启动服务器 我们发现此时就不会出现bind error错误了

理解listen的第二个参数

在我们编写TCP套接字服务器的时候 在进行了套接字的创建和绑定之后需要调用listen函数将服务器变为监听状态 在这之后我们就能使用accept函数建立连接了

我们在调用listen函数的时候需要输入两个参数 第一个参数是我们要设置为监听状态的套接字 第二个参数是一个数字 它表示服务器可以建立的最大连接数

下面我们会通过一个试验来试验下listen的第二个参数是不是这个意思 试验步骤如下 :

  • 首先编写tcp套接字的服务器代码 关于我们这次的代码 我们只需要创建套接字 绑定 监听但是服务器初始化之后我们不调用accept函数建立连接
  • 为了方便验证 我们这里将listen的第二个参数设置为2

服务器代码如下

#include <iostream>    
using namespace std;    
#include <cstring>    
#include <unistd.h>    
#include <sys/types.h>    
#include <sys/socket.h>    
#include <arpa/inet.h>    
#include <netinet/in.h>    
#include <pthread.h>    
using namespace std;    
    
    
int main()    
{    
  // create socket     
  int listen_sockfd = socket(AF_INET , SOCK_STREAM , 0);    
  if (listen_sockfd < 0 )    
  {    
    cerr << "socket error" << endl;    
    exit(1);    
  }    
    
  int opt = 1;    
  setsockopt(listen_sockfd , SOL_SOCKET , SO_REUSEADDR , &opt , sizeof(opt));    
    
  // bind     
  struct sockaddr_in local;    
  memset(&local , '\0' , sizeof(local));    
  local.sin_family = AF_INET;    
  local.sin_port = htons(8081);    
  local.sin_addr.s_addr = INADDR_ANY;    
    
  if (bind(listen_sockfd , (struct sockaddr*)&local , sizeof(local)) < 0 )    
  {    
    cerr << "bind error" << endl;    
    exit(2);    
  }    
                                                                                                                                                                                                                                                                                                                                                                    
  // listen     
  listen(listen_sockfd , 2);    
    
  for(;;)    
  {    
    ;    
  }    
  return 0;    
}  

在运行./sever服务之后 我们可以使用 netstat -nlpt命令 来查看处于监听状态的进程

【Hello Network】TCP协议相关理解
接下来使用Postman向我们的服务器发送一个请求

关于怎么使用Postman大家可以参考这个视频

如何使用Postman

【Hello Network】TCP协议相关理解

说明: 需要说明的是 我们这里之所以不选用浏览器(浏览器的就是基于TCP协议的) 是因为浏览器如果没有收到服务端的响应可能会重复发送 这个现象在我们介绍应用层的HTTP协议的时候有涉及

我们通过Postman发起请求之后可以使用下面的命令查看建立完毕的连接

netstat -npt | head -2 && netstat -npt | grep 8081

【Hello Network】TCP协议相关理解

我们可以发现该连接现在处于ESTABLISHED状态 接下来我们继续时候Postman发送第二 三个请求

【Hello Network】TCP协议相关理解

由于我们的服务器并没有调用accept函数获取已经建立好的连接 所以说服务器已经建立好的连接是三个

我们继续尝试下使用Postman发送第四个请求

我们查看当前服务器的网络连接之后发现了这样子的现象

【Hello Network】TCP协议相关理解
此时我们并没有增加ESTABLISHED状态了 而是增加了一个SYN_RECV状态

而我们回看三次握手的场景

【Hello Network】TCP协议相关理解

我们可以发现SYN_RECV状态出现在客户端给服务器发送建立连接请求但是服务器没有响应的时候

也就是说刚刚的Postman发送请求之后 我们服务器并没有回应客户端的请求

过一段时间之后我们继续服务建立连接的状态

【Hello Network】TCP协议相关理解
我们可以发现在一段时间过后三次握手失败 该SYN_RECV连接就被自动释放了

而在Postman上表现为这样子

【Hello Network】TCP协议相关理解

总结下上面的试验现象

  • 不论有多少的客户端请求连接 最终在我们的服务器端只会有三个连接建立成功
  • 当发来第四个SYN请求的时候 服务只是收到了却不做响应 等待三次握手失败后自动释放连接

listen的第二个参数

我们直接对于上面的现象给出解释

实际上我们在进行TCP连接管理的时候会用到两个连接队列

  • 全连接队列(accept队列) 全连接队列用于保存处于ESTABLISHED状态 但没有被上层调用accept取走的连接
  • 半连接队列 半连接队列用于保存处于SYN_SENT和SYN_RCVD状态的连接 也就是还未完成三次握手的连接

我们在这里谈及Listen第二个参数的原因是 在linux操作系统中 全连接队列的长度就等于Listen的第二个参数+1 所以说我们服务器的Listen参数设置为2 此时服务器的全连接队列长度就为3 也就是说此时能够建立ESTABLISHED状态的连接就为3个

而当全连接队列满了之后服务器此时就不能够建立ESTABLISH连接了 此时如果客户端请求建立连接那么服务器不会进行SYN+ACK响应 而是会将该连接放在半连接队列中 状态变为SYN_RECV 在一段事件后如果三次握手还没有成功则会自动释放连接

此外如果半连接队列也满了 服务器会自动拒绝所有连接请求

如果被accept了全连接队列减少一个连接吗?

如果全连接中的队列被accept之后全连接队列会减少的

我们可以把服务器想象成一个饭店 这个饭店可以提供128人吃饭(服务器可以接收的连接数默认是128)

但是这个饭店太火爆了 几乎去的时候人都是满的 (参考下海底捞) 于是饭店老板便在饭店外面放了X+1个椅子(X为Listen的第二个参数 ) 坐在饭店椅子上的人就可以认为他们正处于EXTABLISHED状态 当饭店里面有人吃饭完饭店服务员就是使用accept函数让他们进来吃饭 此时我们就可以认为全连接队列少了一个连接 (此时在旁边站着的 也就是半连接队列的人就可以坐上椅子 变为EXTABLISHED状态)

为什么底层要维护连接队列?

其实我们在服务器压力较大的时候才能看出来连接队列的作用 我们上面的例子就能很好的说明原因 如果饭店没有满员的话服务员完全可以直接使用accept函数让人进去吃饭 没有必要让别人坐在外面的椅子上了

事实上 在服务器运行的时候我们就预先在服务器上面预先创建了多个线程 当主线程从底层accept上来连接之后这些连接就会交由其他线程处理

在服务器不繁忙的情况下连接一旦在底层建立好就会交由我们的服务线程处理了

可是当服务器繁忙的时候如果没有连接队列 我们的服务器就会拒绝其他的连接请求

如果拒绝之后我们的线程有处理完毕手上的连接了 但是此时没有任何新的连接请求发过来 那么此时该线程就会处于闲置状态

所以说连接队列的存在是必要的 它能够保证我们的服务器满负荷工作

连接队列长度问题

全连接队列的长度我们一般设置为5

事实上全连接的队列的长度不能太长也不能太短

如果说全连接的队列长度太长

  • 这就意味着位于队列尾部的连接需要较长的时间才能享受到服务 此时客户端的请求需要较长时间才能收到响应
  • 服务器维护这些连接也需要成本 如果能够维护很长的连接不如直接扩建“饭店”了

当然服务器全连接的长度也不能太短 比如说如果全连接队列的长度只有一 那么在连接数很大的情况下和没有连接队列没什么两样

全连接队列的长度

全连接队列的长度由两个值决定

  • 用户层调用listen时传入的第二个参数backlog
  • 系统变量net.core.somaxconn 默认值为128

事实上我们全连接队列的长度就等于这两个队列长度的较小值+1

SYN洪水攻击

在这个小节中我们会讲解SYN洪水攻击是什么以及应用层和传输层防范SYN洪水攻击的方式

正常三次握手方式

【Hello Network】TCP协议相关理解

如上图所示

  • 首先客户端会向服务器发送SYN连接请求 服务器收到之后会响应SYN+ACK给客户端并且将该连接放入半连接队列中
  • 此时如果客户端接收到了服务器发送的SYN+ACK报文并回复ACK给服务器 那么服务器就会将该连接从半连接队列中放到全连接队列中
  • 此时上层就可以通过调用accept函数 从全连接队列当中获取建立好的连接了

SYN洪水攻击

【Hello Network】TCP协议相关理解

异常情况:

  • 客户端在发起连接建立请求后突然死机或掉线 那么服务器发出的SYN+ACK就得不到对应的ACK应答
  • 这时候服务器由于收不到对方的ACK就会触发超时重传机制 如果经过多次重发之后还是收不到ACK应答此时服务器就会关闭这个连接 我们将该时间称为SYN timeout
  • 在SYN timeout时间内这个连接一直维护在半连接队列中

此时服务器虽然需要短暂维护这些异常连接 但这种情况毕竟是少数 不会对服务器造成太大影响

但是如果有用户恶意大量模拟这些情况

  • 此时服务器就需要一个非常大的半连接队列 并且实际上这些连接都是无用的
  • 当半连接队列被占满之后 新来的连接都会被服务器直接拒绝
  • 我们把这种发送大量的SYN 但是却不对服务器的 SYN+ACK请求进行响应的行为 叫做SYN洪水攻击

如何防范SYN洪水攻击呢?

防范SYN洪水攻击的时候肯定不光要考虑传输层 还要考虑应用层

应用层:

  • 我们应用层可以建立黑名单机制 如果一个ip在短时间内发送了大量的SYN请求我们就可以将这个ip封禁 对于此ip发送的SYN请求一律置之不理

传输层:

  • 增大半连接队列长度 但是其实这个方案能够起到的效果很少 因为都说了是SYN洪水肯定 肯定能够发送大量的SYN请求如果我们增大长度对方只需要发送更多的SYN请求就好了
  • 减少超时重发的次数 同样的这个也是治标不治本的做法

传输层最有效的方式应该是引入syncookie机制 当服务器收到一个连接建立请求后 会根据这个SYN包计算出一个cookie值 将其作为将要返回的SYN+ACK包的初始序号 然后将这个连接放到一个暂存队列当中

【Hello Network】TCP协议相关理解

当服务器收到客户端的ACK响应时 会提取出当中的cookie值进行对比 对比成功则说明是一个正常连接 此时该连接就会从暂存队列当中移到全连接队列供上层读取

引入了syncookie机制的好处:

  • 引入了syncookie机制之后 这些异常连接就不会堆积到半连接队列中了 而是会放在暂存队列中
  • 对于正常的SYN请求 一般会立刻对于服务器的SYN+ACK进行ACK应答 所以说会很快建立正常连接成功
  • 而异常连接或者说是SYN洪水攻击 不会对于服务器的SYN+ACK请求进行ACK应答 所以说异常连接都会堆积到队列中

TCP和UDP

TCP和UDP协议对比

在对比他们之间我们首先要知道TCP和UDP协议是什么

TCP协议

TCP协议叫做传输控制协议(Transmission Control Protocol)它是一种面向连接的 可靠的 基于字节流的传输层协议

TCP协议是面向连接的 如果两台主机之间想要进行通信就需要先建立连接 连接建立成功之后进行数据传输

其次TCP协议是保证可靠的协议 如果数据在传输的过程中出现了丢包 乱序等问题 TCP协议都有自己的相解决方案

UDP协议

UDP协议叫做用户数据报协议(User Datagram Protocol) 它是一种无需连接的 不可靠的 基于数据包的传输层协议

使用UDP协议的时候无需建立连接 如果两台主机之间想要进行通信 那么直接将数据发送给对端主机就可以了

上面的描述其实也就概括了UDP协议是不可靠的 如果数据在传输的过程中出现了丢包 乱序等问题 UDP协议都不知道 更别说解决了

TCP/UDP对比

TCP协议是可靠的 UDP协议是不可靠的 但是这并不意味着TCP协议比UDP协议要好 UDP协议不可靠也就意味着UDP协议足够简单 TCP可靠也就意味着TCP所做的准备工作也就越多 但是需要注意的是TCP所做的准备工作越多并不意味着TCP越慢 事实上它们的效率是差不多的

  • TCP常用于需要可靠传输的情况 比如说文件传输 重要更新等场景
  • UDP常用于对高速传输和实时性较高的通信领域 比如说QQ 视频传输等 此外UDP也可以用于广播

UDP协议和TCP协议不存在孰优孰劣这种说法 只有在某个场景中使用哪个协议更合适

用UDP实现可靠传输(经典面试题)

这里有一道经典的面试题:

使用UDP来实现可靠传输

我们提到传输层的可靠协议就能想到TCP协议了 所以说这道面试题的本质其实是在考查你对于TCP协议的了解程度

当我们知道了这道面试题的时候我们首先应该问面试官具体的应用场景 之后根据场景的需要往TCP协议上套

比如说:文章来源地址https://www.toymoban.com/news/detail-435860.html

  • 引入序列号 保证数据按序到达
  • 引入确认应答 确保对端接收到了数据
  • 引入超时重传 如果隔一段时间没有应答 就进行数据重发
  • … …

到了这里,关于【Hello Network】TCP协议相关理解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Hello Network】DNS协议 NAT技术 代理服务器

    本篇博客简介:介绍DNS协议 NAT技术和代理服务器 DNS是一整套从域名映射到IP的系统 为什么要有域名 其实作为我们程序员来说 使用域名还是IP地址是无所谓的 但是站在商业公司和用户的角度就不这么认为了 商业公司希望用户能够快速的记住自己公司的网址 而用户也希望自己

    2024年02月11日
    浏览(38)
  • TCP/IP协议专栏——分片报文详解——网络入门和工程维护必看

    一个链路层数据报能承载的最大数据量称为最大传送单元(MTU)。 因为IP数据报(IP头+DATA)被封装在链路层数据报中,故链路层的MTU严格地限制着IP数据报的长度, 而且在IP数据报的源与目的地路径上的各段链路可能使用不同的链路层协议,有不同的MTU. 例如,以太网的MTU为15

    2024年01月19日
    浏览(59)
  • TCP/IP协议专栏——以太帧结构 详解——网络入门和工程维护必看

    以太网帧发送数据时都是从8个字节的前导码开始的。前导码是1和0的交互。 在以太网中,数据通信的基本单位是 以太网帧 ( frame ),由 头部 ( header )、数据 ( data )以及 校验和 ( checksum )三部分构成: 头部 以太网帧头部包含 3 个字段,依次是: 1、目的地址:长度是 6 字节,用

    2023年04月18日
    浏览(41)
  • TCP协议的相关特性

    目录 TCP特点概要 TCP协议段格式 TCP原理         确认应答          超时重传         连接管理(三次握手,四次挥手)                 三次握手                  四次挥手         流水线传输          滑动窗口         滑动窗口ACK丢失         滑动窗口数据报丢失

    2024年02月08日
    浏览(30)
  • 运输层(TCP运输协议相关)

    网络层、数据链路层、物理层解决了网络中主机和主机之间的通信; 运输层 解决网络中 不同主机上的进程之间的通信 ,这种服务是 端到端 的; 进程通过唯一的端口号进行标识 ; 根据应用需求不同,运输层主要向应用层提供两种不同的运输协议: 1)面向连接的TCP;2)无

    2024年02月16日
    浏览(41)
  • 理解TCP/IP协议

    在计算机网络与信息通讯领域里,人们经常提及 “协议” 一词。互联网中常用的协议有HTTP、TCP、IP等。 简单来说,协议就是计算机与计算机之间通过网络通信时,事先达成的一种 “约定”。这种“约定”使不同厂商的设备、不同的CPU以及不同操作系统组成的计算机之间,只

    2024年01月18日
    浏览(47)
  • 如何理解TCP/IP协议?

    TCP/IP, 传输控制协议 / 网际协议 ,是指能够在多个不同网络间实现信息传输的协议簇 TCP(传输控制协议) 一种面向连接的、可靠的、基于字节流的传输层通信协议 IP(网际协议) 用于封包交换数据网络的协议 TCP/IP协议不仅仅指的是 TCP  和 IP 两个协议,而是指一个由 FTP

    2024年02月08日
    浏览(41)
  • 全面深入理解TCP协议(超详细)

    目录 前言    TCP协议格式 确认应答机制(ACK) 理解可靠性 确认应答的机制 16位窗口大小 缓冲区 流量控制 6个标志位  16位紧急指针 ★三次握手,四次挥手 如何理解连接 如何理解三次握手 如何理解四次挥手 TCP可靠性机制 确认应答机制(补充) ​编辑 超时重传机制 流量控制机

    2024年02月07日
    浏览(44)
  • 深入理解TCP/IP协议栈及其应用

            TCP/IP协议栈是当今互联网世界中广泛应用的网络通信协议,它将数据分为若干个分组,通过网络传输到目的地,确保数据的可靠传输。对于计算机科学专业的学生以及从事网络通信相关行业的从业者而言,深入理解TCP/IP协议栈及其应用是必不可少的技能之一。  

    2024年02月14日
    浏览(45)
  • 理解TCP:传输控制协议的特点和机制

    本文详细介绍了TCP(传输控制协议)的特点和机制,包括其面向连接、可靠、有序、无丢失和无重复的特性,以及其确认机制、重传机制、排序机制和流控机制。

    2024年04月09日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包