c - 解析并绑定(bind)到 IPv6 地址

标签 c bind ipv6 getaddrinfo

我编写了一个程序来解析地址或主机名,然后尝试以 listen(2) 模式绑定(bind)到该地址。由于某些奇怪的原因,我无法使用 IPv6 地址,程序在调用 bind(2) 后失败并显示“无效参数”。使用 IPv4,一切都像我期望的那样工作,名称得到解析,地址被简单地转换为二进制格式。

来源

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <unistd.h>

static int
resolve_addr (const char *hostname, const char *service, int family, struct addrinfo **result)
{
    struct addrinfo addr_hint;

    memset (&addr_hint, 0, sizeof (struct addrinfo));

    addr_hint.ai_socktype = SOCK_STREAM;
    addr_hint.ai_flags = AI_PASSIVE | AI_NUMERICSERV | AI_ADDRCONFIG;
    addr_hint.ai_family = family;

    return getaddrinfo (hostname, service, &addr_hint, result);
}

int
main (int argc, char *argv[])
{
    struct addrinfo *addr;
    int rval, sock, opt_val;

    rval = resolve_addr (argv[1], argv[2], AF_UNSPEC, &addr);

    if ( rval != 0 ){
        fprintf (stderr, "%s\n", gai_strerror (rval));
        return 1;
    }

    sock = socket (addr->ai_family, addr->ai_socktype | SOCK_NONBLOCK, addr->ai_protocol);

    if ( sock == -1 ){
        fprintf (stderr, "error socket: %s\n", strerror (errno));
        return 1;
    }

    if ( setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &opt_val, sizeof (opt_val)) == -1 ){
        fprintf (stderr, "error setsockopt: %s\n", strerror (errno));
        return 1;
    }

    if ( bind (sock, (struct sockaddr*) addr->ai_addr, sizeof (struct sockaddr)) == -1 ){
        fprintf (stderr, "error bind: %s\n", strerror (errno));
        return 1;
    }

    if ( listen (sock, 32) == -1 ){
        fprintf (stderr, "error listen: %s\n", strerror (errno));
        return 1;
    }

    sleep (120);

    freeaddrinfo (addr);
    close (sock);

    return 0;
}

用法

  1. ./test_bind localhost 8888 # 有效
  2. ./test_bind 0.0.0.0 8888 # 有效
  3. ./test_bind 127.0.0.1 8888 # 有效
  4. ./test_bind::8888 # 不起作用:参数无效
  5. ./test_bind::1 8888 # 不起作用:参数无效

旁注

我使用 netcat 来查明我的系统是否有问题,但以下命令有效:

ncat -l::8888

在netstat中可以看到:

tcp6 0 0:::8888:::* LISTEN

最佳答案

我更新了我的代码,IPv4 和 IPv6 都已解析并成功绑定(bind)。问题出在 bind(2) 函数的第三个参数中,该函数正在根据结构计算地址的大小大小,而不是使用 getaddrinfo 结果中的值。

我替换了行:

if ( bind (sock, (struct sockaddr*) addr->ai_addr, sizeof (struct sockaddr)) == -1 ){

与:

if ( bind (sock, (struct sockaddr*) addr->ai_addr, addr->ai_addrlen) == -1 ){

完整来源

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

static int
resolve_addr (const char *hostname, const char *service, int family, struct addrinfo **result)
{
    struct addrinfo addr_hint;

    memset (&addr_hint, 0, sizeof (struct addrinfo));

    addr_hint.ai_socktype = SOCK_STREAM;
    addr_hint.ai_flags = AI_PASSIVE | AI_NUMERICSERV | AI_ADDRCONFIG;
    addr_hint.ai_family = family;

    return getaddrinfo (hostname, service, &addr_hint, result);
}


int
main (int argc, char *argv[])
{
    struct addrinfo *addr;
    int rval, sock, opt_val;

    rval = resolve_addr (argv[1], argv[2], AF_UNSPEC, &addr);

    if ( rval != 0 ){
        fprintf (stderr, "%s\n", gai_strerror (rval));
        return 1;
    }

    sock = socket (addr->ai_family, addr->ai_socktype | SOCK_NONBLOCK, addr->ai_protocol);

    if ( sock == -1 ){
        fprintf (stderr, "error socket: %s\n", strerror (errno));
        return 1;
    }

    opt_val = 1;

    if ( setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &opt_val, sizeof (opt_val)) == -1 ){
        fprintf (stderr, "error setsockopt: %s\n", strerror (errno));
        return 1;
    }

    if ( bind (sock, (struct sockaddr*) addr->ai_addr, addr->ai_addrlen) == -1 ){
        fprintf (stderr, "error bind: %s\n", strerror (errno));
        return 1;
    }

    if ( listen (sock, 32) == -1 ){
        fprintf (stderr, "error listen: %s\n", strerror (errno));
        return 1;
    }

    sleep (120);

    freeaddrinfo (addr);
    close (sock);

    return 0;
}

关于c - 解析并绑定(bind)到 IPv6 地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33886690/

相关文章:

c++ - 全局变量是否意味着更快的代码?

c++ - std::bind 到局部变量作为值

javascript - jQuery On 事件 Off 自动解除绑定(bind)

php - 如何将 PHP 中的 IP 地址作为二进制字符串进行比较?

c++ - IPv4 和 IPv6 之间的混合模式通信

c - 宏重新定义逻辑运算符

c - 如何在不打开和关闭 C 语言文件的情况下通过文件获取输入?

Node.js http.request 和 ipv6 与 ipv4

c - 如何连接两个整数变量并制作一个浮点变量?

javascript - Jquery 添加 attr 操作与元素