Nonblocking I/O and select()

This sample program illustrates a server application that uses nonblocking and the select() API.

 

 

Socket flow of events: Server that uses nonblocking I/O and select()

The following calls are used in the example:

  1. The socket() API returns a socket descriptor, which represents an endpoint. The statement also identifies that the INET (Internet Protocol) address family with the TCP transport (SOCK_STREAM) is used for this socket.
  2. The ioctl() API allows the local address to be reused when the server is restarted before the required wait time expires. In this example, it sets the socket to be nonblocking. All of the sockets for the incoming connections are also nonblocking because they inherit that state from the listening socket.
  3. After the socket descriptor is created, the bind() gets a unique name for the socket.
  4. The listen() allows the server to accept incoming client connections.
  5. The server uses the accept() API to accept an incoming connection request. The accept() API call blocks indefinitely, waiting for the incoming connection to arrive.
  6. The select() API allows the process to wait for an event to occur and to wake up the process when the event occurs. In this example, the select() API returns a number that represents the socket descriptors that are ready to be processed.
    0
    Indicates that the process times out. In this example, the timeout is set for 3 minutes.
    -1
    Indicates that the process has failed.
    1
    Indicates only one descriptor is ready to be processed. In this example, when a 1 is returned, the FD_ISSET and the subsequent socket calls complete only once.
    n
    Indicates that multiple descriptors are waiting to be processed. In this example, when an n is returned, the FD_ISSET and subsequent code loops and completes the requests in the order they are received by the server.
  7. The accept() and recv() APIs are completed when the EWOULDBLOCK is returned.
  8. The send() API echoes the data back to the client.
  9. The close() API closes any open socket descriptors.
Note: By using the examples, you agree to the terms of the Code license and disclaimer information.
  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <sys/ioctl.h>
  4 #include <sys/socket.h>
  5 #include <sys/time.h>
  6 #include <netinet/in.h>
  7 #include <errno.h>
  8 
  9 #define SERVER_PORT  12345
 10 
 11 #define TRUE             1
 12 #define FALSE            0
 13 
 14 main (int argc, char *argv[])
 15 {
 16    int    i, len, rc, on = 1;
 17    int    listen_sd, max_sd, new_sd;
 18    int    desc_ready, end_server = FALSE;
 19    int    close_conn;
 20    char   buffer[80];
 21    struct sockaddr_in6   addr;
 22    struct timeval       timeout;
 23    struct fd_set        master_set, working_set;
 24 
 25    /*************************************************************/
 26    /* Create an AF_INET6 stream socket to receive incoming      */
 27    /* connections on                                            */
 28    /*************************************************************/
 29    listen_sd = socket(AF_INET6, SOCK_STREAM, 0);
 30    if (listen_sd < 0)
 31    {
 32       perror("socket() failed");
 33       exit(-1);
 34    }
 35 
 36    /*************************************************************/
 37    /* Allow socket descriptor to be reuseable                   */
 38    /*************************************************************/
 39    rc = setsockopt(listen_sd, SOL_SOCKET,  SO_REUSEADDR,
 40                    (char *)&on, sizeof(on));
 41    if (rc < 0)
 42    {
 43       perror("setsockopt() failed");
 44       close(listen_sd);
 45       exit(-1);
 46    }
 47 
 48    /*************************************************************/
 49    /* Set socket to be nonblocking. All of the sockets for      */
 50    /* the incoming connections will also be nonblocking since   */
 51    /* they will inherit that state from the listening socket.   */
 52    /*************************************************************/
 53    rc = ioctl(listen_sd, FIONBIO, (char *)&on);
 54    if (rc < 0)
 55    {
 56       perror("ioctl() failed");
 57       close(listen_sd);
 58       exit(-1);
 59    }
 60 
 61    /*************************************************************/
 62    /* Bind the socket                                           */
 63    /*************************************************************/
 64    memset(&addr, 0, sizeof(addr));
 65    addr.sin6_family      = AF_INET6;
 66    memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
 67    addr.sin6_port        = htons(SERVER_PORT);
 68    rc = bind(listen_sd,
 69              (struct sockaddr *)&addr, sizeof(addr));
 70    if (rc < 0)
 71    {
 72       perror("bind() failed");
 73       close(listen_sd);
 74       exit(-1);
 75    }
 76 
 77    /*************************************************************/
 78    /* Set the listen back log                                   */
 79    /*************************************************************/
 80    rc = listen(listen_sd, 32);
 81    if (rc < 0)
 82    {
 83       perror("listen() failed");
 84       close(listen_sd);
 85       exit(-1);
 86    }
 87 
 88    /*************************************************************/
 89    /* Initialize the master fd_set                              */
 90    /*************************************************************/
 91    FD_ZERO(&master_set);
 92    max_sd = listen_sd;
 93    FD_SET(listen_sd, &master_set);
 94 
 95    /*************************************************************/
 96    /* Initialize the timeval struct to 3 minutes.  If no        */
 97    /* activity after 3 minutes this program will end.           */
 98    /*************************************************************/
 99    timeout.tv_sec  = 3 * 60;
100    timeout.tv_usec = 0;
101 
102    /*************************************************************/
103    /* Loop waiting for incoming connects or for incoming data   */
104    /* on any of the connected sockets.                          */
105    /*************************************************************/
106    do
107    {
108       /**********************************************************/
109       /* Copy the master fd_set over to the working fd_set.     */
110       /**********************************************************/
111       memcpy(&working_set, &master_set, sizeof(master_set));
112 
113       /**********************************************************/
114       /* Call select() and wait 3 minutes for it to complete.   */
115       /**********************************************************/
116       printf("Waiting on select()...\n");
117       rc = select(max_sd + 1, &working_set, NULL, NULL, &timeout);
118 
119       /**********************************************************/
120       /* Check to see if the select call failed.                */
121       /**********************************************************/
122       if (rc < 0)
123       {
124          perror("  select() failed");
125          break;
126       }
127 
128       /**********************************************************/
129       /* Check to see if the 3 minute time out expired.         */
130       /**********************************************************/
131       if (rc == 0)
132       {
133          printf("  select() timed out.  End program.\n");
134          break;
135       }
136 
137       /**********************************************************/
138       /* One or more descriptors are readable.  Need to         */
139       /* determine which ones they are.                         */
140       /**********************************************************/
141       desc_ready = rc;
142       for (i=0; i <= max_sd  &&  desc_ready > 0; ++i)
143       {
144          /*******************************************************/
145          /* Check to see if this descriptor is ready            */
146          /*******************************************************/
147          if (FD_ISSET(i, &working_set))
148          {
149             /****************************************************/
150             /* A descriptor was found that was readable - one   */
151             /* less has to be looked for.  This is being done   */
152             /* so that we can stop looking at the working set   */
153             /* once we have found all of the descriptors that   */
154             /* were ready.                                      */
155             /****************************************************/
156             desc_ready -= 1;
157 
158             /****************************************************/
159             /* Check to see if this is the listening socket     */
160             /****************************************************/
161             if (i == listen_sd)
162             {
163                printf("  Listening socket is readable\n");
164                /*************************************************/
165                /* Accept all incoming connections that are      */
166                /* queued up on the listening socket before we   */
167                /* loop back and call select again.              */
168                /*************************************************/
169                do
170                {
171                   /**********************************************/
172                   /* Accept each incoming connection.  If       */
173                   /* accept fails with EWOULDBLOCK, then we     */
174                   /* have accepted all of them.  Any other      */
175                   /* failure on accept will cause us to end the */
176                   /* server.                                    */
177                   /**********************************************/
178                   new_sd = accept(listen_sd, NULL, NULL);
179                   if (new_sd < 0)
180                   {
181                      if (errno != EWOULDBLOCK)
182                      {
183                         perror("  accept() failed");
184                         end_server = TRUE;
185                      }
186                      break;
187                   }
188 
189                   /**********************************************/
190                   /* Add the new incoming connection to the     */
191                   /* master read set                            */
192                   /**********************************************/
193                   printf("  New incoming connection - %d\n", new_sd);
194                   FD_SET(new_sd, &master_set);
195                   if (new_sd > max_sd)
196                      max_sd = new_sd;
197 
198                   /**********************************************/
199                   /* Loop back up and accept another incoming   */
200                   /* connection                                 */
201                   /**********************************************/
202                } while (new_sd != -1);
203             }
204 
205             /****************************************************/
206             /* This is not the listening socket, therefore an   */
207             /* existing connection must be readable             */
208             /****************************************************/
209             else
210             {
211                printf("  Descriptor %d is readable\n", i);
212                close_conn = FALSE;
213                /*************************************************/
214                /* Receive all incoming data on this socket      */
215                /* before we loop back and call select again.    */
216                /*************************************************/
217                do
218                {
219                   /**********************************************/
220                   /* Receive data on this connection until the  */
221                   /* recv fails with EWOULDBLOCK.  If any other */
222                   /* failure occurs, we will close the          */
223                   /* connection.                                */
224                   /**********************************************/
225                   rc = recv(i, buffer, sizeof(buffer), 0);
226                   if (rc < 0)
227                   {
228                      if (errno != EWOULDBLOCK)
229                      {
230                         perror("  recv() failed");
231                         close_conn = TRUE;
232                      }
233                      break;
234                   }
235 
236                   /**********************************************/
237                   /* Check to see if the connection has been    */
238                   /* closed by the client                       */
239                   /**********************************************/
240                   if (rc == 0)
241                   {
242                      printf("  Connection closed\n");
243                      close_conn = TRUE;
244                      break;
245                   }
246 
247                   /**********************************************/
248                   /* Data was received                          */
249                   /**********************************************/
250                   len = rc;
251                   printf("  %d bytes received\n", len);
252 
253                   /**********************************************/
254                   /* Echo the data back to the client           */
255                   /**********************************************/
256                   rc = send(i, buffer, len, 0);
257                   if (rc < 0)
258                   {
259                      perror("  send() failed");
260                      close_conn = TRUE;
261                      break;
262                   }
263 
264                } while (TRUE);
265 
266                /*************************************************/
267                /* If the close_conn flag was turned on, we need */
268                /* to clean up this active connection.  This     */
269                /* clean up process includes removing the        */
270                /* descriptor from the master set and            */
271                /* determining the new maximum descriptor value  */
272                /* based on the bits that are still turned on in */
273                /* the master set.                               */
274                /*************************************************/
275                if (close_conn)
276                {
277                   close(i);
278                   FD_CLR(i, &master_set);
279                   if (i == max_sd)
280                   {
281                      while (FD_ISSET(max_sd, &master_set) == FALSE)
282                         max_sd -= 1;
283                   }
284                }
285             } /* End of existing connection is readable */
286          } /* End of if (FD_ISSET(i, &working_set)) */
287       } /* End of loop through selectable descriptors */
288 
289    } while (end_server == FALSE);
290 
291    /*************************************************************/
292    /* Clean up all of the sockets that are open                 */
293    /*************************************************************/
294    for (i=0; i <= max_sd; ++i)
295    {
296       if (FD_ISSET(i, &master_set))
297          close(i);
298    }
299 }

 

posted @ 2015-02-10 16:37  我的程序人生  阅读(588)  评论(0编辑  收藏  举报