[Chapter 12] 12.3 Handling Multiple Clients
Author: Sriram Srinivasan
Date: 1999-07-02T01:36:55Z

#

| Chapter 12 Networking with Sockets |

## 12.3 Handling Multiple Clients

The fact that `accept`, `read`, and `sysread` are blocking calls has more implications for the server.[1] A single-threaded process can invoke only one of these calls at a time, which is fine if there aren't too many clients clamoring for the server's attention and if no client ties the server up for too long a time. The real world is ugly, and you have to resolve this issue. There are three ways of doing this:

1. Create multiple threads of control (processes or threads) and have each call block in its own thread.

2. Make these calls only when you are absolutely sure they won't block. We'll call this the "select" approach, because we use the `select` call to ensure that a socket has something to offer.

3. Make these calls nonblocking using `fcntl` or `ioctl`.

> [1] `accept` blocks until someone tries to connect.

As we shall see, option 2 should be used in conjunction with option 3 in production systems. In all cases, the client code remains unaffected while we try out these options.

Incidentally, there is a fourth option. Some systems support an asynchronous I/O notification: a `SIGIO` signal is sent to the process if a specified socket is ready to do I/O. We will not pay attention to this approach because there is no way for a signal handler to know which socket is ready for reading or writing.

### 12.3.1 Multiple Threads of Execution

Perl doesn't have threads yet (at least not officially[2]), but on Unix and similarly empowered systems, it supports `fork`, the way to get process-level parallelism. The server process acts as a full-time receptionist: it blocks on `accept`, and when a connection request comes in, it spawns a child process and goes back to `accept`. The newly created child process meanwhile has a copy of its parent's environment and shares all open file descriptors. Hence it is able to read from, and write to, the new socket returned by `accept`. When the child is done with the conversation, it simply exits. Each process is therefore dedicated to its own task and doesn't interfere with the other. The following code shows an example of a forking server:

> [2] Malcolm Beattie has a working prototype of a threaded Perl interpreter, which will be incorporated into the mainstream in the Perl 5.005 release.

    # Forking server
    use IO::Socket;
    $SIG{CHLD} = sub {wait ()};
    $main_sock = new IO::Socket::INET (LocalHost => 'goldengate',
                                       LocalPort => 1200,
                                       Listen    => 5,
                                       Proto     => 'tcp',
                                       Reuse     => 1,
                                      );
    die "Socket could not be created. Reason: $!\n" unless ($sock);
    while ($new_sock = $main_sock->accept()) {
        $pid = fork();
        die "Cannot fork: $!" unless defined($pid);
        if ($pid == 0) {
            # Child process
            while (defined ($buf = <$new_sock>)) {
               # do something with $buf ....
               print $new_sock "You said: $buf\n";
            }
            exit(0);   # Child process exits when it is done.
        } # else 'tis the parent process, which goes back to accept()
    }
    close ($main_sock);

The `fork` call results in two identical processes - the parent and child - starting from the statement following the `fork`. The parent gets a positive return value, the process ID (`$pid`) of the child process. Both processes check this return value and execute their own logic; the main process goes back to `accept`, and the child process reads a line from the socket and echoes it back to the client.

Incidentally, the `CHLD` signal has nothing to do with IPC per se. On Unix, when a child process exits (or terminates abnormally), the system gets rid of the memory, files, and other resources associated with it. But it retains a small amount of information (the exit status if the child was able to execute `exit()`, or a termination status otherwise), just in case the parent uses `wait` or `waitpid` to enquire about this status. The terminated child process is also known as a zombie process, and it is always a good thing to remove it using `wait`; otherwise, the process tables keep filling up with junk. In the preceding code, `wait` doesn't block, because it is called only when we know for sure that a child process has died - the `CHLD` signal arranges that for us. Be sure to read the online documentation for quirks associated with signals in general and `SIGCHLD` in particular.

### 12.3.2 Multiplexing Using select

The reason we forked off a different process in the preceding section was to avoid blocking during `accept`, `read`, or `write` for fear of missing out on what is happening on the other sockets. We can instead use the `select` call, introduced in BSD Unix, that returns control when a socket (any filehandle, in fact) can be read from or written to. This approach allows us to use a single-threaded process - somewhat akin to firing the receptionist and handling all the incoming calls and conversations ourselves.

The interface to the native `select` call is not very pretty, so we use the IO::Select wrapper module instead. Consider

    use IO::Socket;
    use IO::Select;
    $sock1 = new IO::Socket (....);
    $sock2 = new IO::Socket (....);
    $read_set = new IO::Select;
    $read_set->add($sock1);
    $write_set = new IO::Select;
    $write_set->add($sock1, $sock2);

The IO::Select module's `new` method creates an object representing a collection of filehandles, and `add` and `remove` modify this set. The `select` method (which calls Perl's native `select` function) accepts three sets of filehandles, or IO::Select objects, which are monitored for readability, writability, and error conditions, respectively. In the preceding snippet of code, we create two such sets - a filehandle can be added to any or all of these sets if you so wish - and supply them to the `select` method as follows:

    ($r_ready, $w_ready, $error) =
          IO::Select->select($read_set, $write_set, $error_set, $timeout);

`select` blocks until an interesting event occurs (one or more filehandles are ready for reading, writing, or reporting an error condition) or the time-out interval has elapsed. At this point, it creates three separate lists of ready filehandles and returns references to them. The time-out is in seconds but can be expressed as a floating-point number to get millisecond resolution.

Let us use this information to implement a program that retrieves messages from one or more clients:

    # Create main socket ($main_socket) as before ...
    # ....

    use IO::Select;
    $readable_handles = new IO::Select();
    $readable_handles->add($main_socket);
    while (1) {  #Infinite loop
        # select() blocks until a socket is ready to be read or written
        ($new_readable) = IO::Select->select($readable_handles,
                                             undef, undef, 0);
        # If it comes here, there is at least one handle
        # to read from or write to. For the moment, worry only about
        # the read side.
        foreach $sock (@$new_readable) {
            if ($sock == $main_socket) {
                $new_sock = $sock->accept();
                # Add it to the list, and go back to select because the
                # new socket may not be readable yet.
                $readable_handles->add($new_sock);
            } else {
                # It is an ordinary client socket, ready for reading.
                $buf = <$sock>;
                if ($buf) {
                    # .... Do stuff with $buf
                } else {
                    # Client closed socket. We do the same here, and remove
                    # it from the readable_handles list
                    $readable_handles->remove($sock);
                    close($sock);
                }
            }
        }
    }

We create a listening socket, `$main_socket`, and configure it to listen on a well-known port. We then add this socket to a newly created IO::Select collection object. When `select` returns the first time, `$main_socket` has something to read from (or has an error, a possibility that we ignore for the moment); in other words, it has received a connection request and is guaranteed not to block when `accept` is called. Now, we are not interested in being blocked if the socket returned from `accept` has nothing to say, so we add it to the list of filehandles being monitored for readability. When `select` returns the next time, we know that one of the two sockets is ready for reading (or both are ready). If `$main_socket` is ready, we repeat the exercise above. If not, we have a socket with something to read.

`select` also returns if one or more remote sockets are closed. The corresponding sockets on the listening end return 0 when any of the I/O operators are used (0 bytes read or written). The server above removes these sockets from the IO::Select collections to prevent from `select` returning the same defunct sockets every time.

#### 12.3.2.1 Blocking looms again

All we have done in this section is depend on `select` to tell us that a filehandle is ready for reading or writing before actually attempting to read or write from it. Unfortunately, we still don't know how much data has accumulated in the I/O buffers (for purposes of reading) or how much can be written to it (the other side may be reading slowly, and there's a limit to how much you can pump in from this side). Both `sysread` and `syswrite` return the number of bytes actually read or written, so you would have to invoke them in a loop until the entire message is read or written. Once you have drained the buffers (or filled them, as the case may be), there is the very real possibility that it might block the next time you attempt to read or write if the other side doesn't do something quick. One option is to invoke `select` in every iteration of the loop and proceed only if it confirms the socket's availability. This slows you down when the filehandle can accommodate your read or write requests. Besides, you have to quit the loop anyway when `select` tells you that a filehandle isn't ready and make the attempt later on when the file descriptor changes state.

For single-threaded programs the next option is to make the filehandle non-blocking. Read on.

### 12.3.3 Nonblocking Filehandles

Any filehandle can be made nonblocking by using the operating-system-specific `fcntl` or `ioctl` call, like this:

    use POSIX;
    fcntl($sock, F_SETFL(), O_NONBLOCK());

The Fcntl module (file control) makes the constants in the fcntl.h file available as functions. The `fcntl` function takes a command like `F_SETFL` ("set flag") and an argument that is specific to that command. Depending on the operating system, the flag to set nonblocking I/O may also be known as `O_NDELAY` or `FNDELAY`.

In any case, once this operation is carried out, `sysread` and `syswrite` return `undef` (not 0) and set `$!` to `EAGAIN` (or `EWOULDBLOCK` on BSD 4.3) if they cannot carry out the operation right away. The following code accounts for these return and error values when reading a socket:

    # Want to read 1024 bytes
    $bytes_to_read = 1024; $msg = '';
    while ($bytes_to_read) {
        $bytes_read = sysread($sock, $buf, $bytes_to_read);
        if (defined($bytes_read)) {
            if ($bytes_read == 0) {
                # Remote socket closed connection
                close($sock);
                last;
            } else {
                $msg .= $buf;
                $bytes_to_read -= $bytes_read;
            }
        } else {
            if ($! == EAGAIN()) {
                # Can return to select. Here we choose to
                # spin around waiting for something to read.
            } else {
                last;
            }
        }
    }

One simple option is to forget the `select` call and simply spin around in a polling loop, calling `read` (or `sysread`) on each socket (or `accept` on the main socket) to check whether it has anything to say, or calling `write` (or `syswrite`) if we have something to say, without fear that it would block. This approach is a constant drain on the CPU because the process is never idle. You should always strive to build a quiescent [3] server, in client/server parlance.

> [3] "Marked by inactivity or repose," as Webster's Tenth Collegiate Dictionary puts it.

You might have noticed that we have ignored clients in all these discussion. If a client is willing to block, there is no issue at all, since, unlike the server side, it is not talking to more than one entity. But if it contains a GUI, it clearly cannot afford to block, and we have much the same problem. We will revisit this issue in Section 14.1, "Introduction to GUIs, Tk, and Perl/Tk". In a system in which there is no clear delineation between "clients" and "servers" - a cluster of bank computers is an example of such a peer-to-peer system - every process is modeled on the more general server framework described in the preceding pages.

You can now see that all three approaches to creating servers have their individual quirks and failings. The next section introduces us to techniques and strategies used in typical production servers.

12.2 Socket API and IO::Socket |  | 12.4 Real-World Servers