TCP Concurrent Server, One Thread per Client





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. It is a modification of Figure that creates one thread per client, instead of one process per client. This version is very similar to Figure.

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). When that function returns, the connected socket is closed.

Figure 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 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 is not thread-safe. Alternatives 2 and 3 from Section 26.5 were timed with the example in Figure. 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 for the threaded server examples in this chapter.


     Python   SQL   Java   php   Perl 
     game development   web development   internet   *nix   graphics   hardware 
     telecommunications   C++ 
     Flash   Active Directory   Windows