🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Grouping asynchronously received UDP packets together, am I doing it right?

Started by
2 comments, last by jakubp 5 years, 10 months ago

As it is my first post in this forum, I want to say hello to everyone.

Firstly I will tell what I want to acheive, then I will provide some example. So, I'm writing game in c# using MonoGame Framework and my own ECS. It's first time when I try to implement multiplayer feature. In my ECS every system is run one by one and order of systems is important. So I tough that the easiest way to implement networking, will be to receive packets asynchronously and group them together every frame. When client system would be executed every packet, which was collected since last frame would be read and used to update game state. But I don't have experience in asynchronous programming, so it's hard for me to combine asynchronous and synchronous part.

Note, it's not my real production code, only example.


using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Threading.Tasks;

public class ReceiveServerData
{
    private IList<byte[]> LocalPackets;

    private IList<byte[]> GlobalPackets;

    public ReceiveServerData(IList<byte[]> globalPackets)
    {
        GlobalPackets = globalPackets;
        LocalPackets = new List<byte[]>();

        Init();
    }

    public void Init()
    {
        Task.Run(async () =>
        {
            using (var udpClient = new UdpClient(11000))
            {
                while (true)
                {
                    var result = await udpClient.ReceiveAsync();

                    LocalPackets.Add(result.Buffer);
                }
            }
        });
    }

    public void Update()
    {
        GlobalPackets.Clear();

        //-----Can this process be interrupted, by ReceiveAsync? ----
        var localCopy = LocalPackets.ToList(); 
        LocalPackets.Clear();                   
        //----- If yes some packets may be lost due to poor design?

        foreach(var packet in localCopy)
        {
            GlobalPackets.Add(packet);
        }
    }
}

My problem with this code, is that, if I understand correctly, asynchronous code can be executed in any moment, so if the process of rewriting local packets to global packets will be interrupted, packets which was received during this interruption will be lost. I know that when using UDP I should expect some packets to be lost, but here packets will be lost not due to some network problem but because of how code is designed. For me it's indication that something is wrong with my code and design. Also, I use Global packets so every other system after this one, is safe to use GlobalPackets because they will not change until next synchronous update.

Note 2, I want to keep things simple, I'm making this game as a learning experience and I don't want to bother my head with complex network design patterns. Firstly, I want to make it work :)

Advertisement

You can take two routes:

1) Don't use threads. Instead, set the UDP socket to non-blocking mode, and at the beginning of each tick, read from the UDP socket until it returns "no more data." (The kernel will buffer a fair amount; you can control how much with a socket option.) No threads needed!

2) Use threads, and add a lock around the "swap global for local" operation. Specifically, when you add to the local list, you need to hold the lock while inserting into the list. When you call into the receiver object from your main thread, grab the lock, clear the global list, swap the global and local list variables, release the lock, and return the global list (which is now full of what's been received.)

EIther system will work. Also, you don't need async/await at all in this code, because the reading thread can just block in the UDP socket receive function.

 

enum Bool { True, False, FileNotFound };

I went for the second option and everything works right. Thank You!

This topic is closed to new replies.

Advertisement