Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sockets 使用IN6ADDR_SETV4映射和双堆栈套接字_Sockets_Tcp_Ipv6_Ipv4 - Fatal编程技术网

Sockets 使用IN6ADDR_SETV4映射和双堆栈套接字

Sockets 使用IN6ADDR_SETV4映射和双堆栈套接字,sockets,tcp,ipv6,ipv4,Sockets,Tcp,Ipv6,Ipv4,这是本书的延续。我正在尝试使用双堆栈套接字,并试图了解setsockopt与IPV6_V6ONLY在哪些方面有用。关于链接的问题,有人建议我“如果还将服务器绑定到IPV6映射的IPv4地址,则仅将IPV6_v6设置为0会很有用”。我已经在下面完成了这项工作,并希望我的服务器能够接受来自IPv6和IPv4客户端的连接。但令人震惊的是,当我使用V4和V6套接字运行我的客户机时,两者都无法连接 有人能告诉我我做错了什么,或者我误解了IPv6的双栈功能吗 服务器: void ConvertToV4Map

这是本书的延续。我正在尝试使用双堆栈套接字,并试图了解setsockopt与IPV6_V6ONLY在哪些方面有用。关于链接的问题,有人建议我“如果还将服务器绑定到IPV6映射的IPv4地址,则仅将IPV6_v6设置为0会很有用”。我已经在下面完成了这项工作,并希望我的服务器能够接受来自IPv6和IPv4客户端的连接。但令人震惊的是,当我使用V4和V6套接字运行我的客户机时,两者都无法连接

有人能告诉我我做错了什么,或者我误解了IPv6的双栈功能吗

服务器:

void ConvertToV4MappedAddressIfNeeded(PSOCKADDR pAddr)
{
// if v4 address, convert to v4 mapped v6 address
if (AF_INET == pAddr->sa_family)
{
    IN_ADDR In4addr;
    SCOPE_ID scope = INETADDR_SCOPE_ID(pAddr);
    USHORT port = INETADDR_PORT(pAddr);
    In4addr = *(IN_ADDR*)INETADDR_ADDRESS(pAddr);
    ZeroMemory(pAddr, sizeof(SOCKADDR_STORAGE));
    IN6ADDR_SETV4MAPPED(
        (PSOCKADDR_IN6)pAddr,
        &In4addr,
        scope,
        port
        );
    }
} 

addrinfo* result, hints;

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

int nRet = getaddrinfo("powerhouse", "82", &hints, &result);

SOCKET sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);

int no = 0;
if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&no, sizeof(no)) != 0)
    return -1;

ConvertToV4MappedAddressIfNeeded(result->ai_addr);

if (bind(sock, result->ai_addr, 28/*result->ai_addrlen*/) ==  SOCKET_ERROR)
    return -1;

if (listen(sock, SOMAXCONN) == SOCKET_ERROR)
    return -1;

SOCKET sockClient = accept(sock, NULL, NULL);
printf("Got one!\n");
客户:

addrinfo* result, *pCurrent, hints;
char szIPAddress[INET6_ADDRSTRLEN];

memset(&hints, 0, sizeof hints);    // Must do this!
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;

const char* pszPort = "82";

if (getaddrinfo("powerhouse", "82", &hints, &result) != 0)
    return -1;

SOCKET sock = socket(AF_INET, result->ai_socktype, result->ai_protocol);
int nRet = connect(sock, result->ai_addr, result->ai_addrlen);  

我的C语言技能有些生疏,所以这里有一个用Python编写的反例。我的本地IPv4地址是37.77.56.75,因此我将绑定到该地址。我尽可能简单地把重点放在概念上

这是服务器端:

#/usr/bin/env python
导入套接字
#我们绑定到一个IPv6地址,其中包含一个IPv6-mapped-IPv4-address,
#端口5000,我们保留flowinfo(标识流的ID,未使用
#很多)和范围id(基本上是接口,如果使用
#链接本地地址)
主机='::ffff:37.77.56.75'
端口=5000
flowinfo=0
scopeid=0
sockaddr=(主机、端口、流信息、scopeid)
#创建IPv6套接字,将IPv6_V6ONLY设置为0并绑定到映射的地址
sock=socket.socket(socket.AF_INET6,socket.sock_流,0)
sock.setsockopt(socket.IPPROTO_IPV6,仅socket.IPV6,0)
sock.bind(sockaddr)
#收听并接受连接
短袜,听(0)
conn=sock.accept()
#打印远程地址
打印连接[1]
这里我们在代码中绑定到一个IPv6地址,但该地址实际上是一个IPv6映射的IPv4地址,因此实际上我们绑定到一个IPv4地址。这可以在查看例如netstat时看到:

$netstat-an | fgrep 5000
tcp4 0 37.77.56.75.5000**听
然后,我们可以使用IPv4客户端连接到此服务器:

#/usr/bin/env python
导入套接字
#连接到端口5000上的IPv4地址
主机='37.77.56.75'
端口=5000
sockaddr=(主机、端口)
#创建IPv4套接字并连接
sock=socket.socket(socket.AF\u INET,socket.sock\u流,0)
conn=sock.connect(sockaddr)
服务器将使用IPv6地址表示向我们显示已连接的用户:

('::ffff:37.77.56.76',50887,0,0)
在本例中,我从IPv4主机
37.77.56.76
连接,它选择端口
50887
进行连接

在本例中,我们只侦听IPv4地址(使用IPv6套接字,但它仍然是IPv4地址),因此仅IPv6的客户端将无法连接。同时具有IPv4和IPv6的客户端当然可以使用具有IPv6映射IPv4地址的IPv6套接字,但这样它就不会真正使用IPv6,而只是IPv4连接的IPv6表示

双堆栈服务器必须:

  • 侦听通配符地址,这将使操作系统接受任何地址(IPv4和IPv6)上的连接
  • 侦听IPv6地址和IPv4地址(通过创建IPv4套接字,或通过创建IPv6套接字并侦听IPv6映射的IPv4地址,如上所示)
  • 使用通配符地址是最简单的。只需使用上面的服务器示例并替换主机名:

    #我们绑定到通配符IPv6地址,这将使操作系统在这两个端口上都侦听
    #IPv4和IPv6
    主机=':'
    端口=5000
    flowinfo=0
    scopeid=0
    sockaddr=(主机、端口、流信息、scopeid)
    
    “我的Mac OS X”框显示如下:

    $netstat-an | fgrep 5000
    tcp46 0*.0.5000*
    
    请注意
    tcp46
    ,它指示它侦听两个地址族。不幸的是,在Linux上,它只显示
    tcp6
    ,即使在两个系列上都侦听时也是如此

    现在来看最复杂的示例:侦听多个套接字

    #/usr/bin/env python
    导入选择
    导入套接字
    #我们绑定到一个IPv6地址,该地址包含一个IPv6映射的IPv4地址
    sockaddr1=('::ffff:37.77.56.75',5001,0,0)
    sock1=socket.socket(socket.AF_INET6,socket.SOCK_流,0)
    sock1.setsockopt(socket.IPPROTO_IPV6,仅socket.IPV6,0)
    sock1.bind(sockaddr1)
    1.听(0)
    #我们绑定到一个真正的IPv6地址
    sockaddr2=('2a00:8640:1::224:36ff:feef:1d89',5001,0,0)
    sock2=socket.socket(socket.AF_INET6,socket.SOCK_流,0)
    sock2.bind(sockaddr2)
    sock2.听(0)
    #选择激活的套接字
    套接字=[sock1,sock2]
    可读、可写、异常=选择。选择(套接字,[],套接字)
    对于插入式可读:
    #接受连接
    conn=sock.accept()
    #打印远程地址
    打印连接[1]
    
    运行此示例时,两个套接字都可见:

    $netstat-an | fgrep 5000
    tcp6 0 2a00:8640:1::224.5000**听
    tcp4 0 37.77.56.75.5000**听
    

    现在,只有IPv6的客户端才能连接到
    2a00:8640:1::224:36ff:feef:1d89
    ,只有IPv4的客户端才能连接到
    37.77.56.75
    。双栈客户端可以选择要使用的协议。

    您是否尝试过设置
    hits.ai\u family=AF\u INET
    提示.ai_family=AF_INET6正如我现在看到的,您正在查找IPv4通配符地址,而不是IPV6不确定您在那里的意思;当我传递机器名称(“发电站”)时,我没有查找通配符地址……除非我误解了你??啊,我不记得节点名称覆盖了AI_被动标志。AI_被动被忽略,因此您应该删除它以使代码更清晰。发电厂房解决了什么问题(如果您键入主机发电厂房)?我在Windows上,所以“主机”不起作用:)尽管如此,我还是听到了一些好消息