0

I have written a C program that I hope to use as a simple TCP/IP based service. The program has to open a listening TCP socket. Whenever someone writes to the socket, my program has to read the message, do a little processing, then send a different message back. The program has to run forever.

The program compiles and runs. (I’m using gcc 7.4.0) Trouble is, after a couple thousand messages are successfully processed without any problems, the program starts printing this:

...
 Connection FAILED :: Value of errno: 24
 Connection FAILED :: Value of errno: 24
 Connection FAILED :: Value of errno: 24
 Connection FAILED :: Value of errno: 24
...

Errno 24 means “too many file descriptors open,” which makes me wonder if my program is allocating something (sockets? memory?) and not properly deallocating it later. But I can’t spot where I’m going wrong.

Let me show you my code. I followed a tutorial I liked, where they gathered all the relevant socket info in one struct:

typedef struct{
        int sock;
        struct sockaddr address;
        socklen_t addr_len;
} connection_t;

The main() sets up the socket, then listens to it in an infinite loop to listen:

int main(int argc, char ** argv) {
        int             sock = -1;
        struct          sockaddr_in address;
        int             port = 12345;
        connection_t*   connection;


        // Create the listening socket
        sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (sock <= 0){
                fprintf(stderr, "%s: error: cannot create socket\n", argv[0]);
                return -3;
        }

        // Bind socket to port
        address.sin_family = AF_INET;
        address.sin_addr.s_addr = INADDR_ANY;
        address.sin_port = htons(port);
        if (bind(sock, (struct sockaddr *)&address, sizeof(struct sockaddr_in)) < 0){
                fprintf(stderr, "%s: error: cannot bind socket to port %d\n", argv[0], port);
                return -4;
        }

        // Listen on the port
        if (listen(sock, 5) < 0){
                fprintf(stderr, "%s: error: cannot listen on port\n", argv[0]);
                return -5;
        }

        // We're ready to go...!
        printf("%s: ready and listening\n", argv[0]);

        while (1){
                // Accept incoming connections
                connection = (connection_t *)malloc(sizeof(connection_t));
                connection->addr_len = 20;

                connection->sock = accept(sock, &connection->address, &connection->addr_len);

                if (connection->sock <= 0){
                        // ***********************************************
                        // ***  This is where the error happens!
                        printf("Connection FAILED :: Value of errno: %d\n ", errno);
                        // ***********************************************
                }
                else{
                        printf("SERVER...  New Msg received...\n");
                        readMsgAndReply( connection );
                }
                free(connection);
        }
        return 0;
}

You’ll note that I get my “Connection FAILED” message when accept() cannot successfully accept a new, incoming connection – I wish I knew why.

If accept() is successful, my code calls readMsgAndReply():

void readMsgAndReply( connection* conn ){

        char* buffer;
        char* reply = "Your Msg was received!";
        int ret, len, replyLen;
        long addr = 0;

        // First call to read() measures the length of the sent message
        ret = read(conn->sock, &len, sizeof(int));
        if( ret < 0 ){
                printf( "***readMsgAndReply() ERROR:  read() error\n" );
        }
        if( len > 0 ){
                addr = (long)((struct sockaddr_in *)&conn->address)->sin_addr.s_addr;
                buffer = (char *)malloc((len+1)*sizeof(char));
                buffer[len] = 0;

                // Second call to read() actually reads the message from the socket
                ret = read(conn->sock, buffer, len);
                if( ret < 0 ){
                        printf( "***readMsgAndReply() ERROR:  ret < 0\n");
                }
                else{
                        printf("***readMsgAndReply() message size :: %d\n",  len);
                        printf("***readMsgAndReply() is           :: \"%s\"\n", buffer);
                }

                // write reply message back to the client
                replyLen = write(conn->sock, reply, strlen(reply));
                if(replyLen > 0)
                        printf("===>  Reply written, %d bytes sent!\n", replyLen);
                else
                        printf("--->  No reply written\n");

                free(buffer);
        }
}

There you have it. This code works beautifully for the first several thousand received messages, then spits out ERRNO 24. Anyone see what I’m doing wrong?

Pete
  • 1,511
  • 2
  • 26
  • 49
  • 1
    https://stackoverflow.com/questions/43368928/do-i-have-to-close-the-socket-on-the-server-side-after-client-disconnects – Retired Ninja Aug 11 '20 at 03:38
  • when outputting an error message, where the error indication is from a C library function should output to `stderr`, both your error message AND the text reason the system thinks the error occurred. suggest: `perror( "read failed" );` There are several error messages being output in the posted code. They should all be informing the user of the text reason the system thinks the error occurred. – user3629249 Aug 11 '20 at 04:43
  • regarding: `buffer = (char *)malloc((len+1)*sizeof(char));` 1) in C, the returned type is `void*` which can be assigned to any pointer. Casting just clutters the code and is error prone. 2) the expression; `sizeof(char)` is defined in the standard as 1. multiplying anything by 1 has absolutely no effect (and again, just clutters the code) Suggest removing that expression. 3) always check (!=NULL) the returned value to assure the operation was successful. if not successful (==NULL) then call `perror( "malloc failed") and drop handling of that specific connection. (cont) – user3629249 Aug 11 '20 at 04:48
  • (cont) since the posted code is handling client connections 'serially' there is no need to `malloc()` the connection info, However, suggest setting up a `thread pool` and passing each connection to one of the threads in the pool then immediately going back to the top of the `accept()` loop. suggest reading: [thread pool](https://docs.oracle.com/cd/E19253-01/816-5137/ggedn/index.html) – user3629249 Aug 11 '20 at 04:52
  • on the calls to `read()` need to also check for a returned value of 0. 0 means the client hung up – user3629249 Aug 11 '20 at 04:54
  • regarding: `replyLen = write(conn->sock, reply, strlen(reply));` Need to also check that the whole message was written (replyLen == strlen(reply)) and if not == then use a sliding window into the 'reply' to try again, until all the reply is sent – user3629249 Aug 11 '20 at 04:56
  • regarding: `ret = read(conn->sock, buffer, len);` The variable `len` is currently defined as a `int` (a signed value), however; the function is expecting a `size_t` (a long unsigned value) and returns a `ssize_t`, not a `int` – user3629249 Aug 11 '20 at 04:58
  • when compiling, always enable the warnings, then fix those warnings. ( for `gcc`, at a minimum use: `-Wall -Wextra -Wconversion -pedantic -std=gnu11` ) Note: other compilers use different options to produce the same results. – user3629249 Aug 11 '20 at 05:00
  • @user3629249 Wow, thank you - this is a lot of wisdom. I am taking all of it. Thank you for taking the time to help me – Pete Aug 11 '20 at 22:29

1 Answers1

2

Before exiting function readMsgAndReply you need to close socket.

 close(connection->sock);
I S
  • 436
  • 3
  • 7
  • @I S Yes! You are correct! I added that one line, and now the code runs for hours. Well spotted! Thank you, this was awesome advice. :) – Pete Aug 11 '20 at 22:28