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鈥?3
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鈥?3
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.
|