连接前的 WinSock 绑定导致 WSAEADDRNOTAVAIL - 请求的地址在其上下文中无效

Posted

技术标签:

【中文标题】连接前的 WinSock 绑定导致 WSAEADDRNOTAVAIL - 请求的地址在其上下文中无效【英文标题】:WinSock bind before connects results in WSAEADDRNOTAVAIL - The requested address is not valid in its context 【发布时间】:2013-02-12 00:40:51 【问题描述】:

我需要使用 WinSock 从特定的本地网卡打开一个套接字。我问了这个问题并得到了答案here。总之,答案建议你先绑定到本地接口,然后调用connect。

但是,当我这样做时,我得到 WSAEADDRNOTAVAIL (10049)“请求的地址在其上下文中无效。”。为什么是这样?

假设下面的示例代码是在本地机器 192.168.1.3 上运行的应用程序的一部分,并且正在尝试连接到远程服务器 192.168.1.4。我已经检查并仔细检查了本地和远程地址是否正确。我可以 ping 两种方式(从本地到远程和远程到本地)。

我已经为本地尝试了 0 以外的端口;没有不同。如果我在连接之前删除绑定,它就会起作用,但我无法指定网络接口。

那么,知道为什么我不断收到 WSAEADDRNOTAVAIL 吗?

addrinfo localhints = 0;
localhints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
localhints.ai_family = AF_INET;
localhints.ai_socktype = SOCK_STREAM;
localhints.ai_protocol = IPPROTO_TCP;

addrinfo *localaddr = NULL;
getaddrinfo("192.168.1.3", "0", &localhints, &localaddr);
bind(s, localaddr->ai_addr, localaddr->ai_addrlen);
freeaddrinfo(localaddr);

addrinfo remotehints = 0;
remotehints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
remotehints.ai_family = AF_INET;
remotehints.ai_socktype = SOCK_STREAM;
remotehints.ai_protocol = IPPROTO_TCP;

addrinfo *remoteaddr = NULL;
getaddrinfo("192.168.1.4", "12345", &remotehints, &remoteaddr);
connect(s, remoteaddr->ai_addr, remoteaddr->ai_addrlen);
freeaddrinfo(remoteaddr);

编辑:此示例代码有意不进行错误检查,以便以最有效的方式传达我的意图。

编辑 2:绑定到 192.168.1.3 会导致连接失败。绑定到 127.0.0.1 有效。是的,我 100% 确定 192.168.1.3 是正确的本地 IP。

编辑 3:对!一时兴起,我在家用 PC 上试用了测试应用程序,它运行良好。所以,至少代码确实有效,问题肯定与我的工作电脑有关。

【问题讨论】:

看来绑定调用没用。 Windows 将使用目标 IP 和路由表来确定在哪个接口上发送数据包。阅读这两篇文章support.microsoft.com/kb/175396technet.microsoft.com/en-us/magazine/2007.09.cableguy.aspx 没关系。在确定要使用路由表中的哪些条目时,Windows 会考虑绑定 IP。多年来,它一直以这种方式工作。使用特定 IP 调用 bind() 确实可以确保使用 IP 所属的特定网络适配器/接口建立连接。 @KapilKapre:如果到目的地有重复的路线,Windows 确实会发挥神奇的作用并选择最有效的路线。但是,如果我想测试各种路由,我需要专门选择一个路由,发送一个 ping 请求或类似的请求并等待回复。然后我会依次切换到下一条路线并重复。这与我想到的应用程序相当接近。 不,防火墙已禁用 【参考方案1】:

好的,事实证明@claptrap 是对的。

我禁用了我的防火墙,但我不知道,我们的 IT 部门有一些其他防火墙功能紧密集成到企业病毒扫描程序中(我无法卸载或禁用)。一旦我设法让 IT 暂时禁用它,一切都按预期工作。

【讨论】:

【参考方案2】:

调用API函数时总是检查错误代码,例如:

addrinfo localhints = 0;
localhints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
localhints.ai_family = AF_INET;
localhints.ai_socktype = SOCK_STREAM;
localhints.ai_protocol = IPPROTO_TCP;

addrinfo *localaddr = NULL;
int ret = getaddrinfo("192.168.1.3", "0", &localhints, &localaddr);
if (ret == 0)

    ret = bind(s, localaddr->ai_addr, localaddr->ai_addrlen);
    freeaddrinfo(localaddr);

    if (ret == 0)
    
        addrinfo remotehints = 0;
        remotehints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
        remotehints.ai_family = AF_INET;
        remotehints.ai_socktype = SOCK_STREAM;
        remotehints.ai_protocol = IPPROTO_TCP;

        addrinfo *remoteaddr = NULL;
        ret = getaddrinfo("192.168.1.4", "12345", &remotehints, &remoteaddr);
        if (ret == 0)
        
            ret = connect(s, remoteaddr->ai_addr, remoteaddr->ai_addrlen);
            freeaddrinfo(remoteaddr);

            if (ret == 0)
            
                // connect succeeded...

                // can use getsockname() here to discover which port was actually bound to, if needed.
                // On some OS versions, bind() does not perform the actual binding immediately,
                // connect() does the actual binding instead since it can make more informed choices based on the target address being connected to...
            
            else
            
                // connect failed...
            
        
        else
        
            // getaddrinfo() failed
        
    
    else
    
        // bind failed
    

else

    // getaddrinfo() failed...

代码实际能走多远?

【讨论】:

为清楚起见,我在粘贴的示例代码中省略了错误检查。我在我的测试应用程序中安装了它。绑定调用成功(返回 0)。连接调用返回 WSAEADDRNOTAVAIL。如果我删除绑定调用,则连接调用会成功。有趣的是,如果我绑定到 127.0.0.1,绑定和连接都会成功 According to MSDN: "如果name参数指定的结构的地址成员用零填充,connect将返回错误WSAEADDRNOTAVAIL。"这表明getaddrinfo() 正在返回一个addrinfo,其ai_addr 包含零。你检查了吗? 如果我在 connect 调用之后直接打印出 ai_addr,它会打印出我希望在那里的 IP 地址。 如果你在调用connect()之前打印它?您是否尝试使用我之前向您展示的sockaddr_in/inet_addr() 方法而不是使用getaddrinfo()?它是否表现出相同的行为?对于 connect() 失败并出现此错误,“192.168.1.3:0”不可用于绑定,或者“192.168.1.4:12345”不可路由连接。你不能bind() 到“127.0.0.1”,然后connect() 到“192.168.1.4”,没有路由。如果绑定到“127.0.0.1”,则只能连接到“127.0.0.1”。 我已经上传了我的完整代码here。您只需更改 DEFAULT_HOST 和 DEFAULT_PORT 以指向有效的服务器。我只期待一个小的 http 响应(准确地说是 56 个字节),并且我已经进行了相应的编码。感谢您迄今为止所做的所有努力

以上是关于连接前的 WinSock 绑定导致 WSAEADDRNOTAVAIL - 请求的地址在其上下文中无效的主要内容,如果未能解决你的问题,请参考以下文章

Winsock编程基础2(UDP流程)

无法绑定winsock套接字

netsh winsock reset

如何中止winsock阻塞调用?

电脑输入netsh winsock reset有没有啥影响

Winsock - 为啥 ZNC(和 IRC 保镖)不接受我的 winsock 连接?