limp_chimp
limp_chimp

Reputation: 15163

"bind: address already in use" even with SO_REUSEADDR set

I've written a simple echo server, which includes the following line:

int yes = 1;
if (setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
    perror("setsockopt");
    exit(1);
}

However despite this, I'm still getting an error when I try to call bind on a socket I've recently used. In fact, I'm getting this error if I try to call bind on a socket I've used in this program, period, even if it's not recent - like they're not being cleared by the kernel or something. Is there something else I have to do?

Here's the full code:

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

void prepareHints(struct addrinfo *hints, int tcp_udp) {
    memset(hints, 0, sizeof(struct addrinfo));
    hints->ai_family = AF_UNSPEC;
    hints->ai_socktype = (tcp_udp == 1) ? SOCK_STREAM : SOCK_DGRAM;
    hints->ai_flags = AI_PASSIVE; /* autofill IP */
}

void writeSocket(int fd, const char *msg) {
    size_t nbytes = 0;
    size_t len = strlen(msg);
    while (nbytes < len)
        nbytes += send(fd, msg, len, 0);
}

void waitLoop(int sockfd) {
    int clientfd, nbytes;
    struct sockaddr addr;
    socklen_t len;
    char buf[512];
    while(1) {
        clientfd = accept(sockfd, &addr, &len);
        if (clientfd < 0) {
            perror("accept");
            exit(1);
        }
        while ((nbytes = recv(clientfd, buf, 512, 0)) != EOF) {
            buf[nbytes] = '\0';
            strcat(buf, "\r\n");
            writeSocket(clientfd, buf);
        }
        close(clientfd);
    }
}

int main(int argc, char **argv) {
    const char *port = (argc >= 2) ? argv[1] : "7474";
    struct addrinfo hints, *res;
    prepareHints(&hints, 1);

    int status = getaddrinfo(NULL, port, &hints, &res);
    if (status != 0) {
        printf("Error on getaddrinfo\n");
        exit(1);
    }

    /* scan through sockaddr's returned by getaddrinfo until we successfully set up a socket with one */
    int socketfd;
    struct addrinfo *cur;
    for (cur = res; cur != NULL; cur = cur->ai_next) {
        if ((socketfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) >= 0)
            break;
    }
    /* make sure we actually found one */
    if (socketfd == -1) {
        printf("Error on socket\n");
        exit(1);
    }
    /* bind the socket to the struct sockaddr_in contained in res */
    int bindres = bind(socketfd, cur->ai_addr, cur->ai_addrlen);
    if (bindres != 0) {
        perror("bind");
        exit(1);
    }

    int yes = 1;
    if (setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
        perror("setsockopt");
        exit(1);
    }

    if (listen(socketfd, 5) < 0) {
        printf("error on listen\n");
        exit(1);
    }

    printf("success, listening on socket %d, port %d\n", socketfd, ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port));
    waitLoop(socketfd);
    return 0;
}

Upvotes: 5

Views: 10726

Answers (3)

user207421
user207421

Reputation: 310913

You are getting an error on bind() and you are setting SO_REUSEADDR afterwards. It therefore has no effect.

Upvotes: 2

elvena
elvena

Reputation: 421

You are setting SO_REUSEADDR after calling bind(). You need to set it before binding, not after.

Upvotes: 21

xaxxon
xaxxon

Reputation: 19761

The short version is that the kernel keeps it around because there's a period of time in which it can't tell if the packets it is getting are for the old program or the new program. It's always safest to wait. That said, my understanding is with modern networks, the chances of old packets coming in a minute late is very small.

The really short version is that it's a feature (at least it used to be) not a bug.

Upvotes: -2

Related Questions