NETWORK AND SYSTEM PROGRAMMING LAB 15 I/O Multiplexing: select and poll function 15.1 objectives What is a Concurrent server Use of Select System call Use of Poll System call 15.2 What is concurrent server? A server that can handle multiple clients at the same time is called concurrent server. It s necessary and very common to have single server that can serve multiple clients at the same time. 15.3 Select System call One traditional way to write network servers is to have the main server block on accept(), waiting for a connection. Once a connection comes in, the server fork()s, the child process handles the connection and the main server is able to service new incoming requests. With select(), instead of having a process for each request, there is usually only one process that "multi-plexes" all requests, servicing each request as much as it can. So one main advantage of using select() is that your server will only require a single process to handle all requests. Thus, your server will not need shared memory or synchronization primitives for different 'tasks' to communicate. int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
File Descriptors- readfds, writefds, exceptfds: Three independent sets of file descriptors are watched. Those listed in readfds will be watched to see if characters become available for reading (more precisely, to see if a read will not block; in particular, a file descriptor is also ready on end-of-file), those in writefds will be watched to see if a write will not block, and those in exceptfds will be watched for exceptions. On exit, the sets are modified in place to indicate which file descriptors actually changed status. Each of the three file descriptor sets may be specified as NULL if no file descriptors are to be watched for the corresponding class of events. nfds is the highest-numbered file descriptor in any of the three sets, plus 1. timeout is an upper bound on the amount of time elapsed before select() returns. If both fields of the timeval structure are zero, then select() returns immediately. (This is useful for polling.) If timeout is NULL (no timeout), select() can block indefinitely. Four macros are provided to manipulate the sets. void FD_ZERO(fd_set *set); FD_ZERO() clears a set. void FD_SET(int fd, fd_set *set); FD_SET() add a given file descriptor from a set. int FD_ISSET(int fd, fd_set *set); FD_ISSET() tests to see if a file descriptor is part of the set. This is useful after select() returns. void FD_CLR(int fd, fd_set *set); FD_CLR() remove a given file descriptor from a set.
Example select() Concurrent Server: #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/select.h> #include <sys/time.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <fcntl.h> #define MAXLINE 100 #define SERV_PORT 13153 int main(int argc, char **argv) int k, i, maxi, maxfd, listenfd, connfd, sockfd; int nready, client[fd_setsize]; ssize_t n; fd_set rset, allset; char line[maxline],buf[100]; socklen_t clilen; struct sockaddr_in cliaddr, servaddr; listenfd = socket(af_inet, SOCK_STREAM, 0); if (listenfd < 0 ) perror("socket" ); exit(1); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(inaddr_any); servaddr.sin_port = htons(serv_port); bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)); listen(listenfd,5); maxfd = listenfd; /* initialize */ maxi = -1; /* index into client[] array */ for (i = 0; i < FD_SETSIZE; i++) client[i] = -1; /* -1 indicates available entry */ FD_ZERO(&allset); FD_SET(listenfd, &allset); /* end fig01 */
/* include fig02 */ for ( ; ; ) printf("server:i am waiting-----start of Main Loop\n"); rset = allset; /* structure assignment */ nready = select(maxfd+1, NULL, NULL, NULL, NULL); if (FD_SET(listenfd, &rset)) /* new client connection */ clilen = sizeof(cliaddr); connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen); #ifdef NOTDEF printf("new client: %s, port %d\n", inet_ntop(af_inet, &cliaddr.sin_addr, buf, NULL), ntohs(cliaddr.sin_port)); #endif for (i = 0; i < FD_SETSIZE; i++) if (client[i] < 0) client[i] = connfd; /* save descriptor */ break; if (i == FD_SETSIZE) printf("too many clients"); exit(0); // FD_SET(connfd, &allset); /* add new descriptor to set */ if (connfd > maxfd) maxfd = connfd; /* for select */ if (i > maxi) maxi = i; /* max index in client[] array */ if (--nready <= 0) continue; /* no more readable descriptors */ for (i = 0; i <= maxi; i++) /* check all clients for data */ if ( (sockfd = client[i]) < 0) continue; // if (FD_ISSET(sockfd, &rset)) if ( (n = read(sockfd, line, MAXLINE)) == 0) /*4connection closed by client */ close(sockfd); FD_CLR(??); client[i] = -1; /* end fig02 */
/* include fig03 */ else printf("\n output at server\n"); for(k=0;line[k]!='\0';k++) printf("%c",toupper(line[k])); write(sockfd, line, n); if (--nready <= 0) break; /* no more readable descriptors */ /* end fig03 */ Program 15-1: Concurrent Server using Select() TASK 1: Compile and execute Program 15-1 as server and Program 15-2 as client. Remove the errors and find what is actually happening at server side.
Client Code: /* include fig01 */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <fcntl.h> #define MAXBUFFER 1024 void sendstring(int, char *); int main( int C, char *V[] ) int sd,fd; char c; struct sockaddr_in serveraddress; char text[100]; int i=0; sd = socket( AF_INET, SOCK_STREAM, 0 ); if( sd < 0 ) perror( "socket" ); exit( 1 ); if (V[1] == NULL ) printf ("specify the server's IP Address \n"); exit(0); if (V[2] == NULL ) printf ("specify the server's Port No \n"); exit(0); memset( &serveraddress, 0, sizeof(serveraddress) ); serveraddress.sin_family = AF_INET; serveraddress.sin_port = htons(atoi(v[2]));//port NO serveraddress.sin_addr.s_addr = inet_addr(v[1]);//address /* end fig01 */
/* include fig02 */ if (connect(sd,(struct sockaddr*)&serveraddress, sizeof(serveraddress))<0) printf("cannot Connect to server"); exit(1); printf("enter sentence to end enter #"); while(1) c=getchar(); if(c=='#') break; text[i++]=c; text[i]='\0'; sendstring(sd,text); close(sd); return 0; /*********************************************************************** * FUNCTION NAME:sendstring * DESCRIPTION: sends a string over the socket. * NOTES : No Error Checking is done. * RETURNS :void ***********************************************************************/ void sendstring( int sd, /*Socket Descriptor*/ char *fname) /*Array Containing the string */ /********************************************************************/ int n, byteswritten=0, written ; char buffer[maxbuffer]; strcpy(buffer, fname); n=strlen(buffer); while (byteswritten<n) written=write(sd, buffer+byteswritten,(n-byteswritten)); byteswritten+=written; printf("string : %s sent to server \n",buffer); /* end fig02 */ Program 15-2: Client Code
15.4 Poll System Call: #include <poll.h> int poll(struct pollfd fds[], nfds_t nfds, int timeout); struct pollfd int fd; /* descriptor to check */ short events; /* events of interest on fd */ short revents; /* events that occurred on fd */ ; The poll() function provides applications with a mechanism for multiplexing input/output over a set of file descriptors. For each member of the array pointed to by fds, poll() shall examine the given file descriptor for the event(s) specified in events. The number of pollfd structures in the fds array is specified by nfds. The poll() function shall identify those file descriptors on which an application can read or write data, or on which certain events have occurred. The fds argument specifies the file descriptors to be examined and the events of interest for each file descriptor. It is a pointer to an array with one member for each open file descriptor of interest. The array's members are pollfd structures within which fd specifies an open file descriptor and events and revents are bitmasks constructed by OR'ing a combination of the following event flags:
In each pollfd structure, poll() shall clear the revents member, except that where the application requested a report on a condition by setting one of the bits of events listed above, poll() shall set the corresponding bit in revents if the requested condition is true. In addition, poll() shall set the POLLHUP, POLLERR, and POLLNVAL flag in revents if the condition is true, even if the application did not set the corresponding bit in events. If none of the defined events have occurred on any selected file descriptor, poll() shall wait at least timeout milliseconds for an event to occur on any of the selected file descriptors. If the value of timeout is 0, poll() shall return immediately. If the value of timeout is -1, poll() shall block until a requested event occurs or until the call is interrupted. The poll() function shall not be affected by the O_NONBLOCK flag. The poll() function shall support regular files, terminal and pseudo-terminal devices, FIFOs, pipes, sockets and STREAMS-based files. The behavior of poll() on elements of fds that refer to other types of file is unspecified. Regular files shall always poll TRUE for reading and writing. A file descriptor for a socket that is listening for connections shall indicate that it is ready for reading, once connections are available. A file descriptor for a socket that is connecting asynchronously shall indicate that it is ready for writing, once a connection has been established.
Example Poll(): #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <fcntl.h> #include <limits.h> /* for OPEN_MAX */ #include <errno.h> #define MAXLINE 100 //#define POLLRDNORM 5 #define INFTIM 5 #define OPEN_MAX 5 int main(int argc, char **argv) int k, i, maxi, listenfd, connfd, sockfd; int nready; ssize_t n; char line[maxline]; socklen_t clilen; struct pollfd client[open_max]; struct sockaddr_in cliaddr, servaddr; listenfd = socket(af_inet, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(inaddr_any); servaddr.sin_port = htons(serv_port); bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)); listen(listenfd, 5); //31 client[0].fd = listenfd; //client[0].events = POLLRDNORM; for (i = 1; i < OPEN_MAX; i++) client[i].fd = -1; /* -1 indicates available entry */ maxi = 0; /* max index into client[] array */ /* end fig01 */
/* include fig02 */ for ( ; ; ) nready = poll(??, maxi+1, INFTIM); if (client[0].revents & POLLRDNORM) /* new client connection */ clilen = sizeof(cliaddr); connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen); #ifdef NOTDEF printf("new client: %s\n", sock_ntop((struct sockaddr *) &cliaddr, clilen)); #endif for (i = 1; i < OPEN_MAX; i++) if (client[i].fd < 0) client[i].fd = connfd; /* save descriptor */ break; if (i == OPEN_MAX) printf("too many clients"); exit(0); // client[i].events = POLLRDNORM; if (i > maxi) maxi = i; /* max index in client[] array */ */ if (--nready <= 0) continue; /* no more readable descriptors for (i = 1; i <= maxi; i++) /* check all clients for data */ if ( (sockfd = client[i].fd) < 0) continue; if (client[i].revents & (POLLRDNORM POLLERR)) if ( (n = read(sockfd, line, MAXLINE)) < 0) if (errno == ECONNRESET) /*4connection reset by client */ #ifdef NOTDEF printf("client[%d] aborted connection\n", i); #endif /* end fig02 */
/* include fig03 */ close(sockfd); client[i].fd = -1; else printf("readline error"); else if (n == 0) /*4connection closed by client */ #ifdef NOTDEF printf("client[%d] closed connection\n", i); #endif close(sockfd); client[i].fd = -1; else printf("\n data from client is \n"); k=strlen(line); printf(" length=%d data = %s\n", k,line); //write(sockfd, line, n); strcpy(line," "); if (--nready <= 0) break; /* no more readable descriptors */ /* end fig03 */ Program 15-3: I/O Multiplexing using Poll() TASK 2: Compile and execute Program 15-3 as server and Program 15-2 as client. Remove the errors and find what is actually happening at server side.