多进程共享同一端口和平滑升级程序的程序模型

/* 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);
}

你可能感兴趣的:(unixl/inux)