Say What? In Which Great Technical Depth Is Discussed (Again)
TL;DR
Sending a network message doesn’t guarantee that it will arrive.
Topics
In this series I’m covering 5 topics:
- Topology
- Session/Connection Initiation
- Delivery Guarantees
- Named Data Pipes
- Packet Encoding (Architecture)
- Packet Encoding (Lossless Compression/Variable Length Encoding)
- Packet Encoding (Lossy Compression)
Really Brief Glossary
TCP
TCP is the Transmission Control Protocol. TCP makes a socket look like a stream of bytes, bytes which are written to the TCP socket are guaranteed to arrive with no corruption and in the order they were sent.
UDP
UDP is the User Datagram Protocol. UDP allows you to send packets of binary, and the only guarantee it provides is that if a packet arrives, it won’t be corrupted. If the packet was corrupted, it will simply be thrown away before you ever receive it.
IP
IP is the Internet Protocol. IP is the basic protocol which UDP and TCP are built on top of. IP makes no guarantees, your packet might arrive, and if it does it might be (partially or completely) corrupted.
UDP > TCP
Wait, what? That title makes no sense. TCP guarantees that your data will arrive and everything, it sounds great! Why would you ever use UDP?
The problem is how TCP makes those guarantees. At the lowest level TCP is still suffering from the same problems that IP has - packet loss and corruption, some of the bytes you send may not arrive or they may be corrupted (in which case the receiver will throw them away, so corruption is the same as not arriving). The problem is that when some data doesn’t arrive, it will (sort of, find more detail at wikipedia) send back a message asking for that data to be resent and will buffer all other arriving information until it gets a copy of the data which never arrived, so TCP introduces fake latency here by holding perfectly good information in memory. It has to, of course, the guarantee of in order delivery means that it can’t release that data to the receiver until the previous data has been resent.
Hopefully it’s obvious why TCP is totally useless for games. Think about the data about player position, for example, I want it to arrive in order (so that I don’t update to an old position) but I don’t need it to be reliable, if some data is lost then just drop it and give me newer data.
Now, you may be thinking to yourself that TCP isn’t totally useless, after all some data in the game still needs to be reliable, ordered and isn’t time critical (e.g. player text chat, so long as it arrives within 300ms of sending it’s probably ok). It’s still not ok to use TCP even here, the fantastic Glen Fielder in his Networking For Game Programmers series references a paper which essentially says that using TCP and UDP side by side can actually increase packet loss for UDP.
##UDP < TCP
So UDP isn’t quite enough, and TCP is way too much. What we really want is several different transmission modes…
Reliable+Ordered
Packets will arrive, and they will arrive in order. This is exactly what TCP offers.
Reliable+Unordered
Packets will arrive, but may arrive out of order.
Unreliable+Ordered
Packets may get lost, but the packets that do arrive are in order (in practice this simply means dropping packets which arrive late).
Unreliable+Unordered
Packets might not arrive, and they might not arrive in order. This is what plain UDP offers.
To implement these in TCP is impossible, we’d have to remove some of what it does for you (which it doesn’t allow). Implementing them over UDP is perfectly possible though, so that’s what Heist does.
When Should I Use X?
The general policy I have for working out which transmission mode to use is to work out the minimal requirements for what you really have to have and use that. Using the “higher” guarantees also comes with higher costs, and should be avoided as much as possible.
How Heist Does It
Heist actually does not implement all of these new transmission modes itself, instead it uses the fantastic (lidgren networking library)[https://code.google.com/p/lidgren-network-gen3/] to abstract away a lot of the details of how sockets work. Lidgren internally has a single UDP socket, and over the top of that it pretends to maintain connections to other lidgren sockets, keeps up to date ping information and implements some very basic flow control. Of course, these fake connections offer the four transmission modes outlined above.