“绑定:已经在使用的地址”,即使设置了SO_REUSEADDR也是如此

时间:2013-05-04 20:07:14

标签: c sockets bind

我写了一个简单的echo服务器,其中包括以下行:

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

然而,尽管如此,当我尝试在我最近使用的套接字上调用bind时,我仍然会收到错误。事实上,如果我尝试在我在此程序中使用的套接字上调用bind,即使它不是最近的 - 就像它们没有被内核清除一样,我会收到此错误。还有什么我必须要做的吗?

这是完整的代码:

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

3 个答案:

答案 0 :(得分:19)

调用bind()后设置SO_REUSEADDR。您需要在绑定之前设置它,而不是在之后设置它。

答案 1 :(得分:2)

您在bind()上收到错误,之后您正在设置SO_REUSEADDR 。因此它没有效果。

答案 2 :(得分:-2)

简短的版本是内核保留它,因为有一段时间它无法判断它所获得的数据包是用于旧程序还是新程序。等待总是最安全的。也就是说,我的理解是现代网络,旧数据包迟到的可能性非常小。

真正的简短版本是它的功能(至少它曾经是)不是一个bug。