服务器中的父进程卡在接受呼叫中。如何从孩子那里干净地终止父母

时间:2017-11-10 17:32:45

标签: c sockets fork

来自初学C程序员的Hello。

我有一个简单的服务器客户端设置。我只希望一个客户端连接到服务器,但我希望其他客户端能够尝试获取服务器被占用的消息。

我能够通过一个客户端连接到服务器,并让其他尝试连接的客户知道没有空间。当客户端告诉服务器关闭时,我的问题出现了。子进程能够突破循环并终止。但是,父级无法使用管道从子级接收消息,因为它卡在accept上。

我可以使用kill(2)来结束父级,但是我会在关闭套接字和文件时获得干净的终止吗?

我还尝试让父母不要停止使用fcntl(sock_desc, F_SETFL, fcntl(sock_desc, F_GETFL, 0) | O_NONBLOCK);,但这会带来新的问题。

我想以某种方式让孩子告诉父母跳过accept行并继续,以便它获取管道消息并退出循环。

如果这是终止服务器的坏方法,我将不胜感激。

简化的服务器代码:

void termination_handler (int signum)
{
    if(signum == SIGTERM){
        //Is this where the accept call is changed?
    }
}


void main(){
struct sigaction sa = {0}; //2b) Initialise the struct sigaction variable to all 0s beforehand.   
sa.handler = termination_handler; //2a) Set the member for handler (to the signal-handler function)

sigaction(SIGTERM, &sa, NULL);    

pid_t pid; 
int loop = 1;
while(loop){
    int sock = accept(net_sock, NULL, NULL); //After connection 
                                             //parent is stuck here

    if(kill(pid,0) == -1){
        pid = fork();
    }
    else{
         //Tell second client there is no room and close socket
    }

    //Child
    if(pid == 0){
        while(loop){ 
            //Read signal in from client to end child loop and terminate child
            //Write with pipe to parent to end parent loop and terminate parent
            kill(getppid(), SIGTERM) // Is this how I do it?
        }
    }
    //Parent
    else{ 
        close(sock);
        //Read with pipe from child to end loop   
        //After first connection, the parent won't get this message
    }
}

2 个答案:

答案 0 :(得分:2)

操作系统将为您关闭文件描述符。除非您有其他清理工作要做(例如写入文件或删除某些文件),否则使用未处理的终止信号(例如SIGTERMSIGINT)进行杀死就足够了。

如果你还有其他清理工作要做,请让孩子用父母信号处理器建立信号处理父母的信号(你需要用sigaction建立处理程序)。这将使accept与返回码-1errno == EINTR中断,让您可以做任何您需要做的事情。

volatile sig_atomic_t usr1 = 0;
void usr1_handler(int Sig) { usr1 = 1; }
//...
int main() {  //...
   sigaction(SIGUSR1, &(struct sigaction){.sa_handler=usr1_handler},0);
   //...
   usr1 = 0;
   sock =  accept( /*... */ );
   if ( -1 == sock && EINTR == errno && usr1 ) //was interrupted by USR1
      /* cleanup and exit */;

答案 1 :(得分:1)

让孩子在结束前给孩子发信号。如果正确完成,accept()将返回信号接收,返回-1并将errno设置为EINTR

来自accept()'s documentation

  

返回值

     

成功完成后, accept()将返回已接受套接字的非负文件描述符。否则,应返回-1,设置errno以指示错误,[...]

     

[...]

     

<强> 错误

     

如果出现以下情况,accept()函数将失败:

     

[...]

     

[EINTR]          accept()函数被有效连接到达之前捕获的信号中断。