当前位置: 首页 > 知识库问答 >
问题:

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

璩华辉
2023-03-14
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);  

共有1个答案

伍胡媚
2023-03-14

我的C技能有点生疏,所以这里有一个用Python写的反例。我的本地IPv4地址是37.77.56.75,所以我将绑定到这个地址。我把它尽可能简单地集中在概念上。

这是服务器端:

#!/usr/bin/env python
import socket

# We bind to an IPv6 address, which contains an IPv6-mapped-IPv4-address,
# port 5000 and we leave the flowinfo (an ID that identifies a flow, not used
# a lot) and the scope-id (basically the interface, necessary if using
# link-local addresses)
host = '::ffff:37.77.56.75'
port = 5000
flowinfo = 0
scopeid = 0
sockaddr = (host, port, flowinfo, scopeid)

# Create an IPv6 socket, set IPV6_V6ONLY=0 and bind to the mapped address
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, 0)
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
sock.bind(sockaddr)

# Listen and accept a connection
sock.listen(0)
conn = sock.accept()

# Print the remote address
print conn[1]

这里我们在代码中绑定到一个IPv6地址,但该地址实际上是一个IPv6映射的IPv4地址,所以实际上我们绑定到的是一个IPv4地址。这一点可以在查看即netstat时看到:

$ netstat -an | fgrep 5000
tcp4       0      0  37.77.56.75.5000       *.*                    LISTEN     
#!/usr/bin/env python
import socket

# Connect to an IPv4 address on port 5000
host = '37.77.56.75'
port = 5000
sockaddr = (host, port)                   

# Create an IPv4 socket and connect
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) 
conn = sock.connect(sockaddr)
('::ffff:37.77.56.76', 50887, 0, 0)
    null

使用通配符地址是最简单的。只需使用上面的服务器示例并替换主机名:

# We bind to the wildcard IPv6 address, which will make the OS listen on both
# IPv4 and IPv6
host = '::'
port = 5000
flowinfo = 0
scopeid = 0
sockaddr = (host, port, flowinfo, scopeid)

我的Mac OS X box显示如下:

$ netstat -an | fgrep 5000
tcp46      0      0  *.5000                 *.*                    LISTEN     

请注意tcp46,它指示它侦听两个地址族。不幸的是,在Linux上它只显示tcp6,即使在监听两个家族时也是如此。

#!/usr/bin/env python
import select
import socket

# We bind to an IPv6 address, which contains an IPv6-mapped-IPv4-address
sockaddr1 = ('::ffff:37.77.56.75', 5001, 0, 0)

sock1 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, 0)
sock1.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
sock1.bind(sockaddr1)
sock1.listen(0)

# And we bind to a real IPv6 address
sockaddr2 = ('2a00:8640:1::224:36ff:feef:1d89', 5001, 0, 0)

sock2 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, 0)
sock2.bind(sockaddr2)
sock2.listen(0)

# Select sockets that become active
sockets = [sock1, sock2]
readable, writable, exceptional = select.select(sockets, [], sockets)
for sock in readable:
    # Accept the connection
    conn = sock.accept()

    # Print the remote address
    print conn[1]
$ netstat -an | fgrep 5000
tcp6       0      0  2a00:8640:1::224.5000  *.*                    LISTEN     
tcp4       0      0  37.77.56.75.5000       *.*                    LISTEN     
 类似资料:
  • 问题内容: 最近,我一直在寻找Java虚拟机规范(JVMS),以试图更好地理解使我的程序正常工作的原因,但是我找到了一段我不太了解的部分… 第4.7.4节介绍了 StackMapTable 属性,在该节中,文档介绍了有关堆栈映射框架的详细信息。问题是它有点罗word,我以身作则,学得最好。不读书。 我知道第一个堆栈映射框架是从方法描述符派生的,但是我不知道如何(应该在这里进行解释。)而且,我也不完

  • Back Stack是否支持与Android中的嵌套片段交互? 如果是这样,我做错了什么?在我的实现中,后退按钮完全忽略了我将此事务添加到后堆栈的事实。我希望这不是因为嵌套片段的问题,只是我做了一些不正确的事情。 以下代码位于我的一个片段中,用于将新片段与当前显示的任何嵌套片段交换:

  • 问题内容: Linux上的clone()系统调用采用一个指向堆栈的参数,供新创建的线程使用。这样做的明显方法是简单地分配一些空间并传递该空间,但随后必须确保已分配了该线程将使用的尽可能多的堆栈空间(很难预测)。 我记得在使用pthreads时不必这样做,所以我很好奇它做了什么。我遇到了一个网站,该网站解释说:“ Linux pthreads实现使用的最佳解决方案是使用mmap分配内存,并使用标志指

  • 假设我有这些实体: null

  • 在启动springboot应用程序时,当我为testcontroller实现aspect时,出现了以下异常 组织。springframework。豆。工厂BeanCreationException:创建名为“testController”的bean时出错,该bean在文件[build\classes\java\main\com\nijil\fetch\service\identity\testCo

  • 我理解JVM如何从OS提供的可用本机堆中创建java堆。