Critique My Network Design

Hey everyone. I am in the process of designing a Java-based 2D multiplayer game engine type thing (let me know if that was too technical :)), and I thought it best to seek out the advice of those more experienced in such things. I’d like to know your opinions on the efficiency, inefficiency, neatness or sloppiness of my current network system. Any feedback is greatly appreciated!

First of all, I am using UDP sockets (because I hear they are superior for making realtime/high-speed gameplay over a network).

The Server

When the server is started, a new DatagramSocket is created with a user-specified port. A new thread is created solely for receiving packets on this new DatagramSocket (we’ll call it the listen socket). When a packet is received, the server creates a new thread with a new DatagramSocket specifically for communication with this client (this socket is bound to whatever port the system decides). It then sends a verification message to the client on the new socket so that the client knows the server is there, and to give the client the port of the new socket. It sends this message 8 times (for handshake-type packets, I use redundancy to virtually guarantee arrival despite using UDP). The server stores a list of all client threads for later use.

If, however, the server receives a message on the listen socket and there is already a client thread with that address, it ignores the packet. This is virtually guaranteed to happen, since we are using redundancy on the client side as well.

The Client

To join the server, we need the IP address and port of the server’s listen socket (these could be obtained through a matchmaking system, or just using direct IP or LAN or something like that). The client sends a join request to the server 8 times (again, redundancy, the server will ignore all but one of these). We then wait for the verification packet from the server (timeout after ten seconds or so if we don’t get one). Once we get one, we start a new thread that tries to receive data from the server (using a DatagramSocket, of course).

The Network Cycle

On the server, each client thread tries to receive data from a specific client. Any data that is received is stored in an ArrayList of packets (packets are stored as byte arrays). On the client the same thing happens, except there is only one thread instead of many, since the client need only communicate with the server. During every update cycle, we get the list of received packets and properly process each one, then we clear the list. We update the game, then we send any data that we need to send.

My question is, is this a good design? Is there any way I could make it better? If you have any questions, tips, comments, or anything just let me know. Thanks in advance for your advice!

I would recommend you to use TCP for your handshake, it is the better option since it is reliable and stream oriented, which is basically what you are reinventing to make your handshake work.
If you still want to use UDP for this, do not send the ack packets all at once, use a delay of 50 to 100ms instead to not flood the client (i guess you thought about this already).

Regarding your ArrayList, keep in mind that packets can arrive at any point of time, eg when you are just about to remove packets from your list, boom exception!
Make yourself comfortable with the java.util.concurrent package, especially the ConcurrentLinkedQueue to access and store your received packets without introducing nasty concurrency bugs.

KryoNet does what you need btw, i would strongly recommend using it or another proven library over rolling out your own stuff, except if you do this to learn how stuff works, then go ahead and try your solution, i can’t spot anything that will fire back yet :point:

EDIT: Oh and keep in mind that UDP does not need a thread per client since it is not restricted to a blocking point to point pseudo-connection. Your game will likely perform just as good or better with a single socket on the server side like KaiHH pointed out.

Why not build a TCP layer on top of it, so you can send reliable packets. Which you should use for login.

In a WAN setup with the client being behind a NAT/router, the client will never receive this message.
Also what is the reason to have a per-client socket? IMO this is unnecessary and all communication can perfectly be handled over a single socket.

Thanks for the advice, guys. Yeah, this code is a couple of months old, and I already can’t remember why I was using a separate thread for each client. It made sense at the time. If I can’t think of the reason why, I’ll definitely change that.

@KaiHH, this may be a noob question, but why exactly won’t the client receive the verification message? Would it receive the message if the server sent it using the original listen socket?

@VaTTeRGeR, I did encounter ConcurrentModificationException when I was using a method that removed packets from the list as it read them. I changed it into a system where I place the contents of the ArrayList into a new ArrayList, then immediately clear the original list in the very next line of code. I then do stuff with the packets in the new list, leaving the other list to be filled in the background even while I am doing something with these packets. Will I still be in danger of some sort of exception this way?

Thanks again.

I would just process packets right away, putting and getting them from a list is only overhead. You need to think over if you really need to handle packets at that specific moment.

Read about how Network Address Translation works: Wikipedia article.
Of particular interest to you is the categorization of NATs into “Port-restricted cone NAT” and “Symmetric NAT.” Those are the types of NATs that won’t work with your current procedure.

Another reason why randomly generated per-client ports/connections is not a good idea, is when both server and client are behind a NAT/router. With a single known external server port you can statically (or dynamically via UPnP) configure the router/NAT of the server to forward packets with this target port to your server computer.
With arbitrary ports you cannot do this (if your router does not support UPnP).

[quote]Would it receive the message if the server sent it using the original listen socket?
[/quote]
Yes. And the client would then have to send the first packet on the new “connection” to the new server port, for the client NAT to know about this “connection” and let incoming packets through.
This is remotely related to UDP hole punching (although only the client should be behind a NAT). It’s also good to read about that here: Wikipedia article.

I don’t really know how your network thread and game are intervined. I think it would be best to either process them immediately or to store them in a thread-safe queue so that you can process them at the beginning of the update loop for example.

But please get rid of the double list system ;D

Again, thanks for the advice. It really is helpful. I know that it may sound like I’m trying to reinvent the wheel by coding my network system from scratch, but I find that’s the best way to learn how something works, to do it myself.

One question. In order to test how the network system works (including NAT punch-through and the like), do I need the server and client to be behind two different modems, or will two routers on the same modem suffice? This is probably a noob question, but I want to make sure I’m doing this correctly.

Thanks again. :slight_smile:

You should test it in a “real” scenario if anyhow possible, i think the cases you mentioned will behave differently.

Sorry for highjacking this thread for one answer… but
Okay so if I sent this packet to the server:

byte[]{1,2,3,4,5}

Will it come back as…

byte[]{1,2,3,4,5}

When using UDP?

byte[]{1,2,3,4,5} and byte[]{6,2,3,4,5}

sent will return back as

byte[]{6,2,3,4,5} and byte[]{1,2,3,4,5}

because the packets don’t arrive guarenteed, right?

Seriously. Just use TCP. It is not slower. It is the same freaking packets on the same network where switches and routers tend to prioritize TCP.

If TCP is lagging on you, UDP will be long dead.

If you don’t know what 2 generals is, or flow control, or congestion collapse. Just don’t freaking use UDP cus you don’t know networks. We spent decades fine tuning TCP to be good at what it does. Use it. Why reinvent it?

Even the AAA games are using it now. Of course a huge chunk of games have some of the most crappy network code out there. So using games as an example is probably a bad idea.

Hi,

You could consider using MQTT (mosquitto server | eclipse Paho Client).
It’s easy to use, I had my server and client running within 10 minutes.

MQTT is an message bus but is ideal for gaming since you don’t have to handle the connections yourself.