我正在调查Windows上的一个奇怪问题,不知道发生了什么。
我有一个运行Windows 10的Windows主机。该主机连接到两个网络,即我们的LAN和WLAN。该系统上的程序正在通过WLAN与连接到同一WLAN网络的嵌入式系统进行通信。通信通常可以正常工作,并使用HTTP和一些自定义TCP协议。
在嵌入式系统上执行软件更新时,它必须执行重新引导。 Windows程序通过HTTP将命令发送到嵌入式系统。该HTTP事务顺利完成,嵌入式系统执行了重新启动。
HTTP事务完成后,Windows程序等待10秒钟,通过执行TCP连接开始轮询嵌入式设备。使用一秒钟的超时时间,并在120秒后重试TCP重新连接。
网络跟踪显示,在27次尝试建立TCP连接失败之后,Windows系统突然停止在WLAN接口上发送TCP SYN帧,但开始将它们发送到LAN接口上的默认路由器。
我必须知道,为什么Windows突然使用默认路由器尝试到达本地连接的网络上的节点。 :-(
过一会儿,Windows再次开始使用WLAN接口。这似乎是由嵌入式系统的ARP条目超时引起的。 Windows在WLAN接口上发送ARP答复,嵌入式系统发送ARP响应,然后Windows再次将WLAN接口用于TCP SYN帧。不幸的是,那时的120s重试期已经到期。
有人知道,是什么原因导致Windows出现这种奇怪的行为,在哪里可以找到有关它的文档以及如何阻止Windows执行此操作?
在此先感谢
马里奥
更新
今天,我检查了Windows 7上的问题,并且Windows 7的行为与Windows 10相同。我还观察到源自Internet Explorer的HTTP连接请求被发送到默认网关,而不是正确的接口。我在行为异常期间检查了路由表和ARP表,发现路由表是正确的,但是ARP表缺少嵌入式系统的条目。
似乎是某些原因导致Windows丢弃ARP表条目,而不是在路由表中找到的接口上发出ARP请求以使用默认路由。
下一个更新
这似乎是预期的行为(至少根据https://superuser.com/a/743329/941271和https://superuser.com/a/743329/941271。
我了解到,arp -a
命令窗口在窗口:-(上没有显示完整的ARP表。使用netsh interface ipv4 show neighbors
时,我可以看到目标IP地址的条目标记为不可访问。
我尚未在Microsoft文档中找到有关此“功能”的任何文档。
又一次更新
我仍然没有从Microsoft找到关于此行为的任何描述,但是我找到了一个用于操作邻居表的API:GetIpNetTable2()和DeleteIpNetEntry2()。在尝试连接目标设备之前调用DeleteIpNetEntry2()解决了我的问题。
这是我的代码:
#pragma comment(lib, "iphlpapi.lib")
static void PurgeUnreachableEntry(const boost::asio::ip::address &Addr)
{
int AF;
if (Addr.is_v4()) AF=AF_INET;
else if (Addr.is_v6()) AF=AF_INET6;
else return;
PMIB_IPNET_TABLE2 pipTable = NULL;
unsigned long status = GetIpNetTable2(AF, &pipTable);
if (status != NO_ERROR)
{
printf("PurgeUnreachableEntry: GetIpNetTable() failed: %s\n",
boost::system::error_code(status, boost::system::system_category()).message().c_str());
return;
}
boost::shared_ptr<MIB_IPNET_TABLE2> IpTable(pipTable, FreeMibTable);
for (unsigned i= 0; i < pipTable->NumEntries; i++) {
MIB_IPNET_ROW2 &Entry=pipTable->Table[i];
if (!Entry.IsUnreachable) continue;
boost::asio::ip::address EntryAddr;
switch (Entry.Address.si_family)
{
case AF_INET:
EntryAddr = boost::asio::ip::address_v4(*(boost::asio::ip::address_v4::bytes_type*)&Entry.Address.Ipv4.sin_addr);
break;
case AF_INET6:
EntryAddr = boost::asio::ip::address_v6(*(boost::asio::ip::address_v6::bytes_type*)&Entry.Address.Ipv6.sin6_addr, Entry.Address.Ipv6.sin6_scope_id);
break;
default:
continue;
}
if (Addr != EntryAddr) continue;
DeleteIpNetEntry2(&Entry);
}
}