网络编程(15)——服务器如何主动退出

十五、day15

服务器主动退出一直是服务器设计必须考虑的一个方向,旨在能通过捕获信号使服务器安全退出。我们可以通过asio提供的信号机制绑定回调函数即可实现优雅退出。

之前服务器的主函数如下

cpp 复制代码
#include "CSession.h"
#include "CServer.h"

int main()
{
    try {
        boost::asio::io_context ioc;
        CServer s(ioc, 10086);
        ioc.run();
    }
    catch (std::exception& e) {
        std::cerr << "Exception: " << e.what() << '\n';
    }
    boost::asio::io_context io_context;
}

有两种修改方式,第一种基于condition_variable和lock_quit;第二种基于asio库

1)第一种实现方式

cpp 复制代码
#include "CSession.h"
#include "CServer.h"
#include <csignal>
#include <thread>
#include <mutex>

bool bstop = false;
std::condition_variable cond_quit;
std::mutex mutex_quit;

void sig_handler(int sig) {
    if (sig == SIGINT or sig == SIGTERM) {
        std::unique_lock<std::mutex> lock_quit(mutex_quit);
        bstop = true;
        cond_quit.notify_one();
    }
}

int main()
{
    try {
        boost::asio::io_context ioc;
        std::thread net_work_thread([&ioc] { // 使用子线程运行网络服务
            CServer s(ioc, 10086);
            ioc.run();
            });
        // 主线程执行以下操作
        // 捕获信号,并执行信号对应的回调函数
        signal(SIGINT, sig_handler);
        signal(SIGTERM, sig_handler);
        // 循环会占用资源,为了防止占用资源我们选择挂起
        while (!bstop) {
            std::unique_lock<std::mutex> lock_quit(mutex_quit);
            cond_quit.wait(lock_quit);
        }

        ioc.stop();
        net_work_thread.join();
    }
    catch (std::exception& e) {
        std::cerr << "Exception: " << e.what() << '\n';
    }
    boost::asio::io_context io_context;
}
  • 条件变量、关服标志、锁必须都是全局变量

  • 自定义关服回调函数sig_handler一旦捕获到SIGINT(Ctrl+C触发)或SIGTERM(终止进程信号),系统调用sig_handler函数。在回调函数内首先进行加锁,以免重复操作,然后将关服标志位置为true,并将主线程唤醒,'}'结束后自动解锁

  • 启动子线程运行网络服务

  • 主线程种设置信号处理函数并将主线程挂起,等待退出信号(循环会占用cpu资源,这里通过挂起避免循环,节省资源)

  • 当主线程被唤醒后,退出循环,执行ioc.stop() 来停止io_context,并将子线程中的ioc.run()返回

  • net_work_thread.join()确保子线程在主线程退出之前先结束运行,防止资源泄露

注:信号处理是异步的,即使主线程在等待条件变量,也可以被信号打断,执行信号处理函数

2)第二种实现方式

cpp 复制代码
int main()
{
    try {
        boost::asio::io_context ioc;
        boost::asio::signal_set signals(ioc, SIGINT, SIGTERM);
        // 必须异步等待,否则建立线程进行处理
        signals.async_wait([&ioc](auto, auto) {
            ioc.stop();
            });

        CServer s(ioc, 10086);
        ioc.run();
    }
    catch (std::exception& e) {
        std::cerr << "Exception: " << e.what() << '\n';
    }
    boost::asio::io_context io_context;
}

第二种方式是我推荐的,最优雅最简单。

主要通过asio库的signal_set 函数执行信号处理操作,允许用户在指定的io_context 上异步等待这些信号的到来,并在信号到达时执行相应的回调

  • 异步处理: 通过将信号处理与 io_context 结合,程序能够继续处理其他异步任务,而不需要在信号到达时阻塞主线程。

  • 多线程支持: 如果程序使用多线程,信号的处理仍然可以有效地协调各个线程的操作。

3)测试

两种方式均能得到以下测试结果

相关推荐
不惑_12 分钟前
小白入门 · 腾讯云轻量服务器部署 Hadoop 3.3.6
服务器·hadoop·腾讯云
阿甘知识库30 分钟前
宝塔面板跨服务器数据同步教程:双机备份零停机
android·运维·服务器·备份·同步·宝塔面板·建站
诚丞成1 小时前
计算世界之安生:C++继承的文水和智慧(上)
开发语言·c++
东风吹柳2 小时前
观察者模式(sigslot in C++)
c++·观察者模式·信号槽·sigslot
A懿轩A2 小时前
C/C++ 数据结构与算法【栈和队列】 栈+队列详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·栈和队列
zhou周大哥2 小时前
linux 安装 ffmpeg 视频转换
linux·运维·服务器
loong_XL3 小时前
服务器ip:port服务用nginx 域名代理
服务器·tcp/ip·nginx
大胆飞猪3 小时前
C++9--前置++和后置++重载,const,日期类的实现(对前几篇知识点的应用)
c++
1 9 J3 小时前
数据结构 C/C++(实验五:图)
c语言·数据结构·c++·学习·算法
夕泠爱吃糖3 小时前
C++中如何实现序列化和反序列化?
服务器·数据库·c++