Learn more about Russian war crimes in Ukraine.

How to write a TCP server with the pthread API

I’ve described several ways to write a TCP server:

Today I’ll describe another: using the pthread library to serve multiple clients. The pthread functions are not system calls, but they are part of the standard POSIX API. (In future, I’ll describe how pthreads are implemented on top of system calls.)

The library in pthread.h includes many functions, but today we’ll just use the most fundamental one:

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);

The pthread_create function is somewhat like the fork system call: it creates two threads of control where previously there was one. The important differences today are:

Here’s the program, which runs an “echo” server for every TCP client, using pthread_create for each new TCP connection:

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

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

void * thread_func(void * arg) {
  intptr_t conn_fd = (int) arg;
  printf("thread: serving fd %ld\n", conn_fd);
  char buf[1024];
  for (;;) {
    int bytes_received = guard(recv(conn_fd, buf, sizeof(buf), 0), "Could not recv");
    if (bytes_received == 0) { goto stop_serving; }
    int bytes_sent = 0;
    while (bytes_sent < bytes_received) {
      ssize_t bytes_sent_this_call = send(conn_fd, buf+bytes_sent, bytes_received-bytes_sent, 0);
      if (bytes_sent_this_call == -1) { goto stop_serving; }
      bytes_sent += bytes_sent_this_call;
    }
  }
  stop_serving:
  guard(close(conn_fd), "Could not close socket");
  printf("thread: finished serving %ld\n", conn_fd);
  return NULL;
}

int main(void) {
  int listen_fd = guard(socket(AF_INET, SOCK_STREAM, 0), "Could not create TCP listening socket");
  guard(listen(listen_fd, 100), "Could not listen");
  struct sockaddr_in addr;
  socklen_t addr_bytes = sizeof(addr);
  guard(getsockname(listen_fd, (struct sockaddr *) &addr, &addr_bytes), "Could not get sock name");
  printf("Listening on port %d\n", ntohs(addr.sin_port));
  for (;;) {
    intptr_t conn_fd = guard(accept(listen_fd, NULL, NULL), "Could not accept");
    pthread_t thread_id;
    int ret = pthread_create(&thread_id, NULL, thread_func, (void*) conn_fd);
    if (ret != 0) { printf("Error from pthread: %d\n", ret); exit(1); }
    printf("main: created thread to handle connection %ld\n", conn_fd);
  }
  return 0;
}

What can computers do? What are the limits of mathematics? And just how busy can a busy beaver be? This year, I’m writing Busy Beavers, a unique interactive book on computability theory. You and I will take a practical and modern approach to answering these questions — or at least learning why some questions are unanswerable!

It’s only $19, and you can get 50% off if you find the discount code ... Not quite. Hackers use the console!

After months of secret toil, I and Andrew Carr released Everyday Data Science, a unique interactive online course! You’ll make the perfect glass of lemonade using Thompson sampling. You’ll lose weight with differential equations. And you might just qualify for the Olympics with a bit of statistics!

It’s $29, but you can get 50% off if you find the discount code ... Not quite. Hackers use the console!

More by Jim

Tagged . All content copyright James Fisher 2017. This post is not associated with my employer. Found an error? Edit this page.