As you’ll recall from Chapter 4, Managing Processes: A Deconstructed Shell, fork() duplicates a running process in place. It returns different values so that the “parent” and the “child” can take different actions, and it generally requires the parent to call wait() (or one of its variants) to “reap” exited children. How can we use this to improve our server?
The traditional pattern is to call accept() in the parent, and then immediately fork() to create a dedicated child process to handle each incoming connection. From a high level, the flow of system calls follows the order shown in the illustration.
We can implement this logic in a fork_and_handle() function that calls our previous handle_connection() function for the application behaviors.
We’ll also need to provide a cleanup_children() method to clean up the child processes, using the waitpid() function that I described in the previous chapter. In particular, waitpid(-1, NULL, WNOHANG) will allow us to reap any terminated children without blocking, which is exactly what we need for this situation. We’ll call it until one of two conditions are reached:
In either case, we’re done with cleanup, so we can return immediately:
| def fork_and_handle(conn_fd:Int, max_size:Int = 1024): Unit = { |
| val pid = fork() |
| if (pid != 0) { |
| // In parent process |
| println("forked pid $pid to handle connection") |
| close(conn_fd) |
| cleanup_children() |
| return |
| } else { |
| // In child process |
| println("fork returned $pid, in child process") |
| handle_connection(conn_fd, max_size) |
| sys.exit() |
| } |
| } |
| |
| def cleanup_children(): Unit = { |
| val child_pid = waitpid(-1, NULL, WNOHANG) |
| if (child_pid <= 0) { |
| return |
| } else { |
| cleanup_children() |
| } |
| } |
Now, our server should be able to handle simultaneous connections, and withstand as much load as we can generate with netcat and basic shell scripting. Later in this chapter, we’ll subject it to much more rigorous stress tests, but for now, we can consider this to be a minimal TCP server framework to build upon.