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:
- 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.
- 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.
- After the socket descriptor is created, the bind() gets a unique name for the socket.
- The listen() allows the server to accept incoming client connections.
- 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.
- 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.
- The accept() and recv() APIs are completed when the EWOULDBLOCK is returned.
- The send() API echoes the data back to the client.
- 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 }