我编写了一个服务器 - 客户端(两个程序)结构,只有在使用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毫秒。
答案 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性能和稳定性,但反之亦然。