看来,我在Windows中发现了一个错误... 好吧,不要这么可怜。我正在尝试为UDP执行泛型sendto()操作,偶尔发现WinXP(32位,SP3,在实际和虚拟机上检查)返回与WSAGetLastError()一起发送的“-1”字节为错误10014(也称为WSAEFAULT)。仅在IPv4地址上发生(与IPv6目标相同的代码完美运行)。重现的主要条件是在全局范围内声明的“const struct sockaddr_in”的使用。这是VS2010的普通C代码(我也尝试过使用Eclipse + MinGW,结果相同):
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <winsock2.h>
#include <stdint.h>
#pragma comment(lib, "Ws2_32.lib")
#define INADDR_UPNP_V4 0xEFFFFFFA
#define htons(x) ((((uint16_t)(x) & 0xFF00) >> 8) | (((uint16_t)(x) & 0x00FF) << 8))
#define htonl(x) ((((uint32_t)(x) & 0xFF000000) >> 24) | (((uint32_t)(x) & 0x00FF0000) >> 8) | (((uint32_t)(x) & 0x0000FF00) << 8) | (((uint32_t)(x) & 0x000000FF) << 24))
// Magic "const" qualifier, causes run-time error
const struct sockaddr_in addr_global = {
AF_INET,
htons(1900),
{
htonl(INADDR_UPNP_V4)
},
{0},
};
int main(int argc, char** argv)
{
#define CR_LF "\r\n"
// these two lines to un-buffer console window output at Win32, see URL below for details
// http://wiki.eclipse.org/CDT/User/FAQ#Eclipse_console_does_not_show_output_on_Windows
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
printf("Started\n");
const struct sockaddr_in addr_local = {
AF_INET,
htons(1900),
{
htonl(INADDR_UPNP_V4)
},
{0},
};
const char *MSEARCH_REQUEST_V4 = "M-SEARCH * HTTP/1.1"CR_LF
"Host:239.255.255.250:1900"CR_LF
"MAN:\"ssdp:discover\""CR_LF
"ST:ssdp:all"CR_LF
"MX:3"CR_LF
CR_LF;
const int MSEARCH_LEN = strlen(MSEARCH_REQUEST_V4);
WSADATA wsaData;
int res = WSAStartup(MAKEWORD(2, 2), &wsaData);
int af = AF_INET;
int sock_id = socket(af, SOCK_DGRAM, IPPROTO_UDP);
if (-1 == sock_id) {
printf("%s: socket() failed with error %i/%i\n", __FUNCTION__,
errno, WSAGetLastError());
return 1;
}
int data_sent = 0;
printf("1st sendto()\n");
data_sent = sendto(sock_id, MSEARCH_REQUEST_V4,
MSEARCH_LEN, 0,
(const struct sockaddr * const)&addr_local,
sizeof(struct sockaddr_in));
if (data_sent < 0) {
printf("%s: sendto(local) failed with error %i/%i\n", __FUNCTION__,
errno, WSAGetLastError());
}
printf("2nd sendto(), will fail on WinXP SP3 (32 bit)\n");
data_sent = sendto(sock_id, MSEARCH_REQUEST_V4,
MSEARCH_LEN, 0,
(const struct sockaddr * const)&addr_global,
sizeof(struct sockaddr_in));
if (data_sent < 0) {
printf("%s: sendto(global) failed with error %i/%i\n", __FUNCTION__,
errno, WSAGetLastError());
}
closesocket(sock_id);
res = WSACleanup();
printf("Finished\n");
return 0;
}
因此,例如,如果您在Win7上运行此代码,那就绝对没问题。但是如果WinXP配备了“const”限定符(参见上面的“魔术”评论),则无法使用addr_global。此外,“输出”窗口显示:
SendtoBugXP.exe中0x71a912f4的第一次机会异常:0xC0000005: 访问冲突写入位置0x00415744。
借助“Autos”窗口,可以很容易地发现0x00415744位置是addr_global.sin_zero字段的地址。似乎,WinXP在那里写零并违反内存访问标志。或者这只是愚蠢的我,试图出错门?
非常感谢您的评论。提前谢谢。
答案 0 :(得分:1)
答案 1 :(得分:0)
总结来自其他论坛的结果:是的,这是Windows错误,存在于“桌面”中的WinXP和“服务器”段中的Win2003。 WinSock代码尝试用零强制填充“sin_zero”字段。并且“const”全局范围会导致内存访问冲突。堆栈跟踪就像这样:
线程[1] 0(暂停:信号:SIGSEGV:分段故障)
WSHTCPIP!WSHGetSockaddrType()位于0x71a912f4
0x71a52f9f
WSAConnect()位于0x71ab2fd7
test(main)的main():77 0x401584
其他人在bind()上观察到相同的行为。