1

我正在制作一个多人游戏,每一帧,玩家的位置都必须发送到服务器,服务器会收到所有其他玩家的位置作为响应。

我添加了这一行以使我的代码非阻塞:

u_long one = 1; // Giggety
ioctlsocket(sock, FIONBIO, &one);

但现在的问题是,除非我启用自己的阻塞代码(while 循环),否则我无法从服务器接收其他玩家的位置。

while(recvfrom(sock, receiveBuffer, 255, 0, (struct sockaddr *)&server, &sockaddr_in_sizePtr) <= 0) {
}
// Do stuff with response

但是自从我这样做后,游戏就非常滞后,因为本地游戏在我得到响应之前不会运行。

我认为解决方案只是在 while 循环中调用我的显示函数,但这似乎没有任何作用。

另一个问题是我正在使用 UDP 套接字,这意味着数据甚至可能无法到达我,在这种情况下,它需要超时。所以我添加了这个:

time_t timer;
time(&timer);
while(recvfrom(sock, receiveBuffer, 255, 0, (struct sockaddr *)&server, &sockaddr_in_sizePtr) < 0) {
    display(); // Doesn't stop lag
    if(timer != time(NULL)) return; 
}

但是现在,我得到的时间实际上是每秒 1 帧。这太可怕了。

有人可以阐明如何保持游戏运行并让它同时监听套接字吗?

4

1 回答 1

1

每个交互式模拟程序都会运行一些非阻塞的主循环。在该循环中的某个时刻,您收集从外部到达的所有数据。如果一些新数据通过网络到达,您将读取它并将其处理为您的程序状态。如果没有任何东西到达,您只需像往常一样继续主循环。

但是,要使此方案正常工作,您的 SO 接收缓冲区必须足够大,以便它们可以积累大量传入数据包以供您接收。

所以你所做的就像

while(running) {
    if( hasDataArrivedOn(socket) ) {
        receiveFrom(socket);
    }
    
    if( isThereUserInput(mouse) ) {
        processMouseMovement();
    }

    if( isThereUserInput(keyboard) ) {
        processKeypresses();
    }

    simulate();

    display();
}

“hasDataArrivedOn”函数实际上是另一个 ioctl,即FIONREAD返回最后一个数据包中收到的八位字节数,如果没有待处理的数据包,则返回 0(如果最后收到的 UDP 数据包确实不包含有效负载,则返回 0;您的程序必须能够处理这个问题,否则它可能会被不包含有效负载的 UDP 数据包淹没而被 DoSed)。


更新 – GLUT 空闲回调变体

void idle()
{
    if( hasDataArrivedOn(socket) ) {
        receiveFrom(socket);
    }

    simulate();

    glutPostRedisplay();
}
于 2013-07-07T22:04:06.443 回答