- Connecting the Client
- Establishing the Server
- Threading Your Server
- Bringing It Together
Threading Your Server
The server described in the preceding section can only handle one connection at a time, because the current task is always busy processing the longest-waiting client. Processing only one client at a time is really an inefficient use of the server's timeslice, because the program is usually blocked on I/O. One way to recover the lost time is to split off processes or threads to handle each incoming connection.
It's easier to fork off separate tasks, but that's a little heavy-handed. Instead, you can use POSIX threads.
Threads are special processes that share as much data space with the parent as possible. This reduces the time needed to flush and reload the caches and memory managers. Most Linux distributions include pThreads, which is a POSIX 1c-compliant library.
You use two main calls to create and use simplified threads: pthread_create() and pthread_detach(). These two calls allow you to create and "let go of" a child thread. If you don't let the thread go, you have to track it like any other task, waiting for the child to terminate. See Linux Socket Programming for a complete discussion of multitasking. Consider an example of how to add threading to the earlier code snippet:
/*--- Process client's requests ---*/ #include <pthread.h> pthread_t child; pthread_create(&child, 0, servlet, fp); /* start thread */ pthread_detach(child); /* don't track it */
This code creates the child thread, telling the child to run servlet() with fp (the FILE* from above) as its parameter.
CAUTION
From the point that the program calls pthread_create(), you must consider that the child is runningboth the child and the parent can access any global or shared data. This is very powerful and very dangerous. If you're not careful, each task can corrupt the other's data regions.
The code fragment above also passes a pointer to a function servlet(). You need to create this function, and all threads in the pThread library require a particular function prototype. The following code fragment demonstrates how to create a compatible interface:
void *servlet(void *arg) /* servlet thread */ { FILE *fp = (FILE*)arg; /* get & convert the data */ while (fgets(s, sizeof(s), fp) != 0 && strcmp(s, "bye\n") != 0) /* proc client's requests */ { printf("msg: %s", s); /* display message */ fputs(s, strlen(s), fp); /* echo it back */ } fclose(fp); /* close the client's channel */ return 0; /* terminate the thread */ }
Because the parent detaches the thread, you only have to worry about firing off as many threads as the clients need (and the host can support). Likewise, you can multithread your clients, letting them do other things while waiting for the network.