1

I have the following C program whose main function runTCPConnectivityCheck opens a TCP socket, sends a string of bytes to it and closes it:

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

static int runTCPConnectivityCheck(const int port)
{
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0)
    {
        printf("socket failed\n");
        return 1;
    }

    int reuseAddrTrue = 1;
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseAddrTrue, sizeof(reuseAddrTrue)) != 0)
    {
        printf("setsockopt failed\n");
        return 1;
    }

    struct sockaddr_in serv_addr = { 0 };
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(port);

    if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) != 1)
    {
        printf("inet_pton failed\n");
        return 1;
    }

    if (connect(fd, (struct sockaddr*)(&serv_addr), sizeof(serv_addr)) != 0)
    {
        printf("connect failed\n");
        return 1;
    }

    const char* message = "hello";
    const size_t messageLength = strlen(message);
    if (send(fd, message, messageLength, 0) != messageLength)
    {
        printf("send failed\n");
        return 1;
    }

    shutdown(fd, SHUT_RDWR);
    close(fd);

    return 0;
}

int main()
{
    for (int i = 0; i < 10; ++i)
    {
        printf("%s\n", runTCPConnectivityCheck(5555) == 0 ? "success" : "failure");
        //sleep(1);
    }
}

If I attempt to test the same port repeatedly, the first call to runTCPConnectivityCheck works just fine but all subsequent calls fail on the call to connect. If I add the call to sleep in, then it also works. Presumably, the 1-second delay gives the network stack sufficient time to clean up the socket so that I can connect to the same port again.

First of all: Am I shutting the socket down properly? Secondly: If I am shutting it down properly, what is the standard way to determine when the network stack is ready to connect to the same host and port again (instead of the lame sleep I'm using currently)?

Update

More details: connect is failing with error code 61 or ECONNREFUSED while strerror is returning "Connection refused".

The server in this case is the netstat/nc command running in a loop on the same machine as the test program:

#!/bin/bash
while true; do
  nc -l 5555
done

Program is now C, for real.

Richard Cook
  • 32,523
  • 5
  • 46
  • 71
  • 1
    You're not doing anything wrong. Instead I would say that it's a problem in the peer you connect to, that it doesn't properly handle closed connections. – Some programmer dude Jan 04 '18 at 18:23
  • 1
    However, it would be helpful if you printed the value of `errno` if any system function (like `connect`, `send` etc.) fails. Or use `strerror` to get a printable string of the error, or print it using `perror`. Just so you know *what* the error is. Please do that, and then edit your question to include the error. – Some programmer dude Jan 04 '18 at 18:24
  • Do try and get out of the habit of `using namespace std`. That namespace prefix exists for a reason. – tadman Jan 04 '18 at 18:31
  • @NominalAnimal This has a lot of C++ stream code in it. – tadman Jan 04 '18 at 18:32
  • your code is fine, its the server thats suspect – pm100 Jan 04 '18 at 18:38
  • I can easily imagine that server might throttle connections by requiring a certain quite time between connection attempts. – SergeyA Jan 04 '18 at 18:41
  • Details updated: `connect` is returning `ECONNREFUSED`. The server is `nc` running locally. – Richard Cook Jan 04 '18 at 19:07
  • Program is also now legit C. – Richard Cook Jan 04 '18 at 19:14
  • 1
    Are you missing SO_REUSEADDR? – Mark Setchell Jan 04 '18 at 19:19
  • @MarkSetchell: I've added a call to `setsockopt`. This doesn't seem to make any difference. – Richard Cook Jan 04 '18 at 19:39
  • `SO_REUSEADDR` is a good point, but should in almost all cases not be needed for a client-type application or connection. It should almost always only be needed for the server-side. Also, only a single second should not be enough for the TCP state `TIME_WAIT` (which is what `SO_REUSEADDR` is used to bypass). – Some programmer dude Jan 04 '18 at 19:40
  • Can you run `nc` in verbose mode (add the `-v` flag, or possible `-vv` to be extra verbose)? What does it say? – Some programmer dude Jan 04 '18 at 19:45
  • 1
    @Someprogrammerdude: Unfortunately, it looks like neither `-v` nor `-vv` do anything with the version of `nc` running on my Mac. I agree with the general opinion that it's probably something on the server side. I've just hacked together a simple Python echo server and my client works just fine with that. – Richard Cook Jan 04 '18 at 21:41

1 Answers1

0

The general consensus is that there is nothing particularly wrong with the client code. It turns out that my test server is not able to keep up with my client:

#!/bin/bash
while true; do
  nc -l 5555
done

I wrote a forking server in Python which is able to service all of the client connections without difficulty.

Richard Cook
  • 32,523
  • 5
  • 46
  • 71