Introduction
A huge aspect of game programming is multiplayer. A huge share of all the popular video games today support internet in sme ways. But this introduces several new challanges. Both in the communciation over internet itself but also in what data to send, how to react to data and how to make everything synchronized. This is going to be a sub series in itself starting off with how to do the actual communication.
Packages
When you’re sending something over the Internet, it’ll get split into quite small packages. Each of these packages are usually a few kilobytes or smaller. There are actually several types of packages which get wrapped in each other. I won’t get into the details of this, but what we need to know is that there are several types of packets and they get wrapped in each other.
One of the basic packet types is the IP
( Internet Protocol
) packet. You can look at this as the package that leaves our computer and is sent out on the Internet. Every package that is sent across the Internet is of this type.
We won’t go into the details of each of these fields, the image is there just to show you how it looks. I might go into the details on a later point in time, but that is a big topic and we don’t really need to know all of that in order to do multiplayer.
The package is then sent out on the Internet and will eventually find its way to its destination PC. This is similar to sending a letter. You put the letter inside an envelope and write the destination address on it. Then the mail carrier will take care of the rest.
Actually, that isn’t entirely true because there is another step before the package leaves your computer; we need a protocol.
Protocols
Above we saw an example of in
IP
package.
So now that we know about packages, the next topic is protocol. A protocol is basically a set of rules about how the two parts should communicate. Most important to us, these rules dictate how to detect package loss and how to deal with them.
Why do we need protocols?
Most of the times when you send something, everything goes fine and the recipient gets the data packages he need. But things can go wrong. Let’s take an example.
Say we have sent our letter to someone. But on it’s way, the letter gets lost. But how can we know? The mail carrier doesn’t know that the letter is lost. The recipient doesn’t know you’ve sent the letter and you yourself is assuming the letter arrived at its destination without any issues.
The exact same thing could happen on the Internet ; packages can get lost, and none will know. So how can the problem be avoided? This is where protocols come in!
There are two protocols, we’ll cover, TCP
and UDP
. This time, I’ll only cover UDP
. I’ll cover the other one, TCP
, in a later post.
UDP
The most basic of the protocols we’ll look at is
UDP
( User Datagram Protocol
. ) Actually, UDP
is so basic, there is no rules about how to deal with package loss. You have to handle that yourself.
Another issue is that there is no guarantee that the packages will be received in order. So you could get packages 1,2,3 as 3,2,1 or 3,1,2 or 2,3,1 etc.. And that’s if you do get all packages. Needless to say, using UDP
can cause lots of problems. But its’ simple so we’ll start with it.
UDP
is generally used for performance or simplicity reasons :
- Video streaming
- If a package is lost, this is just a tiny piece of the stream data and you might not even notive it
- In cases where you just want to send a state to the server
- Like using
ping
where you just get an echo back - If you don’t get a message back, you can retry again and again, or report an error
- Like using
- Games
- For reducing lag
- This means they have their own way of dealing with package loss
Addressing on the internet
On the Internet, there are two units used for addressing, the
IP
address and the port
number. We need both of these to communicate over the Internet.
IP adress
The
IP
address is used to address computer. Every unit on the Internet has an IP
address that refers to that unit. You can look at it as the address of a house. So when you send a letter to someone in a house, you write the address to that house. When you send a packet to a computer, you send a packet to that IP
address.
Port numbers
Port numbers are used to distinguish between connections. Each connection has a separate port number tied to it. If we didn’t have port numbers, all data would go into a large buffer and you’d have no idea which of these datas where yours.
So if an IP adress
refers to a house, a port
could be looked at as a name.
When you set up a connection, you need both of these an IP
address and a port
number. Actually you need two of both since the receiver needs to know who sent the package so that it needs to know who to reply to. So all in all, we need two IPs ( out IP and destination IP ) and two port numbers ( our port numbers, destination port numbers. )
So basically, you neeed :
- Your own IP adress and port number
- The recipients IP address and port number
Setting up the connection
Actually,
UDP
is not really a connection, it’s just two parts sending data back and fort. But both parts still needs to know both IP and port number of each other. And it is common and more practical to think about it as an actual connection.
There are two roles in a UDP
“connection”.
- A client
- Tries to connect to a server
- A server
- Waits for a connection from a client
So the procedure for a “connection” will be something like this
- Server with ip 123.123.123.123 waits for connection, listening to port 123
- Client sends a packet to 123.123.123.123, port number 123
- Server stores IP and port number for client
- Server and client now knows both port number and IP of each other
- Now the server and client can now send data back and forth
Sockets
The final part we need to cover about connections are sockets. A socket is a combination of IP address and ports that is unique on every PC for every connection. It consists of the following :
- IP and port of the client side of the connection
- IP and port of our the remote part of the connection
- This part is mostly used for
TCP
connections, we won’t use it
- This part is mostly used for
- Type of connection (
UDP
,TCP
, etc… )
We’ll be using socket
s as a unit to keep track of a connection. You can look at scoket
s to the socket
s you plug your electric devices to. Following that analogy, the wire would be the connection ( network cable ). So, in essence, it’s what connects your application to the Internet.
I realize this all might be a lot of information and hard to wrap your hand around it all, but it’ll get clearer when we put it to use.
SLD_net
Now that we know a tiny bit about
UDP
connections, let’s try to set up one ourselves. For that purpose, we need the SDL_net
library. It is capable of setting up and maintaining both UDP
and TCP
connections. Since UDP
connections are way simpler, we’ll only cover that for now.
Networking is, just like text rendering and .png loading, a separate part of SDL
called SDL_net
. We install it the exact same way as before :
Installation
Installing
SDL2_net
is done exactly like SDL2_image. Just replace SDL2_image
with SDL2_net
Here’s the short version :
Linux
For Linux you can use need to install
-lSDL2_net
or -libSDL2_net
or -SDL2_net
( the actual name might be different in different distributions. )
The linker flag is -lSDL2_net
The process is more or less identical to that of setting up SDL2_image.
If you can’t find SDL2_net
in any repositories and it’s not installed by default, you might have to compile it yourself. For more information, see my blog post about setting up SDL2.
Windows
Similar to setting up SDL2 base.
The difference is that you have to download the development files for SDL2_net
And similarly add SDL2_net.lib
to library includes and add SDL2_net.lib
to the library flags ( where you previously added SDL2_image.lib
)
And with that, it should work.
Mac
See the first part of my tutorial. Just install
SDL2_net
instead of SDL2
Using SDL_net to set up a connection
Setting up a connection with
SDL_net
is a bit more complicated than what we’ve previously seen. This is because there are a few steps, the code will be very C
( not C++
) and there are some buffers ( raw arrays ) we need to keep track off.
We’ll be cutting out all GUI because we simply don’t need it. It will make our code shorter and it’ll be easier to display the results.
Structures of SDL_net
SDL_net
contains two parts we need for out UDP
connection. Let’s start with the simplest, IPAddress
.
IPAdress
A simple struct with the following fields :
uint_32_t host
–IP v4
addressuint16_t host
– protocol port
It is used for keeping IP
and port number
together. Some functins takes this as one of the parameters.
UDPSocket
A pointer to a data type that holds to a pointer. Since it a pointer, it can be
NULL
, in which case there is no connection and we can’t send data back and forth.
UDPpacket
Our data packet. Contains the data we are sending back and forth along with some other information.
int channel
- The src/dst channel of the packet
- We won’t be using this
Uint8 *data
- The packet data we’re sending
- Can be of any length
int len
- The length of the packet data
- Used to find the end of the data in the data pointer
int maxlen
- The max size of the data buffer
- Always as large or larger than
len
- Only used for data package creation on the senders side
int status
- Packet status after sending
- Number of data sent
-1
on failure
IPaddress address
- the source/dest address of apacket
- For received packages this is the
IP
/port
of the remote part. - For sent packages this is the
IP
/port
to send to.
The various fields of a UDP
packet is set with various function used for sending and receiving data. It might seem confusing right now, but it’ll get clearer once we get into the actual code.
Functions of SDL_net
SDLNet_Init
This function is just like
SDL_Init
and TTF_Init
; it initializes the SDL_net
1 int SDLNet_Init( )
SDLNet_UDP_Open
This function is used for creating a
socket
which we will use later to send data.
1234 UDPsocket SDLNet_UDP_Open(Uint16 port)
Parameters :
Uint16 port
– the port we want to use. If you use0
,SDL_Net
will assign a port for you.
Return value :
A valid
UDPsocket
,NULL
on error. Remember thatUDPSocket
is a pointer.
As we saw earlier, UDP
isn’t actually a connection. All we are doing is sending data back and forth. And all we need to do that is a socket. Now that we’ve opened this socket, we can start dealing with packages.
SDLNet_ResolveHost
As stated before, we need an
IP
address and port
number in order to send data. The problem is that there are several ways to represent IP
addresses and port
numbers. The difference between them is the order in which the they are converted to binary. These orders are refereed to as little endian
and big endian
I won’t dive more into this, but you can read about it here.
The issue is that different system use different endian. So we need a uniform way of setting the IP
address and port
number. This is where SDLNet_ResolveHost
comes in. What it does, is that it sets the name values of an IPAdress
for us so we don’t have to think about endians at all.
123456 int SDLNet_ResolveHost(IPaddress* address,const char* host,Uint16 port)
Parameters :
IPaddress* address
– a pointer to anIPAdress
. Needs to be allocated / created in advance. ( In our case, we’ll use a variable and not a pointer so we don’t have to worry about this. )const char* host
–IP address
to send to ( xxx.xxx.xxx.xxx )Uint16 port
– theport number
to send to
Return value :
0
on success, otherwise-1
. In this case,address.host
will beINNADDR_NONE
. This can happen if the address is invalid or leads to nowhere
SDLNet_AllocPacket
Allocates a
UDP_Packet
and returns a pointer to it.
1234 UDPpacket* SDLNet_AllocPacket(int size)
Parameters :
int size
– size of the packet in bytes.0
is invalid.
Return value :
A valid pointer to
UDPpacket
,NULL
on error ( such as out of memory )
The size of the packet determines how much data we get every time. It’ll never be more than this size, but it can be less. You can also expect that some packages gets mfSerged or split up into different segments. This is something we’ll need to handle.
After allocation space for a packet, we can finally fill that packet up with something. Which is kinda the point of this ordeal.
SDLNet_UDP_Send
Sends a
UDPpacket
123456 int SDLNet_UDP_Send(UDPsocket sock,int channel,UDPpacket* packet)
Parameters :
UDPsocket sock
– Oursocket
to send data from ( the one we created withSDLNet_UDP_Open
)int channel
– We’ll completely ignore this parameter and just set it to-1
( all channels )UDPpacket* packet
– the data we want to send ( finally! )
Return value :
The number of destinations the packet was sent to. In our case, this will be
1
. But it could be more. Because of this0
is returned on errors. Anything higher than0
means partial success ( since we were able to send to at least one destination. )
In our case, the function should always return 1
but I find it better to just check for 0
.
SDLNet_AllocPacket
Now that we know how to send data, we also need to know how to recieve them.
12345 int SDLNet_UDP_Recv(UDPsocket sock,UDPpacket *packet)
Parameters :
UDPsocket sock
– Oursocket
to receive data from ( the one we created withSDLNet_UDP_Open>
)UDPpacket* packet
– the data we received
Return value :
The int value
1
when a packet is received,0
when no packets where received, and-1
on errors.
Example
To make it simpler to use SDL_Net, I’ve made a helper class that takes care of everything. You’ll find an example at how to use it below.
Compile
To compile it on Linux or Mac. Simply run :
clang++ UDPExample.cpp -std=c++11 -lSDL2_net -o UDPTest
Starting the example
To use the example, you need two instance of the application. So start up two instances of it.
You’ll be asked to enter local IP. This is the IP
of the computer you are sitting on. You can use 127.0.0.1
which simply means “this computer”. You can do this in both instances. You’ll also be asked to enter a local port and remote port. These needs to be opposite on the two instances ; the local port of the first one, needs to be the remote port of the other. This is because we need to know where to send it to and where to listen for data on.
Instance 1
1234567891011121314151617 ==========================================================================================================UDP connection - A simple test for UDP connections using SDL_Net!==========================================================================================================You'll be asked to enter the following :Remote IP : The IP you want to connect toRemote Port : The port you want to connect toLocal port : Uour portLocal port should be the same as remote port on the other instance of the application==========================================================================================================Enter remote IP ( 127.0.0.1 for local connections ) : 127.0.0.1...and remote port : 123123Enter local port : 321321Connecting toIP : 127.0.0.1Port : 123123Local port : 321321
Instance 2
1234567891011121314151617 ==========================================================================================================UDP connection - A simple test for UDP connections using SDL_Net!==========================================================================================================You'll be asked to enter the following :Remote IP : The IP you want to connect toRemote Port : The port you want to connect toLocal port : Uour portLocal port should be the same as remote port on the other instance of the application==========================================================================================================Enter remote IP ( 127.0.0.1 for local connections ) : 127.0.0.1...and remote port : 123123Enter local port : 321321Connecting toIP : 127.0.0.1Port : 321321Local port : 123123
( notice the difference in local and remote port on the two instances. )
Using the example
After inserting IP and port data, you’ll be presented with a simple menu :
1234 Your command :0 : Send 'test'1 : Quit2 : Check for data
Nothing will happen before you do one of the three options. And if your message doesn’t show up on the other instance, make sure you’ve entered ‘2’
And now you should be able to set up connections. Feel free to use the UDPConnection struct as you like.
Feel free to comment if you have anything to say or ask questions if anything is unclear. I always appreciate getting comments.
You can also email me : olevegard@headerphile.com
Very nice tutorial! Could you possibly post the source code, since I was getting lost with the parameters I would use for a simple chat between clients. Could you do more tutorials on this? With this tutorial I was able to understand what all the functions do, but not how to implement them. Thanks a lot for these tutorials. Keep up the good work! π
Thank you! =)
The source code should be embedded now. If not, you can find it here : https://gist.github.com/15d8cff035162f02413b
I do intend to do a tutorial on TCP connections, soon. But I’m currently starting a new job, so I don’t have too much spare time to write these. Feel free to ask if there’s anything else, though. I’ll be happy to help
hey thanks for the link to the github code! I’m just having alittle trouble dissecting the different parts of the code but I guess I need to analyze the code longer. Thanks a lot for these tutorials. Happy 2015! I also saw High Flyer by GameGrape Studios that used SDL2. Here’s this link if you want to check it out, http://gamegrape-studios.itch.io/ Hey do you have a Skype? We should talk sometime! see you later.
I think that’s completely normal. It’s a C-library so function names are short an not very descriptive. But just experiment with it on your own. Try writing your own chat client or something simple like that. If there is anything particularly you struggle with, feel free to post a comment about it.
Give me your Skype, and I’ll add you. I don’t use it much though, so I’m rarely online.
Oh Sorry I forgot to come back xD (4 months later) my Skype is djcyberslash1. I’m about to implement Online Multiplayer into one of my games so this is a great time. I just wanted to stop by since i haven’t been here in a while and see how everythings going.Are you still making these articles, they are very good! Oh yeah I wrote my own simple chat client! It was local ( It may of also been public since I set up the port and stuff) and the clients could see the servers messages but couldn’t send messages to the server for all the clients. It was a start but I haven’t worked with SDL2_Net for a while since I lost all my data (microsd cards). Anyway, Talk to you soon! π
Glad to see you back : )
I have been kinda busy since I got a job way back in November. But I have gotten back into writing a little. Hopefully, I’ll have an article on TCP soon-ish. It’ll be fairly extensive and I might also update this post with some improvements.
Regarding this part ; there is a bug in which you can only send one word at a time, I’ll update the article asap with a fix.
I highly recommend getting git. That way, you can save your code to Github and you’ll never loose your code again. It’s also helpful for experimenting with the code, going back in time, etc. I use it for all my code. You can read more here : https://help.github.com/
Hey,
Thanks for the great tutorial and example, helps out a lot!
I was able to get your example to work locally on my computer and it does as expected.
However I am unsure of what I need to do if I want to talk to other computers on my network?
Do I need to change the IP address to something else? If so what could they be?
Thank you again π
Hello and thank you, I’m glad you find my tutorial helpful.
Yes, you need different IP’s. The ip
127.0.0.1
basically means “my computer”. So in order for two different computer to communicate, both need to know the IP of the other. You can find the ip by opening your command prompt / terminal. On Windows you can start this by pressingWindows + R
, then typingcmd
and hitting enter.When you are in the command prompt / terminal type
ipconfig
orifconfig
on Linux/Mac. From there you can find the ip. Then you can use to IP from the other computer instead of127.0.0.1
This should work, but let me know if you have any other issues. π
Hey!! Thanks for the tutorial! It helped me too much with my multiplayer game but i am having a big trouble about the latence.. for testing my game i usually open two instance of the game, these two games can communicate perfectly but if i move the charachter in the first game, in the other game it updates with some delay…( But if i move the charachter in the second game, the first game updates perfectly..strange ). I discovered that the delay time is equal to the time i spent since opening a game and opening the other game ( they have the same souce code ). The game consists of a loop that sends data of the player 5 times (5 ints) and receive data 5 times.. so if the game 1 send 5 packets per frame, the second game, at its runtime, and with the same velocity of loading packets (5 per frame) will not reach the number of game1 packets sent. So it will load all the packets since the older to the younger and know i understand why this delay.. i don’t understand how to fix it, the two games will never run at the same time.. can you help me?
Thank you very much.
I?d have to check with you here. Which is not something I usually do! I like reading. Also, thanks for allowing me to comment!
Now we know a bit about the UDP connection, let’s try to set ourselves up. For this purpose, we need the SDL_net library. It is capable of setting and retaining both UDP and TCP connections. Since UDP connections are easy, we will only cover it for it only.