Designing concurrent servers
Concurrent Servers
Problem: How to allow a single server to handle multiple clients simultaneously?
4 methods:
1. Multiple processes using fork.
2. Multiple threads within a single process.
3. Single process and thread using select.
4. Single process and thread using signals.
(Note: UDP servers are less likely to use concurrency, since they can read requests from multiple clients on a single socket.)
Multiple processes:
create a socket s;
bind it to an address;
make it a listening socket;
while(1){
ss = accept(s,...); // accept a connection
if( fork() == 0 ){
while( read(ss, request, requestlen) > 0 ){
process request;
write( ss, results, ... );
}
exit(0);
}
}
problems:
1. Parent and children do not share any variables, so how can they communicate?
Maybe they don't have to
Through the file system
2. Need to clean up when children terminate
Set up a SIGCHLD signal handler
The signal handler calls wait() (in a loop) to remove zombie children
Multiple threads:
create a socket s;
bind it to an address;
make it a listening socket;
while(1){
ss = accept(s, ... ); // accept a connection
create thread to handle connection;
pthread_create( . handler(void*), ss);
}
handler(void*arg)
{
while( read((int) arg, ... ) > 0){
process request;
write((int)arg, ...);
}
return;
}
problems:
1. Parent and children can share data now, but need to synchronize access. May need to use mutexes and condition variables.
Alternative design: Prespawn the threads. This saves thread creation time.
Main thread:
create mutex;
create socket;
bind it;
make it a listener;
create n child threads;
Child thread
while(1){
mutex_lock(&mlock);
cannfd = accept(listenfd, ... );
mutex_unlock(&mlock);
while(read request){
process request;
write results;
}
}
Single process concurrent server using select. "Apparent concurrency"
retcode = select(int maxfds, fdset*refds, fdset*wrifds, fdset*exfds, struct timeval * timeout);
"fdset" is a bit string with one bit per file descriptor. The ith bit corresponds to descriptor i. It represents a set of file descriptors.
fd_set fdset;
macros:
FD_SET(i, &fdset); // fdset[i] = 1; (add i to fdset)
FD_CLR(i, &fdset); // fdset[i] = 0; (remove i from fdset)
FD_ISSET(i, &fdset); // returns fdset[i]; (does i belong to fdset?)
FD_ZERO(&fdset); // clear all bits in fdset;
To select:
The fdsets are the sets of descriptors we are interested in.
On return:
The fdsets are the sets of ready descriptors
return value = number of ready descriptors. (0 if timeout; -1 if error, that is, signal arrived before timeout)
How to use it:
int maxfd;
fd_set fdset, testset;
maxfd = listenfd;
FD_ZERO(&fdset);
FD_SET(listenfd, &fdset);
while(1){
testset = fdset;
nready = select(maxfd+1, &testset, 0, 0, 0);
if(FD_ISSET(listenfd, &testset(({
counfd = accept(&testset))\
insert connfd into fdset;
if( connfd > maxfd )
maxfd = connfd;
}
for ( i=0; i<=maxfd; i++)
if( FD_ISSET(i, &testset)) {
read request from descriptor i;
if(okay) {
process request;
write reply to descriptor i;
}
else /* EOF */ {
FD_CLR(i, &fdset);
close(i);
}
}
}
problems:
Not so good if processing time for requests is highly variable.