For example, let’s say that your game server sends out a position update every 20 milliseconds, and it does this all over an individual TCP connection to each player.
If one client starts to lag, the server can notice that those updates are piling up on that client’s send queue, and stop sending updates to that client. Then, as soon as the client’s buffer starts to be consumed, it can send whatever the most recent state is, which compensates for the most serious problem of TCP with respect to real time protocols, which is that stale updates clog the connection and leave a player experiencing some temporary packet loss far out of date.
But your operating system's TCP stack provides feedback mechanisms to determine that there has been packet loss – or, for that matter, other types of network slowdown. While UDP is indeed optimal for real-time protocols if it's very carefully used, you can preserve many of the properties of TCP (for example, not needing to implement your own congestion control) without blindly sending all of your players’ movements across the network.
The issue is what happens when there's packet loss. If you blindly blast out all of your player's movements with UDP, then when there is some packet loss, their movements will start to appear jerky, they'll pause periodically, and they'll take impossible paths, but, assuming their network recovers, they'll generally end up in the right place.
If you blindly send your players movements over TCP, then when there's packet loss, they'll pause, then rapidly catch up, but take an accurate path.
The thing is, once you've classified all your types of information and designed your messages, you can actually do a form of this optimization with TCP too.
Then you have to carefully build a protocol on top of UDP. You need to be careful to include all the relevant information that will need to be used to contextualize the information that's received in a datagram. At the very least this means a datagram number, so that you can determine which one was sent the most recently, since they might arrive out of order.
First, you need to classify all of the information you're sending to each player according to whether losing updates is OK. Is it a rarely-updated statistic, like how much money a player has? Probably best to send it via TCP, since a missed update means they might have a wrong number on their screen for a long time. Is it a constantly-updated value like their
<x,y,z> position? You're going to be sending a constant stream of these when they're moving, and it would be fine to send a periodic pulse when they're standing still, and only the latest value matters, so UDP might be OK.
So what makes UDP “faster” is that if you are sending a sequence of updates, and you only care about the most recent one, then if your network is dropping packets you can catch up to real time a bit faster.
In order to do take advantage of this property, you don't just need to “use UDP”. You need to use it in a way where it will help.
UDP, on the other hand, may deliver A then B then C (which is what will happen when you're testing your game on your LAN) or it may deliver C then A then A then C (which might happen if you're in a low-bandwidth internet cafe). That's right; it could duplicate your traffic, put it out of order, and drop it. It makes no promises except a weak offer that probably the contents of C will be the same on one end of the wire and the other (and that it won't break up C into pieces).
You see, TCP makes a promise to you. The promise is that if you use a TCP connection between application X and application Y, and you send A, then, B, then C, A will get there before B gets there, and B will get there before C gets there.
Notice I didn't say that TCP promises that A will get there at all. Maybe a vindictive garden gnome cut your ethernet cable with pinking shears. Maybe your router caught fire. Who knows; sometimes the traffic doesn't get through. TCP will simply fail in those cases, but it will promise that B will not get through unless A does first. It might have to break up A into multiple pieces, so you might get A A A B C, but you'll never get A C or C A B.
Physical networks fail.
They get unplugged. Squirrels chew through the wires. Squirrels partially chew through the wires and water leaks onto them. Your router will be misconfigured. Your ISP's router will be misconfigured. Power will fail. Power will surge. Copper corrodes. Fiber decays.
It is because of the pervasive nature of this failure that TCP even exists.
Especially for the really novice game developers out there, it's important to note that UDP isn't “faster”. There's a very specific thing that UDP does differently than TCP, and it has to do with what happens to the underlying physical network.
One of the features in TCP which can make TCP “slow” is that TCP is sometimes intentionally slow. For example, TCP uses a startup algorithm called “slow start” and other congestion-control techniques to avoid congestive collapse, i.e. your network blowing itself up and becoming useless. TCP uses rate-limiting algorithms to intentionally avoid swamping networks to make them useless, and if your game naively blasts out traffic as fast as possible, that's exactly what it will do if a lot of people play it.
Even when UDP should perform better, the public internet is increasingly dominated by specialized, highly tuned hardware that knows about the semantics of TCP, that lets TCP cheat. It won't understand your special UDP protocol, so you won't get any speed boost from fancy cheater hardware on the path between you and your players.
When everything is working perfectly and there is no packet loss (assuming Nagle is disabled), UDP and TCP will perform approximately equivalently; data gets through immediately, it's delivered to the application, there's no need to retransmit. And on most modern consumer networks, packet loss is very low. You may never even see the conditions where UDP should perform better.
You may find, after all of that, that UDP won't even help you in the ways that it is supposed to.
The general principle here is that "if you can't measure it, you can't manage it", but there are more specific issues too.
If you're a game programmer who wants to start adding some networking to a game, just use TCP for now.
Then, learn about:
When you are done with all of that, then it's time to start investigating UDP.
You also need to learn how to use TCP for a game, for several reasons:
UDP is particularly tricky and nuanced to learn, partially because it's non-deterministic, but mostly because it's actually totally deterministic right up until the point where it isn't. In testing, on your LAN, on
lo0, when there's not a lot going on in the game, delivery is totally reliable, so the code path for recovering from missed datagrams is never covered, the usability where users are dealing with lost traffic is never stressed.
Which means that you never see any flaws in your broken, slow retransmission logic, until it's in front of real users actually playing it on the public internet.
"Use UDP" advice typically comes to exactly the wrong sort of programmer: one who doesn't know anything about networking specifically (and even a limited amount about concurrency and message-passing generally), and is learning. A reliable-delivery transport is hard enough to understand; adding nondeterminism into the mix makes the up-front burden of learning that much greater.
I'm here to tell you that this advice is wrong (the data on performance notwithstanding).
Fundamentally there's one reason: UDP is an optimization, so any discussion of it should be at the late stages of implementation, once you have something you can optimize.
But it's more complicated than that, of course, and there are a lot of related reasons.