How to prevent Socket server to disconnect

797 views Asked by At

I wrote a code about sockets and I cannot prevent the server to close when the client sends a message or when the client do a CTRL + C The server goes off.

Server.c:

#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>

#define PORT 8888
#define BACKLOG 10
#define LOOP(i, n) for((i) = 0; (i) < (n); (i)++)
#define BUFFSIZE 6500

int create_fd( struct sockaddr_in *server );
int bind_fd  ( struct sockaddr_in *server, int servSocket );
int list_fd( int servSocket );
int accept_fd( struct sockaddr_in *server, int servSocket  );
int select_fd ( fd_set *readfds, int max_sd );
ssize_t send_fd( int new_fd, const char *const msg );

int main( void ){
    const char msg[BUFFSIZE] = "Server is On\n";
    char recive[BUFFSIZE];
    int servSocket ,clientSocket ,i ,sd;
    int client_socket_max[30] = { 0 } , max_clients = 30;
    int max_sd;
    ssize_t valread;
    struct sockaddr_in server;
    for (i = 0; i < max_clients; i++)
    {
        client_socket_max[i] = 0;
    }

    //set of socket descriptors
    fd_set readfds;

    size_t addr_size = sizeof(server);

    //create
    servSocket = create_fd( &server );

    //bind the socket to localhost port 8888
    bind_fd(&server, servSocket );

    //listen the socket to localhost port 8888
    printf("\tServer is On\nListener on port %d \n", PORT);
    list_fd( servSocket );

    //accept the incoming connection
    printf( "\n\tWaiting for connections ...\n" );
    while(1){
        //clear the socket set
        FD_ZERO(&readfds);
        //add master socket to set
        FD_SET(servSocket, &readfds);
        max_sd = servSocket;
        //add child sockets to set
        LOOP( i, max_clients ){
            //socket descriptor
            sd = client_socket_max[i];
            //if valid socket descriptor then add to read list
            if(sd > 0){
                FD_SET( sd , &readfds);
            }
            //highest file descriptor number, need it for the select function
            if(sd > max_sd){
                max_sd = sd;
            }
        }
        //wait for an activity on one of the sockets , timeout is NULL , so wait indefinitely
        select_fd( &readfds, max_sd );
        //select( max_sd + 1 , &readfds , NULL , NULL , NULL);

        //If something happened on the master socket , then its an incoming connection
        if ( FD_ISSET( servSocket, &readfds ) ){
               clientSocket = accept_fd( &server, servSocket );

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

            //send new connection greeting msg
            send_fd( clientSocket, msg );
            /*if( send(clientSocket, msg, strlen(msg), 0) != (long int)strlen(msg) ){
                perror("send");
            }*/

            puts("Welcome msg sent successfully");
            //add new socket to array of sockets
            LOOP( i, max_clients ){
                //if position is empty
                if( client_socket_max[i] == 0 ){
                    client_socket_max[i] = clientSocket;
                    printf("Adding to list of sockets as %d\n" , i);
                    break;
                }
            }
        }
        //else its some IO operation on some other socket :)
        LOOP( i, max_clients ){
            sd = client_socket_max[i];
            if (FD_ISSET( sd , &readfds)){
                //Check if it was for closing , and also read the incoming msg
                if (( valread = read( sd , recive, 1024)) == 0){
                    //Somebody disconnected , get his details and print
                    getpeername(sd , (struct sockaddr*)&server , (socklen_t*)&addr_size);
                    printf("Host disconnected , ip %s , port %d \n" , inet_ntoa(server.sin_addr) , ntohs(server.sin_port));

                    //Close the socket and mark as 0 in list for reuse
                    close( sd );
                    client_socket_max[i] = 0;
                }else{//Echo back the msg that came in
                    //set the string terminating NULL byte on the end of the data read
                    recive[valread] = '\0';
                    send(sd , recive , strlen(recive) , 0 );
                }
            }
        }
    }
    close( servSocket );
}

int create_fd( struct sockaddr_in *server ){
    int opt = 1;
    int servSocket = socket(AF_INET , SOCK_STREAM , 0);
    if (servSocket == -1 ){
        printf("Error, socket()\n");
        fprintf(stderr, "socket: %s (%d)\n", strerror(errno), errno);
        exit ( EXIT_FAILURE );
    }
    //set master socket to allow multiple connections , this is just a good habit, it will work without this
    if( setsockopt(servSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 ){
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }
        //type of socket created
    server->sin_family      = AF_INET;
    server->sin_addr.s_addr = INADDR_ANY;
    server->sin_port        = htons( PORT );

    //printf("socket() \tOK\n");
    return servSocket;
}

int bind_fd  ( struct sockaddr_in *server, int servSocket ){
    int bindfd = bind( servSocket, (struct sockaddr *)server, sizeof(*server) );
    if (bindfd == -1 ){
        printf("Error, bind(), check line 34\n");
        fprintf(stderr, "bind: %s (%d)\n", strerror(errno), errno);
        exit ( EXIT_FAILURE );
    }else{
        //printf("bind() \t\tOK\n");
        return bindfd;
    }
}

int list_fd( int servSocket ){
    int listfd = listen(servSocket, BACKLOG);
    if (listfd == -1 ){
        printf("Error, listen()\n");
        fprintf(stderr, "listen: %s (%d)\n", strerror(errno), errno);
        exit ( EXIT_FAILURE );
    }else{
        //printf("listen() \tOK\n");
        return listfd;
    }
}

int accept_fd( struct sockaddr_in *server, int servSocket  ){
    int new_fd;
    socklen_t addr_size = sizeof( server );
    new_fd = accept(servSocket, (struct sockaddr *)server, (socklen_t*)&addr_size);
    if (new_fd == -1 ){
        printf("Error, accept()\n");
        fprintf(stderr, "accept: %s (%d)\n", strerror(errno), errno);
        exit ( EXIT_FAILURE );
    }else{
        //printf("accept() \tOK\n");
        return new_fd;
    }
}

int select_fd ( fd_set *readfds, int max_sd ){
    int activity = select( max_sd + 1 , readfds , NULL , NULL , NULL);
    if ((activity < 0) && (errno!=EINTR)){
        printf("select error");
        exit ( EXIT_FAILURE );
    }else{
        return activity;
    }
}

ssize_t send_fd( int new_fd, const char *const msg ){
    size_t len = strlen(msg);
    ssize_t sendfd;
    sendfd = send( new_fd, msg, len, 0 );
    if (sendfd == -1 ){
        printf("Error, write()\n");
        fprintf(stderr, "recv: %s (%d)\n", strerror(errno), errno);
        exit ( EXIT_FAILURE );
    }else{
        printf("write() \tOK\n");
        printf("Client sent:\t%s", msg );
        return sendfd;
    }
}

Client.c:

#include <stdio.h>
#include <string.h>   //strlen
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>   //close
#include <arpa/inet.h>    //close
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h> //FD_SET, FD_ISSET, FD_ZERO macros

#define PORT 8888
#define BUFFSIZE 6500

int create_fd( struct sockaddr_in server );
int connect_fd( const int servSocket, struct sockaddr_in *server );
int opt = 1;


int main ( void ){
    struct sockaddr_in server;
    char recivemsg[BUFFSIZE];
    char sendmsg[BUFFSIZE];
    memset( recivemsg, 0, sizeof(*recivemsg) );
    memset( sendmsg, 0, sizeof(*sendmsg) );

    int servSocket = socket(AF_INET , SOCK_STREAM , 0);
    if (servSocket == -1 ){
        printf("Error, socket()\n");
        fprintf(stderr, "socket: %s (%d)\n", strerror(errno), errno);
        exit ( EXIT_FAILURE );
    }
    //set master socket to allow multiple connections , this is just a good habit, it will work without this
    if( setsockopt(servSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 ){
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }
        //type of socket created
    server.sin_family      = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port        = htons( PORT );

    if ( ( connect( servSocket, ( struct sockaddr* )&server, sizeof( server ) ) ) < 0 ){
        printf("Error, connect()\n");
        fprintf(stderr, "connect: %s (%d)\n", strerror(errno), errno);
        exit( EXIT_FAILURE );
    }

    if ( recv( servSocket, recivemsg, sizeof ( recivemsg ), 0 ) < 0 ){
        printf("Error, recv()\n");
        fprintf(stderr, "recv: %s (%d)\n", strerror(errno), errno);
        exit( EXIT_FAILURE );
    }

    printf("Send a msg to the Server:> ");
    fgets(sendmsg, BUFFSIZE, stdin );
    if ( send( servSocket, sendmsg, sizeof ( sendmsg ), 0 ) < 0 ){
        printf("Error, send()\n");
        fprintf(stderr, "send: %s (%d)\n", strerror(errno), errno);
        exit( EXIT_FAILURE );
    }

    close( servSocket );
}
1

There are 1 answers

2
Some programmer dude On

There are a few problems with the code that together could lead to premature exits from your server program.

The most important is that you don't check for errors from either your read or send calls.

If the read call fails it will return -1 which you don't check for (and which will lead to you using -1 as index into your array receive, which is out of bounds and lead to undefined behavior). That will in turn lead to you calling send with a broken connection, which will cause the operating system to send a SIGPIPE signal to your process.

The default behavior of SIGPIPE is to terminate the process.

The usual way to handle this is to ignore the SIGPIPE signal, as then send would return with an error (it returns -1 with errno set to EPIPE) that you should handle.

The common way to handle errors (from either read or send) is to simply close your end of the connection. That's because most errors are simply not recoverable.