Winsock 的 connect() 永久返回 WSAETIMEDOUT,但 PuTTY 连接在同一端口上
可能是迄今为止我遇到的最奇怪的错误。
我需要连接到在 Windows XP Embedded 下运行的某些第三方应用程序。网络连接存在且有效:我能够使用在 Windows XP SP3 上运行的 PuTTY 连接到预期的端口,并执行一些类似 telnet 的操作。现在,我的应用程序是一个非常简单的 C (VC++ 2008) 程序:
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsaData = { 0 };
if (SOCKET_ERROR == WSAStartup(MAKEWORD(2, 0), &wsaData))
return Error("Failed to initialize sockets.");
SOCKET client = INVALID_SOCKET;
const char *pserver = "172.22.1.3";
client = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET != client)
{
sockaddr_in s = { 0 };
s.sin_family = AF_INET;
s.sin_port = htons(4799);
hostent *e = gethostbyname(pserver);
memmove(&s.sin_addr, e->h_addr, e->h_length);
std::cout << "Connecting to: " << pserver << std::endl;
if (SOCKET_ERROR != connect(client, (sockaddr*)&s, sizeof(s)))
{
std::cout << "Successfully connected." << std::endl;
}
else
std::cout << "Can't connect: " << WSAGetLastError() << std::endl;
}
getchar();
if (INVALID_SOCKET != client)
closesocket(client);
return 0;
}
每次编译和运行该程序时,我都会收到 WSAETIMEDOUT 错误。困惑之余,我开始深入挖掘,并在接收端生成了两个 Wireshark 转储 - 一个使用 PuTTY,另一个使用我的应用程序,从同一台 PC 运行并连接到相同的设备(下面是第一个 SYN 数据包)。
PuTTY:
No. Time Source Destination Protocol Info
1 0.000000 172.22.1.61 172.22.1.3 TCP atc-lm > 4799 [SYN] Seq=0 Win=65535 Len=0 MSS=1460 WS=0 TSV=0 TSER=0
Frame 1 (78 bytes on wire, 78 bytes captured)
Ethernet II, Src: Dell_b8:4a:31 (00:26:b9:b8:4a:31), Dst: EEPD_96:04:48 (00:e0:33:96:04:48)
Internet Protocol, Src: 172.22.1.61 (172.22.1.61), Dst: 172.22.1.3 (172.22.1.3)
Transmission Control Protocol, Src Port: atc-lm (1170), Dst Port: 4799 (4799), Seq: 0, Len: 0
0000 00 e0 33 96 04 48 00 26 b9 b8 4a 31 08 00 45 00 ..3..H.&..J1..E.
0010 00 40 04 a1 40 00 80 06 9b aa ac 16 01 3d ac 16 .@..@........=..
0020 01 03 04 92 12 bf 9a 99 fc ec 00 00 00 00 b0 02 ................
0030 ff ff 2c bd 00 00 02 04 05 b4 01 03 03 00 01 01 ..,.............
0040 08 0a 00 00 00 00 00 00 00 00 01 01 04 02 ..............
我的应用程序:
No. Time Source Destination Protocol Info
1 0.000000 Dell_b8:4a:31 EEPD_96:04:48 FC [Malformed Packet]
Frame 1 (78 bytes on wire, 78 bytes captured)
Ethernet II, Src: Dell_b8:4a:31 (00:26:b9:b8:4a:31), Dst: EEPD_96:04:48 (00:e0:33:96:04:48)
MDS Header(Unknown(0)/Unknown(0))
[Malformed Packet: FC]
0000 00 e0 33 96 04 48 00 26 b9 b8 4a 31 00 00 00 00 ..3..H.&..J1....
0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0020 00 00 00 00 00 00 00 00 6d 8b 00 00 00 00 b0 02 ........m.......
0030 ff ff 90 73 00 00 02 04 05 b4 01 03 03 00 01 01 ...s............
0040 08 0a 00 00 00 00 00 00 00 00 01 01 04 02 ..............
基本上,整个 IP 负载都被 0x00 而不是有效字节擦除。
现在最奇怪的部分来了。我为 .NET 3.5 编写了一个类似的程序:
static void Main(string[] args)
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Unspecified);
socket.Connect("172.22.1.3", 4799);
Console.WriteLine("Connected");
Console.ReadLine();
socket.Disconnect(true);
}
相同的 Windows XP SP3 PC,相同的网络连接 - 我的 .NET 应用程序只是连接到远程应用程序!此外,我原来的 C 应用程序在 Windows 7 x64 下的另一台笔记本电脑上运行得很好。经过一整天的头带后,我有两台 Windows XP SP3 笔记本电脑,我的 C 应用程序无法连接到远程客户端,还有一台 Windows XP SP3 和一台 Windows 7 x64 笔记本电脑,我的 C 应用程序可以正常工作。
我的第一个猜测是网络驱动程序,但 PuTTY 和 .NET 应用程序只能在同一台笔记本电脑上运行!
有没有人经历过类似的事情并且可以给我建议?
谢谢。
Probably the weirdest bug I've encountered so far.
I need to connect to some 3rd-party application running under Windows XP Embedded. Network connectivity present and works: I'm able to connect to the expected port using PuTTY running on Windows XP SP3 and perform some telnet-like operations. Now, my application is a very simple C (VC++ 2008) program:
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsaData = { 0 };
if (SOCKET_ERROR == WSAStartup(MAKEWORD(2, 0), &wsaData))
return Error("Failed to initialize sockets.");
SOCKET client = INVALID_SOCKET;
const char *pserver = "172.22.1.3";
client = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET != client)
{
sockaddr_in s = { 0 };
s.sin_family = AF_INET;
s.sin_port = htons(4799);
hostent *e = gethostbyname(pserver);
memmove(&s.sin_addr, e->h_addr, e->h_length);
std::cout << "Connecting to: " << pserver << std::endl;
if (SOCKET_ERROR != connect(client, (sockaddr*)&s, sizeof(s)))
{
std::cout << "Successfully connected." << std::endl;
}
else
std::cout << "Can't connect: " << WSAGetLastError() << std::endl;
}
getchar();
if (INVALID_SOCKET != client)
closesocket(client);
return 0;
}
Every time I compile and run this program I'm getting WSAETIMEDOUT error. Puzzled, I started digging deeper and produced two Wireshark dumps on the receiver side - one with PuTTY and another one with my application, being ran from the same PC and connecting to the same equipment (first SYN packets below).
PuTTY:
No. Time Source Destination Protocol Info
1 0.000000 172.22.1.61 172.22.1.3 TCP atc-lm > 4799 [SYN] Seq=0 Win=65535 Len=0 MSS=1460 WS=0 TSV=0 TSER=0
Frame 1 (78 bytes on wire, 78 bytes captured)
Ethernet II, Src: Dell_b8:4a:31 (00:26:b9:b8:4a:31), Dst: EEPD_96:04:48 (00:e0:33:96:04:48)
Internet Protocol, Src: 172.22.1.61 (172.22.1.61), Dst: 172.22.1.3 (172.22.1.3)
Transmission Control Protocol, Src Port: atc-lm (1170), Dst Port: 4799 (4799), Seq: 0, Len: 0
0000 00 e0 33 96 04 48 00 26 b9 b8 4a 31 08 00 45 00 ..3..H.&..J1..E.
0010 00 40 04 a1 40 00 80 06 9b aa ac 16 01 3d ac 16 .@..@........=..
0020 01 03 04 92 12 bf 9a 99 fc ec 00 00 00 00 b0 02 ................
0030 ff ff 2c bd 00 00 02 04 05 b4 01 03 03 00 01 01 ..,.............
0040 08 0a 00 00 00 00 00 00 00 00 01 01 04 02 ..............
My application:
No. Time Source Destination Protocol Info
1 0.000000 Dell_b8:4a:31 EEPD_96:04:48 FC [Malformed Packet]
Frame 1 (78 bytes on wire, 78 bytes captured)
Ethernet II, Src: Dell_b8:4a:31 (00:26:b9:b8:4a:31), Dst: EEPD_96:04:48 (00:e0:33:96:04:48)
MDS Header(Unknown(0)/Unknown(0))
[Malformed Packet: FC]
0000 00 e0 33 96 04 48 00 26 b9 b8 4a 31 00 00 00 00 ..3..H.&..J1....
0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0020 00 00 00 00 00 00 00 00 6d 8b 00 00 00 00 b0 02 ........m.......
0030 ff ff 90 73 00 00 02 04 05 b4 01 03 03 00 01 01 ...s............
0040 08 0a 00 00 00 00 00 00 00 00 01 01 04 02 ..............
Basically, the whole IP payload is being wiped with 0x00 instead of valid bytes.
Now comes the weirdest part. I wrote a similar program for .NET 3.5:
static void Main(string[] args)
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Unspecified);
socket.Connect("172.22.1.3", 4799);
Console.WriteLine("Connected");
Console.ReadLine();
socket.Disconnect(true);
}
Same Windows XP SP3 PC, same network connection - my .NET application simply connects to the remote application! Moreover, my original C application works just fine from another laptop under Windows 7 x64. After daylong headbanding I have two Windows XP SP3 laptops using which my C application doesn't connect to remote client, and one Windows XP SP3 and one Windows 7 x64 laptops where my C application works without any issues.
My first guess was network drivers, but PuTTY and .NET applications just work on the same laptop!
Had anyone exerienced something similar and could advise me?
Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
这对我来说看起来不太正确:
无权访问 VC 文档,但它不应该是
&e->h_addr
吗?只是一个 WAG 由于您的版本的 IP 块已清零,因此 memmove 会从错误的位置读取并复制一块未使用但已清零的内存。
This doesn't look right to me:
Don't have access to the VC docs, but shouldn't it be
&e->h_addr
?Just a W.A.G. Since your version's IP block is zeroed out, stands to reason the memmove is reading from the wrong spot and copying a block of unused-but-zeroed memory.
由于您要连接到 IP 地址,因此请勿使用 gethostbyname() (特别是因为您没有检查它是否有错误回复)。使用 inet_addr() 代替:
Since you are connecting to an IP address, do not use gethostbyname() (especially since you are not checking it for an error reply). Use inet_addr() instead:
我有一些类似的问题;它返回给我的是 WSAEADDRINUSE,而不是 WSAETIMEDOUT。 setsockopt() 解决了这个问题。
我还建议您使用 socket(..., IPPROTO_TCP) 更新 socket(..., 0) 调用。
I had some what similar problem; instead of WSAETIMEDOUT, it was returning me WSAEADDRINUSE. setsockopt() solved that problem.
I would also suggest you to update socket(..., 0) call with socket(..., IPPROTO_TCP).
第一 - 不要相信 API 中的默认设置 - 例如确保强制使用 TCP 作为协议并设置实际需要的套接字选项。很容易记住,您并不是静态链接所有代码,因此您始终可以面对不同的版本和应用的更新程度。强制所有选项和默认值有助于减少无数 QFE 的奇怪且未记录的副作用。
第二 - 看起来您正在构建 {x86,x64} 检查 x86 和 x64 构建之间是否存在任何可能的差异。确保您构建的双方都绝对干净(意味着明确删除可见的所有内容 - 构建多平台时总是会发生所谓的混合构建。
第三 - Wireshark 是否确实注意到来自您的应用程序的流量,还是只是它收到的网络噪音?我问的原因是,如果没有真正的流量,那么可能是组策略和/或防火墙 - PUTTY 很可能会进行显式 API 调用来打开内置防火墙上的端口,
这已经耗尽了我的想法哦,你有吗?只是出于好奇而尝试 x64 XP?
1st - Don't trust defaults in API - like make sure that you force TCP as a protocol and that you set socket options that you actually need. It's easy to remember that you are not statically linking all your code so you can always face different versions and degrees of updates applied. Forcing all options and defaults helps to reduce weird and undocumented side-effects of uncountable QFE-s.
2nd - Looks like you are building {x86,x64} Check any possible diffs between your x86 and x64 builds. Make sure that you build absolutely clean both sides (meaning explicitly deleting everything in sight - so called mixed builds can always happen when building multi platform.
3rd - Does Wireshark actually notice traffic from your app or is it just a network noise it gets? The reason I ask is that if there's no real traffic then is can be group policy and/or firewall - PUTTY most probably makes explicit API calls to open a port on built in firewall.
That exhaust my ideas at this point. Oh and do you have x64 XP to try? Just out of curiosity.
一定要在
socket
调用中显式指定IPPROTO_TCP
,不要让 API 为您选择。在你尝试过之前,不值得考虑其他任何东西。Definitely explicitly specify
IPPROTO_TCP
in yoursocket
call, don't let the API choose for you. Until you've tried that it's not worth looking at anything else.