在UDP连接上sendto()获取错误10049(地址不是avai),但bind()工作

问题描述:

编辑#2:问题是我愚蠢地让memset参数顺序倒退,所以,我没有过零我的结构,因此必须在一些很少使用的字段中有一些垃圾值。这大概解释了为什么它最初在我的系统上工作,而不是在客户上。我不认为我甚至得到了一个编译器警告。在UDP连接上sendto()获取错误10049(地址不是avai),但bind()工作

编辑#1:雷米建议我使用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地址。所以,大概我应该可以从......发送到......: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()相同。

我最初打开套接字的代码就在这里。我目前没有设置任何套接字选项。不知道我需要什么。我只是调用套接字()和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地址,所以它应该可以工作,但它不会。任何想法,我可能会失踪?如果它是愚蠢的,也许不会downvote我太糟糕,哈哈

哦,我不妨列出从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

我不完全确定是否支持在同一台计算机上的两个不同地址之间发送数据包。 (我知道在某些配置中虚拟机无法与主机进行通信,这看起来可能是相关的。)另一方面,它可能是一个bug;如果更改其中一个端口号,它会起作用吗?也许Windows错误地认为你正试图向你发送一个数据包。 – 2014-09-06 21:32:40

+0

是的,您可以将数据从一个本地IP发送到同一台机器上的另一个本地IP。 WinSock在内部处理必要的连接,因此它不通过网络物理传输数据。在VM环境中,您必须使用VM的内置路由器才能在VM和主机之间传递数据。请记住,虚拟机作为一*立的机器运行,因此操作系统会相应地执行操作,这就是虚拟机需要虚拟网络设置的原因。 – 2014-09-06 21:59:02

+0

@RemyLebeau:我一直在努力记住细节;我认为如果VM设置为通过NAT共享主机的IP地址,VM主机通信不起作用。虚拟机可以很好地与本地网络的其余部分通话,而不是主机。但是现在我想到了,虚拟机软件可能已经与WinSock级别以下的网络堆栈进行了接口连接,并且在任何情况下,与同一个IP地址进行通信都是一个问题,而不是一个不同的问题。您对OP问题的解释似乎更有可能。 – 2014-09-07 20:32:09

1004 9(WSAEADDRNOTAVAIL)表示指定的IP地址无效。很明显,你在to变量中做错了,你将传递给sendto(),并且你的调试消息很可能隐藏了这个问题。

例如,使用ipv6rev()对我来说是可疑的。如果你正确地跟踪IP地址,你不应该反转它们,因为它们应该从一开始就采用正确的格式。出于显示目的,您应该使用像InetNtop()RtlIpv6AddressToString()这样的功能,而不是手动格式化它们。

这表明to变量可能以错误的格式开头。它看起来像to使用来自rtu_to_ipv6_map[]的数据得到填充,但是您没有显示如何填充该数组,所以很难确定它是否出现问题的根源。

这种情况下,你真的需要提供SSCCE,以便其他人可以重现该问题。

+0

嘿,雷米,你到处都不是吗?大声笑。我做了很多Delphi,或者至少已经习惯了,所以,你们多次帮助我。我将开始使用你的建议格式化地址,并从那里开始工作。 – user3681853 2014-09-08 11:46:06

+0

雷米,我改变了代码使用InetNtop(),我得到了相同的结果,相同的打印输出。我修改了我的问题。我将在下一个SSCCE工作。谢谢。 – user3681853 2014-09-08 12:29:34

+0

我做了一个小测试程序,它的工作原理。所以,我的主程序一定是错的。我注意到我交换了memset()调用中的参数,其中交换了大小和字节值。也许这就是问题所在,我会进一步测试。谢谢您的帮助。 – user3681853 2014-09-08 19:14:41