Single Process, Concurrent, Connection-Oriented Servers (TCP) (Chapter 12)
INTRODUCTION Last time: Concurrent Connection-Oriented server - echo server - that supports multiple clients at the same time using multiple processes Today: similar echo server but uses only one single process.
Motivation for apparent concurrency using a single process Cost of process creation Sharing of information among all connections Apparent concurrency among processes that share memory can be achieved if the total load of requests presented to the server does not exceed its capacity to handle them.
Single-process, Concurrent Server Idea: Arrange for a single process to keep TCP connections open to multiple clients. In this case a server handles a given connection when data arrives. Thus arrival of data is used to trigger processing.
How Concurrent Execution Differs From Single-process Execution In concurrent execution a server creates a separate slave process to handle each new connection. So theoretically it depends on operating systems time slicing mechanism to share the CPU among the processes and thus among the connections. However in reality the arrival of data controls processing.
“Concurrent servers that require little processing time per request often behave in a sequential manner where the arrival of data triggers execution. Timesharing only takes over if the load becomes so high that the CPU canot handle it sequentially.” Process Structure of a connection-oriented server that achieves concurrency using a single process:
How Does Single Process Mechanism Works? In a single process server, a single server process has TCP connections open to many clients. The process blocks waiting for data to arrive. On the arrival of data, on any connection, the process awakens, handles the request and sends the reply.
Advantages of Single-process Server Over Multiple Process Concurrent Server Single- process implementation requires less switching between process contexts. Thus it may be able to handle slightly higher load than implementation that uses multiple processes.
Socket for sockets for connection individual connections requests server Server <--- application process Operating <---system
Details of Single-process Server A single-process server must perform the duties of both master and slave process A set of socket is maintained. One socket is set bound to the well known port at which master can accept connection. The other socket in the set correspond to a connection over which a slave can handle request.
Details of Single-process Server If the descriptor corresponding master socket is ready, it calls accept on the socket to obtain a new connection. If the descriptor corresponding to slave is ready, it calls read to obtain request and answers it. The above step is then repeated.
Algorithm Create a socket and bind to well-known port for the service. Add socket to the list of those on which I/O is possible. Use select to wait for I/O on existing sockets. If original socket is ready, use accept to obtain the next connection, and add the new socket to the list of those on which I/O is possible.
Algorithm (Cont.) If some socket other than the original is ready, use read to obtain the next request, form a response, and use write to send the response back to the client. Continue processing with step 2 above.
The select system call retcode = select (numfds, refds, wrfds, exfds, time) Select provides asynchronous I/O by permitting a single process to wait for the first of any file descriptors in a specified set to become ready. The caller can also specify a maximum timeout for the wait. Arguments –int numfds –&fd_set refds –&fd_set wrfds –&fd_set exfds –&struct timeval returns: number of ready file descriptors
Example - Single Process ECHO Server /* TCPmechod.c - main, TCPechod */ /* include header files here */ #define QLEN 5 /*max. connection queue length */ #define BUFSIZE4096 extern int errno; int echo (int fd) /*echo data until end of file */ int errexit (const char *format, …); int passiveTCP (const char *service, int qlen);
/* main- concurrent TCP server for ECHO service */ int main(int argc, *argv[]) { char *service = “echo”; /*service name or port number */ struct sockaddr_in fsin; /* the from address of a client */ int alen; /* length of a client’s address */ int msock; /* master server socket */ fd_set rfds; /* read file descriptor set */ fd_set afsd; /* active file descriptor set */ int fd; /* check arguments - not detailed here*/
msock = passiveTCP (service, QLEN); FD_ZERO (&afds); FD_SET (msock, &afds); while(1) { memcpy(&rfds, &afds, sizeof(rfds)); if ( select (FD_SETSIZE, &rfds, (fd_set *) 0, (fd_set *) 0, (struct timeval *) 0) < 0) errexit (“select: %s\n”, strerror(errno));
if ( FD_ISSET (msock, &rfds)) { int ssock; alen = sizeof (fsin); ssock = accept(msock,(struct sockaddr *)&fsin, &alen); if ( ssock < 0) errexit (“accept: %s\n, strerror (errno)); FD_SET (ssock, &afds); }
for ( fd = 0; fd < FD_SETSIZE; ++fd) if (fd!=msock && FD_ISSET(fd, &rfds)) if (echo(fd) ==0 ) { (void) close (fd); FD_CLR (fd, &afds); }
/* Echo - echo one buffer of data, returning byte count */ int echo (int fd) { char buf[BUFSIZE]; int cc; cc = read (fd, buf, sizeof(buf)); if ( cc < 0 ) errexit(“echo read: %s\n”, strerror(errno)); if (cc && write(fd, buf, cc) < 0 ) errexit (“echo write: %s\n”, strerror(errno)); return cc; }