/* File Name: server.c */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEFAULT_PORT 8000
#define MAXLINE 4096
#define listsize(x) (sizeof(x)/sizeof(x[0]))
int gflag_exit=0;
int grunchilds=1;
int gexitchilds;
int gchangebinary=0;
typedef struct item
{
int pid;
int use;
}childitem;
childitem gchildlist[512];
void waitchild(int signo)
{
pid_t pid;
int stat;
while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0) //非阻塞
{
gexitchilds++;
freechilditem(pid);
}
}
void functionSIGINT(int signo)
{
gflag_exit=1;
}
void functionSIGUSR2(int signo)
{
gchangebinary=1;
}
void childproc(int socket_fd)
{
//子进程业务逻辑
while(gflag_exit == 0)
{
char buff[4096]={0};
char sbuff[4096]={0};
int n,ret;
int connect_fd=-1;
fd_set rdfds;
struct timeval tv;
FD_ZERO(&rdfds);
FD_SET(socket_fd,&rdfds);
tv.tv_sec = 0;
tv.tv_usec = 1000 *10;
ret = select(socket_fd+1,&rdfds,NULL,NULL,&tv);
if (ret <0 ) {
if(errno == EINTR)
{
//信号中断继续
continue;
}
perror("select");
break;
} else if (ret == 0) {
//超时继续
continue;
}
if(FD_ISSET(socket_fd,&rdfds))
{
//阻塞直到有客户端连接,不然多浪费CPU资源。
if((connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1){
if(errno == EAGAIN || errno ==EWOULDBLOCK || errno==EINTR)
{
printf("again %d\n",getpid());
continue;
}
perror("accept socket");
break;
}
//接受客户端传过来的数据
n = recv(connect_fd, buff, MAXLINE, 0);
//向客户端发送回应数据
sprintf(sbuff,"pid:%d,recv msg from client: %s\n", getpid(),buff);
printf("%s\n",sbuff);
if(send(connect_fd, sbuff, sizeof(sbuff),0) == -1)
{
perror("send error");
}
close(connect_fd);
}
}
close(socket_fd);
printf("child process:%d exit! \n",getpid());
exit(0);
}
int usechilditem(int pid)
{
int i;
for(i=1;i < listsize(gchildlist); i++)
{
if(gchildlist[i].use ==0 )
{
gchildlist[i].pid =pid;
gchildlist[i].use =1;
break;
}
}
return (i >= listsize(gchildlist))? -1: i;
}
int freechilditem(int pid)
{
int i =0;
for(i=1;i < listsize(gchildlist); i++)
{
if(gchildlist[i].pid ==pid )
{
gchildlist[i].use=0;
break;
}
}
return (i >= listsize(gchildlist))? -1: i;
}
int sigalchild(int sig)
{
int i =0;
for(i=0;i < listsize(gchildlist); i++)
{
if(gchildlist[i].use ==1 && gchildlist[i].pid != 0 && gchildlist[i].pid !=1)
{
kill(gchildlist[i].pid,sig);
}
}
}
void new_process(int socket_fd)
{
//不能设置该句柄在子进程中关闭,这里是非常重要的(其他文件句柄在execve可以在关闭)
/*
int val=fcntl(socket_fd,F_GETFD);
val|=FD_CLOEXEC;
printf("%x\n",FD_CLOEXEC);
if (fcntl(socket_fd, FD_CLOEXEC,val) == -1) {
perror("execve");
exit(0);
}
*/
char socket_buff[15]={0};
char oldpid_buff[15]={0};
sprintf(socket_buff,"%d",socket_fd);
sprintf(oldpid_buff,"%d",getppid());
char *argv[]={"a.out", socket_buff,oldpid_buff, NULL};
//char *envp[]={"PATH=/bin", NULL}
//注意:复制父进程的句柄
if(execve("./a.out",argv,NULL)==-1)
{
//调用成功,这里永远不会被执行
perror("execve");
exit(0);
}
}
int main(int argc, char** argv)
{
int socket_fd, connect_fd;
struct sockaddr_in servaddr;
int pid,oldpid=-1;
int val=1;
int flags;
gexitchilds =grunchilds;
if(argc == 1)
{
//初始化Socket
if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
//初始化
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。
servaddr.sin_port = htons(DEFAULT_PORT);//设置的端口为DEFAULT_PORT
//将本地地址绑定到所创建的套接字上
if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
//设置端口可以复用
if(setsockopt(socket_fd,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val))!=0)
{
printf("error!setsockopt failed! ");
exit(0);
}
//设置为非阻塞模式
flags = fcntl(socket_fd, F_GETFL, 0);
fcntl(socket_fd, F_SETFL, flags|O_NONBLOCK);
//开始监听是否有客户端连接
if( listen(socket_fd, 10) == -1){
printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
}
else if(argc == 3)
{
//父进程共享过来的fd
socket_fd = atoi(argv[1]);
oldpid = atoi(argv[2]);
}
//注册子进程退出处理函数
printf("======parent process:%d======\n",getpid());
signal(SIGCHLD,waitchild);
signal(SIGINT,functionSIGINT);
signal(SIGUSR2,functionSIGUSR2);
while(gflag_exit == 0)
{
//监控子进程是否退出
while(gexitchilds >0)
{
pid=fork();
if(pid==0)
{
childproc(socket_fd);
}
else
{
gexitchilds--;
usechilditem(pid);
printf("======fork child process:%d======\n",pid);
}
}
if(gchangebinary==1)
{
gchangebinary=0;
pid=fork();
if(pid==0)
{
new_process(socket_fd);
}
else
{
printf("======new binary process:%d======\n",pid);
}
}
sleep(1);
//关闭原来进程的pid
if(oldpid != -1)
{
kill(oldpid,SIGINT);
oldpid = -1;
}
}
sigalchild(SIGINT);
while(gexitchilds !=grunchilds)
{
sleep(1);
}
close(socket_fd);
printf("parent process:%d exit! \n",getpid());
exit(0);
}