简体   繁体   中英

Parent process in server stuck in accept call. How do I terminate the parent cleanly from child

Hello from a beginner C programmer.

I have a simple server client setup. I only want one client to connect to the server, but I want other clients to be able to try and get a message that the server is occupied.

I am able to connect to the server with one client, and let other clients trying to connect know there is no room. My problem occurs when the client tells the server to shut down. The child process is able to break out of the loops and terminate. The parent, however, is not able to receive the message from the child using pipe, because it is stuck on accept .

I could use kill(2) to end the parent, but will I get a clean termination with closing of sockets and files then?

I have also tried to let the parent not stop at accept using fcntl(sock_desc, F_SETFL, fcntl(sock_desc, F_GETFL, 0) | O_NONBLOCK); but this opens up new problems.

I want to somehow make the child tell the parent to skip the accept line and continue so that it gets the pipe message and exits the loop.

If this is a bad way to terminate servers I would appreciate to learn about that.

Simplified server code:

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
    }
}

The OS will close filedescriptors for you. Unless you have other cleanup work to do (such as writing into files or removing some files), a kill with an unhandled terminating signal (eg, SIGTERM or SIGINT ) should be sufficient.

If you do have other cleanup work to do, have the child signal the parent with a signal for which the parent has a signal handler established (you need to establish the handler with sigaction ). That will break accept with return code -1 and errno == EINTR , allowing you to do whatever you need to do.

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

Let the child signal it's parent before ending. If done correctly accept() returns on signal reception, returning -1 and setting errno to EINTR .

From accept() 's documentation :

RETURN VALUE

Upon successful completion, accept() shall return the non-negative file descriptor of the accepted socket. Otherwise, -1 shall be returned, errno shall be set to indicate the error, [...]

[...]

ERRORS

The accept() function shall fail if:

[...]

[EINTR] The accept() function was interrupted by a signal that was caught before a valid connection arrived.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM