Hey,

How can I terminate a server after some number of connections (don't want it to keep on running forever). I've written in it C, using the select system call, and an array to store the connections made in..

while (1) {
        FD_ZERO(&rdset);
        FD_SET(clients[fd], &rdset);
        ( select(max+1, &rdset, NULL, NULL, NULL) <= 0){
            continue;
        }
        for (i = 0 ; clients[i] <= max; i++) {

            if (FD_ISSET(clients[i], &rdset)) {
                if (clients[i] == fd) {
                    length = sizeof (client_struct);
                    new_fd = accept(server_soc, (struct sockaddr *) &client_struct, &length);
                    clients[i] = new_fd;
                    FD_SET(clients[i], &rdset);
                }
                 else {
                     // Processing data
                }
            }
        }
    }

Here's the formula I usually use for this problem.

  • Set a variable 'numcon' to the number of connections to allow.
  • After a successful 'accept' call, decrement numcon.
  • When numcon equals zero, stop accepting new connections.
  • Continue running the server until all clients have disconnected. Alternatively, set a timer and force-disconnect everyone who's currently online when it runs out.
  • Clean up the program and shut down.

Following this scheme, your while(1) loop should basically be

while ( numcon > 0 || i_still_have_connected_clients() )

You'd want to exit in the while loop or 'break' the while loop when the event/condition you care about happens. Exactly what triggers the end is up to you... if it were me I would probably not want existing connections to be abruptly terminated just because of some random event like a new connection attempt but depending on your requirements you might not care about that.

I've added a counter on the number of connections added, which is incremented every time a new connection is added, but didn't work as expected, because each client would initiate an extra random connection/request after its initial request has been processed, and therefore the server exits before it processes the amount of connections allowed.

One more thing...

My server is required to handle multiple connections from multiple clients concurrently, but my web server is doing the following:
1- It adds the new connection to the array of connections.
2- The select system call checks if any of the connections has any data to be read.
3- Accordingly, it reads that information into a buffer.
4- Forks a child process to process that information.
5- It also sets up a pipe, used for writing the response back to the server (i.e. the parent), and then closes the pipe.
6- The server then writes this response into the client's socket.
7- Closes that client's socket.

I'm not sure if that is considered as handling multiple clients at the same time...since I've noticed that connections are always being added to the same location (index) in the array...so am I missing something?

I'm also supposed to check how the forked child process terminates, in order to determine what kind of response should the server send back to the client, but its difficult to do without using a wait system call in the server, that allows me to check the status of the child process after it terminates, but I cannot use that since it would make the server "wait".

Any ideas would be greatly appreciated. Thank you for your time.

I've added a counter on the number of connections added, which is incremented every time a new connection is added, but didn't work as expected, because each client would initiate an extra random connection/request after its initial request has been processed, and therefore the server exits before it processes the amount of connections allowed.

The server doesn't need to exit immediately. After accepting a desired number of connections, do not put the listening socket into rdset.

I'm also supposed to check how the forked child process terminates, in order to determine what kind of response should the server send back to the client, but its difficult to do without using a wait system call in the server, that allows me to check the status of the child process after it terminates, but I cannot use that since it would make the server "wait".

Handle SIGCHLD.

The server doesn't need to exit immediately. After accepting a desired number of connections, do not put the listening socket into rdset.


Handle SIGCHLD.

Tried to handle SIGCHLD the following way, and my server terminated after "detecting an interrupt signal":

void sigchld_handler(int code){
         if (code == 0){
                  printf("Child terminated normally with code %d\n", code);
         }
         else {
                  printf("Child terminated abnormally with code %d\n", code);
         }
}

void fork_function(){

       struct sigaction signal;
       signal.sa_flags = 0;
       signal.sa_handler = sigchld_handler;
       sigaction(SIGCHLD, &signal, NULL);

       int pid = fork();

       // Parent code
       if ( pid > 0 ){
                // Do whatever
       }

       // Child code
       else if ( pid == 0){
               // Executes the program provided by the client by calling exec...
       }
}

You need to investigate signal blocking and how to block certain signals from terminating your server. This is normal stuff to deal with. Find a reference / tutorial help page on signals and blocking. Good luck. I know it seems difficult but you'll be happy you learned it because you won't even have to think much on your future projects that need it.

Edited 6 Years Ago by sunsetrainbow: n/a

You need to investigate signal blocking and how to block certain signals from terminating your server. This is normal stuff to deal with. Find a reference / tutorial help page on signals and blocking. Good luck. I know it seems difficult but you'll be happy you learned it because you won't even have to think much on your future projects that need it.

Hey there thanks for your advice..I have been reading all about signal blocking/handling and unfortunately can't seem to be able to come up with a working code for this.. I've tried different things with no success here's what I'm trying now and please let me know where's my mistake:

// SIGCHLD handler function:
void SIGCHLD_handler(int status) {
    fprintf(stderr, "Child terminated with code %d\n", status);
    // Do some stuff here
    // Ignore the SIGCHLD signal now
    signal(SIGCHLD, SIG_IGN);
}

void function(){

    // Handling the SIGCHLD signal:
    struct sigaction signal;
    signal.sa_handler = SIGCHLD_handler;
    sa.sa_flags = SA_NOCLDSTOP;
    if (sigaction(SIGCHLD, &sa, NULL) == -1) {
        perror("sigaction");
        exit(1);
    }

    int child_pid = fork();

    if (child_pid == 0){
           // Child process
    } else if ( child_pid > 0 ){
          // Parent process
    }
}

Any input is greatly appreciated. Thank you for your help.

There are a lot of signals you may wish to block. You are just trying to do 1 in your example. So look at the list of signals on your system and investigate what they mean. Often what a coder does is have a list of signals and use some loop to add them to a signal mask.

Tried to handle SIGCHLD the following way, and my server terminated after "detecting an interrupt signal":

void sigchld_handler(int code){
         if (code == 0){
                  printf("Child terminated normally with code %d\n", code);
         }
         else {
                  printf("Child terminated abnormally with code %d\n", code);
         }
}

void fork_function(){

       struct sigaction signal;
       signal.sa_flags = 0;
       signal.sa_handler = sigchld_handler;
       sigaction(SIGCHLD, &signal, NULL);

       int pid = fork();

       // Parent code
       if ( pid > 0 ){
                // Do whatever
       }

       // Child code
       else if ( pid == 0){
               // Executes the program provided by the client by calling exec...
       }
}

A few problems here.
First, fprintf() is not safe to call from a signal handler. See a Notes section of man 2 signal.
Second, the argument to the handler is not a termination code, but a signal which triggered the handler, SIGCHLD in this case. To obtain the exit status, you still need to wait. The signal just guarantees that some child has died, so wait has something meaningful to report.
Third, you don't have to setup the handler at each fork. The handler is not per-child. However, you have to reset the sigaction form within the handler itself. At some platforms, setting a signal up is a one-time deal. Once triggered, it drops back to some default state, which may explain the behaviour you observed.
Finally, remember that signals are not queued. The handler must expect that more than one child terminated, and wait for them in the loop. Of course, it can't call wait(); instead loop until waitpid(-1, &status, WNOHANG) returns -1 with a proper errno .
That's all for now.

A few problems here.
First, fprintf() is not safe to call from a signal handler. See a Notes section of man 2 signal.
Second, the argument to the handler is not a termination code, but a signal which triggered the handler, SIGCHLD in this case. To obtain the exit status, you still need to wait. The signal just guarantees that some child has died, so wait has something meaningful to report.
Third, you don't have to setup the handler at each fork. The handler is not per-child. However, you have to reset the sigaction form within the handler itself. At some platforms, setting a signal up is a one-time deal. Once triggered, it drops back to some default state, which may explain the behaviour you observed.
Finally, remember that signals are not queued. The handler must expect that more than one child terminated, and wait for them in the loop. Of course, it can't call wait(); instead loop until waitpid(-1, &status, WNOHANG) returns -1 with a proper errno .
That's all for now.

I got rid of the idea of handling SIGCHLD signals, instead I added a while loop with its condition of what you have mentioned:

while ( (childPID = waitpid(-1, &status, WNOHANG)) > 0){
          // Check the termination status of the child here
}

But I'm facing one small issue, I cannot figure out which client does this terminated child process belong to (i.e. the child process that was initially forked to process that particular client's request...)

It would be really helpful if you have any ideas..
Thank you

I cannot figure out which client does this terminated child process belong to (i.e. the child process that was initially forked to process that particular client's request...)

It would be really helpful if you have any ideas..
Thank you

Nothing but a pid-to-client map, which you have to maintain manually.

Edited 6 Years Ago by nezachem: n/a

And that's exactly what I've done... One more question though.. what is the order of events/changes to the client and the server that should take place after a condition like a child process of some client got killed or terminated abnormally, or even went through the request normally..
I don't get how should the server take appropriate actions regarding a return from the child process.

And that's exactly what I've done... One more question though.. what is the order of events/changes to the client and the server that should take place after a condition like a child process of some client got killed or terminated abnormally, or even went through the request normally..
I don't get how should the server take appropriate actions regarding a return from the child process.

Really, that's an application logic. It all depends on what your server is doing, what sort of the protocol is agreed upon, what per-child state is kept in the parent, etc, etc.

This article has been dead for over six months. Start a new discussion instead.