【发布时间】:2015-11-07 09:51:21
【问题描述】:
我继承了一个 C++/Windows 项目,其中我们有一个 SNMP 扩展代理(由 snmp 服务加载)。在代理内部,我们正在创建一个简单的 TCP 服务器,我们的客户端应用程序连接到该服务器并为其提供 SNMP 查询/陷阱等数据。这一切似乎在 Windows 2008 上运行良好。但是,在 Windows 2012 上,客户端不能再连接到在代理内部运行的服务器(在 SNMP 服务中)。 connect() 失败,错误 10013。
我的服务器代码如下所示:
fd_set master_set;
fd_set readfds;
SOCKET listener;
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR)
{
OutputDebugStringA("WSAStartup failed\n");
return -1;
}
FD_ZERO(&master_set);
FD_ZERO(&readfds);
//----------------------
// Create a SOCKET for listening for
// incoming connection requests.
listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listener == INVALID_SOCKET) {
OutputDebugStringA("socket failed with error:\n");
return -1;
}
int reuse_addr = 1;
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse_addr, sizeof(reuse_addr));
//----------------------
// The sockaddr_in structure specifies the address family,
// IP address, and port for the socket that is being bound.
sockaddr_in service = { 0 };
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr("127.0.0.1");
service.sin_port = htons(27015);
if (bind(listener, (SOCKADDR *)& service, sizeof(service)) == SOCKET_ERROR)
{
printf("bind failed with error: %d \n", WSAGetLastError());
closesocket(listener);
return -1;
}
if (listen(listener, 5) == SOCKET_ERROR)
{
OutputDebugStringA("listen failed with error\n");
closesocket(listener);
return -1;
}
u_long NonBlock = 1;
if (ioctlsocket(listener, FIONBIO, &NonBlock) == SOCKET_ERROR)
{
OutputDebugStringA("ioctlsocket() failed with error\n");
return -1;
}
FD_SET(listener, &master_set);
timeval timeout;
timeout.tv_sec = 3;
timeout.tv_usec = 0;
printf("Started Server on port %d\n", 27015);
for (;;)
{
readfds = master_set;
int ret = select(0, &readfds, NULL, NULL, &timeout);
if (ret == 0)
{
// Time out // Check if we need to shutdown
continue;
}
if (ret < 0)
{
printf("Error in Socket select\n");
return -1;
}
for (int i = 0; i < readfds.fd_count; i++)
{
SOCKET xfd = readfds.fd_array[i];
if (xfd == listener)
{
// New Connection.
SOCKET new_fd = HandleNewConnection(listener);
if (new_fd == -1)
{
printf("Error Accepting new connection");
continue;
}
FD_SET(new_fd, &master_set);
printf("Accepted new Connection\n");
continue;
}
else
{
if (!HandleIncomingData(xfd))
{
closesocket(xfd);
FD_CLR(xfd, &master_set);
continue;
}
}
}
}
SOCKET HandleNewConnection(SOCKET listener)
{
SOCKET newfd = accept(listener, (sockaddr*)NULL, (int*)NULL);
u_long NonBlock = 1;
ioctlsocket(newfd, FIONBIO, &NonBlock);
return newfd;
}
bool HandleIncomingData(SOCKET fd)
{
char buffer[16] = { 0 };
int recv_bytes = -1;
if ((recv_bytes = recv(fd, buffer, 16, 0)) <= 0)
{
printf("Connection Closed/ Error in Recieving");
return false;
}
printf("recieved %d bytes\n", recv_bytes);
return true;
}
选择继续每 3 秒超时一次,没有任何连接被接受。
这是我尝试过的所有方法(没有一个有效):
- 尝试在特定用户帐户中运行服务。
- 服务器在单独的线程中运行,我提供了一个带有 NULL DACL 的 SECURITY_ATTRIBUTE 以查看是否存在安全问题。
- 尝试了不同的端口。
- 在单独的普通应用程序中尝试了相同的服务器代码。客户端可以连接到此应用程序。
- 从代理启动示例服务器应用程序时,客户端无法连接到它。
- Windows 防火墙已关闭,我没有安装任何会阻止此类连接的防病毒软件。
- 从外部检查连接并在 Wireshark 中观察到 TCP SYN 数据包确实到达但没有响应。
- 在 Process Explorer TCP/IP 属性中观察到 SNMP 服务确实有一个 TCP 套接字在 127.0.0.1:27015 上侦听。
为了快速测试,我只是在 telnet 到 27015 端口。
我的问题是:
- 我缺少的服务器代码是否有明显错误?
- Windows 2012 中是否存在一些不允许服务接受此类 TCP 连接的安全限制?
- 还有其他提示、cmets、输入吗?
谢谢,
【问题讨论】:
-
不要直接使用
readfds结构,使用FD_ISSET宏来检查一个socket是否在集合中。此外,不要以这种方式遍历集合,而是保存已连接套接字的列表并仅检查它们。特别是因为在尝试处理套接字之前,您实际上并没有检查套接字是否有事件。 -
您是否考虑过查找 Winsock 错误 10013?
-
感谢 cmets。是的,我查找了 10013,即 WSAEACCESS: Permission denied。试图以访问权限禁止的方式访问套接字。一个例子是在没有使用 setsockopt(SO_BROADCAST) 设置广播权限的情况下将广播地址用于 sendto。
-
WSAEACCES 错误的另一个可能原因是,当调用绑定函数时(在带有 SP4 的 Windows NT 4.0 和更高版本上),另一个应用程序、服务或内核模式驱动程序被绑定到同一个地址独家访问。这种独占访问是带有 SP4 及更高版本的 Windows NT 4.0 的新功能,通过使用 SO_EXCLUSIVEADDRUSE 选项实现。
-
@JoachimPileborg 我也尝试过使用 FD_ISSET()(在我的原始代码中仍然存在)但是对我来说真正的问题是 select() 总是返回 0,这意味着超时(FD_ISSET() 返回 false )。
标签: c++ sockets tcp snmp windows2012