Network Programming with ENet
Cross-platform network programming made easy.
Creating a multiplayer game can be a lot of fun, but navigating the complexities of IP network programming can be a headache. That's kind of a strange statement, but the two go hand in hand. You can't write a multiplayer game without some sort of network-based communications, and game-related network programming introduces difficulties not often found with more simple applications. For example, most game developers are concerned with bandwidth utilization and throttling. There's also player session management to contend with. Then, there's the problem of message fragmentation, acknowledgement and sequencing. Oh, and you'd really like to be able to make your game run on both Linux and Windows. That's a tall order for developers who probably are more concerned with writing their games than they are in becoming experts in cross-platform network programming. Fortunately, the ENet library (http://enet.bespin.org) takes care of these details and presents developers with a simple, flexible and consistent API.
ENet's event-driven programming model makes client session management very simple. The library dispatches an event when a peer connects or disconnects, and when a message is received from a peer. The developer simply writes event handlers that take care of initializing and deallocating resources, and acting upon incoming messages. This means you don't have to worry about the complexities of forking, preforking, threading or nonblocking calls to connect() and accept() in order to handle multiple connections. With ENet, all you do is make periodic calls to its event dispatcher and handle the events as they come in.
ENet provides for both reliable and unreliable transmission. The networking industry really needs to find a better term than "unreliable", however. Unreliable means that a packet will be sent out, but the receiving end won't be expected to acknowledge receiving the packet. In a "reliable" protocol, every packet must be acknowledged upon receipt. If a peer sends out a packet and requests acknowledgement and doesn't receive it in a timely fashion, the packet will be resent automatically until it is acknowledged, or the peer is deemed to be unreachable. The nice thing about ENet is that the same API provides both reliable and unreliable semantics.
ENet also makes it easy to write real-time client-server applications by taking care of packet fragmentation and sequencing chores for you. Put simply, fragmentation and reassembly is done automatically and is transparent to the developer. Sequencing also is handled transparently and can be turned on and off at will. ENet's concept of a communications channel is related to sequencing. ENet buffers each channel separately and empties each buffer in numerical sequence. The end result is that you can transmit multiple data streams and that lower-numbered channels have higher priority. For example, you could put all real-time game update packets into channel 1, while system status packets could be in a lower-priority channel.
For the sake of demonstration, I discuss both the client and server for a simple chat program. The code I'm using is based on a 3-D video game I'm writing in my limited free time. However, while stripping the code down to its basics, I left something out and couldn't get it to work. So, I posted a description of my problem and code snippets to the ENet e-mail list. Within an hour, I had a reply that showed me how to fix my problem. Thanks, Nuno.
In this example, a user starts the client and provides a user name as a command parameter. Once a client session has been created, the client is expected to tell the server the name of the user, as the very first message sent from the client. Then, anything the user types will be sent to the server. Any messages that come from the server will be displayed on the client's screen. Because all user input is taken in a blocking fashion, the user won't actually see any incoming messages until pressing the Enter key. This isn't ideal, but the point of the code is to demonstrate the ENet library, not nonblocking I/O on STDIN and the necessary cursor control. (In a real-world situation, your programs would be generating messages, such as player movement and projectile impact, in real time anyway.) If the user simply presses the Enter key, no message is sent to the server, but any queued-up messages will be displayed. If the user types the letter q and presses Enter, the client disconnects and terminates.
The server also is very simple. When a client connects, the server waits for the client to identify the user. Then, the server sends a broadcast message announcing the new user's connections. When a client disconnects, that fact also is broadcast to all connected users. When a client sends a message, that message is sent to every connected client, except the one who sent it. Like I said, it's a very simple chat system.
Let's look at some code. First, let's get a few #defines out of the way. Take a look at config.h shown in Listing 1.
Listing 1. config.h11088l1.qrk
#define HOST "localhost"
#define PORT (7000)
#define BUFFERSIZE (1000)
#define HOST "localhost"
#define PORT (7000)
#define BUFFERSIZE (1000)
This is pretty straightforward, so let's look at the client code shown in Listing 2.
Listing 2. Client Code
1 #include <stdio.h>
2 #include <string.h>
3 #include <enet/enet.h>
4 #include "config.h"
5 #include <unistd.h>
6 char buffer[BUFFERSIZE];
7 ENetHost *client;
8 ENetAddress address;
9 ENetEvent event;
10 ENetPeer *peer;
11 ENetPacket *packet;
12 int main(int argc, char ** argv) {
13 int connected=0;
14 if (argc != 1) {
15 printf("Usage: client username\n");
16 exit;
17 }
18 if (enet_initialize() != 0) {
19 printf("Could not initialize enet.\n");
20 return 0;
21 }
22 client = enet_host_create(NULL, 1, 2, 5760/8, 1440/8);
23 if (client == NULL) {
24 printf("Could not create client.\n");
25 return 0;
26 }
27 enet_address_set_host(&address, HOST);
28 address.port = PORT;
29 peer = enet_host_connect(client, &address, 2, 0);
30 if (peer == NULL) {
31 printf("Could not connect to server\n");
32 return 0;
33 }
34 if (enet_host_service(client, &event, 1000) > 0 &&
35 event.type == ENET_EVENT_TYPE_CONNECT) {
36 printf("Connection to %s succeeded.\n", HOST);
37 connected++;
38 strncpy(buffer, argv[1], BUFFERSIZE);
39 packet = enet_packet_create(buffer, strlen(buffer)+1,
ENET_PACKET_FLAG_RELIABLE);
40 enet_peer_send(peer, 0, packet);
41 } else {
42 enet_peer_reset(peer);
43 printf("Could not connect to %s.\n", HOST);
44 return 0;
45 }
46 while (1) {
47 while (enet_host_service(client, &event, 1000) > 0) {
48 switch (event.type) {
49 case ENET_EVENT_TYPE_RECEIVE:
50 puts( (char*) event.packet->data);
51 break;
52 case ENET_EVENT_TYPE_DISCONNECT:
53 connected=0;
54 printf("You have been disconnected.\n");
55 return 2;
56 }
57 }
58 if (connected) {
59 printf("Input: ");
60 gets(buffer);
61 if (strlen(buffer) == 0) { continue; }
62 if (strncmp("q", buffer, BUFFERSIZE) == 0) {
63 connected=0;
64 enet_peer_disconnect(peer, 0);
65 continue;
66 }
67 packet = enet_packet_create(buffer, strlen(buffer)+1,
ENET_PACKET_FLAG_RELIABLE);
68 enet_peer_send(peer, 0, packet);
69 }
70 }
71 enet_deinitialize();
72 }
Mike Diehl is a freelance Computer Nerd specializing in Linux administration, programing, and VoIP. Mike lives in Albuquerque, NM. with his wife and 3 sons. He can be reached at mdiehl@diehlnet.com
Today’s modular x86 servers are compute-centric, designed as a least common denominator to support a wide range of IT workloads. Those generic, virtualized IT workloads have much different resource optimization requirements than hyperscale and cloud applications. They have resulted in a “one size fits all” enterprise IT architecture that is not optimized for a specific set of IT workloads, and especially not emerging hyperscale workloads, such as web applications, big data, and object storage. In this report, you will learn how shifting the focus from traditional compute-centric IT architectures to an innovative disaggregated fabric-based architecture can optimize and scale your data center.
Sponsored by AMD
Built-in forensics, incident response, and security with Red Hat Enterprise Linux 6
Every security policy provides guidance and requirements for ensuring adequate protection of information and data, as well as high-level technical and administrative security requirements for a system in a given environment. Traditionally, providing security for a system focuses on the confidentiality of the information on it. However, protecting the data integrity and system and data availability is just as important. For example, when processing United States intelligence information, there are three attributes that require protection: confidentiality, integrity, and availability.
Learn more about catching the bad guy in this free white paper.
Sponsored by DLT Solutions
| Using Salt Stack and Vagrant for Drupal Development | May 20, 2013 |
| Making Linux and Android Get Along (It's Not as Hard as It Sounds) | May 16, 2013 |
| Drupal Is a Framework: Why Everyone Needs to Understand This | May 15, 2013 |
| Home, My Backup Data Center | May 13, 2013 |
| Non-Linux FOSS: Seashore | May 10, 2013 |
| Trying to Tame the Tablet | May 08, 2013 |
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- Using Salt Stack and Vagrant for Drupal Development
- New Products
- Validate an E-Mail Address with PHP, the Right Way
- Drupal Is a Framework: Why Everyone Needs to Understand This
- A Topic for Discussion - Open Source Feature-Richness?
- Home, My Backup Data Center
- New Products
- Tech Tip: Really Simple HTTP Server with Python
- RSS Feeds
- Find new cell phone and tablet pc
2 min 8 sec ago - Epistle
1 hour 30 min ago - Automatically updating Guest Additions
2 hours 39 min ago - I like your topic on android
3 hours 26 min ago - Reply to comment | Linux Journal
3 hours 47 min ago - This is the easiest tutorial
10 hours 1 min ago - Ahh, the Koolaid.
15 hours 40 min ago - git-annex assistant
21 hours 39 min ago - direct cable connection
22 hours 2 min ago - Agreed on AirDroid. With my
22 hours 12 min ago
Enter to Win an Adafruit Prototyping Pi Plate Kit for Raspberry Pi

It's Raspberry Pi month at Linux Journal. Each week in May, Adafruit will be giving away a Pi-related prize to a lucky, randomly drawn LJ reader. Winners will be announced weekly.
Fill out the fields below to enter to win this week's prize-- a Prototyping Pi Plate Kit for Raspberry Pi.
Congratulations to our winners so far:
- 5-8-13, Pi Starter Pack: Jack Davis
- 5-15-13, Pi Model B 512MB RAM: Patrick Dunn
- Next winner announced on 5-21-13!
Free Webinar: Linux Backup and Recovery
Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.
In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.



Comments
nice.. but..
it sounds good but installation is trouble-matic :P
WTH? Why don't you just use
WTH? Why don't you just use the proactor in boost.asio? Boost is cross-platform, it is the "official" c++ lib (well, as official as an unofficial library could get :)), has pretty good documentation, and a good model.
Since games are usually developed in c++ (as far as I know), this would be a perfect fit.
By the way, there are really short and concise server/client examples with boost.asio.
Oh, and also, boost is pre-installed on most modern day linux distros, getting the boost lib is as easy as `sudo apt-get install libboost-dev` (for ubuntu), and linking to it is as easy as `-lboost_asio-mt`.
If you really want a c lib for networking, why not just pick GLib? Many major open source (also cross platform) projects are built on it.
However, GLib is much worse in documentation than boost (and obviously less user friendly automatically, since it's c and not c++).
I got problems on compilation
I got problems on compilation too:
1. For the client ther's some warning about using gets function and in about the switch statement (ther's no default clause)!
2. For the server, the variable ipeerCount is not defined and when i replace it with : server->peerCount i got a violent SEGFAULT on the line 43 (enet_peer_send), Any ideas?
Thanks.
I got problems on compilation
I got problems on compilation too:
1. For the client ther's some warning about using gets function and in about the switch statement (ther's no default clause)!
2. For the server, the variable ipeerCount is not defined and when i replace it with : server->peerCount i got a violent SEGFAULT on the line 43 (enet_peer_send), Any ideas?
Thanks.
Nice tutorial, but the code
Nice tutorial, but the code does not compile, please fixe it. Thanks