08 I/OMultiplexing
selectandpollfunctions l t d ll f ti
Dr.Mostafa HassanDahshan
CollegeofComputerandInformationSciences
KingSaudUniversity
Introduction
Problemwithpreviousechoclientexample Clientblockedinfgets Client blocked in fgets DidntseeFINwhenitwassentfromserver Onlydetectedserverclosewhencalledread NeedtobenotifiedwhenoneormoreI/O N dt b tifi d h I/O conditionsareready CalledI/Omultiplexing Providedbyselect andpoll Provided by select and poll functions
2
ApplicationsforI/OMultiplexing
Clienthandlingmultipledescriptors
e.g.theTCPechoclient
I/OModels
BlockingI/O Nonblocking I/O I/OMultiplexing(select,poll) I/O Multiplexing (select poll) SignaldrivenI/O(SIGIO) AsynchronousI/O
BlockingI/OModel
Allexamplesseensofar Defaultforsockets Default for sockets
Nonblocking I/OModel
Dontputprocesstosleep Returnerror(EWOULDBLOCK)instead Needtocheckperiodically(polling) WasteofCPU
I/OMultiplexing
Blockinselectorpoll,notinactualI/Ocall Returnwhensocketisreadable Return when socket is readable
SignalDrivenI/OModel
InstallhandlerforSIGIO(signal,sigaction) Noblock,notifiedwhendescriptorisready No block notified when descriptor is ready
AsynchronousI/O
Similartosignaldrivenexcept Notifiedafterdataisreadinbuffer Notified after data is read in buffer POSIXspec,stillnotwidelysupported
Comparison
10
select Function
#include <sys/select.h> #include <sys/time.h>
int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout); Returns: positive count of ready descriptors, 0 on timeout, 1 on error struct timeval { long tv_sec; long tv_usec; };
/* seconds */ /* microseconds */
11
select Function
Waitforanyormultipleevents Returnwhenoneormoreoccurortimeout Example,returnwhen Example return when
anyofdescriptors{1,4,5}isreadyforreading anyofdescriptors{2,7}isreadyforwriting anyofdescriptors{1,4}haveexceptionpending y p { , } p p g 10.2secondshaveelapsed
12
select Function
maxfdp1
numberofdescriptorstobetested maximumdescriptornumber 1 maximum descriptor number +1
FD_SETSIZE constantdefinedin<sys/select.h>
maxfdp1isspecifiedforefficiency
nottestunsetdescriptors not test unset descriptors
13
readset,writeset,exceptset
Valueresultarguments Oncalling,specifydescriptorsofinterest Onreturn,indicatedescriptorsthatareready On return indicate descriptors that are ready
14
timeoutPossibilities
Value NULL Specified value secand sec 0 Action Waitforeveror untilspecifieddescriptorsareready p p y Wait forfixedtime(ordescriptorsready) Dontwaitatall,returnimmediatelyaftercheckingdescriptors D i ll i di l f h ki d i
SpecifyingDescriptorValues
void FD_ZERO(fd_set *fdset); /* clear all bits in fdset */ void FD_SET(int fd, fd_set *fdset); /* turn on the bit for fd in fdset */ void FD_CLR(int fd, fd_set *fdset); /* turn off the bit for fd in fdset */ int FD_ISSET(int fd, fd_set *fdset); /* is the bit for fd on in fdset ? */
fd_set isabitarray(detailsarehidden) fd set is a bit array (details are hidden) Eachelementcorrespondstodescriptor ManipulatedbyFD_XXXX Manipulated by FD XXXX macros
16
SpecifyingDescriptorValues
fd_set rset; FD_ZERO(&rset); FD ZERO(&rset); /* initialize the set: all bits off */ FD_SET(1, &rset); /* turn on bit for fd 1 */ FD_SET(4, &rset); /* turn on bit for fd 4 */ FD_SET(5, FD SET(5 &rset); /* turn on bit for fd 5 */
17
ReadyforReadingConditions
Bytesinsocketreceivebuffer>lowwatermark
defaultis1forTCP,UDPsocket canbesetwithSO_RCVLOWAT can be set with SO RCVLOWAT readwillnotblock(return>0)
Readhalfoftheconnectionclosed R d h lf f h i l d
TCPconnectionreceivedFIN read willnotblock(return0)
18
ReadyforReadingConditions
Listeningsocket:completedconnections>0
accept willnotblock
19
ReadyforWritingConditions
Availablespaceinsendbuffer>lowwatermark
socketconnected(TCP)ornotrequireconn (UDP) canbesetwithSO_SNDLOWAT can be set with SO SNDLOWAT defaultto2048forTCP,UDP
Writehalfofconnectionclosed W i h lf f i l d
writeoperationwillgenerateSIGPIPE (errno)
20
ReadyforWritingConditions
Nonblocking socket
connectfunctioncompletedorfailed
21
DescriptorConditionsSummary
Condition Datatoread Data to read Read halfoftheconnectionclosed Newconnectionreadyforlisteningsocket Spaceavailableforwriting Space available for writing Writehalfoftheconnectionclosed Nonblocking connect() completedorfailed Pendingerror d Readable? Writable?
22
TCPEchoClientusingselect
tcpechosel1_cli.c
23
TCPEchoClientusingselect
str_cli() rewrittenusingselect Blockedinselect insteadoffgets Waiteithersocketorstdin tobereadable Wait either socket or stdin to be readable Notifiedassoonasserverterminates Onlyneedonedescriptorsetforreadability writesetandexceptionset NULL it t d ti t
24
ConditionsHandledusingselect
PeerTCP(server)senddata
read returns>0
PeerTCPsendRST(crashed)
read returns1 errno =specificerror
25
ProblemwithBatchInput
Previouscodeworksokforinteractiveinput Forbatchinput(e.g.redirectedfromfile)
willdomultiplewritesthensendEOFattheend will do multiple writes then send EOF at the end atEOF,willreturntomain,exit becauseofspeed,willnotreceiveserverresponse
Needtoclosehalfconnection(write) ( )
leavereadopentoreceiveserverresponse useshutdown functionforthat use shutdown function for that
26
Limitationsofclose Function
Onlyclosessocketifrefcountreaches0
sometimesweneedtoclosesocketregardless
27
shutdown Function
#include <sys/socket.h> int shutdown(int sockfd, int howto);
Returns: 0 if OK, 1 on error howto Value SHUT_RD Action readhalfof connectionclosed nomoredatacanbereceived d t b i d anydataonreceivebufferisdiscarded writehalfofconnectionclosed nomoredatacanbesent any datacurrentlyinsentbufferwillbesentfollowedbyTCP connectionterminationsequence combines bothactionsabove
28
SHUT_WR
SHUT_RDWR
ImprovedClientusingselect
tcpechosel2_cli.c
29
ImprovedClientusingselect
WhenEOFinput stdin_eof flagisset
closewritehalfofconnection(SHUT_WR) close write half of connection (SHUT WR)
read usedinsteadoffgets
improveperformanceforbatchinput
30
TCPEchoServerusingselect
tcpechosel1_srv.c
31
TCPEchoServerusingselect
Onlyusereadfd_set Assumeserverstartedinforeground
fd 0 = stdin fd 1 = stdout fd 2 = stderr 0=stdin,fd 1=stdout,fd 2=stderr listeningsocketwillusefd =3
Maintainarrayof clientfds
initiallyall=1 addclientsocketfd valuesastheyconnect add client socket fd values as they connect
32
Handlingselect
Newconnection
callaccept,updatedatastructuresandarrays
33
poll Function
#include <poll.h>
int poll (struct pollfd *fdarray, unsigned long nfds, int timeout); Returns: count of ready descriptors, 0 on timeout, 1 on error
34
poll Function
Similarfunctionalitytoselect MoreinfoonSTREAM devices(TCP) Firstargumentisarrayofpollfd First argument is array of pollfd
fd:descriptorstobepolled events:conditionstobechecked revents:conditionsactuallyoccurred y
Moresystemssupportselect thanpoll
35
Inputevents andReturnedrevents
Constant POLLIN POLLRDNORM POLLRDBAND POLLPRI POLLOUT POLLWRNORM POLLWRBAND POLLERR POLLHUP POLLNVAL Inputto events? Resultfrom revents? Description Normalorprioritydatacanberead Normaldatacanberead Prioritybanddatacanbe Priority band data can be read Highprioritydatacanberead Normaldatacanbewritten Normaldatacanbewritten Normal data can be written Prioritybanddatacanbewritten Errorhasoccurred Hangup h H hasoccurred d Descriptor isnotanopenfile
36
TCPEchoServerusingpoll
tcpechopoll1_srv.c
37
AdditionalReferences
select
http://opengroup.org/onlinepubs/007908775/xsh/sel h // / l b/ / h/ l ect.html http://beej.us/guide/bgnet/output/html/multipage/a http //beej us/guide/bgnet/output/html/multipage/a dvanced.html#select
poll
http://opengroup.org/onlinepubs/007908799/xsh/pol l.html l html http://beej.us/guide/bgnet/output/html/multipage/p ollman.html
38
File: /home/mostafa/netprog/tcpsockex/tcpechosel1_cli.c
Page 1 of 2
/* TCP echo client using select works good only for interactive input cannot handle batch input redirected from another file */ #include <stdio.h> /* printf, fgets, fputs and standard i/o*/ #include <sys/socket.h> /* socket, bind, listen, accept, socklen_t */ #include <arpa/inet.h> /* sockaddr_in, inet_ntop */ #include <string.h> /* memset, strlen */ #include <stdlib.h> /* exit */ #include <unistd.h> /* close, read */ #include <sys/select.h> /* select */ #define #define #define #define SRV_PORT 5105 /* default port number */ LISTEN_ENQ 5 /* for listen backlog */ MAX_SEND_BUF 256 MAX_RECV_BUF MAX_SEND_BUF
int str_cli(FILE*, int); /* prototype for function str_cli */ int main(int argc, char* argv[]) { int sock_fd; /* client socket */ struct sockaddr_in srv_addr; /* server address structure */ if (argc < 2) { /* user entered no arguments */ printf("usage: %s <IP address>\n", argv[0]); /* arg[0] is prog name*/ return -1; } /* create a client socket */ sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); srv_addr.sin_port = htons(SRV_PORT); srv_addr.sin_family = AF_INET; /* convert command line argument to numeric IP */ if ( inet_pton(AF_INET, argv[1], &(srv_addr.sin_addr)) < 1 ) { printf("Invalid IP address\n"); return -1; } if( connect(sock_fd, (struct sockaddr*) &srv_addr, sizeof(srv_addr)) < 0 ){ perror("connect error"); return -1; } /* run the client using standard input */ if (str_cli(stdin, sock_fd) < 0 ) exit(-1); else exit(0); } int str_cli(FILE* fp, int sock_fd) { char recv_str[MAX_RECV_BUF]; /* buffer to hold received data */ char send_str[MAX_SEND_BUF]; /* buffer to hold data to be sent */
File: /home/mostafa/netprog/tcpsockex/tcpechosel1_cli.c
Page 2 of 2
fd_set read_set; /* only read (from input or from socket) */ int fp_no, /* file descriptor number of fp */ max_fd; /* maximum file descriptor */ ssize_t rcvd_bytes, sent_bytes; fp_no = fileno(fp); FD_ZERO(&read_set); /* initialize read_set to zero */ for(;;) { FD_SET(fp_no, &read_set); /* set bit corresponding to fp */ FD_SET(sock_fd, &read_set); /* set bit corresponding to sock_fd */ max_fd = ( (fp_no > sock_fd) ? fp_no : sock_fd ); select(max_fd+1, &read_set, NULL, NULL, NULL); if (FD_ISSET(sock_fd, &read_set)) /* socket is readable */ { memset(recv_str, 0, MAX_RECV_BUF); // clear recv_str /* read back from socket into recv_str */ if ( (rcvd_bytes = read(sock_fd, recv_str, MAX_RECV_BUF)) < 0 ) { perror("read error"); return -1; } if (rcvd_bytes == 0) { /* server has been shutdown */ printf("Server terminated prematurely\n"); return -1; } /* write received line to standard output */ fputs(recv_str, stdout); } // if if (FD_ISSET(fp_no, &read_set)) /* fp (stdin) is readable */ { /* get input from file fp into send_str buffer */ /* fgets returns NULL when EOF, EOL or MAX_SEND_BUF reached */ if( fgets(send_str,MAX_SEND_BUF, fp) == NULL ) { puts("EOF"); return 0; /* reached EOF ^D */ } /* else, write send_str contents to socket */ if ( (sent_bytes = write(sock_fd, send_str, strlen(send_str))) < 0 ) { perror("write error"); return -1; } } //if } // for return 0; }
File: /home/mostafa/netprog/tcpsockex/tcpechosel2_cli.c
Page 1 of 3
/* TCP echo client using select can handle batch input redirected from another file */ #include #include #include #include #include #include #include #define #define #define #define <stdio.h> /* printf, fgets, fputs and standard i/o*/ <sys/socket.h> /* socket, bind, listen, accept, socklen_t */ <arpa/inet.h> /* sockaddr_in, inet_ntop */ <string.h> /* memset, strlen */ <stdlib.h> /* exit */ <unistd.h> /* close, read */ <sys/select.h> /* select */ SRV_PORT 5105 /* default port number */ LISTEN_ENQ 5 /* for listen backlog */ MAX_SEND_BUF 256 MAX_RECV_BUF MAX_SEND_BUF
int str_cli(FILE*, int); /* prototype for function str_cli */ int main(int argc, char* argv[]) { int sock_fd; /* client socket */ struct sockaddr_in srv_addr; /* server address structure */ if (argc < 2) { /* user entered no arguments */ printf("usage: %s <IP address>\n", argv[0]); /* arg[0] is prog name*/ return -1; } /* create a client socket */ sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); srv_addr.sin_port = htons(SRV_PORT); srv_addr.sin_family = AF_INET; /* convert command line argument to numeric IP */ if ( inet_pton(AF_INET, argv[1], &(srv_addr.sin_addr)) < 1 ) { printf("Invalid IP address\n"); return -1; } if( connect(sock_fd, (struct sockaddr*) &srv_addr, sizeof(srv_addr)) < 0 ){ perror("connect error"); return -1; } /* run the client using standard input */ if (str_cli(stdin, sock_fd) < 0 ) exit(-1); else exit(0); } int str_cli(FILE* fp, int sock_fd) { char recv_str[MAX_RECV_BUF]; /* buffer to hold received data */ char send_str[MAX_SEND_BUF]; /* buffer to hold data to be sent */
File: /home/mostafa/netprog/tcpsockex/tcpechosel2_cli.c
Page 2 of 3
fd_set read_set; /* only read (from input or from socket) */ int fp_no, /* file descriptor number of fp */ max_fd; /* maximum file descriptor */ ssize_t rcvd_bytes, sent_bytes, read_bytes; int stdin_eof; /* flag indicate eof from stdin */ fp_no = fileno(fp); stdin_eof = 0; FD_ZERO(&read_set); /* initialize read_set to zero */ for(;;) { if(stdin_eof == 0) FD_SET(fp_no, &read_set); /* set bit corresponding to fp */ FD_SET(sock_fd, &read_set); /* set bit corresponding to sock_fd */ max_fd = ( (fp_no > sock_fd) ? fp_no : sock_fd ); select(max_fd+1, &read_set, NULL, NULL, NULL); if (FD_ISSET(sock_fd, &read_set)) /* socket is readable */ { memset(recv_str, 0, MAX_RECV_BUF); // clear recv_str /* read back from socket into recv_str */ if ( (rcvd_bytes = read(sock_fd, recv_str, MAX_RECV_BUF)) < 0 ) { perror("read error"); return -1; } if (rcvd_bytes == 0) { if (stdin_eof == 1) return 0; /* normal termination */ else { /* server has been shutdown */ printf("Server terminated prematurely\n"); return -1; } } /* write received line to standard output */ fputs(recv_str, stdout); } // if if (FD_ISSET(fp_no, &read_set)) /* fp (stdin) is readable */ { /* read input from file fp into send_str buffer */ if ( (read_bytes = read(fp_no, send_str, MAX_SEND_BUF)) == 0) { stdin_eof = 1; shutdown(sock_fd, SHUT_WR); /* send FIN, can still recv */ FD_CLR(fp_no, &read_set); puts("EOF"); continue; } /* else, write send_str contents to socket */ if ( (sent_bytes = write(sock_fd, send_str, read_bytes)) < 0 ) {
File: /home/mostafa/netprog/tcpsockex/tcpechosel2_cli.c
Page 3 of 3
File: /home/mostafa/netprog/tcpsockex/tcpechosel1_srv.c
Page 1 of 3
/* TCP echo server using select based on example in figure 6.21 in the textbook */ #include #include #include #include #include #include #define #define #define #define <stdio.h> <sys/socket.h> <arpa/inet.h> <string.h> /* memset */ <unistd.h> /* close, read, write */ <sys/select.h> /* select */ SRV_PORT 5105 /* default port number */ LISTEN_ENQ 5 /* for listen backlog */ MAX_RECV_BUF 256 MAX_CLIENTS FD_SETSIZE
int main(int argc, char* argv[]) { int listen_fd, conn_fd, clients [MAX_CLIENTS], /* array of client sockets */ client_fd, /* current client being serviced */ max_fd, /* maximum file descriptor */ i, /* index to use with clients[] array */ max_i, /* max index of connected client */ n_ready; /* number of ready fds returned from select() */ fd_set read_set, /* readable file descriptors to use with select() */ all_set; /* all file descriptors, to prepare read_set */ char recv_str[MAX_RECV_BUF]; ssize_t rcvd_bytes, sent_bytes; struct sockaddr_in srv_addr, cli_addr; socklen_t cli_len; /* start */ if ( (listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0 ) { perror("listen error"); return -1; } memset(&srv_addr, 0, sizeof(srv_addr)); memset(&cli_addr, 0, sizeof(cli_addr)); srv_addr.sin_family = AF_INET; srv_addr.sin_addr.s_addr = htonl(INADDR_ANY); srv_addr.sin_port = htons(SRV_PORT); if ( bind(listen_fd, (struct sockaddr*) &srv_addr, sizeof(srv_addr))< 0 ){ perror("bind error"); return -1; } if ( listen(listen_fd, LISTEN_ENQ) < 0 ) { perror("listen error"); return -1; } max_fd = listen_fd; /* initially, maximum fd is listening socket fd */ max_i = -1; /* no connected clients yet */ for(i=0; i < MAX_CLIENTS; i++)
File: /home/mostafa/netprog/tcpsockex/tcpechosel1_srv.c
Page 2 of 3
clients[i] = -1; /* -1 indicates available entry */ FD_ZERO(&all_set); /* clear all_set */ FD_SET(listen_fd, &all_set); /* set entry for listen_fd */ for(;;) /* main loop */ { read_set = all_set; /* copy all_set to read_set */ /* block in select until a fd is ready, no timeout */ n_ready = select(max_fd + 1, &read_set, NULL, NULL, NULL); /* once select returns, check which fds are ready */ if (FD_ISSET(listen_fd, &read_set)) /* new client connection */ { cli_len = sizeof(cli_addr); if ( (conn_fd = accept(listen_fd, (struct sockaddr*) &cli_addr, &cli_len) ) < 0 ) { perror("accept error"); continue; /* go back to main loop */ } for (i=0; i < MAX_CLIENTS ; i++) if (clients[i] < 0) { clients[i] = conn_fd; /* assign conn_fd to first available */ break; /* slot in clients array */ } if ( i == MAX_CLIENTS ) { fputs("too many clients", stderr); close (conn_fd); /* disconnect this client */ continue; /* go back to main loop */ } FD_SET(conn_fd, &all_set); /* add new fd to next loop select */ if( conn_fd > max_fd) max_fd = conn_fd; /* update max_fd */ if (i > max_i) max_i = i; /* update max_i */ if (--n_ready <= 0) /* no more descriptors */ continue; /* go back to main loop */ }//end if FD_ISSET /* loop through active clients to see if any has data */ for(i = 0; i <= max_i; i++) /* client loop */ { if ( (client_fd = clients[i]) < 0 ) continue; if ( FD_ISSET(client_fd, &read_set) ) /* ready data for this client */ { rcvd_bytes = read(client_fd, recv_str, MAX_RECV_BUF);
File: /home/mostafa/netprog/tcpsockex/tcpechosel1_srv.c
Page 3 of 3
if (rcvd_bytes <= 0) {/* read error OR connection closed by client */ if (rcvd_bytes < 0) perror("read"); close(client_fd); FD_CLR(client_fd, &all_set); clients[i] = -1; } else {/* client sent valid data, echo back */ sent_bytes = write(clients[i], recv_str, rcvd_bytes); if (sent_bytes < 0) /* write error */ { close(client_fd); FD_CLR(client_fd, &all_set); clients[i] = -1; perror("write error"); } } if (--n_ready <= 0) /* no more readable descriptors */ break; /* exit the client loop */ } } //end for (client loop) }//end for (main loop) return 0; }// main
File: /home/mostafa/netprog/tcpsockex/tcpechosel2_srv.c
Page 1 of 2
/* TCP echo server using select (more compact) based on example from Beej's tutorial to network programming */ #include #include #include #include #include #include <stdio.h> <sys/socket.h> <arpa/inet.h> <string.h> /* memset */ <unistd.h> /* close, read, write */ <sys/select.h> /* select */
#define SRV_PORT 5105 /* default port number */ #define LISTEN_ENQ 5 /* for listen backlog */ #define MAX_RECV_BUF 256 int main(int argc, char* argv[]) { int listen_fd, conn_fd, max_fd, /* maximum file descriptor */ i; /* index */ fd_set read_set, /* readable file descriptors to use with select() */ all_set; /* all file descriptors, to prepare read_set */ char recv_str[MAX_RECV_BUF]; ssize_t rcvd_bytes, sent_bytes; struct sockaddr_in srv_addr, cli_addr; socklen_t cli_len; /* start */ if ( (listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0 ) { perror("listen error"); return -1; } memset(&srv_addr, 0, sizeof(srv_addr)); memset(&cli_addr, 0, sizeof(cli_addr)); srv_addr.sin_family = AF_INET; srv_addr.sin_addr.s_addr = htonl(INADDR_ANY); srv_addr.sin_port = htons(SRV_PORT); if ( bind(listen_fd, (struct sockaddr*) &srv_addr, sizeof(srv_addr))< 0 ){ perror("bind error"); return -1; } if ( listen(listen_fd, LISTEN_ENQ) < 0 ) { perror("listen error"); return -1; } max_fd = listen_fd; /* initially, maximum fd is listening socket fd */ FD_ZERO(&all_set); /* clear all_set */ FD_SET(listen_fd, &all_set); /* set entry for listen_fd */ for(;;) /* main loop */ { read_set = all_set; /* copy all_set to read_set */ /* block in select until a fds is ready, no timeout */
File: /home/mostafa/netprog/tcpsockex/tcpechosel2_srv.c
Page 2 of 2
if( select(max_fd + 1, &read_set, NULL, NULL, NULL) < 0 ) { perror("select error"); return -1; } /* once select returns, check which fds is ready */ for (i=0; i <= max_fd; i++) { if (FD_ISSET(i, &read_set)) /* fd ready [new connection or data] */ { if (i == listen_fd) /* it is a new client connection */ { cli_len = sizeof(cli_addr); if ( (conn_fd = accept(listen_fd, (struct sockaddr*) &cli_addr, &cli_len) ) < 0 ) { perror("accept error"); continue; /* go back to main loop */ } else // accept successful { FD_SET(conn_fd, &all_set); /* add new fd to next loop select */ if(conn_fd > max_fd) max_fd = conn_fd; /* update max_fd */ } }//end if new connection else /* it is data for client */ { rcvd_bytes = read(i, recv_str, MAX_RECV_BUF); if (rcvd_bytes <= 0) {/* read error OR connection closed by client */ if (rcvd_bytes < 0) perror("read"); close(i); FD_CLR(i, &all_set); }//if else /* no read errors */ { sent_bytes = write(i, recv_str, rcvd_bytes); if (sent_bytes < 0)/* write error */ { close(i); FD_CLR(i, &all_set); perror("write error"); } } }//end else (data for client) }//end if FD_ISSET } //end for (loop on fds) }//end for (main loop) return 0; }// main
File: /home/mostafa/netprog/tcpsockex/tcpechopoll1_srv.c
Page 1 of 3
/* TCP echo server using poll based on example in figure 6.25 in the textbook */ #include #include #include #include #include #include #define #define #define #define <stdio.h> <sys/socket.h> <arpa/inet.h> <string.h> /* memset */ <unistd.h> /* close, read, write */ <poll.h> /* poll */ SRV_PORT 5105 /* default port number */ LISTEN_ENQ 5 /* for listen backlog */ MAX_RECV_BUF 256 MAX_CLIENTS 256
int main(int argc, char* argv[]) { int listen_fd, conn_fd, client_fd, i, max_i, n_ready; char recv_str[MAX_RECV_BUF]; ssize_t rcvd_bytes, sent_bytes; struct sockaddr_in srv_addr, cli_addr; socklen_t cli_len; struct pollfd clients[MAX_CLIENTS]; /* array of pollfd structures */ /* start */ if ( (listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0 ) { perror("listen error"); return -1; } memset(&srv_addr, 0, sizeof(srv_addr)); memset(&cli_addr, 0, sizeof(cli_addr)); srv_addr.sin_family = AF_INET; srv_addr.sin_addr.s_addr = htonl(INADDR_ANY); srv_addr.sin_port = htons(SRV_PORT); if ( bind(listen_fd, (struct sockaddr*) &srv_addr, sizeof(srv_addr))< 0 ){ perror("bind error"); return -1; } if ( listen(listen_fd, LISTEN_ENQ) < 0 ) { perror("listen error"); return -1; } clients[0].fd = listen_fd; /* first element is listening socket */ clients[0].events = POLLIN; /* poll for input data read */ for (i=1; i< MAX_CLIENTS; i++) clients[i].fd = -1; /* initialize the rest of the array */ max_i = 0; /* max index of the array */ for(;;) /* main loop*/ { n_ready = poll(clients, max_i + 1, -1); /* -1 means no timeout */ if (n_ready < 0) {
File: /home/mostafa/netprog/tcpsockex/tcpechopoll1_srv.c
Page 2 of 3
perror("poll"); return -1; } if (clients[0].revents & POLLIN) {/* listening socket data, i.e. new connection */ cli_len = sizeof(cli_addr); if ( (conn_fd = accept(listen_fd, (struct sockaddr*) &cli_addr, &cli_len) ) < 0 ) { perror("accept error"); continue; /* go back to main loop */ } for (i=1; i < MAX_CLIENTS ; i++) if (clients[i].fd < 0) { clients[i].fd = conn_fd; /* assign conn_fd to first available */ break; /* slot in clients array */ } if ( i == MAX_CLIENTS ) { fputs("too many clients", stderr); close (conn_fd); /* disconnect this client */ continue; /* go back to main loop */ } clients[i].events = POLLIN; /* add events for poll to new client */ if (i > max_i) max_i = i; /* update max_i */
if (--n_ready <= 0) /* no more descriptors */ continue; /* go back to main loop */ }//end if new connection for(i = 1; i <= max_i; i++) /* client loop */ { if ( (client_fd = clients[i].fd) < 0 ) /* find first nonnegative fd */ continue; if ( clients[i].revents & POLLIN ) {/* connected socket data or error */ rcvd_bytes = read(client_fd, recv_str, MAX_RECV_BUF); if (rcvd_bytes <= 0) { if (rcvd_bytes < 0) perror("read"); close(client_fd); clients[i].fd = -1; } else {/* client sent valid data, echo back */ sent_bytes = write(client_fd, recv_str, rcvd_bytes); if (sent_bytes < 0) /* write error */
File: /home/mostafa/netprog/tcpsockex/tcpechopoll1_srv.c
Page 3 of 3
{ close(client_fd); clients[i].fd = -1; perror("write error"); } } if (--n_ready <= 0) /* no more readable descriptors */ break; /* exit the client loop */ }//end connected socket data }//end client loop } //end main loop }// main