Notifications
Article
Basics of Networking with Unity
Published 6 months ago
111
0
A Quick Intro to Internet Protocol Networking

Introduction

A couple of years ago, Unity introduced UNET (Unity Networking) in two forms: the Low Level API (LLAPI) and the High Level API (HLAPI). What's nice about this was that Unity shipped the Sockets API in .NET in all versions of Unity to support this. The point is, if you understand Sockets concepts, the UNET API gets a bit easier to understand. In the detail below, I'm going refer to the Microsoft MSDN documentation for .NET Framework 3.5 as this most closely matches the Mono .NET included with Unity.
The base protocols we're going to discuss are:
  • IP - Internet Protocol (where "IP" in "IP Address" comes from)
  • UDP - User Datagram Protocol
  • TCP - Transmission Control Protocol
IP is the fundamental protocol for all Internet traffic. Every single message sent over the Internet is sent with IP. The purpose of IP is to define a way to get any kind of message from one computer to the destination, allowing routers or firewalls to help re-transmit the message as needed.
For UDP and TCP, let me use cell phones as an analogy because we all have them and it will make some ideas easier to understand. UDP is like a text message. Any text longer than 160 characters is broken down into smaller messages and sent in turn to the other handset (SMS in GSM networks allow up to 160 characters per message). SMS messages are not guaranteed and could be lost anywhere between the sender and the receiver, although they usually get through. The same is true for UDP. Messages are sent in 512 byte messages and any message could get lost but they usually get there.
TCP is very different to UDP and is more like making a phone call. Just like a call, TCP asks to make a connection to a remote computer and waits for the connection to be routed through a network and for a connection accept message to be received before doing any further activity. I said "base" protocol before because protocols like HTTP are application protocols that sit on top of these base protocols. HTTP for instance uses TCP to transmit HTML and any images, CSS, javascript etc.

A Quick Note on Ports and Sockets

You've probably noticed that protocols on the Internet have addresses and ports. You might have wondered why...
Ports add to the IP Address to allow a specific service or application to be addressed on the computer. Ports therefore allow a computer to support multiple connections or messages at the same time. UDP ports are considered different to TCP ports so if you want to use port 1800 for messages and connections, you need to deal with both UDP port 1800 and TCP port 1800. There's also a distinction of source port and destination port. For instance, HTTP uses port 80 as the destination TCP port.
A unique socket is made up of:
Source IP, Source Port : Destination IP, Destination Port ("IP" means IP Address)
A socket is resource controlled by the operating system so you definitely can't open the exact same socket multiple times.
Usually this is not a big worry because you usually don't bother with source ports and let the computer find the next available (called an "ephemeral port") but the destination port on the destination computer is the thing that matters. It's this that lets you open up multiple tabs all pointing to http://google.com at the same time.
Most UNIX like systems (yes, even Git for Window BASH) has a list of known ports in the file /etc/services and it indicates the protocol as well as the port number and service name.

Diving Into Detail

Why Use TCP or UDP?

TCP needs a connection and UDP just sends discrete messages and so their basic operations are different for the most part with some similarities due to IP. There are some interesting differences between the two. The basic operations I'm referring to, are methods such as Accept, Bind, Close, Connect, Disconnect, Listen, Receive, Select, Send and Shutdown.
UDP is great for sending information (like text messages on a mobile) where the most important message is the one sent just now and anything older is irrelevant. UDP has lower overhead so a message usually (but not always) arrives shortly after it was sent. This means it's great for info that ages quickly so if it never arrives (UDP is not guaranteed) then it doesn't matter. UDP is great in a multiplayer game for position/movement messages, collision messages, damage updates and other event triggered messages. UDP doesn't guarantee order either, but in practical terms that is only an issue if you send a burst of messages. Hey, if that matters, you can number them.
Alternatively, TCP guarantees delivery but because it resends lost packets etc, you never, ever, EVER know when the information arrives. Could be 5 minutes later. The data is more like a stream and is presented to the application on other side when the information is ready. So you can send JSON such as {"text": "hi there", "character": "Tom"} over TCP but it might be received as {"text": "hi th followed a short time later by ere", "character": "Tom"} which is a perfectly normal thing for TCP to do. It just means that the receiving application needs to assemble or parse the information when the envelope is complete. By 'envelope,' I mean any boundary that you might use in your data. In JSON, the bracies ("{" and "}")are a nice boundary. Some protocols let the Close signal the end of the stream and the receiver can take that to mean the data is complete. HTTP does this for image transfers (or at least HTTP 1.0 did).
So, if you're downloading a texture (or other asset), use TCP but if you're transmitting position information or enemy fire or collision info, then use UDP.

Usual TCP Operations

The operations for TCP game host (a server) differs to a game client. A host needs to wait for clients to join whereas a client needs to find a server and try to connect - basic differences. Also, a computer usually has multiple IP addresses but one is never addressable on the Internet (localhost - 127.0.0.1) so you might need to use all available IP addresses or choose an address that can be routed over the Internet.
I'll be stressing the use of the Select() operation before doing any reads on a socket to avoid blocking the thread. Otherwise, Unity with freeze until data is received. Also, an Accept() is like a read because it waits for a connection request.

Gaming Host - TCP

For a Game Host Using TCP to send textures, level setup info or other complex file / data transfers. Assuming you've created a Socket object for TCP Socket, for example (from MSDN).
var listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Tcp);
The operations for it are:
  1. Bind() to declare a server (destination) address and port
  2. Listen() to tell the socket to expect incoming TCP connection requests.
Then in a loop, check for incoming requests:
  • Use Socket.Select() to see any incoming activity on current connections (with clients) or for the listening-socket, for incoming connection requests.
  • When the listening socket is marked readable, it means there's a connection request waiting - do Accept() to accept the connection and now you can send it information or check it for a request.
Then when the gaming host is finished with a client connection (that's one of the accepted connections), you can call Shutdown() and then Close(). For the listening socket, Close() should be fine.

Gaming Client - TCP

Assuming your protocol is simple, a client might connect with the game host, request a level layout, texture or other asset and then store all received content as that asset, locally.
The steps are then:
  • Create the TCP socket object (same as above)
  • Do a Connect() call, specifying the game host address and port as the EndPoint.
  • Send() a request message
  • Use Select() in a loop to wait for content to arrive and when this indicates the socket is readable,
  • Read the socket with Receive() and add to a buffer or write it to a local file - up to you, really.
  • When you're done, Shutdown() and Close() the socket as above.

Game Peers - UDP Steps

The interesting thing about UDP is that all applications are peers, generally, and any game peer can send or receive a message from another peer. There is no concept of a connection so there's no waiting for incoming connections (game host) or sending connection requests (game client) but instead the UDP game peer needs to receive a message and in the process identify the message source.
First, say in Start(), you'll need to create a UDP socket object like the following (from MSDN):
Socket s = new Socket(endPoint.Address.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
You probably will want to track the different peers in some Dictionary or List collection. You might have peers announce themselves via a multi-cast message (I'm going to leave the research for that as work for the reader). Anyhow, assuming you know all the peers you're trying to alert about game events, the steps will generally be:
  • Use SendTo() to send a message to a given peer - simple case might be to send a message to all known peers for some event.
  • Use Select() to see if any UDP socket objects are readable.
  • If readable, use ReceiveFrom() to get the message and the source ip/port details so you know who to reply to - if that's important for your game.
  • When you're done, you can just Close() your socket object.
And that's the general skeleton of a UDP game peer. It is possible to use Bind() upfront but that generally limits the peers that can be involved. If you use Bind(), you can then use Send / Receive instead of SendTo and ReceiveFrom because the conversation becomes more one-to-one. I think an open collection of peers is more fun.

Using this in a Unity MonoBehaviour

Some of the operations (above) are for setup - once only (or thereabouts); some for whenever events might arrive - like in an Update loop; and some when you're shutting everything down. I'm going to refer to the standard Start(), Update() and OnDestroy() methods in a MonoBehaviour for the different lifecycle stages.
If you do all these things - create a socket, check for events, send and receive - every time, in a loop (eg Update), you'll kill your framerate and very likely create a lot of objects on the heap, eventually putting pressure on the garbage collector and then you'll really suffer a bad frame rate. So don't do this.
Instead, I recommend you create your socket (whether UDP or TCP) in the Start() method and handle any issues if the socket could not be created. Then during Update(), and only if the socket is still active, use Select() to see if there's any data to fetch - if so, use Receive() or ReceiveFrom() to get it.
Finally, in OnDestroy() clean up and Close() the socket.

Think About Commands Between Hosts

For game hosts or clients, you probably need some simple way for the client to indicate what it wants - a request command. I suggest you keep this simple (at least at first) and make one command per connection. That way the debugging and management of information in your classes is simpler.
For game peers sending game-play events (eg UDP), think about whether a response is needed. How will you link GameObjects to IP addresses? How will non-player characters run - as in which host is in control of the NPCs or should they have a few NPCs each? What happens when a peer disappears unexpectedly? Say it crashes. A lot of these decisions depend on the nature of your game.

Unity's Transport Layer

If you take a look at the Unity Transport Layer API (used to be called the LLAPI), it makes similar distinctions to TCP and UDP in the API - there are connection events (like TCP) and data events (like UDP) and the transport layer is built on top of TCP and UDP. They have the idea of channels that let you keep some network interactions message based and others connection based. Unity have created an abstraction on top of UDP and TCP and baked in the considerations of which is good for what networking situation.
You still need to think about the kind of information your game is to send and whether that information is short-lived and event-driven versus those situations involving bigger data transfers that are one-off in nature - say like file transfers.

Conclusion

Regardless how you go about using networking, understanding the differences in the types of data communication will make a big difference to your game-play. Deciding that some messages are short-lived and event driven where others are content transfers, that are naturally longer-lived and where order of transmission is important, will be key to getting good results.

Warwick Molloy Lokel Digital Pty Ltd.

Warwick Molloy
Tecchnologist and Developer - Programmer
1
Comments