Using TCP for real-time game, too slow?

Started by
21 comments, last by markr 19 years, 8 months ago
Does anyone use TCP (instead of UDP) for real-time network games like FPS or other types of action games? And is it fast enough? I have done very little network programming related to games, but I have the impression that TCP might be a bit slow because of the guaranteed delivery. Thanks for any input, -Trond
-Trond
Advertisement
Depends on what speed your connection is, how your network code is set up. The faster the connection is (relative to how much data you are sending, of course), the less the difference between TCP and UDP is. If you write your network code such that the program can compensate from lost packets then go with UDP. If the program absolutely needs every packet, then go with TCP. They both get the job done if implemented correctly.. and I believe many games allow the player to select which protocol to use.
Disclaimer: "I am in no way qualified to present advice on any topic concerning anything and can not be held responsible for any damages that my advice may incurr (due to neither my negligence nor yours)"
I was thinking TCP was easier to use because it simplifies a lot, considering it guarantees the package delivery. Handling packet loss may introduce a whole bunch of bugs, especially when I'm not experienced doing networked games... If TCP can be almost as fast as UDP, there's no doubt I'll go for that :)

Thanks for your time,
-Trond
-Trond
well, here are my thoughs, if your sending the absolute coordinates every update, the last packet is useless, so, if a packet is lost, youd just end up falling behind if you tried to get the packet a second time, instead, forget about it and move on. of course, most solutions have things set up differently, like say only sending changed directions and such. In which case youd really need every packet, UNLESS, you had every ssay, 10 packets, be the absolute coordinates, that way, you didnt need every one, of course this kind of eliminates the point of sending relative coordinates... but... well there are no buts, ive contradicted myself... and rambled, maybe my lack of though will help you think some more about your networking system :-D

-Dan
When General Patton died after World War 2 he went to the gates of Heaven to talk to St. Peter. The first thing he asked is if there were any Marines in heaven. St. Peter told him no, Marines are too rowdy for heaven. He then asked why Patton wanted to know. Patton told him he was sick of the Marines overshadowing the Army because they did more with less and were all hard-core sons of bitches. St. Peter reassured him there were no Marines so Patton went into Heaven. As he was checking out his new home he rounded a corner and saw someone in Marine Dress Blues. He ran back to St. Peter and yelled "You lied to me! There are Marines in heaven!" St. Peter said "Who him? That's just God. He wishes he were a Marine."
The big problem with TCP is "hiccups". If one packet gets stalled for some reason, then all of the packets get stalled with it. as soon as the packet finally arrives, then the following packets arrive immediately after, in one big burst.

This is bad. If you are a client and you are watching 2 characters move on the screen, then if a position update of one of the characters gets stalled, then the other player will also get stalled, even though his packets are probably still arriving smoothly!

Anyway, I *highly* recommend that you use the ENet library. It's a thin layer on top of UDP. It has a very easy to use C API. It basicly allows you to send packets, each packet can be either reliable or unreliable. It also makes sure that packets arrive in the correct order. It uses multiple "channels" of communication, so if a packet in one channel stalls, the other channels continue to flow uninterrupted. Oh yeah, it's MIT license.

highly recommended: http://enet.cubik.org

If you want something more powerful with more features, that let's you work on a higher level then sending/receiving packets, then I recommend RakNet. It's GPL. google it.
eNet is good. Use it.
...
However, TCP or UDP is a massive subject which none of the above responses have really covered. Unfortunately, the details are critically important.

Fundamentally, TCP is perfect and better than UDP in every way (yes, that's deliberately OTT ;)) except ... it has two "features" that are utterly destructive to computer games, yet neither is optional. AND...it has a slightly larger header size (20 bytes instead of 8, IIRC) which means that it requires slightly more bandwidth (but you MUST see below!)

The first is "in-order delivery" (mentioned above). If just one packet is dropped, your TCP stack will *refuse* to let you read any more data until that packet arrives. It's like a very very narrow bottleneck that just makes life hell for games developers.

The other is "guaranteed delivery". If any packet is lost, it will be re-transmitted perfectly as many times as necessary until it arrives. Doesn't sound a problem, right? Well, if the packet was to say "your ship moves forwards" but by the time it's successfully received the player is already dead then it was useless information. But with TCP there is no way to "expire" information like this - once you pass it to the network lib to send, you cannot withdraw / cancel it.

UDP is simply TCP without a lot of the features. Both of them are siblings, and the term "TCP/IP" is used to indicate that TCP is implemented on top of another protocol - IP. UDP is implemented on top of exactly the same protocol. This means that at a basic level they are identical (they are both packet-based, for instance).

Unfortunately, one of the TCP features that is missing from TCP - "guaranteed delivery" - is VERY hard to implement. Do not believe anyone who tells you it is easy - they either haven't implemented it before, or think they have but haven't yet realised that their implementation is broken (subtle bugs). As noted above, we don't necessarily want "guaranteed delivery", what we really want is one of it's elements - the chunk of code that keeps retrying until the thing is successfully delivered (i.e. if we could add a method to that which let us say "OK, give up on that packet. I don't want it any more" then we'd be happy). For reference, the UDP protocol has 2 "states" which affect the protocol; TCP has 22 states - and the protocol acts differently depending upon the precise combination of states that the client and server are in - you can imagine how many different combinations there are!

The best people in the world make mistakes when trying to reproduce guaranteed delivery using UDP - even Sun (you know, the networking company with $6bn in the bank!) screwed up one of their commercial implementations (RMI in Java) and it took them a few years to fix it.

Which is why, if you can, you use eNet (or any other alternative protocols) because the pain of getting your implementation correct is generally not even close to worth it. There are times when those protocols are not an option themselves - e.g. if you have *very* unusual protocol needs - but they are few and far between. FYI, the nasty part of using UDP is that trying to implement re-transmit logic is a lot more complex than it seems at first.

The *good* part is that TCP is extensively documented and it's free to go and read exactly how it works - so there exists, if you like, a "manual" on how to make your own "TCP-without-the-bits-I-hate implemented using UDP". Unfortunately, the documentation is VERY long, and there's so much to do that invariably you make some difficult to find and hard to fix mistakes :(.


OK...back to the bandwidth issue. Both protocols are built on top of IP, which IIRC adds another 20 bytes. So, now it's 28 bytes (UDP) versus 40 bytes (TCP), and the UDP saving in percentage terms is shrinking. Then you get even more headers for e.g. anyone using a cable modem or DSL (PPP, PPPoE etc add more header data). If you are sending just an x-coord and a y-coord and a speed in a packet (e.g. you have a racing game, and the packet represents the new velocity of a car) then you may be sending just 3 bytes of data - but your packet has a header of 28 or 40 bytes, so you are "wasting" either 90% (UDP) or 93% (TCP) of your bandwidth!

However...TCP is the main application protocol of the internet, and most of the internet has special features to make it run faster. For instance, PPP compresses TCP headers from 40 bytes down to ... 6 bytes or less! (IIRC it can go down to just 3 bytes). PPP does NOT compress UDP at all - so now your UDP packet may have 28 bytes + PPP header (can't remember how big) overhead, whereas your TCP packet only has 3 bytes - and instead of wasting 93% of your TCP b/w you're only wasting 50% - a massive saving compared to UDP. There are many other tricks that go on on the internet - for instance, routers tend to have special code for dealing with TCP that is passing through them, but they can't do that for "my custom UDP-based computer game protocol" because the manufacturers don't know who you are, don't care, and have no idea how your protocol works.

So. The most commonly found advice that I would suggest (after "use enet") is: "Use TCP until the point where it causes your game to not work properly; then investigate UDP". If you have a game that will definitely need UDP and you know it, then you just have to bite the bullet and enter the world of low-level protocol debugging (not fun). Despite what I said above it's far from impossible to get it right, it's just painful and dull.

PS if you want more info on this, then I'll be covering it extensively in my book on game server programming, due out March 2005 - c.f. http://www.gamedev.net/community/forums/topic.asp?topic_id=263487
I believe the header compression you're talking about (with PPP) is van Jacobsen compression, which compresses the IP header (not UDP or TCP) to 5 bytes. Thus, that header compression benefits UDP as well as TCP.

Anyway, when it comes to "realiable UDP", most games don't want specific individual packets to be reliable; they want the underlying data to be reliable. Because of packet overhead, most games will stuff a bunch of different data into the same physical network packet, some of which is reliable, and some of which isn't.

Thus, you should design your system such that reliable BITS of data get re-sent in some future packets; you should not think about data in terms of the physical packets. And, often, you want "eventual consistency of state" rather than "guaranteed delivery of messages," which also changes how you design.
enum Bool { True, False, FileNotFound };
Quote:Original post by redmilamber
This means that at a basic level they are identical (they are both packet-based, for instance).


As i understood it, TCP is stream based and UDP is packed based. i could be wrong tho, the only in depth network code i wrote was a multicast receiver.
i is 1337
Quote:Original post by SoDdit
Quote:Original post by redmilamber
This means that at a basic level they are identical (they are both packet-based, for instance).


As i understood it, TCP is stream based and UDP is packed based. i could be wrong tho, the only in depth network code i wrote was a multicast receiver.


TCP/IP (which is what you mean in 99.999% of cases where you say "TCP") is a stream built upon packets. If you are in any doubt, look for ANY discussion of TCP and notice how often the word "packet" comes up :D.

There are other protocols that are streams built upon streams, e.g. ATM off the top of my head. There are fundamental differences - e.g. ATM doesn't have a concept of retransmission because it is not physically possible for a packet within the stream to be dropped.

The entire subject of "in-order delivery" makes not a jot of sense if TCP is implemented without using packets! The TCP "hiccup" is a direct by-product of it's packet base! Etc...

(NB: there are versions of TCP implemented on things other than IP; that is why TCP/IP is nearly always the correct way to refer to TCP. The fact that these are extremely rare outside of particular niches has given rise to the shorthand of just inferring /IP whenever you don't explicitly say otherwise).

EDIT: i.e., TCP/IP converts from a packet protocol to a stream protocol. When I said "packet-based" I meant the protocol itself is packet-based (it is: it's base is packets) whereas you mean that the USAGE of the protocol is stream-based (it is: you send streams via TCP, not packets).
If you don't want the hassle of a reliable UDP implementation; use both TCP and UDP at the same time.

This topic is closed to new replies.

Advertisement