You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++进程中调用自重启脚本并解决端口占用报错问题

问题核心原因
  • 你调用std::system时,系统会fork子进程执行脚本,子进程默认继承父进程所有打开的文件描述符,包括websocket监听9090端口的socket。哪怕原进程被杀死,持有socket的脚本子进程还在运行,导致端口一直被占用。
  • kill -9是强制终止信号,进程没有机会执行资源清理逻辑,进一步加剧了socket资源未释放的问题。
  • 你配置的set_reuse_addr(true)仅能处理端口处于TIME_WAIT状态的复用,无法解决其他进程仍持有该端口socket文件描述符的场景。
可行解决方案

1. 修复C++端脚本调用逻辑,避免子进程继承socket fd

两种可选实现方式:

方式1:创建socket时自动设置FD_CLOEXEC

websocketpp创建socket前添加配置,让所有新建的socket自动带上执行时关闭标记,fork出的子进程会自动关闭这类fd:

m_endpoint.set_socket_init_handler([](websocketpp::connection_hdl, boost::asio::ip::tcp::socket& sock) {
    int fd = sock.native_handle();
    int flags = fcntl(fd, F_GETFD, 0);
    fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
});

方式2:改用posix_spawn调用脚本,显式禁止继承非必要fd

相比std::system可以更精准控制fd继承:

#include <spawn.h>
#include <sys/wait.h>

extern char **environ;

void call_restart_script() {
    pid_t pid;
    char* argv[] = { (char*)"sh", (char*)"-c", (char*)"/TEST/restart.sh", NULL };
    posix_spawn_file_actions_t actions;
    posix_spawn_file_actions_init(&actions);
    // 关闭所有继承的fd,只保留stdin/stdout/stderr
    for (int i = 3; i < sysconf(_SC_OPEN_MAX); i++) {
        posix_spawn_file_actions_addclose(&actions, i);
    }
    int ret = posix_spawn(&pid, "/bin/sh", &actions, NULL, argv, environ);
    posix_spawn_file_actions_destroy(&actions);
    if (ret == 0) {
        // 父进程直接退出,不要等子进程
        _exit(0);
    }
}

2. 优化重启脚本,优先优雅终止进程

将kill -9替换为先发SIGTERM信号,给进程留资源清理的时间,仅优雅退出失败时再强制杀死:

#!/bin/bash
PID=$(pidof processA)
if [ -n "$PID" ]; then
    # 先发送SIGTERM让进程优雅退出
    kill $PID
    # 最多等5秒确认进程退出
    for i in {1..5}; do
        if ! kill -0 $PID 2>/dev/null; then
            break
        fi
        sleep 1
    done
    # 进程还没退就强制杀
    if kill -0 $PID 2>/dev/null; then
        kill -9 $PID
        sleep 1
    fi
fi
/TEST/bin/processA &

3. 添加C++端优雅退出逻辑

在进程收到SIGTERM信号时主动关闭websocket服务,释放端口资源:

#include <signal.h>

std::atomic<bool> g_running = true;
WebSocketServer* g_ws_server = nullptr;

void signal_handler(int sig) {
    if (sig == SIGTERM || sig == SIGINT) {
        g_running = false;
        if (g_ws_server) {
            // 停止websocket服务,关闭监听socket
            g_ws_server->m_endpoint.stop_listening();
            g_ws_server->m_endpoint.stop();
        }
    }
}

// 程序初始化时注册信号处理函数
int main() {
    signal(SIGTERM, signal_handler);
    signal(SIGINT, signal_handler);
    // 其他初始化逻辑,创建websocket服务赋值给g_ws_server
    // ...
    while (g_running) {
        // 主循环逻辑
    }
    // 退出前清理资源
    return 0;
}

4. 兜底启用SO_REUSEPORT配置

除了现有set_reuse_addr(true)外,额外开启端口复用,兜底处理极端场景下的端口占用:

void WebSocketServer::run()
{
    m_endpoint.set_reuse_addr(true);
    // 添加SO_REUSEPORT配置
    m_endpoint.set_socket_init_handler([](websocketpp::connection_hdl, boost::asio::ip::tcp::socket& sock) {
        int opt = 1;
        setsockopt(sock.native_handle(), SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
    });
    m_endpoint.listen(9090);
    m_endpoint.start_accept();
    m_endpoint.run();
}

内容的提问来源于stack exchange,提问作者CryptoDude

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 00:24:02