2012-12-19 32 views
2

实际上,我将一个IPv4服务器应用程序移植到Linux上的双重堆栈IPv4/IPv6应用程序。将IPv4应用程序移植到Dualstack IPv4/IPv6

基本功能我以解决:

serv_addr.sin6_family = AF_INET6; 
serv_addr.sin6_addr = in6addr_any; 
... 
bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)); 
... 
listen(sock, 5); 
... 
newsock = accept(syn->sock, (struct sockaddr *) &cli_addr, &clilen); 

我可以与IPv4和IPv6连接和使用的连接。但是,当我想用​​IP:

switch(data->sa_family) { 
    case AF_INET: 
    inet_ntop(AF_INET, &(((struct sockaddr_in*)data)->sin_addr), buffer, size); 
    break; 
    case AF_INET6: 
    inet_ntop(AF_INET6, &(((struct sockaddr_in6*)data)->sin6_addr), buffer, size); 
    break; 

    default: 
    buffer[0] = '?'; 
    buffer[1] = 0; 
} 

我总是得到一个IPv6地址不如预期,或者如果它是一个IPv4连接类似于将:: ffff:127.0.0.1

我有什么更改为以127.0.0.1(不带:: ffff:-prefix)形式显示为普通旧IPv4地址?

感谢 泰迪

回答

1

有2种方式,你可以去:

  1. 就脱掉这::ffff:从一开始,如果存在的话,并把它作为IPv4的指标。

  2. 使用两个不同的套接字。在那里有操作系统不支持IPv4和6通过一个套接字(IOW,他们总是启用V6ONLY)。尤其是,WinXP是这样做的。

    那么只有一两件事可以做:

    • 使用getaddrinfo()AI_PRIVATE用于获取所有本地套接字地址,你可以有
    • 全部使用它们来创建一个监听套接字,并立即启用V6ONLY,如果可能的
    • 将它们的描述符保存在一个数组中,并使用select()等来确定它们中的哪一个要调用accept()

    听起来比它更复杂真的是......

1

您请求IPv4映射连接的呈现,就好像它们实际上是IPv4应用与类似下面的代码:

int cli = accept(server, NULL, NULL); 
int addrform = AF_INET; 
setsockopt(cli, IPPROTO_IPV6, IPV6_ADDRFORM, &addrform, sizeof(addrform)); 

这需要在每个新客户端套接字上完成。它会因IPv6连接失败;您可以忽略该选项或首先检查,只有在使用IPv4映射客户端时才会调用它。

完成后,您可以拨打getpeername()以首选格式获取客户端地址信息,而不是将其作为accept()的呼叫的一部分。

1

有一个宏观的帮助,但只成功了一半:

if (IN6_IS_ADDR_V4MAPPED(&serv_addr.sin6_addr)) { 
     struct sockaddr_in tmpsa; 
     tmpsa.sin_family = AF_INET; 
     tmpsa.sin_port = 0; 
     tmpsa.sin_addr.s_addr = serv_addr.sin6_addr.s6_addr32[3]; 
    /* process IPv4 address in tmpsa ... */ 
     inet_ntop (AF_INET, &tmpsa.sin_addr, buffer, size); 
    } else { 
    /* process IPv6 address in serv_addr.. */ 
     inet_ntop (AF_INET6, &serv_addr.sin6_addr, buffer, size); 
    } 

小的修改可以取决于平台的需要,这里是使用GNU标准。

相关问题