【问题标题】:Cannot Bind Socket to Port in Both IPv4 and IPv6无法在 IPv4 和 IPv6 中将套接字绑定到端口
【发布时间】:2018-08-18 18:39:36
【问题描述】:

我是套接字编程的新手,并且正在编写一些临时代码以更好地感受它,但遇到了障碍。任何关于我做错了什么的指导将不胜感激!

我正在尝试编写一个简单的程序来绑定和侦听用户指定的端口,并向任何连接发送“Hello”消息。为了好玩,我想我会在同一个端口上监听所有 IPv4 和 IPv6 地址。这是一个代码sn-p:

memset(&hints, 0, sizeof hints);
hints.ai_family   = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags    = AI_PASSIVE;

if ( getaddrinfo(NULL, argv[1], &hints, &res) != 0 ) { 
    printf("getaddrinfo failed!\n");
    return 1;
}

for ( addrinfo* p = res; p != NULL; p = p->ai_next ) { 
    inet_ntop(p->ai_family, get_addr_ptr(p->ai_addr), ipstr, sizeof ipstr);
    printf("Found IP: %s\n",ipstr);

    printf("\tGetting socket...\t");
    sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
    if ( sockfd == -1 ) { 
        perror("\t\tError");
        continue;
    }
    printf("OK\n");

    printf("\tBind Socket to Port...\t");
    if ( bind(sockfd, p->ai_addr, p->ai_addrlen) == -1 ) { 
        perror("\t\tError");
        close(sockfd);
        continue;
    }
    printf("OK\n");

    printf("\tListen on socket...\t");
    if ( listen(sockfd, BACKLOG) == -1 ){
        perror("\t\tError");
        continue;
    }
    printf("OK\n");
}

freeaddrinfo(res);
while (1) {/* accept connections */}

当我运行代码时,我得到这个输出:

$ ./simpleServer 8080
Found IP: 0.0.0.0
    Getting socket...       OK
    Bind Socket to Port...  OK
    Listen on socket...     OK
Found IP: ::
    Getting socket...       OK
    Bind Socket to Port...
        Error: Address already in use

但是,如果我在程序运行时查看netstat,我看不到任何与:: 相关联的冲突端口,或与此相关的任何其他 IPv6 地址。

我对此进行了更多尝试,发现我可以仅使用 IPv4 或仅使用 IPv6 绑定到端口,但不能同时使用这两者,我不明白。我创建了两个套接字,具有以下 ai_addr:

  • 家庭:AF_INET 地址信息:0.0.0.0:8008
  • 家庭:AF_INET6 地址信息::::8080

我觉得我可能遗漏了一些基本的东西,但我看不到它。

谢谢!

【问题讨论】:

标签: c++ c sockets networking


【解决方案1】:

您面临的问题可能是您使用的是双栈模式。 至少在 Linux 上(其他人经常禁用它,例如 FreeBSD)指定 :: 作为地址在绑定到* 时产生,正如netstatss 所说的那样。 这意味着,它同时接受 IPv6 和 IPv4 地址。 虽然 IPv4 映射到 ::ffff:<your normal ipv4 address>。 所以我猜你也遇到了同样的问题。

如果af_family == AF_INET6 不允许这种行为,您可以使用IPV6_V6ONLY 套接字选项。

IPV6_V6ONLY(自 Linux 2.4.21 和 2.6 起) 如果此标志设置为真(非零),则套接字仅限于发送和接收 IPv6 数据包。在这种情况下,一个 IPv4 和一个 IPv6 应用程序可以同时绑定到一个端口。 如果此标志设置为 false(零),则套接字可用于向 IPv6 地址或 IPv4 映射的 IPv6 地址发送和接收数据包。

参数是一个指向整数中布尔值的指针。

此标志的默认值由文件 /proc/sys/net/ipv6/bindv6only 的内容定义。该文件的默认值为 0 (false)。

取自 man 7 ipv6

请注意,您正在覆盖您之前创建/绑定/侦听的套接字。 还可以使用gai_strerror 从返回值中获取有意义的错误 getaddrinfo.

【讨论】:

  • 非常感谢!这非常有帮助。
猜你喜欢
  • 2015-03-05
  • 2015-12-25
  • 1970-01-01
  • 2021-04-28
  • 2021-11-15
  • 2020-04-19
  • 2021-10-15
  • 2021-08-25
  • 2015-07-04
相关资源
最近更新 更多