Linux C++ Overlapped Server and Client Socket Example

Aim
The goal of this example is to create simple Linux C++ server that accepts socket connections and uses overlapped IO - ayncrhonous non blocking sockets - to service multiple sockets while being single threaded. This covers all the steps required to open a C++ client/server socket, set it to non-blocking and the basics of using the POSIX asynchronous API such as using 'select' and FD_SET to test the descriptors. Please leave any comments or questions at the end of this tutorial and I will endeavour to answer them. This tutorial shares the client code with Linux C++ Socket Example with Client Server and Mulit-Threading .

Assumptions
This article assumes that you have a Linux based C++ compiler installed and configured. You can use cygwin if you do not have a Linux environment. Also, this tutorial assumes that you have familiarized yourself with the Linux C++ forked and multithreaded examples available at this site. Overlapped io can be a little difficult to digest so it's best to be familiar with sockets, compiling and running the demo


Versions used in this example
Sofware/ComponentImage
Fedora Core 5N/A
Links to these files can be found here

You can download the zipped example here.

Steps required for a client socket
    Please see the Linux C++ Socket Example with Client Server and Mulit-Threading tutorial for the client.

Write and compile the Server
  1. The server listens on port 1101 for a connection. When it receives a connection it puts the socket into an array of file descriptors. It then uses select to test the file desctiptors to see which are ready to read and which are ready to write. Please be warned that the overlapped model in this example is an extremely simplified model and it doesn't reuse free'd sockets from the list of sockets, so you will ultimately exhaust the list. Rewrite this to use colsed/free'd sockets and you will be fine.
     
     1. #include <fcntl.h>
     2. #include <string.h>
     3. #include <stdlib.h>
     4. #include <errno.h>
     5. #include <stdio.h>
     6. #include <fcntl.h>
     7. #include <netinet/in.h>
     8. #include <resolv.h>
     9. #include <sys/socket.h>
    10. #include <arpa/inet.h>
    11. #include <unistd.h>
    12. 
    13. #define NOOF_SOCKETS 100
    14. 
    15. int main(int argv, char** argc){
    16. 
    17.     int host_port= 1101;
    18. 
    19.     int i;
    20. 
    21.     int socketlist[NOOF_SOCKETS]={0};
    22.     char socketbuffer[NOOF_SOCKETS][100];
    23.     int socketcounter=0;
    24.     int highestsocket=0;
    25. 
    26.     struct sockaddr_in my_addr;
    27. 
    28.     int hsock;
    29.     int * p_int ;
    30. 
    31.     socklen_t addr_size = 0;
    32.     sockaddr_in sadr;
    33. 
    34.     fd_set  rFd, rFdx;
    35.     fd_set  eFd, eFdx;
    36.     fd_set  wFd, wFdx;
    37. 
    38. 
    39.     hsock = socket(AF_INET, SOCK_STREAM, 0);
    40.     if(hsock == -1){
    41.         printf("Error initializing socket %d\n", errno);
    42.         goto FINISH;
    43.     }
    44.     
    45.     p_int = (int*)malloc(sizeof(int));
    46.     *p_int = 1;
    47.         
    48.     if( (setsockopt(hsock, SOL_SOCKET, SO_REUSEADDR, (char*)p_int, sizeof(int)) == -1 )||
    49.         (setsockopt(hsock, SOL_SOCKET, SO_KEEPALIVE, (char*)p_int, sizeof(int)) == -1 ) ){
    50.         printf("Error setting options %d\n", errno);
    51.         free(p_int);
    52.         goto FINISH;
    53.     }
    54.     free(p_int);
    55. 
    56.     /* Set the socket in non blocking mode */
    57.         if(fcntl(hsock, F_SETFL, O_NONBLOCK)==-1){
    58.         printf("Error setting in non-block %d",errno );
    59.         goto FINISH;
    60.     }
    61. 
    62. 
    63.     my_addr.sin_family = AF_INET ;
    64.     my_addr.sin_port = htons(host_port);
    65.     
    66.     memset(&(my_addr.sin_zero), 0, 8);
    67.     my_addr.sin_addr.s_addr = INADDR_ANY ;
    68.     
    69.     if( bind( hsock, (sockaddr*)&my_addr, sizeof(my_addr)) == -1 ){
    70.         fprintf(stderr,"Error binding to socket, make sure nothing else is listening on this port %d\n",errno);
    71.         goto FINISH;
    72.     }
    73.     if(listen( hsock, 10) == -1 ){
    74.         fprintf(stderr, "Error listening %d\n",errno);
    75.         goto FINISH;
    76.     }
    77. 
    78.     //Now lets do the server stuff
    79.     FD_ZERO(&rFd);
    80.     FD_ZERO(&eFd);
    81.     FD_ZERO(&wFd);
    82. 
    83.     addr_size = sizeof(sockaddr_in);
    84. 
    85.     int retv;
    86.     int csock;
    87. 
    88.     FD_SET(hsock, &rFd);
    89.     FD_SET(hsock, &eFd);
    90.     FD_SET(hsock, &wFd);
    91. 
    92.     while(true){
    93. 
    94.         memcpy(&rFdx, &rFd, sizeof(rFd));
    95.                 memcpy(&wFdx, &wFd, sizeof(wFd));
    96.         memcpy(&eFdx, &eFd, sizeof(eFd));
    97. 
    98.         if( (retv = select((hsock<highestsocket?highestsocket:hsock)+1, &rFdx, &wFdx, &eFdx, 0) ) > 0){
    99.             if(FD_ISSET(hsock, &rFdx)){
    100.                 if((csock = accept( hsock, (sockaddr*)&sadr, &addr_size))!= -1){
    101.                     printf("---------------------\nReceived connection from %s\n",inet_ntoa(sadr.sin_addr));
    102.                     socketlist[socketcounter++]= csock;
    103.                     if(csock > highestsocket)
    104.                            highestsocket=csock;    
    105.                     FD_SET(csock, &rFd);
    106.                     FD_SET(csock, &eFd);
    107.                 }
    108.             }
    109.             for(i=0;i<socketcounter;i++){
    110.                 if(socketlist[i]){
    111.                      if(FD_ISSET(socketlist[i], &rFdx)){
    112.                         int recvbytes;
    113.                         switch(recvbytes=recv(socketlist[i], socketbuffer[i], 100,0)){
    114.                         case -1: 
    115.                             printf("Read Error\n");
    116.                             FD_CLR(socketlist[i], &rFd);
    117.                             FD_CLR(socketlist[i], &eFd);
    118.                             socketlist[i]=0;
    119.                             break;
    120.                         case 0:
    121.                             printf("Socket closed\n");
    122.                             FD_CLR(socketlist[i], &rFd);
    123.                             FD_CLR(socketlist[i], &eFd);
    124.                             socketlist[i]=0;
    125.                             break;
    126.                         default:
    127.                             printf( "Read %d bytes from %d\n", recvbytes, socketlist[i]);    
    128.                             socketbuffer[i][recvbytes]='\0';
    129.                             FD_SET(socketlist[i], &wFd);
    130.                         }
    131.                     }
    132.                      if(FD_ISSET(socketlist[i], &wFdx)){
    133.                         int writebytes;
    134.                         if((writebytes=send(socketlist[i], socketbuffer[i], strlen(socketbuffer[i]), 0))>0){
    135.                             printf("Sent %d bytes to %d\n", writebytes, socketlist[i]);
    136.                             FD_CLR(socketlist[i], &wFd);
    137.                         }
    138.                         else{
    139.                             printf("Write Error\n");
    140.                             FD_CLR(socketlist[i], &rFd);
    141.                             FD_CLR(socketlist[i], &wFd);
    142.                             FD_CLR(socketlist[i], &eFd);
    143.                             socketlist[i]=0;
    144.                         }
    145.                     }
    146.                 }
    147.             }
    148.         }
    149.     }
    150.     
    151. FINISH:
    152. ;
    153. }
    Hide line numbers
    Save this file as OverlappedServer.cpp.

  2. Now compile the file you created in step 1.

    ..workspace\SocketExample>>g++ OverlappedServer.cpp -lsocket -g -o server -lnsl

Write and compile the Client
  1. The client reads a line from the console and sends this to the server. It then reads from the server and displays it on the console. Save this code as LinClient.cpp.
     1. #include <fcntl.h>
     2. #include <string.h>
     3. #include <stdlib.h>
     4. #include <errno.h>
     5. #include <stdio.h>
     6. #include <netinet/in.h>
     7. #include <resolv.h>
     8. #include <sys/socket.h>
     9. #include <arpa/inet.h>
    10. #include <unistd.h>
    11. 
    12. int main(int argv, char** argc){
    13. 
    14.     int host_port= 1101;
    15.     char* host_name="127.0.0.1";
    16. 
    17.     struct sockaddr_in my_addr;
    18. 
    19.     char buffer[1024];
    20.     int bytecount;
    21.     int buffer_len=0;
    22. 
    23.     int hsock;
    24.     int * p_int;
    25.     int err;
    26. 
    27.     hsock = socket(AF_INET, SOCK_STREAM, 0);
    28.     if(hsock == -1){
    29.         printf("Error initializing socket %d\n",errno);
    30.         goto FINISH;
    31.     }
    32.     
    33.     p_int = (int*)malloc(sizeof(int));
    34.     *p_int = 1;
    35.         
    36.     if( (setsockopt(hsock, SOL_SOCKET, SO_REUSEADDR, (char*)p_int, sizeof(int)) == -1 )||
    37.         (setsockopt(hsock, SOL_SOCKET, SO_KEEPALIVE, (char*)p_int, sizeof(int)) == -1 ) ){
    38.         printf("Error setting options %d\n",errno);
    39.         free(p_int);
    40.         goto FINISH;
    41.     }
    42.     free(p_int);
    43. 
    44.     my_addr.sin_family = AF_INET ;
    45.     my_addr.sin_port = htons(host_port);
    46.     
    47.     memset(&(my_addr.sin_zero), 0, 8);
    48.     my_addr.sin_addr.s_addr = inet_addr(host_name);
    49. 
    50.     if( connect( hsock, (struct sockaddr*)&my_addr, sizeof(my_addr)) == -1 ){
    51.         if((err = errno) != EINPROGRESS){
    52.             fprintf(stderr, "Error connecting socket %d\n", errno);
    53.             goto FINISH;
    54.         }
    55.     }
    56. 
    57.     //Now lets do the client related stuff
    58. 
    59.     buffer_len = 1024;
    60. 
    61.     memset(buffer, '\0', buffer_len);
    62. 
    63.     printf("Enter some text to send to the server (press enter)\n");
    64.     fgets(buffer, 1024, stdin);
    65.     buffer[strlen(buffer)-1]='\0';
    66.     
    67.     if( (bytecount=send(hsock, buffer, strlen(buffer),0))== -1){
    68.         fprintf(stderr, "Error sending data %d\n", errno);
    69.         goto FINISH;
    70.     }
    71.     printf("Sent bytes %d\n", bytecount);
    72. 
    73.     if((bytecount = recv(hsock, buffer, buffer_len, 0))== -1){
    74.         fprintf(stderr, "Error receiving data %d\n", errno);
    75.         goto FINISH;
    76.     }
    77.     printf("Recieved bytes %d\nReceived string \"%s\"\n", bytecount, buffer);
    78. 
    79.     close(hsock);
    80.     
    81. FINISH:
    82. ;
    83. }
    Hide line numbers

  2. Open a prompt to the working directory and compile the code using your C++ compiler

    ..workspace\SocketExample>g++ -o client LinClient.cpp


Run the example
  1. Open a prompt to the working directory and run the server.

    ..workspace\SocketExample>server

  2. Open another prompt to the working directory and run the Client.

    ..workspace\SocketExample>client

  3. Now type something into the client prompt and press ENTER 
  4. You will see the exchange between client and server. You can open many client prompts at the same time and test.

3 comments:

sarah said...

very informative post indeed .being enrolled in http://www.wiziq.com/course/5776-object-oriented-programming-with-c
i was looking for such articles online to assist me and your article helped me a lot. i really like that you are providing such information.

Baqer Naqvi said...

does it works on windows platform ???

Anonymous said...

Nope. Only UNIX.
For windows - rebuild with windows library.