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
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Sponsored by AMD
If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.
Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.
Sponsored by ActiveState
| Speed Up Your Web Site with Varnish | Jun 19, 2013 |
| Non-Linux FOSS: libnotify, OS X Style | Jun 18, 2013 |
| Containers—Not Virtual Machines—Are the Future Cloud | Jun 17, 2013 |
| Lock-Free Multi-Producer Multi-Consumer Queue on Ring Buffer | Jun 12, 2013 |
| Weechat, Irssi's Little Brother | Jun 11, 2013 |
| One Tail Just Isn't Enough | Jun 07, 2013 |
- Speed Up Your Web Site with Varnish
- Containers—Not Virtual Machines—Are the Future Cloud
- Linux Systems Administrator
- Lock-Free Multi-Producer Multi-Consumer Queue on Ring Buffer
- RSS Feeds
- Senior Perl Developer
- Technical Support Rep
- Non-Linux FOSS: libnotify, OS X Style
- UX Designer
- Web & UI Developer (JavaScript & j Query)
- It is quiet helping
2 hours 18 min ago - Technology
2 hours 35 min ago - Reachli - Amplifying your
3 hours 52 min ago - excellent
4 hours 40 min ago - good point!
4 hours 43 min ago - Varnish works!
4 hours 52 min ago - Reply to comment | Linux Journal
5 hours 22 min ago - Reply to comment | Linux Journal
7 hours 48 min ago - Reply to comment | Linux Journal
11 hours 48 min ago - Yeah, user namespaces are
13 hours 4 min ago
Featured Jobs
| Linux Systems Administrator | Houston and Austin, Texas | Host Gator |
| Senior Perl Developer | Austin, Texas | Host Gator |
| Technical Support Rep | Houston and Austin, Texas | Host Gator |
| UX Designer | Austin, Texas | Host Gator |
| Web & UI Developer (JavaScript & j Query) | Austin, Texas | Host Gator |
Free Webinar: Hadoop
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Some of key questions to be discussed are:
- What is the “typical” Hadoop cluster and what should be installed on the different machine types?
- Why should you consider the typical workload patterns when making your hardware decisions?
- Are all microservers created equal for Hadoop deployments?
- How do I plan for expansion if I require more compute, memory, storage or networking?



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