[ Team LiB ] Previous Section Next Section

30.10 TCP Concurrent Server, One Thread per Client

The last five sections have focused on one process per client, both one fork per client and preforking some number of children. If the server supports threads, we can use threads instead of child processes.

Our first threaded version is shown in Figure 30.26. It is a modification of Figure 30.4 that creates one thread per client, instead of one process per client. This version is very similar to Figure 26.3.

Main thread loop

19–23 The main thread blocks in a call to accept and each time a client connection is returned, a new thread is created by pthread_create. The function executed by the new thread is doit and its argument is the connected socket.

Per-thread function

25–33 The doit function detaches itself so the main thread does not have to wait for it and calls our web_client function (Figure 30.3). When that function returns, the connected socket is closed.

Figure 30.26 main function for TCP threaded server.

server/serv06.c

 1 #include    "unpthread.h"

 2 int
 3 main(int argc, char **argv)
 4 {
 5     int     listenfd, connfd;
 6     void    sig_int(int);
 7     void   *doit(void *);
 8     pthread_t tid;
 9     socklen_t clilen, addrlen;
10     struct sockaddr *cliaddr;

11     if (argc == 2)
12         listenfd = Tcp_listen(NULL, argv[1], &addrlen);
13     else if (argc == 3)
14         listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
15     else
16         err_quit("usage: serv06 [ <host> ] <port#>");
17     cliaddr = Malloc(addrlen);

18     Signal(SIGINT, sig_int);

19     for ( ; ; ) {
20         clilen = addrlen;
21         connfd = Accept(listenfd, cliaddr, &clilen);

22         Pthread_create(&tid, NULL, &doit, (void *) connfd);
23     }
24 }

25 void *
26 doit(void *arg)
27 {
28     void    web_child(int);

29     Pthread_detach(pthread_self());
30     web_child((int) arg);
31     Close((int) arg);
32     return (NULL);
33 }

We note from Figure 30.1 that this simple threaded version is faster than even the fastest of the preforked versions. This one-thread-per-client version is also many times faster than the one-child-per-client version (row 1).

In Section 26.5 we noted three alternatives for converting a function that is not thread-safe into one that is thread-safe. Our web_child function calls our readline function, and the version shown in Figure 3.18 is not thread-safe. Alternatives 2 and 3 from Section 26.5 were timed with the example in Figure 30.26. The speedup from alternative 3 to alternative 2 was less than one percent, probably because readline is used only to read the five-character count from the client. Therefore, for simplicity we use the less efficient version from Figure 3.17 for the threaded server examples in this chapter.

    [ Team LiB ] Previous Section Next Section