编辑#1:Remy建议我使用InetNtop而不是我自己的小ipv6rev()函数来打印出IPv6地址。我这样做了,得到了基本相同的结果。他的下一个建议是建立一个我将继续努力的SSCCE。但是,就目前而言,我只是编辑它以注意使用InetNtop()。
我正在编写的应用程序是一种软件路由器,我在一个套接字(恰好是IPv4)上获取数据,然后在IPv6 UDP上发送另一个套接字。我让它在我的机器上工作,但是,当我在客户安装我的应用程序时,sendto调用失败。我最初与另一台机器进行通信,但为了使测试尽可能简单,我将UPD IPv6接收器放在同一台机器上,并使用自己的IP。使用netstat,我似乎看到我已正确绑定:
UDP [2620:175:e10:2000:10:90:177:104]:20000 *:*
[NATerator.exe]
UDP [2620:175:e10:2000:10:90:177:fff0]:20000 *:*
[node.exe]
“NATerator”是我的软件路由器应用程序,接收器是Java应用程序(node.exe)因此,它们都绑定到端口20000,但是绑定到不同的IP地址。所以,据推测,我应该能够从...发送......:104到...:fff0我想。
但是sendto()以10049失败。这似乎表明我正在为套接字发送错误的IP地址或端口。所以,我添加了调试消息来打印出IP地址和端口,看起来我正在发送到正确的地址。打印出调试消息:
2014-09-08 05:17:47.155 NATERATOR 7564 [TID=0x4cdc] - omniSocketThread: socket 2264 received bytes 24. NAT ok, calling sendto() 2620:0175:0e10:2000:0010:0090:0177:fff0 port 20000 {.\NATerator.cpp:669}
2014-09-08 05:17:47.181 NATERATOR 7564 [TID=0x4cdc] - omniSocketThread: Error sending data 10049
以下是sendto之前的调试消息的代码,然后调用sendto和之后的调试:
char ipv6String[100];
sockaddr_in6 *ipv6_addr;
ipv6_addr = (sockaddr_in6 *) &to;
InetNtop(AF_INET6, &ipv6_addr->sin6_addr, ipv6String, sizeof(ipv6String));
cpu_debug(CPU_DEBUG_ERROR,
"omniSocketThread: socket %d received bytes %d. NAT ok, calling sendto() IP %s port %d\n",
*csock, bytecount, ipv6String, htons(ipv6_addr->sin6_port));
bytecount = sendto(rtusock, (char *) buffer, bytecount, 0, (const sockaddr *) &to, tolen);
if(bytecount==SOCKET_ERROR)
{
"omniSocketThread: Error sending data %d\n", WSAGetLastError());
continue;
}
请注意,“cpu_debug”是一个内部调试记录器,其操作类似于printf()。
我最初打开套接字的代码就在这里。我目前没有设置任何套接字选项。不确定我需要什么。我只是像这样调用socket()和bind():
rtusock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if (rtusock == -1)
{
return(false);
}
in6_addr ipv6addr;
lookupHost(local_ipv6, &ipv6addr);
memset(&sinIPv6, 0, sizeof(sinIPv6));
sinIPv6.sin6_family = AF_INET6;
// sinIPv6.sin6_addr = in6addr_any;
memcpy(&sinIPv6.sin6_addr, &ipv6addr, sizeof(in6_addr));
sinIPv6.sin6_port = htons(rtuport);
sockstatus = bind(rtusock, (struct sockaddr *) &sinIPv6, sizeof(sockaddr_in6));
if( sockstatus == -1 )
{
return(false);
}
lookupHost函数填充了in6_addr结构,我认为这种结构必须正常工作,因为nestat显示应用程序绑定。 “rtuport”定义为20000.我应该在所有大写字母中创建名称,因为它是#define常量。因此,socket()和bind()调用的创建看起来就像工作一样。但也许我需要设置一些套接字选项?
无论如何,最后一段相关代码是我填写“to”结构的地方。它被定义为:
struct sockaddr_storage to;
int tolen;
tolen = sizeof(to);
我有一个填充到的函数:
bool omniNATerator(struct sockaddr_storage *to, USHORT received_dest)
{
struct sockaddr_in6 *ipv6_addr = (sockaddr_in6 *) to;
memset(to, sizeof(sockaddr_storage), 0);
memcpy(&ipv6_addr->sin6_addr, &rtu_to_ipv6_map[received_dest], sizeof(in6_addr) );
ipv6_addr->sin6_family = AF_INET6;
ipv6_addr->sin6_port = htons(rtuport);
rtu_to_ipv6_map的定义如下:
in6_addr rtu_to_ipv6_map[65536];
并且,我有理由相信该数组中的值是正确的地址,因为我在调试消息中将其解压缩,并且它正确地指出了目标地址。
所以,也许我忽视了一些简单的事情。但是,尽管我可以看到,我已经打开套接字,我正在发送一个可用且合理的IP地址。 (我可以ping两个IP地址。所以它应该可以工作,但事实并非如此。任何想法我可能会缺少什么?如果它是我的愚蠢,也许不会让我太糟糕了,大声笑。
哦,我不妨列出IPconfig的输出:
Ethernet adapter IPv6:
Connection-specific DNS Suffix . :
IPv6 Address. . . . . . . . . . . : 2620:175:e10:2000:10:90:177:104
IPv6 Address. . . . . . . . . . . : 2620:175:e10:2000:10:90:177:fff0
Link-local IPv6 Address . . . . . : fe80::cdd7:56a8:1afd:39e9%13
Default Gateway . . . . . . . . . :
那么,有什么想法吗?
PS。我不是专家套接字程序员。但是,我已经编写了一些与套接字相关的应用程序,以及此站点之前的一些帮助。但是,我知道有很多我不知道。谢谢你的帮助!
答案 0 :(得分:2)
10049(WSAEADDRNOTAVAIL
)表示指定的IP地址无效。很明显,您正在对传递给to
的{{1}}变量做错了,而您的调试消息可能隐藏了这个问题。
例如,使用sendto()
对我来说是可疑的。如果您正确跟踪IP,则不必反转它们,因为它们应该从一开始就采用正确的格式。出于显示目的,您应该使用InetNtop()
或RtlIpv6AddressToString()
之类的功能,而不是手动格式化。
这表明ipv6rev()
变量可能以错误的格式开头。看起来to
使用to
中的数据填充,但您没有显示如何填充该数组,因此很难确定这是否是您的根问题发生的地方。
在这种情况下,您确实需要提供SSCCE,以便其他人可以重现此问题。