最新资讯

  • 【仿Mudou库one thread per loop式并发服务器实现】项目介绍+前置技术知识点

【仿Mudou库one thread per loop式并发服务器实现】项目介绍+前置技术知识点

2026-01-28 18:52:04 栏目:最新资讯 2 阅读

HTTP协议模块实现

  • 1. 项目实现的目标
  • 2. 项目储备知识
    • 2.1 HTTP服务器
    • 2.2 Reactor模型
  • 3. 功能模块划分
    • 3.1 SERVER模块
      • 3.1.1 Buffer模块
      • 3.1.2 Socket模块
      • 3.1.3 Channel模块
      • 3.1.4 Poller模块
      • 3.1.5 EventLoop模块
      • 3.1.6 Connection模块
      • 3.1.7 7. Acceptor模块
      • 3.1.8 TimerQueue模块
      • 3.1.9 通信模块总结
      • 3.1.10 TcpServer模块
    • 3.2 HTTP协议模块
      • 3.2.1 Util模块
      • 3.2.2 HttpRequest模块
      • 3.2.3 HttpResponse模块
      • 3.2.3 HttpContext模块
      • 3.2.4 HttpServer模块
  • 4. 项目前置知识技术点
    • 4.1 C++11中的bind
    • 4.2 简单的秒级定时任务实现
    • 4.3 正则库的简单使用
    • 4.5 通用类型any类型的实现

点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃

1. 项目实现的目标

仿muduo库One Thread One Loop式主从Reactor模型实现高并发服务器这个项目是通过咱们实现的高并发服务器组件,可以简洁快速的完成一个高性能的服务器搭建。并且,通过组件内提供的不同应用层协议支持,也可以快速完成一个高性能应用服务器的搭建。在这里,要明确的是咱们要实现的是一个高并发服务器组件,因此当前的项目中并不包含实际的业务内容。

2. 项目储备知识

2.1 HTTP服务器

HTTP(Hyper Text Transfer Protocol),超文本传输协议是应用层协议,是一种简单的请求-响应协议(客户端根据自己的需要向服务器发送请求,服务器针对请求提供服务,完毕后通信结束)。

协议细节在linux网络部分有详细介绍,这里不在赘述。但是需要注意的是HTTP协议是一个运行在TCP协议之上的应用层协议,这一点本质上是告诉我们,HTTP服务器其实就是个TCP服务器,只不过在应用层基于HTTP协议格式进⾏数据的组织和解析来明确客户端的请求并完成业务处理。

因此实现HTTP服务器简单理解,只需要以下几步即可

  1. 搭建一个TCP服务器,接收客户端请求。
  2. 以HTTP协议格式进⾏解析请求数据,明确客户端目的。
  3. 明确客户端请求目的后提供对应服务。
  4. 将服务结果以HTTP协议格式进行组织,发送给客户端

实现一个HTTP服务器很简单,但是实现一个高性能的服务器并不简单,这个项目中将讲解基于Reactor模式的高性能服务器实现。

当然准确来说,因为我们要实现的服务器本身并不存在业务,咱们要实现的应该算是一个高性能服务器基础库,是一个基础组件。

2.2 Reactor模型

Reactor 模式,是指通过一个或多个输入同时传递给服务器进行请求处理时的事件驱动处理模式。

服务端程序处理传入多路请求,并将它们同步分派给请求对应的处理线程,Reactor 模式也叫Dispatcher 模式。

简单理解就是使用 I/O多路复用 统一监听事件,收到事件后分发给处理进程或线程,是编写高性能网络服务器的必备技术之一。

分类

单Reactor单线程:单I/O多路复用+业务处理

  1. 通过IO多路复用模型进行客户端请求监控
  2. 触发事件后,进行事件处理
    • a. 如果是新建连接请求,则获取新建连接,并添加至多路复用模型进行事件监控。
    • b. 如果是数据通信请求,则进行对应数据处理(接收数据,处理数据,发送响应)。

优点:所有操作均在同⼀线程中完成,思想流程较为简单,不涉及进程/线程间通信及资源争抢问题。

缺点:无法有效利用CPU多核资源,很容易达到性能瓶颈。
适用场景:适用于客户端数量较少,且处理速度较为快速的场景。(处理较慢或活跃连接较多,会导致串行处理的情况下,后处理的连接长时间无法得到响应)。

单Reactor多线程:单I/O多路复用+线程池(业务处理)

  1. Reactor线程通过I/O多路复用模型进行客户端请求监控
  2. 触发事件后,进行事件处理
    • a. 如果是新建连接请求,则获取新建连接,并添加至多路复用模型进行事件监控。
    • b. 如果是数据通信请求,则接收数据后分发给Worker线程池进行业务处理。
    • c. 工作线程处理完毕后,将响应交给Reactor线程进行数据响应

优点:充分利用CPU多核资源

缺点:多线程间的数据共享访问控制较为复杂,单个Reactor 承担所有事件的监和响应,在单线程中运行,高并发场景下容易成为性能瓶颈。

多Reactor多线程:多I/O多路复用+线程池(业务处理)

  1. 在主Reactor中处理新连接请求事件,有新连接到来则分发到子Reactor中监控
  2. 在子Reactor中进行客户端通信监控,有事件触发,则接收数据分发给Worker线程池
  3. Worker线程池分配独立的线程进行具体的业务处理
    • a. 工作线程处理完毕后,将响应交给子Reactor线程进行数据响应

优点:充分利用CPU多核资源,主从Reactor各司其职

咱们要实现的是主从Reactor模型服务器,也就是主Reactor线程仅仅监控监听描述符,获取新建连接,保证获取新连接的高效性,提高服务器的并发性能。

主Reactor获取到新连接后分发给子Reactor进行通信事件监控。而子Reactor线程监控各自的描述符的读写事件进行数据读写以及业务处理。

One Thread One Loop的思想就是把所有的操作都放到一个线程中进行,一个线程对应一个事件处理的循环。

当前实现中,因为并不确定组件使用者的使用意向,因此并不提供业务层工作线程池的实现,只实现主从Reactor,而Worker工作线程池,可由组件库的使用者的需要自行决定是否使用和实现。

3. 功能模块划分

基于以上的理解,我们要实现的是一个带有协议支持的Reactor模型高性能服务器,因此将整个项目的实现划分为两个大的模块:

  • SERVER模块:实现Reactor模型的TCP服务器;
  • 协议模块:对当前的Reactor模型服务器提供应用层协议支持

3.1 SERVER模块

SERVER模块就是对所有的连接以及线程进行管理,让它们各司其职,在合适的时候做合适的事,最终 完成高性能服务器组件的实现。

而具体的管理也分为三个方面:

  • 监听连接管理:对监听连接进行管理。
  • 通信连接管理:对通信连接进行管理。
  • 超时连接管理:对超时连接进行管理。

基于以上的管理思想,将这个模块进行细致的划分又可以划分为多个子模

3.1.1 Buffer模块

Buffer模块是一个缓冲区模块,用于实现通信中用户态的接收缓冲区和发送缓冲区功能。

3.1.2 Socket模块

Socket模块是对套接字操作封装的一个模块,主要实现的socket的各项操作。

3.1.3 Channel模块

Channel模块是对一个描述符需要进行的IO事件管理的模块,实现对描述符可读,可写,错误…事件的管理操作,以及Poller模块对描述符进行IO事件监控就绪后,根据不同的事件,回调不同的处理函数功能。

3.1.4 Poller模块

Poller模块是对epoll进行封装的一个模块,主要实现epoll的IO事件添加,修改,移除,获取活跃连接功能。

3.1.5 EventLoop模块

EventLoop模块可以理解就是我们上边所说的Reactor模块,它是对Poller模块,TimerQueue模块,进行所有描述符的事件监控。

EventLoop模块是一个EventLoop对象对应一个线程的模块,线程内部的目的就是运行EventLoop的启动函数。

EventLoop模块为了保证整个服务器的线程安全问题,因此要求使用者对于Connection的所有操作一定要在其对应的EventLoop线程内完成,不能在其他线程中进行(比如组件使用者使用Connection发送数据,以及关闭连接这种操作)。

EventLoop模块保证自己内部所监控的所有描述符,都要是活跃连接,非活跃连接就要及时释放避免资源浪费。

  • EventLoop模块内部包含有一个eventfd:eventfd其实就是linux内核提供的一个事件fd,专门用于事件通知。
  • EventLoop模块内部包含有一个Poller对象:用于进行描述符的IO事件监控。
  • EventLoop模块内部包含有一个TimerQueue对象:用于进行定时任务的管理。
  • EventLoop模块内部包含有一个PendingTask队列:组件使用者将对Connection进行的所有操作,都加入到任务队列中,由EventLoop模块进行管理,并在EventLoop对应的线程中进行执行。
  • 每一个Connection对象都会绑定到一个EventLoop上,这样能保证对这个连接的所有操作都是在一个线程中完成的

具体操作流程:

  1. 通过Poller模块对当前模块管理内的所有描述符进行IO事件监控,有描述符事件就绪后,通过描述符对应的Channel进行事件处理。
  2. 所有就绪的描述符IO事件处理完毕后,对任务队列中的所有操作顺序进行执行。
  3. 由于epoll的事件监控,有可能会因为没有事件到来而持续阻塞,导致任务队列中的任务不能及时得到执行,因此创建了eventfd,添加到Poller的事件监控中,用于实现每次向任务队列添加任务的时候,通过向eventfd写⼊数据来唤醒epoll的阻塞。

3.1.6 Connection模块

Connection模块是对Buffer模块,Socket模块,Channel模块的一个整体封装,实现了对一个通信套接字的整体的管理,每一个进行数据通信的套接字(也就是accept获取到的新连接)都会使用Connection进行管理。

  • Connection模块内部包含有三个由组件使用者传入的回调函数:连接建立完成的回调,任意事件的回调,新数据来到的回调,关闭连接的回调。

  • Connection模块内部包含有两个组件使用者提供的接口:数据发送接口,连接关闭接口

  • Connection模块内部包含有两个用户态缓冲区:用户态接收缓冲区,用户态发送缓冲区

  • Connection模块内部包含有一个Socket对象:完成描述符面向系统的IO操作

  • Connection模块内部包含有一个Channel对象:完成描述符IO事件就绪的处理

具体处理流程如下:

  1. 实现向Channel提供可读,可写,错误等不同事件的IO事件回调函数,然后将Channel和对应的描述符添加到Poller事件监控中。
  2. 当描述符在Poller模块中就绪了IO可读事件,则调用描述符对应Channel中保存的读事件处理函数,进行数据读取,将socket接收缓冲区全部读取到Connection管理的用户态接收缓冲区中。然后调用由组件使用者传入的新数据到来回调函数进行处理。
  3. 组件使者者进行数据的业务处理完毕后,通过Connection向使用者提供的数据发送接⼝,将数据写入Connection的发送缓冲区中。
  4. 启动描述符在Poll模块中的IO写事件监控,就绪后,调用Channel中保存的写事件处理函数,将发送缓冲区中的数据通过Socket进行面向系统的实际数据发送。

3.1.7 7. Acceptor模块

Acceptor模块是对Socket模块,Channel模块的一个整体封装,实现了对一个监听套接字的整体的管理。

  • Acceptor模块内部包含有一个Socket对象:实现监听套接字的操作
  • Acceptor模块内部包含有一个Channel对象:实现监听套接字IO事件就绪的处理

具体处理流程如下:

  1. 实现向Channel提供可读事件的IO事件处理回调函数,函数的功能其实也就是获取新连接
  2. 为新连接构建一个Connection对象出来

3.1.8 TimerQueue模块

TimerQueue模块是实现固定时间定时任务的模块,可以理解就是要给定时任务管理器,向定时任务管理器中添加一个任务,任务将在固定时间后被执行,同时也可以通过刷新定时任务来延迟任务的执行。

这个模块主要是对Connection对象的生命周期管理,对非活跃连接进行超时后的释放功能。

TimerQueue模块内部包含有一个timerfd:linux系统提供的定时器。
TimerQueue模块内部包含有一个Channel对象:实现对timerfd的IO时间就绪回调处理。

3.1.9 通信模块总结

通过Accpect模块创建一个监听套接字listsock,并给listsock对应的channel设置读事件回调,然后添加到主EventLoop线程中的Poller添加读事件监控,当读事件就绪后,通过Accpect模块给listsock设置读回调函数获取新连接,获取新连接之后,然后在调用主EventLoop线程给它设置的新连接获取之后的回调函数,创建一个Connection对象,它内部也有对应的Channel对象,创建Connection对象后它构造函数内部对应的Channel对象已经设置好对应的读、写、错误、挂断事件的回调,所以就不用管。然后设置给这个Connection对象分配给某个从EventLoop线程,并且给它设置调用者设置的连接建立好后的回调、新数据来了后的回调,任意事件的回调、关闭连接的回调。再看是否启动非活跃连接,然后给Connection启动读事件监控。

如果该Connection对应的fd读事件就绪后,就可以通过管理该Connection的从EventLoop中的Poller模块中拿到,然后调用给它内部Channel设置读事件回调,从Connection中的Socket模块读取数据到Buffer模块接收缓存区,有新数据就调用设置好的新数据来了之后的回调函数。如果业务处理好之后,把返回的数据写到Buffer输出缓存区,然后将该fd写事件的关心添加到管理它的从EventLoop中的Poller中,等下次写事件就绪之后,就调用对应Channel写事件回调将Buffer输出缓存区的数据通过Connection中的socket发送给对方。

此时就完成了一次往返通信的过程。

3.1.10 TcpServer模块

这个模块是一个整体Tcp服务器模块的封装,内部封装了Acceptor模块,EventLoopThreadPool模块。

  • TcpServer中包含有一个EventLoop对象:以备在超轻量使用场景中不需要EventLoop线程池,只需要在主线程中完成所有操作的情况。

  • TcpServer模块内部包含有一个EventLoopThreadPool对象:其实就是EventLoop线程池,也就是子Reactor线程池

  • TcpServer模块内部包含有一个Acceptor对象:一个TcpServer服务器,必然对应有⼀个监听套接字,能够完成获取客⼾端新连接,并处理的任务。

  • TcpServer模块内部包含有一个std::shared_ptr的hash表:保存了所有的新建连接对应的Connection,注意,所有的Connection使用shared_ptr进行管理,这样能够保证在hash表中删除了Connection信息后,在shared_ptr计数器为0的情况下完成对Connection资源的释放操作

具体操作流程如下:

  1. 在实例化TcpServer对象过程中,完成BaseLoop(主Reactor)的设置,Acceptor对象的实例化,以及EventLoop线程池的实例化,以及std::shared_ptr的hash表的实例化。
  2. 为Acceptor对象设置回调函数:获取到新连接后,为新连接构建Connection对象,设置Connection的各项回调,并使用shared_ptr进行管理,并添加到hash表中进行管理,并为Connection选择一个EventLoop线程,为Connection添加一个定时销毁任务,为Connection添加事件监控,
  3. 启动BaseLoop进行监听套接字事件监控。

3.2 HTTP协议模块

HTTP协议模块用于对高并发服务器模块进行协议⽀持,基于提供的协议支持能够更方便的完成指定协议服务器的搭建。

而HTTP协议支持模块的实现,可以细分为以下几个模块

3.2.1 Util模块

这个模块是一个工具模块,主要提供HTTP协议模块所用到的一些工具函数,比如url编解码,文件读写…等。

3.2.2 HttpRequest模块

这个模块是HTTP请求数据模块,用于保存HTTP请求数据被解析后的各项请求元素信息。

3.2.3 HttpResponse模块

这个模块是HTTP响应数据模块,用于业务处理后设置并保存HTTP响应数据的的各项元素信息,最终会被按照HTTP协议响应格式组织成为响应信息发送给客户端。

3.2.3 HttpContext模块

这个模块是一个HTTP请求接收的上下文模块,主要是为了防止在一次接收的数据中,不是一个完整的HTTP请求,则解析过程并未完成,无法进行完整的请求处理,需要在下次接收到新数据后继续根据上下文进行解析,最终得到⼀个HttpRequest请求信息对象,因此在请求数据的接收以及解析部分需要一个上下文来进行控制接收和处理节奏。

3.2.4 HttpServer模块

这个模块是最终给组件使用者提供的HTTP服务器模块了,用于以简单的接口实现HTTP服务器的搭建。

HttpServer模块内部包含有⼀个TcpServer对象:TcpServer对象实现服务器的搭建

HttpServer模块内部包含有两个提供给TcpServer对象的接口:连接建立成功设置上下文接口,数据处理接口。

HttpServer模块内部包含有一个hash-map表存储请求与处理函数的映射表:组件使用者向HttpServer设置哪些请求应该使用哪些函数进行处理,等TcpServer收到对应的请求就会使用对应的函数进行处理。

4. 项目前置知识技术点

4.1 C++11中的bind

bind (Fn&& fn, Args&&... args)

我们可以将bind接口看作是一个通用的函数适配器,它接受一个函数对象,以及函数的各项参数,然后返回一个新的函数对象,但是这个函数对象的参数已经被绑定为设置的参数。运行的时候相当于总是调用传入固定参数的原函数。

但是如果进行绑定的时候,给与的参数为 std::placeholders::_1, _2... 则相当于为新适配生成的函数对象的调用预留一个参数进行传递。

#include 
#include 
#include 
#include 

void print(const std::string &str, int num)
{
    std::cout << str << num << std::endl;
}

int main()
{
    using Task = std::function<void()>;
    std::vector<Task> arry;
    arry.push_back(std::bind(print, "hello", 10));
    arry.push_back(std::bind(print, "leihou", 20));
    arry.push_back(std::bind(print, "nihao", 30));

    for (auto &f:arry) {
        f();
    }
    return 0;
}

基于bind的作用,当我们在设计一些线程池,或者任务池的时候,就可以将将任务池中的任务设置为函数类型,函数的参数由添加任务者直接使用bind进行适配绑定设置,而任务池中的任务被处理,只需要取出一个个的函数进行执行即可。

这样做有个好处就是,这种任务池在设计的时候,不用考虑都有哪些任务处理方式了,处理函数该如何设计,有多少个什么样的参数,这些都不用考虑了,降低了代码之间的耦合度。

4.2 简单的秒级定时任务实现

在当前的高并发服务器中,我们不得不考虑一个问题,那就是连接的超时关闭问题。我们需要避免一个连接长时间不通信,但是也不关闭,空耗资源的情况。

这时候我们就需要一个定时任务,定时的将超时过期的连接进行释放。

Linux提供给我们的定时器

#include 
#include 
#include 
#include 

//linux下的定时器

//创建定时器
// int timerfd_create(int clockid, int flags);

//clockid:
//CLOCK_REALTIM   -- 以系统时间作为记时基准值(如果系统时间发生改变就会出问题)
//CLOCK_MONOTONIC -- 以系统启动时间进行递增的一个基准值(定时不会随着系统时间改变而改变)

//flags:对定时器读取操作是阻塞/非阻塞 0 默认阻塞操作
//linux下一切皆文件,定时器的操作跟文件操作没什么区别
//定时器定时原理,每隔一段时间(定时器的超时时间),系统就会给这个文件描述符对应的定时器写入一个8字节的数据
//没到时间就阻塞,到时间就可以读取成功了



//启动定时器
//int timerfd_settime(int fd, int flags,   
//        const struct itimerspec *new_value,     
//       struct itimerspec *_Nullable old_value);

//fd: timerfd_create返回值
// flags: 0-相对时间, 1-绝对时间;默认设置为0即可.
//  new: ⽤于设置定时器的新超时时间
//  old: ⽤于接收原来的超时时间

//  struct timespec {
//      time_t tv_sec; /* Seconds */  秒
//      long tv_nsec; /* Nanoseconds */ 纳秒
//  };
//
//  struct itimerspec {
//      struct timespec it_interval; /* 第⼀次之后的超时间隔时间 */
//      struct timespec it_value; /* 第⼀次超时时间 */
//  };



int main()
{
    int timefd = timerfd_create(CLOCK_MONOTONIC,0);
    if(timefd < 0)
    {
        std::cout<<"open file fail"<<std::endl;
        return -1;
    }


    struct itimerspec item;
    item.it_value.tv_sec = 1; 
    item.it_value.tv_nsec = 0; //第一次超时时间
    item.it_interval.tv_sec = 1;
    item.it_interval.tv_nsec = 0;//第⼀次之后的超时间隔时间


    timerfd_settime(timefd,0,&item,nullptr);

    //sleep(10);
    
    while(1)
    {
        uint64_t cnt = 0;
        int ret = read(timefd,&cnt,8);
        if(ret < 0)
        {
            std::cout<<"read fail"<<std::endl;
            break;
        }

        std::cout<<"超时了,距离上次超时次数: "<<cnt<<std::endl;
    }

    close(timefd);
    return 0;
}

上边例子,是一个定时器的使用示例,是每隔1s钟触发一次定时器超时。

基于这个例子,我们可以实现每隔n秒,检测一下哪些连接超时了,然后将超时的连接释放掉。

时间轮思想

上述的例子,存在一个很大的问题,每次超时都要将所有的连接遍历一遍,如果有上万个连接,效率无疑是较为低下的。

这时候大家就会想到,我们可以针对所有的连接,根据每个连接最近一次通信的系统时间建立一个小根堆,这样只需要每次针对堆顶部分的连接逐个释放,直到没有超时的连接为止,这样也可以大大提高处理的效率。

上述方法可以实现定时任务,但是这立给大家介绍另一种方案:时间轮

时间轮的思想来源于钟表,如果我们定了一个3点钟的闹铃,则当时针走到3的时候,就代表时间到了。

同样的道理,如果我们定义了一个数组,并且有一个指针,指向数组起始位置,这个指针每秒钟向后走动一步,走到哪里,则代表哪里的任务该被执行了,那么如果我们想要定一个3s后的任务,则只需要将任务添加到tick+3位置,则每秒中走一步,三秒钟后tick走到对应位置,这时候执行对应位置的任务即可。

但是,同一时间可能会有大批量的定时任务,因此我们可以给数组对应位置下拉一个数组,这样就可以在同一个时刻上添加多个定时任务了。

当然,上述操作也有一些缺陷,比如我们如果要定义一个60s后的任务,则需要将数组的元素个数设置为60才可以,如果设置一小时后的定时任务,则需要定义3600个元素的数组,这样无疑是比较麻烦的。

因此,可以采用多层级的时间轮,有秒针轮,分针轮,时针轮, 60

因为当前我们的应用中,倒是不用设计的这么麻烦,因为我们的定时任务通常设置的30s以内,所以简单的秒级时间轮就够用了。

但是,我们也得考虑一个问题,当前的设计是时间到了,则主动去执行定时任务,释放连接,那能不能在时间到了后,自动执行定时任务呢,这时候我们就想到一个操作—类的析构函数一个类的析构函数,在对象被释放时会自动被执行,那么我们如果将一个定时任务作为一个类的析构函数内的操作,则这个定时任务在对象被释放的时候就会执行。

但是仅仅为了这个目的,而设计一个额外的任务类,好像有些不划算,但是,这里我们又要考虑另一个问题,那就是假如有一个连接立成功了,我们给这个连接设置了一个30s后的定时销毁任务,但是在第10s的时候,这个连接进行了一次通信,那么我们应该时在第30s的时候关闭,还是第40s的时候关闭呢?无疑应该是第40s的时候。也就是说,这时候,我们需要让这个第30s的任务失效,但是我们该如何实现这个操作呢?

这里,我们就用到了智能指针shared_ptr,shared_ptr有个计数器,当计数为0的时候,才会真正释放一个对象,那么如果连接在第10s进行了一次通信,则我们继续向定时任务中,添加一个30s后(也就是第40s)的任务类对象的shared_ptr,则这时候两个任务shared_ptr计数为2,则第30s的定时任务被释放的时候,计数-1,变为1,并不为0,则并不会执行实际的析构函数,那么就相当于这个第30s的任务失效了,只有在第40s的时候,这个任务才会被真正释放。

#include 
#include 
#include 
#include 
#include 
#include 
#include 


using TaskFunc = std::function<void()>;
using ReleaseFunc  = std::function<void()>;

class TimerTask
{
private:
    uint64_t _id;   // 定时器任务对象ID
    uint32_t _timeout; //定时任务的超时时间
    bool _cancel;      // 定时任务是否取消,false不取消,true取消
    TaskFunc _task_cb;      // 定时器对象要执行的定时任务
    ReleaseFunc _release_cb;   /// 用于删除TimerWheel中保存的定时器对象信息

public:
    TimerTask(uint64_t id,uint32_t time,const TaskFunc& cb):_id(id),_timeout(time),_task_cb(cb),_cancel(false){}
    ~TimerTask()
    {
        if(_cancel == false)
        {
            _task_cb();
        }
        _release_cb();
    }
    uint32_t DelayTime()
    {
        return _timeout;
    }
    void SetRelease(const ReleaseFunc & rb)
    {
        _release_cb = rb;
    }
    void Cancel()
    {
        _cancel = true;
    }

};

using SharedPtr = std::shared_ptr<TimerTask>;
using WeakPtr = std::weak_ptr<TimerTask>;

class TimeWheel
{
private:
    int _tick; //滴答指针,当前的秒,走到哪里释放哪里,释放哪里,就相当于执行哪里的任务
    int _capacity; //表盘最大数量---其实就是最大延迟时间
    std::vector<std::vector<SharedPtr>> _wheel; //时间轮
    std::unordered_map<uint64_t,WeakPtr> _timers;//通过id找到任务对象,方便后续刷新任务
    //unordered_map第二个参数不能用shared_ptr,如果还用用shared_ptr去指向原始对象,那么就和之前用shared_ptr指向原始对象
    //而产生的引用计数根本就不是同一个引用计数,这样如果unordered_map第二个参数引用计数是1,而其他指向它引用计数是2
    //万一unordered_map第二个参数shared_ptr被释放了,这个原始对象就会被释放,这是由问题的.

    //释放时间轮上任务对象
    void RemoveTimer(uint64_t id)
    {
        auto it = _timers.find(id);
        if(it == _timers.end())
        {
            return;
        }
        _timers.erase(it);
    }

public:
    TimeWheel():_tick(0),_capacity(60),_wheel(_capacity){}
    ~TimeWheel(){}

    //设置定时任务
    void TimerAdd(uint64_t id,uint32_t delay,const TaskFunc& cb)
    {
        SharedPtr spt(new TimerTask(id,delay,cb));
        spt->SetRelease(std::bind(&TimeWheel::RemoveTimer,this,id));
        int pos = (_tick + delay) % _capacity;
        _wheel[pos].push_back(spt);
        _timers[id] = WeakPtr(spt);
    }
    
    //刷新/延迟定时任务
    void TimerRefresh(uint64_t id)
    {
        auto it = _timers.find(id);
        if(it == _timers.end())
        {
            return;
        }
        //spt是临时对象,出栈后会自动销毁,不会增加引用计数
        SharedPtr spt = it->second.lock();//lock获取weak_ptr管理的对象对应的shared_ptr
        int delaytime = spt->DelayTime();
        int pos = (_tick + delaytime) % _capacity;
        _wheel[pos].push_back(spt);
    }

    //这个函数应该每秒钟被执行一次,相当于秒针向后走了一步
    void RunTimerTask()
    {
        _tick = (_tick + 1) % _capacity;
        //清空指定位置的数组,就会把数组中保存的所有管理定时器对象的shared_ptr释放掉
        _wheel[_tick].clear();//执行任务,释放shared_ptr指针,当引用计数到0,调用Task析构,执行定时任务
    }

    //取消定时任务,等时间到了,释放任务对象,但是析构里面的超时任务不去执行
    void TimerCancel(uint64_t id)
    {
        auto it = _timers.find(id);
        if(it == _timers.end())
        {
            return;
        }
        //spt是临时对象,出栈后会自动销毁,不会增加引用计数
        SharedPtr spt = it->second.lock();
        if(spt) spt->Cancel();
    }

};



class Test {
    public:
        Test() {std::cout << "构造" << std::endl;}
        ~Test() {std::cout << "析构" << std::endl;}
};

void DelTest(Test *t) {
    delete t;
}

int main()
{
    TimeWheel tw;

    Test *t = new Test();

    tw.TimerAdd(888, 5, std::bind(DelTest, t));

    for(int i = 0; i < 5; i++) {
        sleep(1);
        tw.TimerRefresh(888);//刷新定时任务
        tw.RunTimerTask();//向后移动秒针
        std::cout << "刷新了一下定时任务,重新需要5s中后才会销毁
";
    }

    tw.TimerCancel(888);

    while(1) {
        sleep(1);
        std::cout << "-------------------
";
        tw.RunTimerTask();//向后移动秒针
    }
    return 0;
}

4.3 正则库的简单使用

正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等

正则表达式的使用,可以使得HTTP请求的解析更加简单(这用指的时程序员的工作变得的简单,这并不代表处理效率会变高,实际上效率上是低于直接的字符串处理的),使我们实现的HTTP组件库使用起来更加灵活。

#include 
#include 
#include 

int main()
{
    //HTTP请求行格式:  GET /bitejiuyeke/login?user=xiaoming&pass=123123 HTTP/1.1

    //std::string str = "GET /bitejiuyeke/login?user=xiaoming&pass=123123 HTTP/1.1";

    std::smatch matches;
    //请求方法的匹配  GET HEAD POST PUT DELETE ....
    //std::regex e("(GET|HEAD|POST|PUT|DELETE) .*");//首先字符串格式一定要匹配,然后才能提取内容
    //std::regex e("(GET|HEAD|POST|PUT|DELETE) ([^?]*).*");
    //std::regex e("(GET|HEAD|POST|PUT|DELETE) ([^?]*)?(.*) .*");
    //std::regex e("(GET|HEAD|POST|PUT|DELETE) ([^?]*)?(.*) (HTTP/1.[01])");

    //std::string str = "GET /bitejiuyeke/login?user=xiaoming&pass=123123 HTTP/1.1
";
    std::string str = "GET /bitejiuyeke/login HTTP/1.1
" ;
    std::regex e("(GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:?(.*))? (HTTP/1.[01])(?:n|
)?", std::regex::icase);
    
    //() 表示捕获组,满足里面条件就捕获,不符合条件捕获组就为空
    //(?:) 表示非捕获组,即使满足条件但也不捕获,不满足更不捕获
    // ? 表示匹配前面表达式0次或者1次
    //()? 表示满足()里条件就只去匹配一次,并且符合条件就去捕获,不符合条件捕获组为空,不满足()里条件捕获组也是为空
    //(?:)? 表示满足或者不满足条件都不捕获
    //(?:?())? 表示有以?作为开头的字符串,满足()里面条件就去捕获?后面的,不满足()捕获组为空
    // . 点匹配除"
"和"
"之外的任何单个字符
    // [] 只允许字符串中一个字符,去匹配[]括号里面的任意一个字符, 
 [
] 可以, 
 [
] 不行

    // GET|HEAD|POST|PUT|DELETE   |表示匹配并提取其中任意一个字符串
    // [^?]*     [^?]匹配非问号字符, 后边的*表示0次或多次, +表示1次或多次
    // ?(.*)   ?  表示原始的?字符 (.*)表示提取?之后的任意字符0次或多次,直到遇到空格
    
    /*(?:?(.*?))? 最后的?表⽰匹配前边的表达式0次或1次,因为有的请求可能没有查询字符串
      (?:?(.*?)) (?:pattern)表⽰匹配pattern但是不获取匹配结果
      ?(.*?) ?表⽰原始的?字符,这⾥表⽰以?字符作为起始
      . 表⽰
之外任意单字符,
      * 表⽰匹配前边的字符0次或多次, 
 	  ?跟在*或+之后表⽰懒惰模式, 也就是说以?开始的字符串就只匹配这⼀次就⾏,后边还有以?开始的同格式字符串也不不会匹配
	 () 表⽰捕捉获取符合内部格式的数据
     合并起来表⽰的就是,匹配以?开始的字符串,但是字符串整体不要,
     只捕捉获取?之后的字符串,且只匹配⼀次,就算后边还有以?开始的同格式字符串也不不会匹配
   */
 
    // HTTP/1.[01]  表示匹配以HTTP/1.开始,后边有个0或1的字符串
    //(?:
|
)?   (?: ...) 表示匹配某个格式字符串,但是不提取, 最后的?表示的是匹配前边的表达式0次或1次(前面有字符串或者没有字符串都可以)

	/*bool std:regex_match (const std:string &src, std:smatch &matches, std:regex &e)
	src:原始字符串
	matches:正则表达式可以从原始字符串中匹配并提取符合某种规则的数据,提取的数据就放在matches中是一个类似于数组的容器
	e:正则表达式的匹配规则
	返回值:用于确定匹配是否成功*/

    bool ret = std::regex_match(str, matches, e);
    if (ret == false) {
        return -1;
    }

    for(int i = 0; i < matches.size(); ++i)
    {
        std::cout << i << " : ";
        std::cout << matches[i] << std::endl;
    }

    return 0;
}

4.5 通用类型any类型的实现

每一个Connection对连接进行管理,最终都不可避免需要涉及到应用层协议的处理,因此在Connection中需要设置协议处理的上下文来控制处理节奏。但是应用层协议千千万,为了降低耦合度,这个协议接收解析上下文就不能有明显的协议倾向,它可以是任意协议的上下文信息,因此就需要一个通用的类型来保存各种不同的数据结构。

在C语言中,通⽤类型可以使用void*来管理,但是在C++中,boost库和C++17给我们提供了⼀个通用类型any来灵活使用,如果考虑增加代码的移植性,尽量减少第三方库的依赖,则可以使用C++17特性中的any,或者自己来实现。而这个any通用类型类的实现其实并不复杂,以下是简单的部分实现。

#include 
#include 
#include 
#include 
#include 

/*Any类主要是实现⼀个通⽤类型出来,在c++17和boost库中都有现成的可以使⽤,但是这⾥实现⼀下了解其思想,这样也就避免了第三⽅库的使⽤了*/

/*⾸先Any类肯定不能是⼀个模板类,否则编译的时候 Any a, Anyb,需要传类型作为模板参数,也就是说在使⽤的时候就要确定其类型*/

/*因此考虑Any内部设计⼀个模板容器placehoder类,可以保存各种类型数据*/

/*但在Any类中⽆法定义这个placehoder对象或指针,因为any也不知道这个类要保存什么类型的数据,因此⽆法传递类型参数*/

/*所以,定义⼀个基类让holder,让placeholde继承于让holder,⽽Any类保存⽗类指针即可*/

/*当需要保存数据时,则new⼀个带有模板参数的⼦类holder对象出来保存数据,然后让Any类中的⽗类指针,指向这个⼦类对象就搞定了*/

class Any
{
    class holder
    {
        public:
            virtual ~holder(){}
            virtual const std::type_info& type() = 0;
            virtual holder *clone() = 0;
    };

    template<class T>
    class placeholder : public holder
    {
    public:
        
        placeholder(const T& val) : _val(val) {}

        ~placeholder(){}

        // 获取子类对象保存的数据类型
        virtual const std::type_info& type()
        {
            return typeid(T);
        }

        // 针对当前的对象自身,克隆出一个新的子类对象
        virtual holder *clone()
        {
            return new placeholder(_val);
        }
	public:
        T _val;
    };

private:
    holder* _content;

public:
    Any():_content(nullptr){}

    ~Any() 
    { 
        delete _content; 
    }

    template<class T>
    Any(const T& val)
    {
        _content = new placeholder<T>(val);
    }

    Any(const Any& other)
    {
        _content = _content != nullptr ? other._content->clone() : nullptr;
    }

    // 返回子类对象保存的数据的指针
    template<class T>
    T* Get()
    {
        assert(typeid(T) == _content->type());
        //父类指针指向子类中属于父类的,找不到子类中成员,因此需要转成子类指针
        return &(((placeholder<T>*)_content)->_val);
    }

    Any &swap(Any &other) 
    {
        std::swap(_content, other._content);
        return *this;
    }

    //赋值运算符的重载函数
    template<class T>
    Any& operator=(const T &val) 
    {
        Any(val).swap(*this);
        return *this;
    }

    Any& operator=(const Any &other)
    {
        Any(other).swap(*this);
        return *this;
    }

};


class Test{
    public:
        Test() {std::cout << "构造" << std::endl;}
        Test(const Test &t) {std::cout << "拷贝" << std::endl;}
        ~Test() {std::cout << "析构" << std::endl;}
};
int main()
{

    std::any a;
    a = 10;
    int *pi = std::any_cast<int>(&a);
    std::cout << *pi << std::endl;

    a = std::string("hello");
    std::string *ps = std::any_cast<std::string>(&a);
    std::cout << *ps << std::endl;
    
    // Any a;
    // {
    //     Test t;
    //     a = t;
    // }
    
    // a = 10;
    // int *pa = a.Get();
    // std::cout << *pa << std::endl;
    
    // a = std::string("nihao");
    // std::string *ps = a.Get();
    // std::cout << *ps << std::endl;
    
    while(1) sleep(1);
    return 0;
}

本文地址:https://www.yitenyun.com/562.html

搜索文章

Tags

#服务器 #python #pip #conda #ios面试 #ios弱网 #断点续传 #ios开发 #objective-c #ios #ios缓存 #远程工作 香港站群服务器 多IP服务器 香港站群 站群服务器 #kubernetes #笔记 #平面 #容器 #linux #学习方法 #运维 #进程控制 #开发语言 #云原生 #iventoy #VmWare #OpenEuler #fastapi #html #css #docker #后端 #数据库 #Trae #IDE #AI 原生集成开发环境 #Trae AI #cpolar #人工智能 #node.js #低代码 #爬虫 #音视频 #MobaXterm #ubuntu #Conda # 私有索引 # 包管理 #学习 #物联网 #websocket #内网穿透 #网络 #算法 #大数据 #vscode #mobaxterm #深度学习 #计算机视觉 #开源 #RTP over RTSP #RTP over TCP #RTSP服务器 #RTP #TCP发送RTP #数信院生信服务器 #Rstudio #生信入门 #生信云服务器 #安全 #nginx #tcp/ip #unity #c# #游戏引擎 #android #腾讯云 #云计算 #windows #web安全 #kylin #架构 #Dell #PowerEdge620 #内存 #硬盘 #RAID5 #面试 #gemini #gemini国内访问 #gemini api #gemini中转搭建 #Cloudflare #todesk #多个客户端访问 #IO多路复用 #回显服务器 #TCP相关API #我的世界 #ssh #hadoop #hbase #hive #zookeeper #spark #kafka #flink #qt #c++ #jenkins #java #ide #C++ #jar #oracle #我的世界服务器搭建 #minecraft #http #cpp #项目 #高并发 #jmeter #功能测试 #软件测试 #自动化测试 #职场和发展 #华为 #ModelEngine #mvp #个人开发 #设计模式 #DisM++ # GLM-4.6V # 系统维护 #金融 #大模型 #mcp #金融投资Agent #Agent #claude #国产化 #vue.js #前端 #性能优化 #1024程序员节 #github #git #n8n #本地部署 #udp #c语言 #网络协议 #gitlab #chatgpt #codex #umeditor粘贴word #ueditor粘贴word #ueditor复制word #ueditor上传word图片 #stm32 #单片机 #缓存 #需求分析 #scala #测试用例 #测试工具 #压力测试 #微信小程序 #小程序 #微信 #健身房预约系统 #健身房管理系统 #健身管理系统 #react.js #mamba #凤希AI伴侣 #asp.net #sqlserver #apache #PyTorch # Triton # 高并发部署 #北京百思可瑞教育 #百思可瑞教育 #北京百思教育 #企业开发 #ERP #项目实践 #.NET开发 #C#编程 #编程与数学 #spring boot #部署 #AI编程 #golang #redis #搜索引擎 #debian #screen 命令 #macos #mysql #高级IO #select #计算机网络 #蓝湖 #Axure原型发布 #pycharm #单元测试 #集成测试 #嵌入式硬件 #gpu算力 #编辑器 #京东云 #SAP #ebs #metaerp #oracle ebs #DeepSeek #MCP #蓝耘智算 #AIGC #ida #mcu #MCP服务器 #Anaconda配置云虚拟环境 #openHiTLS #TLCP #DTLCP #密码学 #商用密码算法 #NPU #CANN #unity3d #游戏 #服务器框架 #Fantasy #YOLOFuse # Base64编码 # 多模态检测 #ollama #ai #llm #C2000 #TI #实时控制MCU #AI服务器电源 #vue #阿里云 #麒麟OS #swagger #JumpServer #堡垒机 #Android #Bluedroid #振镜 #振镜焊接 #银河麒麟高级服务器操作系统安装 #银河麒麟高级服务器V11配置 #设置基础软件仓库时出错 #银河麒高级服务器系统的实操教程 #生产级部署银河麒麟服务系统教程 #Linux系统的快速上手教程 #智能手机 #epoll #jvm #SRS #流媒体 #直播 #电气工程 #C# #PLC #php #科技 #自然语言处理 #神经网络 #libosinfo #openlayers #bmap #tile #server #守护进程 #复用 #screen #centos #自动化 #maven #yum #windows11 #microsoft #系统修复 #微服务 #仙盟创梦IDE #东方仙盟 #RAG #知识库 #POC #问答 #交付 #javascript #三维 #3D #三维重建 #信令服务器 #Janus #MediaSoup #源码 #闲置物品交易系统 #毕业设计 #pytorch #spring cloud #spring #CVE-2025-61686 #网络安全 #漏洞 #路径遍历高危漏洞 #万悟 #联通元景 #智能体 #镜像 #webrtc #idm #YOLOv8 # 目标检测 # Docker镜像 #文件管理 #NAS #文件服务器 #jetty #harmonyos #小艺 #鸿蒙 #搜索 #web服务器 #YOLO # GPU租赁 # 自建服务器 #SA-PEKS # 关键词猜测攻击 # 盲签名 # 限速机制 #时序数据库 #电脑 #鸭科夫 #逃离鸭科夫 #鸭科夫联机 #鸭科夫异地联机 #开服 #risc-v #deepseek #AI #大模型学习 #Ansible #Playbook #AI服务器 #SSH公钥认证 # PyTorch # 安全加固 #https #java-ee #语言模型 #昇腾300I DUO #flask #json #fiddler #负载均衡 #sql #tomcat #intellij-idea #vnstat #监控 #Dify #ARM架构 #鲲鹏 #Tracker 服务器 #响应最快 #torrent 下载 #2026年 #Aria2 可用 #迅雷可用 #BT工具通用 #ssl #攻防演练 #Java web #红队 #eBPF #Puppet # IndexTTS2 # TTS #运维开发 #EMC存储 #存储维护 #NetApp存储 #C语言 #驱动开发 #黑群晖 #虚拟机 #无U盘 #纯小白 #支付 #银河麒麟 #系统升级 #信创 #语音识别 #说话人验证 #声纹识别 #CAM++ #Gunicorn #WSGI #Flask #并发模型 #容器化 #Python #性能调优 #PTP_1588 #gPTP #Emby #视频 #Termux #Samba #Linux #gitea #管道Pipe #system V #rust #p2p #Windows #fpga开发 #进程等待 #wait #waitpid #uv #uvx #uv pip #npx #Ruff #pytest #程序员 #大模型教程 #AI大模型 #结构体 #制造 #ping通服务器 #读不了内网数据库 #bug菌问答团队 #GPU #AutoDL ##租显卡 #muduo库 #910B #昇腾 #CTF #postgresql #vivado license #SSE # AI翻译机 # 实时翻译 #VMware #VMWare Tool #svn #可信计算技术 #聊天小程序 #毕设 #RAID #RAID技术 #磁盘 #存储 #华为云 #测评 #CCE #Dify-LLM #Flexus #adb #rustdesk #交互 #cursor #arm开发 #进程 #操作系统 #进程创建与终止 #shell #GPU服务器 #8U #硬件架构 #idea #intellij idea #elasticsearch #serverless #vllm #Streamlit #Qwen #AI聊天机器人 #智能路由器 #ui #5G #SPA #单页应用 #django #web3.py #RustDesk #IndexTTS 2.0 #本地化部署 #Llama-Factory # 树莓派 # ARM架构 #信息与通信 #信号处理 #tcpdump #远程桌面 #远程控制 #银河麒麟操作系统 #openssh #华为交换机 #信创终端 #Java #处理器 #车辆排放 #ms-swift # 大模型 # 模型训练 #mariadb #Spring AI #STDIO协议 #Streamable-HTTP #McpTool注解 #服务器能力 #wsl #智能体来了 #智能体对传统行业冲击 #行业转型 #AI赋能 #LangGraph #CLI #JavaScript #langgraph.json #bash #LLM #chat #transformer #工具集 #Miniconda #SSH #远程开发 #sqlite #muduo #TcpServer #accept #高并发服务器 #wordpress #雨云 #数据结构 #milvus #课程设计 #springboot #web server #请求处理流程 #rdp #个人博客 #分布式 #uni-app #H5 #手机h5网页浏览器 #安卓app #苹果ios APP #手机电脑开启摄像头并排查 #rocketmq #selenium #langchain #大模型开发 #系统架构 #aws #大模型部署 #mindie #大模型推理 #Nacos #web #chrome #SSH反向隧道 # Miniconda # Jupyter远程访问 #TCP #客户端 #嵌入式 #DIY机器人工房 #嵌入式编译 #ccache #distcc #x86_64 #数字人系统 #Node.js # CosyVoice3 # child_process #puppeteer #uvicorn #uvloop #asgi #event #SSH跳板机 #CUDA #KMS #slmgr #大模型入门 #文件传输 #电脑文件传输 #电脑传输文件 #电脑怎么传输文件到另一台电脑 #电脑传输文件到另一台电脑 #eureka #mongodb #链表 #rtsp #转发 #TensorRT # 推理优化 #动态规划 #xlwings #Excel # 自动化运维 #dlms #dlms协议 #逻辑设备 #逻辑设置间权限 #其他 #安全威胁分析 #SQL注入主机 #neo4j #NoSQL #SQL #翻译 #nfs #iscsi #RXT4090显卡 #RTX4090 #深度学习服务器 #硬件选型 #okhttp #前端框架 #计算机外设 #prompt #视频去字幕 #树莓派4b安装系统 #ThingsBoard MCP #scanf #printf #getchar #putchar #cin #cout #LangFlow # 智能运维 # 性能瓶颈分析 #devops #戴尔服务器 #戴尔730 #装系统 #HeyGem # 服务器IP访问 # 端口映射 #mybatis #遛狗 #bug #经验分享 #代理 #Beidou #北斗 #SSR #KMS激活 #排序算法 #jdk #排序 #ddos #arm64 #CSDN #aiohttp #asyncio #异步 #串口服务器 #Modbus #MOXA #信息安全 #信息收集 # 一锤定音 # 大模型微调 #数据仓库 #数据安全 #注入漏洞 #机器学习 #LoRA # lora-scripts # 模型微调 #Triton #算力一体机 #ai算力服务器 #.netcore #数字化转型 #实体经济 #商业模式 #软件开发 #数智红包 #商业变革 #创业干货 #PowerBI #企业 #dify #内存治理 #opencv #数据挖掘 #googlecloud #Qwen3-14B # 大模型部署 # 私有化AI #数据分析 #Zabbix #CosyVoice3 #语音合成 #vp9 #ansible #集成学习 #prometheus #grafana #文心一言 #AI智能体 #Harbor #API限流 # 频率限制 # 令牌桶算法 #TTS私有化 # IndexTTS # 音色克隆 #iBMC #UltraISO #anaconda #虚拟环境 # Python3.11 #LVDS #高速ADC #DDR #unix #SSH别名 # CUDA #CS2 #debian13 #screen命令 #源代码管理 #超时设置 #客户端/服务器 #网络编程 #挖矿 #Linux病毒 #ai编程 #机器人 #azure #llama #门禁 #梯控 #智能一卡通 #门禁一卡通 #消费一卡通 #智能梯控 #一卡通 #推荐算法 #状态模式 #AI-native #dba #国产化OS #react native #汽车 #渗透测试 #黑客技术 #计算机 #文件上传漏洞 #flutter #数码相机 #Android16 #音频性能实战 #音频进阶 #RSO #机器人操作系统 #ASR #SenseVoice #星图GPU #中间件 #MQTT协议 #A2A #GenAI #CVE-2025-68143 #CVE-2025-68144 #CVE-2025-68145 #html5 #weston #x11 #x11显示服务器 #计算几何 #斜率 #方向归一化 #叉积 # 批量管理 #证书 #fabric #winscp #无人机 #Deepoc #具身模型 #开发板 #未来 #ONLYOFFICE #MCP 服务器 #laravel #插件 #开源软件 #FHSS #心理健康服务平台 #心理健康系统 #心理服务平台 #心理健康小程序 # 双因素认证 # TensorFlow #NFC #智能公交 #服务器计费 #FP-增长 #服务器繁忙 #tdengine #涛思数据 #CPU #DAG #nodejs # 数字人系统 # 远程部署 #SSH密钥 #练习 #基础练习 #数组 #循环 #九九乘法表 #计算机实现 #论文笔记 #dynadot #域名 #ETL管道 #向量存储 #数据预处理 #DocumentReader #连接数据库报错 #esb接口 #走处理类报异常 #媒体 #Docker #ffmpeg #硬件工程 #智能家居 #pyqt #DNS #Discord机器人 #云部署 #程序那些事 #网路编程 #百万并发 #银河麒麟部署 #银河麒麟部署文档 #银河麒麟linux #银河麒麟linux部署教程 #运维工具 #C #cosmic #STDIO传输 #SSE传输 #WebMVC #WebFlux #bootstrap #企业微信 #VibeVoice # 高温监控 # 语音合成 #ipmitool #BMC #AI 推理 #NV #visual studio code #kmeans #聚类 #leetcode #IndexTTS2 # 阿里云安骑士 # 木马查杀 #UDP的API使用 #ESP32 # OTA升级 # 黄山派 # WebUI # 网络延迟 #ranger #MySQL8.0 #notepad++ #pve #teamviewer #rsync # 数据同步 #paddleocr #企业级存储 #网络设备 #word #pdf #大模型应用 #API调用 #PyInstaller打包运行 #服务端部署 #Socket网络编程 #大语言模型 #sql注入 #lua #zotero #WebDAV #同步失败 #代理模式 #YOLO26 #目标检测 #openEuler #欧拉 #numpy #pjsip #openresty #LobeChat #vLLM #GPU加速 # IndexTTS 2.0 #WinSCP 下载安装教程 #SFTP #FTP工具 #服务器文件传输 # 批量部署 #能源 #人脸识别sdk #视频编解码 #人脸识别 #AI生成 # outputs目录 # 自动化 #远程连接 #海外服务器安装宝塔面板 #开源工具 #交通物流 #工程设计 #预混 #扩散 #燃烧知识 #层流 #湍流 #SSH保活 #scrapy #AI部署 # ms-swift #node #创业创新 #业界资讯 #政务 #内存接口 # 澜起科技 # 服务器主板 #模拟退火算法 #蓝牙 #LE Audio #BAP #简单数论 #埃氏筛法 #Clawdbot #个人助理 #数字员工 #express #cherry studio #nacos #银河麒麟aarch64 #gmssh #宝塔 #1panel # 服务器迁移 # 回滚方案 #.net #homelab #Lattepanda #Jellyfin #Plex #Kodi #yolov12 #研究生life #宝塔面板部署RustDesk #RustDesk远程控制手机 #手机远程控制 #系统安装 #开关电源 #热敏电阻 #PTC热敏电阻 #铁路桥梁 #DIC技术 #箱梁试验 #裂纹监测 #四点弯曲 #可再生能源 #绿色算力 #风电 #麦克风权限 #访问麦克风并录制音频 #麦克风录制音频后在线播放 #用户拒绝访问麦克风权限怎么办 #uniapp 安卓 苹果ios #将音频保存本地或上传服务器 #wireshark #广播 #组播 #并发服务器 #GLM-4.6V-Flash-WEB # AI视觉 # 本地部署 #IPv6 #zabbix #企业存储 #RustFS #对象存储 #高可用 #AI应用编程 #es安装 #gpu #nvcc #cuda #nvidia #scikit-learn #随机森林 #gpt #若依 #IntelliJ IDEA #Spring Boot #Minecraft #Minecraft服务器 #PaperMC #我的世界服务器 # 大模型推理 #前端开发 #Coturn #TURN #STUN #EN4FE #自由表达演说平台 #演说 #程序员创富 #程序人生 #log4j #Jetty # 嵌入式服务器 #模块 #3d #群晖 #音乐 #蓝桥杯 #建筑缺陷 #红外 #数据集 #kong #Kong Audio #Kong Audio3 #KongAudio3 #空音3 #空音 #中国民乐 #SMARC #ARM #范式 # 代理转发 # 跳板机 #Karalon #AI Test #echarts #流程图 #论文阅读 #图论 # 服务器IP # 端口7860 #国产开源制品管理工具 #Hadess #一文上手 #junit #健康医疗 # 公钥认证 #Reactor #空间计算 #原型模式 # 云服务器 #多模态 #微调 #超参 #LLamafactory #工程实践 #I/O模型 #并发 #水平触发、边缘触发 #多路复用 #AI应用 #CMake #Make #C/C++ #MinIO服务器启动与配置详解 #图像识别 #clickhouse #高考 #数据访问 #SSH复用 # 远程开发 #国产操作系统 #麒麟 #V11 #kylinos #rabbitmq #磁盘配额 #存储管理 #形考作业 #国家开放大学 #系统运维 #自动化运维 #DHCP #C++ UA Server #SDK #跨平台开发 #API #AI写作 #agent #ai大模型 #taro #wps #eclipse #servlet #Linux多线程 #Java程序员 #Java面试 #后端开发 #Spring源码 #Spring #SpringBoot #vps #simulink #matlab #wpf #GATT服务器 #蓝牙低功耗 #软件工程 #lucene #软件 #本地生活 #电商系统 #商城 #poll #散列表 #哈希算法 #机器视觉 #6D位姿 #UOS #海光K100 #统信 #mssql # ControlMaster #webpack #硬件 #Fun-ASR # 语音识别 #AI论文写作工具 #学术写作辅助 #论文创作效率提升 #AI写论文实测 #密码 #传统行业 #信息可视化 #firefox #safari # RTX 3090 #b树 #nmodbus4类库使用教程 #docker-compose #目标跟踪 #微PE # GLM-4.6V-Flash-WEB # AI部署 #材料工程 #智能电视 #AB包 #VMware创建虚拟机 #远程更新 #缓存更新 #多指令适配 #物料关联计划 #windbg分析蓝屏教程 #挖漏洞 #攻击溯源 #编程 #jupyter #blender #warp #le audio #低功耗音频 #通信 #连接 #飞牛NAS #NVR #EasyNVR #防毒面罩 #防尘面罩 #Go并发 #高并发架构 #Goroutine #系统设计 #IFix #c++20 #net core #kestrel #web-server #asp.net-core # 远程连接 #m3u8 #HLS #移动端H5网页 #APP安卓苹果ios #监控画面 直播视频流 #Prometheus #日志分析 #二值化 #Canny边缘检测 #轮廓检测 #透视变换 #FASTMCP #DooTask #Buck #NVIDIA #算力 #交错并联 #DGX #vuejs #matplotlib #postman #安全架构 #产品运营 #Socket #联机教程 #局域网联机 #局域网联机教程 #局域网游戏 #gerrit #opc ua #opc # 环境迁移 #交换机 #三层交换机 #云服务器 #个人电脑 # GLM-TTS # 数据安全 #KMS 激活 #xshell #host key #MC #MC群组服务器 #飞牛nas #fnos #指针 #身体实验室 #健康认知重构 #系统思维 #微行动 #NEAT效应 #亚健康自救 #ICT人 #GB28181 #SIP信令 #视频监控 #云开发 #WT-2026-0001 #QVD-2026-4572 #smartermail #系统管理 #服务 #漏洞挖掘 #asp.net大文件上传 #asp.net大文件上传下载 #asp.net大文件上传源码 #ASP.NET断点续传 #asp.net上传文件夹 #c++高并发 #ip #Modbus-TCP #BoringSSL # ARM服务器 #云计算运维 #编程助手 #asp.net上传大文件 #excel #turn #网安应急响应 # 鲲鹏 #FTP服务器 #http头信息 # GLM # 服务连通性 #uip #ci/cd #k8s #ceph #ambari #arm # 权限修复 #ICE #信创国产化 #达梦数据库 #树莓派 #温湿度监控 #WhatsApp通知 #IoT #MySQL # HiChatBox # 离线AI # 高并发 #数据恢复 #视频恢复 #视频修复 #RAID5恢复 #流媒体服务器恢复 #TCP服务器 #开发实战 #全文检索 #银河麒麟服务器系统 #网站 #截图工具 #批量处理图片 #图片格式转换 #图片裁剪 #鸿蒙PC #Tokio #华为od #华为机试 #SMTP # 内容安全 # Qwen3Guard #新人首发 #SSH跳转 #X11转发 #TTS #可撤销IBE #服务器辅助 #私钥更新 #安全性证明 #双线性Diffie-Hellman #go # GPU集群 #Kylin-Server #服务器安装 #Gateway #认证服务器集成详解 #服务器开启 TLS v1.2 #IISCrypto 使用教程 #TLS 协议配置 #IIS 安全设置 #服务器运维工具 #uniapp #合法域名校验出错 #服务器域名配置不生效 #request域名配置 #已经配置好了但还是报错 #uniapp微信小程序 #短剧 #短剧小程序 #短剧系统 #微剧 #框架搭建 #VMware Workstation16 #服务器操作系统 #hibernate #nosql #平板 #零售 #智能硬件 #glibc #vncdotool #链接VNC服务器 #如何隐藏光标 #H5网页 #网页白屏 #H5页面空白 #资源加载问题 #打包部署后网页打不开 #HBuilderX #套接字 #I/O多路复用 #字节序 #研发管理 #禅道 #禅道云端部署 #samba #CNAS #CMA #程序文件 #后端框架 #IO #网络安全大赛 #r-tree #深度优先 #DFS #outlook #错误代码2603 #无网络连接 #2603 #算力建设 # TURN # NAT穿透 #MCP服务器注解 #异步支持 #方法筛选 #声明式编程 #自动筛选机制 #实时检测 #卷积神经网络 #JNI #pxe #服务器解析漏洞 #云服务器选购 #Saas #线程 #free #vmstat #sar #具身智能 #HarmonyOS APP #网络攻击模型 #spring ai #oauth2 #数据可视化 #r语言 #rtmp #spine #TRO #TRO侵权 #TRO和解 #AI电商客服 # 黑屏模式 # TTS服务器 #tensorflow #ROS # 局域网访问 # 批量处理 #领域驱动 #移动端h5网页 #调用浏览器摄像头并拍照 #开启摄像头权限 #拍照后查看与上传服务器端 #摄像头黑屏打不开问题 #服务器IO模型 #非阻塞轮询模型 #多任务并发模型 #异步信号模型 #多路复用模型 #fs7TF # 远程访问 #系统安全 #npu #memcache #入侵 #日志排查 #大剑师 #nodejs面试题 #ServBay #文件IO #输入输出流 #跨域 #发布上线后跨域报错 #请求接口跨域问题解决 #跨域请求代理配置 #request浏览器跨域 #工业级串口服务器 #串口转以太网 #串口设备联网通讯模块 #串口服务器选型 #embedding #远程软件 #游戏机 #内网 #ansys #ansys问题解决办法 #人大金仓 #Kingbase #Spring AOP #策略模式 # Connection refused #租显卡 #训练推理 #多进程 #python技巧 #代理服务器 #iot #设计师 #图像处理 #游戏美术 #技术美术 #生信 #Smokeping #多线程 #raid #raid阵列 #claudeCode #content7 #elk #跳槽 #工作 #java大文件上传 #java大文件秒传 #java大文件上传下载 #java文件传输解决方案 #odoo #bigtop #hdp #hue #kerberos #pencil #pencil.dev #设计 #轻量化 #低配服务器 #雨云服务器 #教程 #MCSM面板 #Anything-LLM #IDC服务器 #私有化部署 #Apple AI #Apple 人工智能 #FoundationModel #Summarize #SwiftUI #journalctl #docker安装seata #Langchain-Chatchat # 国产化服务器 # 信创 # 串口服务器 # NPort5630 #appche #Ubuntu #PyCharm # 远程调试 # YOLOFuse #ftp #sftp #YOLO识别 #YOLO环境搭建Windows #YOLO环境搭建Ubuntu # 轻量化镜像 # 边缘计算 #OpenHarmony #Syslog #系统日志 #日志监控 #生产服务器问题查询 #日志过滤 #Python办公自动化 #Python办公 #Autodl私有云 #深度服务器配置 #版本控制 #Git入门 #开发工具 #代码托管 # 水冷服务器 # 风冷服务器 #VoxCPM-1.5-TTS # 云端GPU # PyCharm宕机 #win11 #database #儿童AI #图像生成 #everything #硬盘克隆 #DiskGenius # 键鼠锁定 #opc模拟服务器 #stl #漏洞修复 #IIS Crypto #cpu #量子计算 #报表制作 #职场 #用数据讲故事 #语音生成 #实时音视频 #esp32 arduino #决策树 #PN 结 #HistoryServer #Spark #YARN #jobhistory #ArkUI #ArkTS #鸿蒙开发 #服务器线程 # SSL通信 # 动态结构体 #ZooKeeper #ZooKeeper面试题 #面试宝典 #深入解析 #RWK35xx #语音流 #实时传输 #ComfyUI # 推理服务器 #超算中心 #PBS #lsf #n8n解惑 #lvs #adobe # 显卡驱动备份 #数据迁移 #计算机毕业设计 #程序定制 #毕设代做 #课设 #powerbi #Hadoop #Exchange #性能 #优化 #RAM #静脉曲张 #腿部健康 #运动 #AI Agent #开发者工具 #边缘AI # Kontron # SMARC-sAMX8 #ET模式 #非阻塞 #remote-ssh #gateway #Comate #产品经理 #就业 #OpenAI #故障 #memory mcp #Cursor #高斯溅射 #UEFI #BIOS #Legacy BIOS #AI智能棋盘 #Rock Pi S #边缘计算 #mtgsig #美团医药 #美团医药mtgsig #美团医药mtgsig1.2 #改行学it #sentinel #Proxmox VE #虚拟化 #MinIO #smtp #smtp服务器 #PHP #声源定位 #MUSIC #分布式数据库 #集中式数据库 #业务需求 #选型误 #HarmonyOS # 服务器配置 # GPU #全链路优化 #实战教程 #copilot #sglang #反向代理 #参数估计 #矩估计 #概率论 #SSH Agent Forwarding # 容器化