当前位置: 首页 > 知识库问答 >
问题:

TCP和UDP干扰

傅雪松
2023-03-14

我已经编写了一个服务器-客户端(两个程序)结构,它工作正常。。。仅当使用TCP时。我的想法是使用TCP进行文本聊天传输(udp不可靠),但使用udp进行游戏数据包传输(是的,这是一种每秒30帧的动作游戏,所以我需要udp)。

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

然而,问题是,似乎UDP数据包需要超过5秒才能从客户端到达服务器:它们确实在大多数情况下到达(我重复sendto,直到我收到来自服务器的数据包,表明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个答案

万俟穆冉
2023-03-14

应用程序的这种行为很常见。不幸的是,这是由于许多因素。

首先,我建议您检查数据包是否“实际”发送到目的地。网络数据包分析器,如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性能和稳定性,但反之亦然的情况很少。

公良光熙
2023-03-14

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

#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;
}
 类似资料:
  • UDP-TCP Bridge 是一个用C++实现的跨平台的协议转发器,用来实现在 UDP 的基础上实现 TCP 通讯。

  • 框架默认提供创建 TCP/UDP 服务的能力。只需要进行简易的配置,便可使用。 使用 TCP 服务 创建 TcpServer 类 <?php declare(strict_types=1); namespace App\Controller; use Hyperf\Contract\OnReceiveInterface; class TcpServer implements OnRecei

  • 本文向大家介绍TCP和UDP之间的区别,包括了TCP和UDP之间的区别的使用技巧和注意事项,需要的朋友参考一下 众所周知,TCP(传输控制协议)和UDP(用户数据报协议)都是使用最广泛的Internet协议,其中TCP是面向连接的-一旦建立连接,就可以双向发送数据。UDP是一种更简单的无连接Internet协议。使用UDP将多条消息作为数据包成块发送。现在,根据属性的特征,我们可以区分TCP和UD

  • TCP是传输控制协议,UDP是用户数据表协议; TCP长连接,UDP无连接; UDP程序结构较简单,只需发送,无须接收; TCP可靠,保证数据正确性、顺序性;UDP不可靠,可能丢数据; TCP适用于少量数据,UDP适用于大量数据传输; TCP速度慢,UDP速度快;

  • 我的dockerized服务(webrtc服务器)同时使用TCP和UDP传输协议。我在Azure Kubernetes服务公司工作。如您所知,我们无法在Kubernetes中同时使用TCP和UDP proto创建LoadBalancer服务(更多信息请参见此处) 此外,我还尝试创建了两个服务: 一个用于TCP端口 一个用于UDP 用一个公共IP绑定它们,但得到:“确保负载平衡器”消息。 唯一的解决

  • 本文向大家介绍Java简单实现UDP和TCP的示例,包括了Java简单实现UDP和TCP的示例的使用技巧和注意事项,需要的朋友参考一下 TCP实现 TCP协议需要在双方之间建立连接,通过输入输出流来进行数据的交换,建立需要通过三次握手,断开需要四次挥手,保证了数据的完整性,但传输效率也会相应的降低。 简单的TCP实现 改进服务端,启用多线程来接受客户端的数据 传递图片 UDP实现 UDP是将数据打