unix 在MacOS上运行的程序的Socket bind()失败,出现参数无效错误

jxct1oxe  于 2022-11-04  发布在  Unix
关注(0)|答案(1)|浏览(378)

我正在尝试运行一个简单的C程序,该程序使用bind()函数将IPv4/IPv6地址绑定到套接字。
下面是代码:
第一个
当这个程序在Linux上编译和运行时,它可以正常工作,没有任何错误。但是当同一个程序在MacOS上编译和运行时,bind()函数返回一个“无效参数”错误。
我查看了bind()函数的手册页,了解了导致此错误的可能原因。三个可能的原因是:
1.套接字已绑定到另一地址
1.传递给函数的addrlen值不正确

  1. addr不是此套接字域的有效地址
    我能够证实这不是由于第一和第三个原因。
    我的问题是,为什么传递给bind()函数的addrlen值在Unix系统上运行时会抛出“参数不正确”错误,而在Linux系统上运行时却能正常工作?
u4vypkhs

u4vypkhs1#

您传递给bind()addrlen值必须与您在socket()中指定的地址族完全匹配。这意味着addrlen必须设置为sizeof(sockaddr_in)(对于AF_INET)和sizeof(sockaddr_in6)(对于AF_INET6)。使用sizeof(sockaddr_storage)是错误的值,由于X1M9N1X被设计为足够大以容纳所有可能的X1M10N1X类型,因此其大小可以大于X1M11N1X。
另一方面,在调用accept()时,需要事先将addrlen设置为addr的完整大小,这样它就知道在将客户端地址写入addr时需要使用多少内存。addrlen将被调整为写入的实际大小。但是,您不能简单地将int*类型转换为socklen_t*,因此addrlen需要是实际的socklen_t类型。此外,bind()需要的是socklen_t,而不是int
请尝试以下内容:

int main(int argc, char *argv[])
{
    int socket_fd = -1, af;
    socklen_t addrlen; // <-- add this!
    struct sockaddr_storage addr = {0};
    unsigned connections = 0;
    pthread_t workers[WORKER_NUM] = { 0 };
    int client_sock_fds[WORKER_NUM] = { 0 };
    char ip_string[64];

    if (argc > 1 && strcmp(argv[1], "inet6") == 0) {
        af = AF_INET6;
        addrlen = sizeof(struct sockaddr_in6); // <-- add this!
        init_sockaddr_inet6((struct sockaddr_in6 *)&addr);
    }
    else {
        af = AF_INET;
        addrlen = sizeof(struct sockaddr_in); // <-- add this!
        init_sockaddr_inet((struct sockaddr_in *)&addr);
    }

    printf("[Server] Create socket\n");
    socket_fd = socket(af, SOCK_STREAM, 0);
    if (socket_fd < 0) {
        perror("Create socket failed");
        goto fail;
    }

    printf("[Server] Bind socket\n");
    if (bind(socket_fd, (struct sockaddr *)&addr, addrlen) < 0) {
        perror("Bind failed");
        goto fail;
    }

    printf("[Server] Listening on socket\n");
    if (listen(socket_fd, 3) < 0) {
        perror("Listen failed");
        goto fail;
    }

    printf("[Server] Wait for clients to connect ..\n");
    while (connections < WORKER_NUM) {
        addrlen = sizeof(addr); // <-- add this!
        client_sock_fds[connections] =
            accept(socket_fd, (struct sockaddr *)&addr, &addrlen);
        if (client_sock_fds[connections] < 0) {
            perror("Accept failed");
            break;
        }

        if (sockaddr_to_string((struct sockaddr *)&addr, ip_string,
                               sizeof(ip_string) / sizeof(ip_string[0])) != 0) {
            printf("[Server] failed to parse client address\n");
            goto fail;
        }

        printf("[Server] Client connected (%s)\n", ip_string);
        if (pthread_create(&workers[connections], NULL, run,
                           &client_sock_fds[connections]) != 0) {
            perror("Create a worker thread failed");
            shutdown(client_sock_fds[connections], SHUT_RDWR);
            break;
        }

        connections++;
    }

    if (connections == WORKER_NUM) {
        printf("[Server] Achieve maximum amount of connections\n");
    }

    for (int i = 0; i < connections; i++) { // <-- needs to be the actual thread count, not WORKER_NUM!
        pthread_join(workers[i], NULL);
    }

    printf("[Server] Shuting down ..\n");
    shutdown(socket_fd, SHUT_RDWR);
    sleep(3);
    printf("[Server] BYE \n");
    return EXIT_SUCCESS;

fail:
    printf("[Server] Shuting down ..\n");
    if (socket_fd >= 0)
        close(socket_fd);
    sleep(3);
    return EXIT_FAILURE;
}

也就是说,应该使用getaddrinfo()来初始化传递给bind()sockaddr_...,而不应该手动初始化它。

...

# include <sys/types.h>

# include <sys/socket.h>

# include <netdb.h>

int main(int argc, char *argv[])
{
    int socket_fd = -1, res;
    ...
    struct addrinfo hints = { 0 };
    struct addrinfo *addrs = NULL;

    if (argc > 1 && strcmp(argv[1], "inet6") == 0)
        hints.ai_family = AF_INET6;
    else
        hints.ai_family = AF_INET;

    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    printf("[Server] Initializing socket address\n");
    res = getaddrinfo(NULL, "1234", &hints, &addrs);
    if (res != 0) {
        fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(res));
        goto fail;
    }

    printf("[Server] Create socket\n");
    socket_fd = socket(addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
    if (socket_fd < 0) {
        perror("Create socket failed");
        freeaddrinfo(addrs);
        goto fail;
    }

    printf("[Server] Bind socket\n");
    if (bind(socket_fd, addrs->ai_addr, addrs->ai_addrlen) < 0) {
        perror("Bind failed");
        freeaddrinfo(addrs);
        goto fail;
    }

    freeaddrinfo(addrs);

    ...
}

相关问题