TCP和UDP干扰

时间:2014-10-18 13:56:41

标签: c++ sockets tcp udp

我编写了一个服务器 - 客户端(两个程序)结构,只有在使用TCP时才能正常工作。 我的想法是使用TCP进行文本聊天转移(udp是不可靠的),但是使用UDP作为游戏数据包(是的,它是30 fps的某种动作游戏,所以我需要UDP)。

但是,当我在客户端进程中与TCP建立连接时,我开始向服务器程序发送UDP数据包并从服务器程序接收。客户端在单个线程中使用非阻塞套接字,UDP和TCP。这里没有多线程,如果你能在一个进程中实现它,我真的不喜欢这个想法。

然而,问题是看起来似乎UDP数据包从客户端到服务器需要5秒以上的时间:它们确实在大多数情况下到达(我重复发送直到我收到来自服务器的数据包,表明UDP传输成功)但它需要太长时间。我在结构中可以想象的唯一问题是我同时使用TCP和UDP。

请注意,我可能使用不同的端口(sendto让操作系统绑定到端口),我在同一台机器上运行客户端和服务器。我在某处读到每个进程一次只能发送一个数据包;如果是这样的话,这可能是UDP的这些负面经历的罪魁祸首吗?

服务器向客户端发送游戏帧的代码。

void SendGameData()
{
    unsigned char i, a, c;
    CBytes cont;
    CLoops(i, 0, app.clients)
    {
    if (client[i].step != 4)
        continue;
    // Send basic data
    tempbuf[0] = 0;
    tempbuf[1] = game.players;
    tempbuf[2] = client[i].player;
    core.CSendTo(net.udp, &client[i].udpaddr, tempbuf, 3);
    // Send player positions
    CLoops(a, 0, game.players)
    {
        tempbuf[0] = a + 1;
        c = 1;
        strcpy(tempbuf + c, player[a]->name);
        c += strlen(player[a]->name) + 1;
        cont.value = player[a]->obj.pos.x;
        tempbuf[c++] = cont.bytes[0];
        tempbuf[c++] = cont.bytes[1];
        tempbuf[c++] = cont.bytes[2];
        tempbuf[c++] = cont.bytes[3];
        cont.value = player[a]->obj.pos.y;
        tempbuf[c++] = cont.bytes[0];
        tempbuf[c++] = cont.bytes[1];
        tempbuf[c++] = cont.bytes[2];
        tempbuf[c++] = cont.bytes[3];
        cont.value = player[a]->dir.x;
        tempbuf[c++] = cont.bytes[0];
        tempbuf[c++] = cont.bytes[1];
        tempbuf[c++] = cont.bytes[2];
        tempbuf[c++] = cont.bytes[3];
        cont.value = player[a]->dir.y;
        tempbuf[c++] = cont.bytes[0];
        tempbuf[c++] = cont.bytes[1];
        tempbuf[c++] = cont.bytes[2];
        tempbuf[c++] = cont.bytes[3];
        cont.value = player[a]->angle;
        tempbuf[c++] = cont.bytes[0];
        tempbuf[c++] = cont.bytes[1];
        tempbuf[c++] = cont.bytes[2];
        tempbuf[c++] = cont.bytes[3];
        tempbuf[c++] = player[a]->defeated;
        tempbuf[c++] = player[a]->health;
        /*player[game.players]->normalspeed = true;
        if (gmode[gdata.modeprofile].mode == M_MATCH && !gmode[gdata.modeprofile].timeorstock)
            player[game.players]->lives = gmode[gdata.modeprofile].stock;
        player[game.players]->score = 0;
        player[game.players]->wait = 0;
        player[game.players]->ammo[WP_HMG] = 0;
        player[game.players]->ammo[WP_CANNON] = 10;
        player[game.players]->ammo[WP_AUTO] = 15;
        player[game.players]->ammo[WP_ART] = 0;
        player[game.players]->ammo[WP_LMG] = 50;
        player[game.players]->ammo[WP_MINI] = 0;
        player[game.players]->ammo[WP_AT] = 0;
        player[game.players]->weapon = WP_AUTO;
        player[game.players]->doubledamage = 0;
        player[game.players]->speedboost = 0;*/
        core.CSendTo(net.udp, &client[i].udpaddr, tempbuf, c);
    }
}

}

在帧时限内从服务器接收游戏状态的客户端代码:

int r;
char k;
CBytes cont;
memset(&cont, 0, sizeof(CBytes));
unsigned char c, count = 0;
CByte8u timez = core.CGetTime() + mswait;
while (true)
{
    r = core.CRecvFrom(net.udp, NULL, net.tempbuf, NET_BUFSIZE);
    if (r > 0)
    {
        if (net.tempbuf[0] == 0) // Basics
        {
            k = net.tempbuf[1] - (char)game.players;
            if (k > 0)
            {
                CLoops(c, 0, (unsigned char)k)
                    SpawnPlayer();
            }
            else if (k < 0)
            {
                CLoops(c, 0, (unsigned char)k)
                    DespawnPlayer(game.players - 1);
            }
            game.you = net.tempbuf[2];
            count |= 0x01;
        }
        else
        {
            c = 0;
            k = net.tempbuf[0] - 1;
            strcpy(player[k]->name, net.tempbuf);
            c += strlen(player[k]->name) + 1;
            cont.bytes[0] = net.tempbuf[c++];
            cont.bytes[1] = net.tempbuf[c++];
            cont.bytes[2] = net.tempbuf[c++];
            cont.bytes[3] = net.tempbuf[c++];
            player[k]->obj.pos.x = cont.value;
            cont.bytes[0] = net.tempbuf[c++];
            cont.bytes[1] = net.tempbuf[c++];
            cont.bytes[2] = net.tempbuf[c++];
            cont.bytes[3] = net.tempbuf[c++];
            player[k]->obj.pos.y = cont.value;
            cont.bytes[0] = net.tempbuf[c++];
            cont.bytes[1] = net.tempbuf[c++];
            cont.bytes[2] = net.tempbuf[c++];
            cont.bytes[3] = net.tempbuf[c++];
            player[k]->dir.x = cont.value;
            cont.bytes[0] = net.tempbuf[c++];
            cont.bytes[1] = net.tempbuf[c++];
            cont.bytes[2] = net.tempbuf[c++];
            cont.bytes[3] = net.tempbuf[c++];
            player[k]->dir.y = cont.value;
            cont.bytes[0] = net.tempbuf[c++];
            cont.bytes[1] = net.tempbuf[c++];
            cont.bytes[2] = net.tempbuf[c++];
            cont.bytes[3] = net.tempbuf[c++];
            player[k]->angle = cont.value;
            player[k]->defeated = (bool)net.tempbuf[c++];
            player[k]->health = net.tempbuf[c];
            player[k]->weapon = WP_AUTO;
            count |= 0x02;
        }
    }
    if (count == 3)
        break;
    else if (r == -2)
        return false;
    else if (core.CGetTime() > timez)
    {
        strcpy(core.inerr, "UDP session timed out.");
        return false;
    }
}
return true;

这些过程在两个独立的过程中同时发生,每帧大约需要20毫秒。

2 个答案:

答案 0 :(得分:1)

您可能喜欢route_io,它是基于c / c ++的库,它使udp / tcp / http都在一个实例中。您只需要获得源代码和示例就可以为您的项目做任何事情。简单的例子

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "route_io.h"

void init_instance(void *arg);
void read_handler(rio_request_t *req);

void init_instance(void *arg) {
  printf("%s\n", "start instance");
}

void read_handler(rio_request_t *req) {
  printf("preparing echo back %.*s\n", (int) (req->in_buff->end - req->in_buff->start), req->in_buff->start);
  rio_write_output_buffer_l(req, req->in_buff->start, (req->in_buff->end - req->in_buff->start));
}

void on_conn_close_handler(rio_request_t *req) {
  printf("%s\n", "connection closing");
}

int main(void) {

  rio_instance_t * instance = rio_create_routing_instance(24, NULL, NULL);
  rio_add_udp_fd(instance, 12345/*port*/, read_handler, on_conn_close_handler);
  rio_add_tcp_fd(instance, 3232/*port*/, read_handler, 64, on_conn_close_handler);

  rio_start(instance);

  return 0;
}

答案 1 :(得分:0)

您应用的这种行为很常见。不幸的是,这是由于很多因素造成的。

首先,我建议您检查数据包是否“实际”发送到目的地。 Wireshark等网络数据包分析器将是一个帮助。如果您没有更多计算机,请尝试使用VirtualBox或Hyper-V的虚拟机,或调用bind(“xxx.xxx.xxx.xxx”),其中xxx.xxx.xxx.xxx是您的计算机IP地址。 Wireshark将向您显示数据包是立即发送还是延迟发送。

除非您通过环回UDP套接字发送超过500MB /秒,否则原因可能在您的程序本身,而不是网络内容。这是一个例子。

假设数据包确实发送得太晚,那么您的客户端应用可能有原因。检查您的应用是否是这样写的:

FrameMove()
{
    a = send_queue.peek_first();
    r = udpSocket.sendTo(dest, a);
    if (r == success)
    {
        send_queue.pop_first();
    }

    ... (other routines)
}

这应该像这样修复:

FrameMove()
{
    while(!send_queue.is_empty())
    {
        a = send_queue.peek_first();
        r = udpSocket.sendTo(dest, a);
        if (r == success)
        {
            send_queue.pop_first();
        }
        else if (r == would_block)
        {
            break;
        }
    }
    ... (other routines)
}

UDP有时会影响TCP性能和稳定性,但反之亦然。