在学习服务器开始时学长提出了一个有趣的问题
如何在不影响客户端与服务器的连通的情况下,更新服务器端的代码并且执行
感觉这个功能应该还是挺重要的,毕竟服务器如果频繁更新的话,每次更新时都让用户退出连接也太不友好了
我能想到的解决办法就是利用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连接断开从而使得子进程依旧可以处理来自客户端的请求(虽然我并没有任何处理)