C program on socket programming for connecting Multiple clients to Server over TCP internet socket

C program on socket programming for connecting Multiple clients to Server over TCP internet socket

neelkanth_surekha#cat server.c 

/************************************************************************
 *  Handle multiple socket connections with select and fd_set on Linux 
 ************************************************************************/ 

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include <errno.h> 
#include <unistd.h> 
#include <arpa/inet.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <sys/time.h> 
#include <errno.h>   

/*******************************************************************
 * MACROS
 *******************************************************************/
#define   TRUE         1 
#define   FALSE        0 
#define   PORT         5000
#define   MAX_CLIENTS  30
//#define   DEBUG        0

/*******************************************************************
 * Global Variables
 *******************************************************************/
int new_socket;
/* Array to store fd's of clients which are connected to the server */
int client_socket[30];
int activity, i , valread , sd;
int max_sd = 0;
/* set of socket descriptors */
fd_set readfds;


/******************************************************************
 * Function Declarations : 
 ******************************************************************/
int  initialize_server_socket_and_listen(int *master_socket_fd);
void initialize_fdset_and_add_active_client_fds_to_readfds(int master_socket_fd);

/*******************************************************************
 * Function: initialize_server_socket_and_listen
 *
 * Definition: create socket, bind ip and port to a socket and listen
 *             for incoming connections. 
 *******************************************************************/    
int initialize_server_socket_and_listen(int *master_socket_fd)
{
    int opt = TRUE;
    struct sockaddr_in address_ser;

    memset(&address_ser, '\0', sizeof(address_ser));

    /* create a master socket */
    if( (*master_socket_fd = socket(AF_INET , SOCK_STREAM , 0)) == 0)
    {
        perror("socket failed");
        return -1;
    }

    /* 
     * set master socket to allow multiple connections , 
     * this is just a good habit, it will work without this.
     */
    if( setsockopt(*master_socket_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&opt,
                sizeof(opt)) < 0 )
    {
        perror("setsockopt");
        return -1;
    }

    /* type of Server socket created */
    address_ser.sin_family = AF_INET;
    address_ser.sin_addr.s_addr = INADDR_ANY;
    address_ser.sin_port = htons( PORT );

    /* Bind the socket to port 5000 */
    if (bind(*master_socket_fd, (struct sockaddr *)&address_ser, sizeof(address_ser))<0)
    {
        perror("bind failed");
        return -1;
    }
#if defined DEBUG
    printf("Listener on port %d \n", PORT);
#endif
    /* Try to specify maximum of 10 pending connections for the master socket */
    if (listen(*master_socket_fd, 10) < 0)
    {
        perror("listen");
        return -1;
    }

    return *master_socket_fd;
}

void initialize_fdset_and_add_active_client_fds_to_readfds(int master_socket_fd)
{
#if defined DEBUG
    printf("!!!!!!!!!!!!!! COMMON PRINT START BEFORE SELECT SYSTEM CALL !!!!!!!!!!!!!!!!\n");
#endif

    /* clear the socket set */
    FD_ZERO(&readfds);
#if defined DEBUG
    printf("Clear 'readfds' socket set\n");
#endif

    /* add master socket to set */
    FD_SET(master_socket_fd, &readfds);
#if defined DEBUG
    printf("Add Master socket fd :%d to 'readfds' set\n", master_socket_fd);
#endif

    /* Intially, set max_sd to master socket file descriptor */
    max_sd = master_socket_fd;

#if defined DEBUG
    printf("Add child sockets to 'readfds' set, if any....\n");
#endif
    for ( i = 0 ; i < MAX_CLIENTS ; i++)
    {
        /* socket descriptor */
        sd = client_socket[i];

        /* If valid socket descriptor, then add to read list */
        if(sd > 0) {
            FD_SET( sd , &readfds);
#if defined DEBUG
            printf("Adding existing client socket fd :%d from client_socket[%d] array to "
                    "'readfds' set\n", client_socket[i], i);
#endif
        }
        /* highest file descriptor number, need it for the select function */
        if(sd > max_sd) {
            max_sd = sd;
#if defined DEBUG
            printf("Current highest FD number that would be needed in "
                    "select call: %d\n", max_sd);
#endif
        }
    }

    return;    
}

int main(int argc , char *argv[])  
{  
    int master_socket_fd;
    socklen_t addrlen ;
    struct sockaddr_in address;  
    int iteration = 0;
    char msg_read_serv[100]; 
    char svr_hello_msg[100] = "I am server. Hello !!! \r\n";  

    /* initialise all client_socket[] to 0, so not checked */
    for (i = 0; i < MAX_CLIENTS; i++) {  
        client_socket[i] = 0;  
    }  

    if (initialize_server_socket_and_listen(&master_socket_fd) 
            == -1)
    {
#if defined DEBUG
        printf("Server Socket Initialization failure \n");
#endif
    }

    /* accept the incoming connection */
    puts("Waiting for connections ...");  

    while(TRUE)  
    {  
        initialize_fdset_and_add_active_client_fds_to_readfds(master_socket_fd);

        iteration++;
        printf("\n\nIteration : %d\n", iteration);

        /* Wait for an activity on one of the sockets , timeout is NULL , 
         * so wait indefinitely 
         */
#if defined DEBUG
        printf("Wait indefinitely here @ select system call and monitor incoming"
                " File descriptors \n");
#endif

        activity = select( max_sd + 1 , &readfds , NULL , NULL , NULL);  
        printf("Incoming connection no.: = %d\n", activity); 
#if defined DEBUG
        printf("!!!!!!!!!!!!!! COMMON PRINT END AFTER SELECT SYSTEM CALL !!!!!!!!!!!!!!!!!!\n\n");
#endif

        if ((activity < 0) && (errno!=EINTR))  
        {  
#if defined DEBUG
            printf("select error\n");  
#endif
        }  

        memset(&address, '\0', sizeof(address));    

        /*******************************************************************************
         * If TRUE : Then, listening on master server's socket.
         *
         * If FALSE: Then, active connection incoming data on client FD.
         *******************************************************************************/
        /* FD_ISSET() tests to see if a file descriptor is part of the set;  
         * this is useful after select() returns.
         */    
        if (FD_ISSET(master_socket_fd, &readfds))  
        { 
            /* If something happened on the master socket, then its an incoming connection. */
            printf("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n"); 
            addrlen = sizeof(struct sockaddr_in);

            printf("\ncase 1:  ACCEPT NEW CONNECTION HERE \n");
            /* ACCEPT NEW CONNECTION HERE */
            if ((new_socket = accept(master_socket_fd, 
                            (struct sockaddr *)&address, &addrlen))<0)  
            {  
                perror("accept");  
                exit(EXIT_FAILURE);  
            }  

            /* inform user of socket number - used in send and receive commands */
            printf("New connection , socket fd is %d , ip is : %s , port : %d\n",
                    new_socket , inet_ntoa(address.sin_addr) , ntohs
                    (address.sin_port));  

            /* add new socket to array of sockets */
            for (i = 0; i < MAX_CLIENTS; i++)  
            {  
                /* if position is empty */
                if( client_socket[i] == 0 )  
                {  
                    client_socket[i] = new_socket;  
                    FD_SET(new_socket, &readfds);  
                    printf("Adding new client socket fd :%d to client_socket[%d]\n" , 
                            client_socket[i], i);  
                    break;  
                }  
            } /* END of FOR LOOP */ 

            printf("send new connection greeting message to client fd: %d\n", 
                    new_socket); 
            if(send(new_socket, svr_hello_msg, strlen(svr_hello_msg), 0) 
                                                  != strlen(svr_hello_msg))  
            {  
                perror("send");  
            }  

            puts("Welcome message sent successfully");  
        } 
        /* Else, it's some IO operation on an active connection. i.e. client socket */
        else 
        {  
            printf("\n case 2: INCOMING CONNECTION IS AN ALREADY EXISTING CONNECTION  \n");    
            for (i = 0; i < MAX_CLIENTS; i++)  
            {  
                sd = client_socket[i];  
                /* FD_ISSET() tests to see, if a file descriptor is part of the set */    
                if (FD_ISSET( sd , &readfds))  
                { 
                    printf("Client fd: %d , is part of the 'readfds set'\n", sd);  
                    /* Read the incoming message from Client */ 
                    valread = read( sd , msg_read_serv, sizeof(msg_read_serv));
                    if (valread  > 0){
                        msg_read_serv[valread] = '\0';
                        printf("message received from client: %s and "
                                "no. of bytes received: %d\n", msg_read_serv, valread); 

                        /* Send message to Client */
                        char msg_client[100] = "I am server. I have received your message";
                        write(sd, msg_client, strlen(msg_client)); 
                        sleep(2);
                    }

                    /* 
                     * Check if, the Client has disconnected. If yes, then 
                     * close the file descriptor and remove the fd from the 
                     * client_socket array.
                     */
                    else if (valread == 0){  
                        /* Somebody disconnected, get his details and print */
                        getpeername(sd , (struct sockaddr*)&address , \
                                (socklen_t*)&addrlen);  
                        printf("Host disconnected , ip %s , port %d \n" , 
                                inet_ntoa(address.sin_addr) , ntohs(address.sin_port));  

                        /* Close the socket and mark as 0 in list for reuse */
                        printf("Client fd :%d removed from 'readfds' set as well as from client_socket[%d]"
                                " array \n", sd, i); 
                        FD_CLR( sd , &readfds);  
                        close( sd );  
                        client_socket[i] = 0;  
                    }
                }  
            } /* END of FOR loop */ 
        } 
        printf("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n"); 
    } /* END of Infinite While loop */  

    return 0;  
}  


neelkanth_surekha#cat client1.c 

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>


int connect_server(char *send_string,
        char *server_ip,
        int server_port)
{
    int sockfd = 0;

    struct sockaddr_in serv_addr;


    if((sockfd = socket(AF_INET, SOCK_STREAM, 0))< 0)
    {
        printf("\n Error : Could not create socket \n");
        return -1;
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(server_port);
    serv_addr.sin_addr.s_addr = inet_addr(server_ip);

    /* Here "connect" system call is used to connect the client
     * socket to the address of the server
     */

    printf("\n Connect to server\n");
    int ret = 0;
    while(1) {
        ret = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
        if((ret < 0) && (errno != ECONNREFUSED))
        {
            /* If Transport endpoint is already connected, then break out 
             * while loop. Till then, wait till server opens up connection
             */
            if (errno == EISCONN) {
                printf("\nConnect : %d %s \n", errno, strerror(errno));
                break;
            }    
            printf("\nConnect Error : %d %s \n", errno, strerror(errno));
            return -1;
        }
        sleep(1);
    }

    printf("\n Wait here till hello message arrives from server \n");
    /* RECEIVE HELLO MESSAGE FROM SERVER. WAIT TILL SERVER TELLS HELLO */
    int n;
    char recvbuff[1024];
    memset(recvbuff, '\0', sizeof(recvbuff));
    n  = read(sockfd, recvbuff, sizeof(recvbuff));
    if (n > 0) {
        recvbuff[n] = '\0';
        printf("Hello message received from server: '%s'\n", recvbuff);   
    } else if (n == 0) {
        printf("Server Connection Disconnected !!\n");
        exit(0);      
    } else {
        printf("\nRead Error : %d %s \n", errno, strerror(errno));
    }

    while(1) {
        write(sockfd, send_string, strlen(send_string));

        n = 0;
        memset(recvbuff, '\0', sizeof(recvbuff));
        n = read(sockfd , recvbuff, 1024);
        if (n  > 0)
        {
            recvbuff[n] = '\0';
            printf("message received from server: %s and "
                    "no. of bytes received: %d\n", recvbuff, n);
        } else if (n == 0) {
            printf("Server Connection Disconnected !!\n");
            break;      
        } 

        sleep(5);  
    }

    close(sockfd);
    return 1;
}


int main(void)
{
    char *server_ip = "127.0.0.1";
    int server_port = 5000;
    char message[1024];
    memset(message, '\0', sizeof(message));

    strcpy(message, "I am client 1");    

    connect_server(message, server_ip, server_port); 

    return 0;
}


Output:

       When Server is started first, clients immediately connect to server and each of the client would receive hello message from the server. Following this, server and client would indefinitely communicate with each other. If server is disconnected, then the clients would get terminated. 
      When Clients are started first, they would wait for Server to open the port  so that they can connect to the server.


Click on the below image to expand

Server logs

neelkanth_surekha#./server
Waiting for connections ...

Iteration : 1
Incoming connection no.: = 1
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
case 1:  ACCEPT NEW CONNECTION HERE
New connection , socket fd is 4 , ip is : 127.0.0.1 , port : 38378
Adding new client socket fd :4 to client_socket[0]
send new connection greeting message to client fd: 4
Welcome message sent successfully
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

Iteration : 2
Incoming connection no.: = 1
 case 2: INCOMING CONNECTION IS AN ALREADY EXISTING CONNECTION
Client fd: 4 , is part of the 'readfds set'
message received from client: I am client 1 and no. of bytes received: 13
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

Iteration : 3
Incoming connection no.: = 1
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
case 1:  ACCEPT NEW CONNECTION HERE
New connection , socket fd is 5 , ip is : 127.0.0.1 , port : 38380
Adding new client socket fd :5 to client_socket[1]
send new connection greeting message to client fd: 5
Welcome message sent successfully
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

Iteration : 4
Incoming connection no.: = 1
 case 2: INCOMING CONNECTION IS AN ALREADY EXISTING CONNECTION
Client fd: 5 , is part of the 'readfds set'
message received from client: I am client 1 and no. of bytes received: 13
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

Iteration : 5
Incoming connection no.: = 1
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
case 1:  ACCEPT NEW CONNECTION HERE
New connection , socket fd is 6 , ip is : 127.0.0.1 , port : 38382
Adding new client socket fd :6 to client_socket[2]
send new connection greeting message to client fd: 6
Welcome message sent successfully
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

Iteration : 6
Incoming connection no.: = 1
 case 2: INCOMING CONNECTION IS AN ALREADY EXISTING CONNECTION
Client fd: 6 , is part of the 'readfds set'
message received from client: I am client 1 and no. of bytes received: 13
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

Iteration : 7
Incoming connection no.: = 1
 case 2: INCOMING CONNECTION IS AN ALREADY EXISTING CONNECTION
Client fd: 4 , is part of the 'readfds set'
message received from client: I am client 1 and no. of bytes received: 13
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

Iteration : 8
Incoming connection no.: = 1
 case 2: INCOMING CONNECTION IS AN ALREADY EXISTING CONNECTION
Client fd: 5 , is part of the 'readfds set'
message received from client: I am client 1 and no. of bytes received: 13

$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

Iteration : 9
Incoming connection no.: = 1
 case 2: INCOMING CONNECTION IS AN ALREADY EXISTING CONNECTION
Client fd: 6 , is part of the 'readfds set'
message received from client: I am client 1 and no. of bytes received: 13
^C
neelkanth_surekha#

Client 1 logs
neelkanth_surekha#./client1
 Connect to server
Connect : 106 Transport endpoint is already connected
 Wait here till hello message arrives from server
Hello message received from server: 'I am server. Hello !!!
'
message received from server: I am server. I have received your message and no. of bytes received: 41
message received from server: I am server. I have received your message and no. of bytes received: 41
Server Connection Disconnected !!
neelkanth_surekha#


Client 2 logs

neelkanth_surekha#./client2
 Connect to server
Connect : 106 Transport endpoint is already connected
 Wait here till hello message arrives from server
Hello message received from server: 'I am server. Hello !!!
'
message received from server: I am server. I have received your message and no. of bytes received: 41
message received from server: I am server. I have received your message and no. of bytes received: 41
Server Connection Disconnected !!


Client 3 logs

neelkanth_surekha#./client3

 Connect to server

Connect : 106 Transport endpoint is already connected 

 Wait here till hello message arrives from server 
Hello message received from server: 'I am server. Hello !!! 
'
message received from server: I am server. I have received your message and no. of bytes received: 41
message received from server: I am server. I have received your message and no. of bytes received: 41
Server Connection Disconnected !!