0
votes

I have coded a server-client (two programs) structure which does work properly... only when TCP is used. My idea was to use TCP to do text chat transfer (udp is unreliable), but to use UDP for the game packets (yes it is some sort of action game with 30 fps, so I need UDP).

However, when I established a connection with TCP in my client process, I start sending UDP packets to and receive from the server program. The client uses non-blocking sockets, UDP and TCP in a single thread. No multi-threading here, I do not really like that idea if you can implement it in one process too.

However, the problem is that is seems as if the UDP packets take more than 5 seconds to arrive from client to server: they do arrive in most cases (I repeat sendto until I received a packet from the server indicating successful UDP transmission) but it takes far too long. The only problem I can imagine in the structure is the fact that I use TCP and UDP at the same time.

Note that I probably use different ports (sendto lets the OS bind to a port) and I ran the client and server on the same machine. I read somewhere that every process can only send one packet at a time; if that is the case, could that be the culprit of these negative experiences with UDP?

Code of server sending a game frame to client.

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);
    }
}

}

Client code of receiving the game state from server within a frame time limit:

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;

These processes happen simultaneously on two separate processes, every frame takes about 20 ms.

2
How and why are you waiting for the UDP packet to be sent successfully? It sounds a lot like you are trying to develop your own TCP protocol using UDP. UDP is not intended for reliable transport. If you need to guarantee packet delivery, just use TCP.Brett Wolfington
Oke, maybe I did not convey myself properly. I use UDP because I want to send the game state from server to client, and if that fails one frame it doesn't matter. However, the first time it has to succeed, that's why I repeat it until I receive it. The remarkable thing is, it takes about 4 seconds after initial attempt to send from server to client before something is received in a successful attempt. This all happens while there is a parallel TCP layer between server and client. There should not be any problem with using both of them, that is why I find this so strange.user209347
How do you check for connections and incoming data in the server? How do you receive data in the server? How do you connect and send data in the client? For both TCP and UDP please. Please show some code, it's always better than trying to describe what you do, to see what you really do.Some programmer dude
And a small tip: Don't try to do everything a once, do one thing at a time, and make sure it works. What I mean is that you should concentrate on getting one communication protocol to work fully before trying to start with the next. So first make sure the TCP chat communication works before doing the game event handling with UDP, or the other way around.Some programmer dude
After seeing your code, there was one thing that caught my eye, and thats the r == -2 check in the receiving code: I assume that core.CRecvFrom have its own error codes that it returns? Because the standard recvfrom will not return -2 specifically.Some programmer dude

2 Answers

1
votes

You may like route_io, it is c/c++ based library, it make udp/tcp/http all in one single instance. You just get the source and example to do whatever you want for your project. Simple Example

#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;
}
0
votes

Such a behavior of your app is usual. Unfortunately, it is due to many factors.

First of all, I advise you to check whether the packet is "actually" sent to the destination. Network packet analyzer such as Wireshark will be a help. If you don't have more computer, try using a virtual machine by VirtualBox or Hyper-V, or call bind("xxx.xxx.xxx.xxx") where xxx.xxx.xxx.xxx is your computer IP address. Wireshark will show you whether the packets are sent immediately or delayed.

Unless you are sending more than 500MB/sec thru loopback UDP socket, The cause may be in your program itself, not network something. Here is an example.

Assuming that packets are really sent too late, then your client app may have the cause. Check if your app is written like this:

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

    ... (other routines)
}

This should be fixed like this:

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 sometimes affects TCP performance and stability, but the vice versa is rare.