5

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

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

3 回答 3

19

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

于 2013-05-04T20:46:39.617 回答
2

你得到一个错误,然后bind()你正在设置。因此它没有效果。SO_REUSEADDR

于 2013-05-05T00:30:26.843 回答
-2

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

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

于 2013-05-04T21:36:33.183 回答