How to write a TCP server using the fork syscall

I’ve previously described some simple TCP servers. First, a program which uses a single process, serving one client at a time. Usually, this is unacceptable: we want to multiplex multiple clients’ connections, so that each client is responded to promptly. I described a couple of ways to do this which also use a single process. One uses the select system call, which blocks waiting for one of many possible events. Another uses the kqueue system calls. But I omitted a more obvious way to achieve multiplexing, which uses one process per client connection.

The process-per-connection approach uses the parent process to accept new connections, and uses the fork system call to spawn a new child process for each accepted connection. When the connection is closed, the child process exits.

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int guard(int n, char * err) { if (n == -1) { perror(err); exit(1); } return n; }

int main(void) {
  int listen_fd = guard(socket(PF_INET, SOCK_STREAM, 0), "Could not create TCP socket");
  printf("Created new socket %d\n", listen_fd);
  guard(listen(listen_fd, 100), "Could not listen on TCP socket");
  struct sockaddr_in listen_addr;
  socklen_t addr_len = sizeof(listen_addr);
  guard(getsockname(listen_fd, (struct sockaddr *) &listen_addr, &addr_len), "Could not get socket name");
  printf("Listening for connections on port %d\n", ntohs(listen_addr.sin_port));
  for (;;) {
    int conn_fd = accept(listen_fd, NULL, NULL);
    printf("Got new connection %d\n", conn_fd);
    if (guard(fork(), "Could not fork") == 0) {
      pid_t my_pid = getpid();
      printf("%d: forked\n", my_pid);
      char buf[100];
      for (;;) {
        ssize_t num_bytes_received = guard(recv(conn_fd, buf, sizeof(buf), 0), "Could not recv on TCP connection");
        if (num_bytes_received == 0) {
          printf("%d: received end-of-connection; closing connection and exiting\n", my_pid);
          guard(shutdown(conn_fd, SHUT_WR), "Could not shutdown TCP connection");
          guard(close(conn_fd), "Could not close TCP connection");
          exit(0);
        }
        printf("%d: received bytes; echoing\n", my_pid);
        guard(send(conn_fd, buf, num_bytes_received, 0), "Could not send to TCP connection");
        printf("%d: echoed bytes; receiving more\n", my_pid);
      }
    } else {
      // Child takes over connection; close it in parent
      close(conn_fd);
    }
  }
  return 0;
}
Tagged #fork, #tcp, #ipc, #c, #networking, #posix, #processes, #system-calls, #programming.

Similar posts

More by Jim

Want to build a fantastic product using LLMs? I work at Granola where we're building the future IDE for knowledge work. Come and work with us! Read more or get in touch!

This page copyright James Fisher 2017. Content is not associated with my employer. Found an error? Edit this page.