Anda di halaman 1dari 32

CEN463 NetworkProgramming

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

Clienthandlingmultiplesockets Client handling multiple sockets


e.g.webclient

Serverhandlinglistenting,connectedsockets ServerhandlingbothTCP,UDP Server handling both TCP, UDP Serverhandlingmultipleservices(xinetd)


3

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>

numberofdescriptorsinfd_set type often1024,fewprogramsusethatmany

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

Forfirsttwocases,selectisinterruptedif For first two cases select is interrupted if processcatchessignal RecommendedtocheckforEINTRerror


15

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

Socketerrorispending Socket error is pending


read willnotblock(return1) errno issettospecificcondition f

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

Socketerrorispending Socket error is pending


write willnotblock(return1) errno issettospecificcondition

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

PeerTCPsendFIN Peer TCP send FIN


read returns0(EOF)

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

Terminatesbothdirectionsofdatatransfer Terminates both directions of data transfer


sometimeswewanttocloseonedirectiononly asencounteredinpreviousexample

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)

IfEOFisreadfromsocket ifstdin_eof wasset,normal


otherwise,serverprocessprematurelyterminated otherwise server process prematurely terminated

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

Clientofdata Client of data


normaldata:echoback FIN(connectionclosing):close,updatedata RST(readerror):close,updatedata,reporterror

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

struct pollfd { int fd; short events; short revents; };

/* descriptor to check */ /* events of interest on fd */ /* events that occurred on fd */

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

perror("write error"); return -1; } } //if } // for return 0; }

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

Anda mungkin juga menyukai