将套接字绑定到IPv6地址

8
我正在尝试编写一个监听IPv4和IPv6地址的Web服务器。然而,我最初编写的代码并没有起作用。后来我发现IPv6结构可用于IPv4和IPv6。因此,我现在使用IPv6结构,但只有IPv4地址有效。这篇文章为什么不能将IPv6套接字绑定到本地链接地址建议添加server.sin6_scope_id = 5;,所以我这样做了,但它仍然无法接受IPv6 telnet连接。因为我被彻底困惑了,所以任何帮助都将不胜感激。
谢谢!

我的代码如下:

void initialize_server(int port, int connections, char* address)
{
        struct sockaddr_in6 socket_struct;
        /*Creates the socket*/
        if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
                syslog(LOG_ERR, "%s\n", strerror(errno));
                exit(EXIT_FAILURE);
        }/*Ends the socket creation*/

        /*Populates the socket address structure*/
                socket_struct.sin6_family = AF_INET6;

        if(address == NULL)
                socket_struct.sin6_addr=in6addr_any;
        else
        {
                inet_pton(AF_INET6, "fe80::216:3eff:fec3:3c22", (void *)&socket_struct.sin6_addr.s6_addr);
        }
        socket_struct.sin6_port =htons(port);
        socket_struct.sin6_scope_id = 0;
        if (bind(sock_fd, (struct sockaddr*) &socket_struct, sizeof(socket_struct)) < 0)
        {
                syslog(LOG_ERR, "%s\n", strerror(errno));
                exit(EXIT_FAILURE);
        }//Ends the binding.

        if (listen(sock_fd, connections) <0)
        {
                syslog(LOG_ERR, "%s\n", strerror(errno));
                exit(EXIT_FAILURE);
        }//Ends the listening function

}//ends the initialize server function.

4
IPv6既不是32位也不是64位,它可以在这两种系统上运行,也可以在其他系统上运行。 - Jonathan Leffler
@GiantHornet 是的,我认为那不对,因为我的 Ubuntu 机器是 i686 架构,即 32 位,并且具有 IPv6 地址。 - tpar44
2
@GiantHornet IPv6地址长度为128位,但我不知道操作系统架构在这里有何重要性。此外,建议放弃IPv6支持是IPv6问题的根源。 - Crozin
2
@GiantHornet,如果我要尝试学习套接字编程,IPv4似乎相当容易,那么我也可以顺便尝试一下IPv6。 - tpar44
到目前为止,我也在进行IPv6的实验。 - GiantHornet
显示剩余5条评论
2个回答

8

说"server.sin6_scope_id = 5;"是任意的。我自己也为此纠结过一段时间,后来发现你需要使用实际接口的实际范围进行绑定。可以通过一个不太常用但非常有用的小函数来找到它。

#include <net/if.h>
server.sin6_scope_id=if_nametoindex("eth0");

当然,将其硬编码到一个特定的适配器是糟糕的、近视的编程方式。更完整的解决方案是遍历所有适配器,并匹配绑定的IP地址。下面的示例函数并不完美,因为它没有考虑非规范地址和具有相同IP的两个适配器等问题。但总体而言,这个示例函数表现很好,可以让你开始工作。
#include <string.h> // strcmp
#include <net/if.h> // if_nametoindex()
#include <ifaddrs.h> // getifaddrs()
#include <netdb.h> // NI_ constants

// returns 0 on error
unsigned getScopeForIp(const char *ip){
    struct ifaddrs *addrs;
    char ipAddress[NI_MAXHOST];
    unsigned scope=0;
    // walk over the list of all interface addresses
    getifaddrs(&addrs);
    for(ifaddrs *addr=addrs;addr;addr=addr->ifa_next){
        if (addr->ifa_addr && addr->ifa_addr->sa_family==AF_INET6){ // only interested in ipv6 ones
            getnameinfo(addr->ifa_addr,sizeof(struct sockaddr_in6),ipAddress,sizeof(ipAddress),NULL,0,NI_NUMERICHOST);
            // result actually contains the interface name, so strip it
            for(int i=0;ipAddress[i];i++){
                if(ipAddress[i]=='%'){
                    ipAddress[i]='\0';
                    break;
                }
            }
            // if the ip matches, convert the interface name to a scope index
            if(strcmp(ipAddress,ip)==0){
                scope=if_nametoindex(addr->ifa_name);
                break;
            }
        }
    }
    freeifaddrs(addrs);
    return scope;
}

6
您正在创建一个属于 AF_INET 家族的套接字,但是尝试将其绑定到 AF_INET6 家族的地址上。请在调用 socket() 时切换为使用 AF_INET6

你只是出于好奇才使用 telnet fe80::216:3eff:fec3:3c22%eth0 8080 进行测试的吗? - tpar44
我将NULL作为该函数的地址传递,以便它绑定到in6addr_any。然后我使用“telnet 0 6666”连接到它。我也无法连接到其他端口正在侦听的本地链接地址。 - qqx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接