0

I'm developing a c++ program in Ubuntu that works with tcp sockets and websockets (using boost) an has to be restarted whenever the program dies. All of this is working properly except for one thing:

Sometimes when the program dies and gets relaunched one of the ports used appears as "not available", however, running netstat -tulpn doesn't show any process using that port. My master program is set to wait until the port is available to relaunch the program and it eventually gets launched (ports take like 1 minute to be available again, maybe a bit less) and I'm wondering if this time without being able to access the port is something related to how I close them or can be avoided somehow. My only idea to help with this was to kill any process using the port but I haven't found anything that way.

This issue isn't a big problem, it just makes the program stay down for a little bit but if I could reduce this delay it would be great.

Thank you!

Edit:

To help with my reply below, this is the port setup that gets hung up:

int start_listener()
{
    Logger* p_logger = NULL; 
    p_logger = Logger::get_instance();
    string log;
    int i_result;

    int listener = -1;

    struct addrinfo *result = NULL;
    struct addrinfo hints;

    char recvbuf[DEFAULT_BUFLEN];
    int recvbuflen = DEFAULT_BUFLEN;

    memset(&hints,0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    // Resolve the server address and port
    i_result = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
    if (i_result != 0) 
    {
    log="[server] getaddrinfo failed with error: {}"+errno;
    p_logger->error(log);
        return 1;
    }

    // Create a SOCKET for connecting to server
    listener = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (listener == -1) 
    {
    log="[server] socket failed with error: {}"+errno;
    p_logger->error(log);
        freeaddrinfo(result);
        return 1;
    }
    //Reuse ports faster
    int reuse = 1;
    if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) < 0)
    {
      p_logger->error("[server] setsockopt(SO_REUSEADDR) failed");
    }      

    if (setsockopt(listener, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuse, sizeof(reuse)) < 0)
    {
      p_logger->error("[server] setsockopt(SO_REUSEPORT) failed");
    }

    // Setup the TCP listening socket
    i_result = bind(listener, result->ai_addr, (int)result->ai_addrlen);
    if (i_result == -1) 
    {
    log="[server] bind failed with error: {}"+errno;
    p_logger->error(log);       freeaddrinfo(result);
        close(listener);
        return 1;
    }

    freeaddrinfo(result);

    i_result = listen(listener, SOMAXCONN);
    if (i_result == -1)
    {
        log="[server] listen failed with error: {}"+errno;
    p_logger->error(log);
        close(listener);
        return 1;
    }

  p_logger->info("[server] Waiting for client...");
  socket = accept(listener, NULL, NULL);
  if (socket == -1)
  {
      log="[server] accept failed with error: {}"+errno;
    p_logger->error(log);
    close(listener);
    return 1;
  }
  p_logger->info("[server] client accepted");
    
    while(true) 
    {
        // Receive until the peer shuts down the connection
        memset(recvbuf, 0, sizeof(recvbuf));
        i_result = recv(socket, recvbuf, recvbuflen, 0);

        if (i_result > 0) 
        {   
            if(!send_socket(convert_to_string(recvbuf, i_result))) 
            {
                p_logger->error("[server] Impossible to send");
            }
            else
            {
                p_logger->debug("[server] Message received");
            }
        }
    }

    // shutdown the connection since we're done
    i_result = shutdown(socket, SHUT_WR);
    if (i_result == -1) 
    {
    log="[server]  shutdown failed with error: {}"+errno;
    p_logger->error(log);
        close(socket);
        return 1;
    }

    // cleanup
    close(socket);
    return 0;

}

edit 2, Here's the other socket which works fine:

int start_server() 
{
    Logger* p_logger = NULL; // Create the object pointer for Logger Class
    p_logger = Logger::get_instance();
    string log;
    int i_result;

    int listen_socket = -1;

    struct addrinfo *result = NULL;
    struct addrinfo hints;

    char recvbuf[DEFAULT_BUFLEN];
    int recvbuflen = DEFAULT_BUFLEN;

    memset(&hints,0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    // Resolve the server address and port
    i_result = getaddrinfo(NULL, DEFAULT_PORT_VISION, &hints, &result);
    if (i_result != 0) 
    {
        log="[server] getaddrinfo failed with error: {}"+errno;
        p_logger->error(log);
        return 1;
    }

    // Create a SOCKET for connecting to server
    listen_socket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (listen_socket == -1) 
    {
        log="[server] socket failed with error: {}"+errno;
        p_logger->error(log);
        freeaddrinfo(result);
        return 1;
    }

    //Reuse ports faster
    int reuse = 1;
    if (setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) < 0)
    {
      p_logger->error("[server] setsockopt(SO_REUSEADDR) failed");
    }      

    if (setsockopt(listen_socket, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuse, sizeof(reuse)) < 0)
    {
      p_logger->error("[server] setsockopt(SO_REUSEPORT) failed");
    }        

    // Setup the TCP listening socket
    i_result = bind(listen_socket, result->ai_addr, (int)result->ai_addrlen);
    if (i_result == -1) 
    {
        log="[server] bind failed with error: {}"+errno;
        p_logger->error(log);
        freeaddrinfo(result);
        close(listen_socket);
        return 1;
    }

    freeaddrinfo(result);

    i_result = listen(listen_socket, SOMAXCONN);
    if (i_result == -1)
    {
        log="[server] listen failed with error: {}"+errno;
        p_logger->error(log);
        close(listen_socket);
        return 1;
    }

  p_logger->info("[server] Waiting for client...");  
  client_socket = accept(listen_socket, NULL, NULL);
  if (client_socket == -1)
  {
    log="[server] accept failed with error: {}"+errno;
    p_logger->error(log);
    close(listen_socket);
    return 1;
  }

    p_logger->info("[server] Client accepted"); 
    while(true) 
    {
        // Receive until the peer shuts down the connection
        memset(recvbuf, 0, sizeof(recvbuf));
        i_result = recv(client_socket, recvbuf, recvbuflen, 0);

        if (i_result > 0) 
        {   
            //printf("Bytes received: %d, buffer: %s\n", i_result, recvbuf);
            if(!cmd_cam.recive_data(recvbuf, i_result)) 
            {
                p_logger->error("[server] Impossible to read the message"); 
            }
        }
    }

    // shutdown the connection since we're done
    i_result = shutdown(client_socket, SHUT_WR);
    if (i_result == -1) 
    {
        log="[server] shutdown failed with error: {}"+errno;
        p_logger->error(log);
        close(client_socket);
        return 1;
    }

    // cleanup
    close(client_socket);

    return 0;
}

Pau
  • 31
  • 1
  • 1
  • 5
  • 1
    Does anything in [How do `SO_REUSEADDR` and `SO_REUSEPORT` differ?](https://stackoverflow.com/questions/14388706/how-do-so-reuseaddr-and-so-reuseport-differ) help? – Ted Lyngmo Jul 22 '20 at 08:41
  • A shorter version of what you need to do. [boost asio `SO_REUSEPORT`](https://stackoverflow.com/questions/34596638/boost-asio-so-reuseport) – Ted Lyngmo Jul 22 '20 at 08:55
  • If the local peer closes its endpoint before the remote peer closes its endpoint, the local endpoint enters `TIME_WAIT` state, which can take a few minutes to clear. The OS does this to make sure any pending data gets flushed from the network. `SO_REUSEADDR`/`SO_REUSEPORT` can help a local peer reuse the same port again while it is in this state. – Remy Lebeau Jul 23 '20 at 00:13
  • Hey! Thank you everyone for your answers and sorry for the delay replying. Setting both REUSEADDR and REUSEPORT worked great with 3 out of 4 ports in the program (I have 2 socket ports and 2 websockets ports). One of the socket ports fails binding a lot and reuseport doesn't help. Is there a way this could be a mistake in my code or something else related to how ports work? edit: I added code above to help my answer – Pau Jul 29 '20 at 10:49
  • There is a single port/service identifier DEFAULT_PORT mentioned in your code. Are you trying to bind both sockets to the same port? – n. m. could be an AI Jul 29 '20 at 11:15
  • That's a result from copy-pasting the setup code, i have a DEFAULT_PORT (27010) and DEFAULT_PORT_VISION (27015), both in the same file but different functions called separately, 27015 works ok but 27010 seems odd, fail only sometimes) – Pau Jul 29 '20 at 11:23
  • So you have two identical functions that only differ in this one identifier, right? – n. m. could be an AI Jul 29 '20 at 11:30
  • Mostly but not exactly the same, I posted the other function above – Pau Jul 29 '20 at 11:33
  • Anyway, 27015 seems to be used by some games. – n. m. could be an AI Jul 29 '20 at 11:33
  • This kind of code duplication would get your pull request thrown out with prejudice anywhere, any time. – n. m. could be an AI Jul 29 '20 at 11:35
  • I'm using a nvidia board at work so no games here, and the one failing is 27010 not 27015 – Pau Jul 29 '20 at 11:36
  • First thing first, use errno, strerror and/or perror to see what kind of error you are getting. Then use netstat to look at occupied ports. `"[server] getaddrinfo failed with error: {}"+errno` doesn't look like a valid operation to me. – n. m. could be an AI Jul 29 '20 at 12:09

0 Answers0