在Windows

时间:2016-08-14 10:09:28

标签: c# c++ sockets

我正在使用工具与游戏服务器进行通信。要建立与游戏服务器的连接,我正在发送登录数据包,然后从那里继续。我还使用了一个相同的工具,但它是由C#中的其他人用预先制作的库编写的。这个应用程序在使用它几个小时并移植它之后有一些堆栈溢出异常的问题linux并不是很有趣,因此我决定在C ++中从头开始编写我自己的应用程序。

我的脚本看起来很像:

while (!connected) {
     if (connectCounter == 0)
        std::cout << "Trying to connect..." << std::flush;
    else
        std::cout << "." << std::flush; // add point

    connectCounter++;

    int selectSize = 0;
    struct timeval timeout;
    timeout.tv_sec = 5;
    timeout.tv_usec = 0;
    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(mysocket, &fds);

    selectSize = select(mysocket + 1, &fds, 0, 0, &timeout);
    if (selectSize == 1) {
        // we might now be logged in, check routines
        connected = true;
     }
} 

现在两个应用程序中都有一个随机发生的“bug”,一个是C#中的其他人写的,也是我自己的一个。我应该提一下,我之前从未有过这种行为,但是我把我的电脑格式化了,我第一次看到这个问题开心了。

问题:Gameserver离线了好几个小时,计算机可能刚刚启动了。游戏服务器仍然停机,我启动了应用程序。现在它尝试登录但由于游戏服务器仍然处于脱机状态而无法成功。现在它写了“试图连接”。由于超时设置,它应等待5秒,然后在每次尝试失败后添加1点。相反,它会在不等待超时的情况下逐点触发。这种情况发生在应用程序,其他人编写的C#应用​​程序和我自己的应用程序中。在这两个应用程序中,它只是随机发生,而不是每次我启动应用程序时。正如我所提到的,我在格式化计算机之前从未遇到过这个问题。我还将此应用程序移植到我的linux服务器上,并没有在linux上遇到这种行为。我的一个朋友也使用这两个应用程序,从未向我报告过这类问题。

这对我来说太奇怪了,我无法弄清楚它的原因。从我得到的,这不能真正与代码相关,因为它发生在两个完全不同的应用程序和我可以告诉自我重新安装Windows。

编辑1:现在我发现了一些有趣的东西,我在windows和linux上添加了以下代码:

selectSize = select(mysocket + 1, &fds, 0, 0, &timeout);
std::cout << selectSize << std::cout;

有趣的是,在Windows上,我的控制台现在将输出:Trying to connect...0.1.0.1.0.1.0.1

重新启动应用程序并输出Trying to connect...0.0.0.0.0.1 在linux上,它始终返回Trying to connect...0.0.0.0.0,永远不会出现误报。

仍然只发生在Windows上。甚至不知道C#应用程序使用了什么方法,但重新安装Windows后会出现同样的问题。

编辑2:我想我发现了问题。

在超时设置和select()之前,我正在使用我的登录数据包进行sendto()。我想无论出于何种原因,都会有回传,所以selectSize在某些情况下可能会变为1。这是否有可能导致Windows上的问题,而它在Linux上工作?

3 个答案:

答案 0 :(得分:0)

引自“the”POSIX规范(a copy of it online)

  

当对具有O_NONBLOCK clear的输入函数的调用不会阻塞时,该函数将成功传输数据时,应认为描述符已准备好读取。 (该函数可能返回数据,文件结束指示,或者表示被阻止的错误以外的错误,并且在每种情况下,描述符都应被视为已准备好进行读取。

所以我要说的是为了修复你的代码,你必须另外检查“准备好阅读”的文件描述符是否没有任何错误或eof指示。

答案 1 :(得分:0)

要检查套接字是否已连接,您应该检查它是否可写,而不是可读性。变化

selectSize = select(mysocket + 1, &fds, 0, 0, &timeout);

selectSize = select(mysocket + 1, 0, &fds, 0, &timeout);

答案 2 :(得分:0)

好吧,似乎我终于找到了至少部分答案我的初步问题,为什么linux给了我一个工作结果,而windows破坏了我的应用程序。根据我在Windows平台上阅读的内容,select()返回WSAECONNECTRESET而不是阻塞或超时,请参阅:WinSock Recvfrom() now returns WSAECONNRESET instead of blocking or timing out

所以这似乎是应用程序在linux上完美运行(为了我的目的)的原因,其中select()似乎仍然返回超时,而Windows返回该错误并在一定程度上破坏我的应用程序。

<强>解决方案: 所以我终于找到了解决办法。特别感谢提醒我使用Wireshark的那个人。起初,当我将登录数据包发送到游戏服务器时,我认为select()返回1应该为0,而它离线时完全是随机的,但事实上我发现我不时会得到一个&#34 ; ICMP端口无法访问&#34;,这导致select()返回1而不是0(参见上面的链接)显然我只希望select()在来自服务器的实际登录响应时返回1。在Linux上,这开箱即用,并没有引起任何问题。 对于Windows,我通过在select()函数之前添加此代码找到了一个简单的修复:

#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR, 12)
DWORD lpcbBytesReturned = 0;
BOOL lpvInBuffer = FALSE;

WSAIoctl(mysocket, SIO_UDP_CONNRESET, &lpvInBuffer, sizeof(lpvInBuffer), NULL, 0, &lpcbBytesReturned, NULL, NULL);