服务器热重启

本文探讨了在服务器更新代码时如何保持客户端连接不断,通过使用fork()函数创建子进程来执行新的服务器代码,实现热重启。这种方法确保了在父进程关闭时,子进程能继续处理客户端请求,维持TCP连接的稳定。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在学习服务器开始时学长提出了一个有趣的问题

如何在不影响客户端与服务器的连通的情况下,更新服务器端的代码并且执行

感觉这个功能应该还是挺重要的,毕竟服务器如果频繁更新的话,每次更新时都让用户退出连接也太不友好了

我能想到的解决办法就是利用fork()函数

创建子进程重新执行server端代码,将server端与之连接的客户端套接字当做参数传递给子进程

然后结束父进程,子进程接收来自客户端的请求

server端代码

#include <iostream>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>

using namespace std;

int main(int argc,char *argv[])
{
    
    if(argc == 1) {
        int fd = socket(AF_INET,SOCK_STREAM,0);
        if(fd < 0) {
            cout << "套接字申请失败" << endl;
            exit(0);
        }

        struct sockaddr_in serv;
        memset(&serv,0,sizeof(serv));
        serv.sin_family = AF_INET;
        serv.sin_port = htons(5678);
        serv.sin_addr.s_addr = htonl(INADDR_ANY);

        int num = 1;
        int length = sizeof(num);
	    setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&num,length);
        if(bind(fd,(struct sockaddr *)&serv,sizeof(serv)) < 0) {
            cout << "绑定失败 " << endl;
            exit(0);
        }

        if(listen(fd,5) < 0) {
            cout << "创建监听套接字失败" << endl;
            exit(0);
        }

        int sockfd = accept(fd,NULL,NULL);
        char ch = 'H';
        send(sockfd,&ch,1,0);

        pid_t pid;
        const char *ar = to_string(sockfd).c_str();
        int flag;
        cin >> flag;
        //接收到信号后重新执行server可执行文件
        if(flag == 1) {
            pid = fork();
            if(pid == 0) { //子进程
                cout << "子进程准备执行" << endl;
                //执行新的server文件
                if(execl("/home/wh/class/server","./server",ar,NULL) < 0) {
                    cout << "子进程开始执行" << endl;
                } 
            }
            else if(pid > 0) {
                //等待子进程与客户端连接完成
                cout << "父进程等待子进程执行" << endl;
                recv(sockfd,&ch,1,0);
                if(ch == 'a') {
                    cout << "子进程执行,结束父进程" << endl;
                    close(sockfd);
                    exit(0);

                }
            }
            close(fd);
        }
    }
    else {
        cout << "子进程开始执行" << endl;
        int fd = argv[1][0] - '0';
        char ch = 'a';
        send(fd,&ch,1,0);
        
        sleep(1);
        char info[] = "welldown";
        send(fd,info,9,0);
        
    }

    return 0;
}

客户端代码

#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>

using namespace std;

int main()
{
    int fd = socket(AF_INET,SOCK_STREAM,0);
    if(fd < 0) {
        cout << "套接字创建失败" << endl;
        exit(0);
    }

    struct sockaddr_in servaddr;
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    servaddr.sin_port = htons(5678);

    if(connect(fd,(const struct sockaddr *)&servaddr,sizeof(servaddr)) < 0) {
        cout << "连接失败" << endl;
        exit(0);
    }

    char ch;
    while(recv(fd,&ch,1,0) > 0) {
        if(ch == 'a') {
            send(fd,&ch,1,0);
        }
        cout << ch << endl;
    }
    
    return 0;
}

具体原理就是子进程与父进程共享文件描述符,当开启子进程时文件描述符引用计数增加,父进程结束了引用计数减一

父进程调用close()并不会引发TCP连接断开从而使得子进程依旧可以处理来自客户端的请求(虽然我并没有任何处理)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值