Introduction
ENet is a smart and powerful library to send packets using UDP. It can send those package even reliably. It relieves you from creating sockets to do communication. Nothing in this tutorial is new, that is not explained in the standard tutorial of enet.bespin.org or linuxjournal. First I could not figure out how to combine different parts of the tutorial to create a client and a server. When I read its usage on some mailing list it became clear to me how it would work. This tutorial will try to explain a simple chat application I found there
Code
Let’s see the code first, I will explain each parts of the code after that.
Server Code
// server.c #include <enet/enet.h> #include <stdio.h> int main(int argc, char **argv) { ENetAddress address; ENetHost *server; ENetEvent event; int eventStatus; // a. Initialize enet if (enet_initialize() != 0) { fprintf(stderr, "An error occured while initializing ENet.\n"); return EXIT_FAILURE; } atexit(enet_deinitialize); // b. Create a host using enet_host_create address.host = ENET_HOST_ANY; address.port = 1234; server = enet_host_create(&address, 32, 2, 0, 0); if (server == NULL) { fprintf(stderr, "An error occured while trying to create an ENet server host\n"); exit(EXIT_FAILURE); } // c. Connect and user service eventStatus = 1; while (1) { eventStatus = enet_host_service(server, &event, 50000); // If we had some event that interested us if (eventStatus > 0) { switch(event.type) { case ENET_EVENT_TYPE_CONNECT: printf("(Server) We got a new connection from %x\n", event.peer->address.host); break; case ENET_EVENT_TYPE_RECEIVE: printf("(Server) Message from client : %s\n", event.packet->data); // Lets broadcast this message to all enet_host_broadcast(server, 0, event.packet); break; case ENET_EVENT_TYPE_DISCONNECT: printf("%s disconnected.\n", event.peer->data); // Reset client's information event.peer->data = NULL; break; } } } }
Client Code
#include <enet/enet.h> #include <stdio.h> #include <string.h> int main(int argc, char **argv) { ENetAddress address; ENetHost *client; ENetPeer *peer; char message[1024]; ENetEvent event; int eventStatus; // a. Initialize enet if (enet_initialize() != 0) { fprintf(stderr, "An error occured while initializing ENet.\n"); return EXIT_FAILURE; } atexit(enet_deinitialize); // b. Create a host using enet_host_create client = enet_host_create(NULL, 1, 2, 57600/8, 14400/8); if (client == NULL) { fprintf(stderr, "An error occured while trying to create an ENet server host\n"); exit(EXIT_FAILURE); } enet_address_set_host(&address, "localhost"); address.port = 1234; // c. Connect and user service peer = enet_host_connect(client, &address, 2, 0); if (peer == NULL) { fprintf(stderr, "No available peers for initializing an ENet connection"); exit(EXIT_FAILURE); } eventStatus = 1; while (1) { eventStatus = enet_host_service(client, &event, 50000); // If we had some event that interested us if (eventStatus > 0) { switch(event.type) { case ENET_EVENT_TYPE_CONNECT: printf("(Client) We got a new connection from %x\n", event.peer->address.host); break; case ENET_EVENT_TYPE_RECEIVE: printf("(Client) Message from server : %s\n", event.packet->data); // Lets broadcast this message to all // enet_host_broadcast(client, 0, event.packet); enet_packet_destroy(event.packet); break; case ENET_EVENT_TYPE_DISCONNECT: printf("(Client) %s disconnected.\n", event.peer->data); // Reset client's information event.peer->data = NULL; break; } } printf("Say > "); gets(message); if (strlen(message) > 0) { ENetPacket *packet = enet_packet_create(message, strlen(message) + 1, ENET_PACKET_FLAG_RELIABLE); enet_peer_send(peer, 0, packet); } } }
Code Details
a. Initialize enet using enet_initialize
function.
b. Create a host using enet_host_create function.
This function takes NULL
as the first parameter if it is a client and ENetAddress
if it is a server. These objects are structures, because we don’t have classes in c. In case of server you can define number of clients as second parameter. That number is going to be 1
if it is a client code. Other two parameters are incoming and outgoing bandwidth. If you choose this value as 0 you send or receive unlimited data. In case of client you can restrict incoming to 56k
using 57600/8
and outgoing to 14k
using using 14400/8
values. You can bind hosts to specific address and port using enet_address_set_host
function. In case of client it becomes necessary to specify where you want to connect to, but for server you can accept any address using ENET_HOST_ANY
as value for host property of address structure, before creating a host using enet_host_create
function.
c. Connect and use service.
The core function for establishing communication is enet_host_service
. This function takes three parameters i.e ENetHost
, ENetEvent
and time
in milliseconds to wait for event. We use ENetEvent
structure to get packet
, address
and port
of peer
, channelID
etc. After calling enet_host_service
we get a return type of 0
if no event was called. This value can be used to terminate the service. The type
field of event
structure contains different values that signifies what type of event occured. Typical values are ENET_EVENT_TYPE_NONE
(It is 0 for no data), ENET_EVENT_TYPE_CONNECT
(When connection with either server or client happened), ENET_EVENT_TYPE_RECEIVE
(when data is received), ENET_EVENT_TYPE_DISCONNECT
(when connection could not be established with other party).
d. Send some data
Sending data can be done by wrapping it in ENetPacket
object. We create a packet using enet_packet_create
and send it using enet_peer_send
function. The function enet_packet_create
can take parameters like ENET_PACKET_FLAG_RELIABLE
which specifies we want a reliable connection even over UDP.
We can use gets(char *)
method to fill in a character buffer and call method enet_packet_create
as
ENetPacket *packet = enet_packet_create(message, strlen(message) + 1, ENET_PACKET_FLAG_RELIABLE); enet_peer_send(peer, 0, packet);
Here peer
is an object of type ENetPeer which we construct using
ENetPeer *peer = enet_host_connect(client, &address, 2, 0);
Here we are sending our peer
a packet that contains the message we wanted to send.
e. If we want to destroy the packet and start over again, we can do so using enet_packet_destroy(event.packet)
. We can call enet_host_service
as many times we want, because it pauses only the specified amount of time we provide as the third parameter to fill in our event
structure. We can use this event
structure to get all the information and data we need from other part of communicating host.
Compile
$ gcc -o server server.c `pkg-config --libs libenet` $ gcc -o client client.c `pkg-config --libs libenet`
Note: One difference this code has from the tutorial is that it only uses one enet_host_service
function. If the server has to send individual message it too has to create packet and send as the client did, however we have used enet_host_broadcast
method to broadcast everything to the client. One thing that makes this application a little of the track is that the server sends last filled message to the client who is currently interacting with it. I will surely work on it so that server sends timely message and all the client capture that. This way we have have standard chat application.
Conclusion
Through this tutorial we saw how to use enet library to send and receive message. Good Luck on the tutorial.
November 6, 2012 at 6:20 am |
thx.
February 18, 2013 at 4:58 pm |
what is the output of this command ( `pkg-config –libs libenet`) in your distribution? I’m using fedora and it’s not workng
February 20, 2013 at 4:14 pm |
It says -lenet
December 3, 2013 at 1:28 pm |
When with the above code I try to compile the .c code from linux I am not able to initialize enet and also not able to create enet server host.
The file which I got after compile is having this errors : “An error occured while initializing ENet” and “An error occured while trying to create an ENet server host”.
I am compiling using this command :
gcc -c server.c -l/usr/local/include
gcc -c client.c -l/usr/local/include
December 5, 2013 at 2:12 am |
I don’t have any problems getting it to work. E.g I installed enet library and assuming pkg-config is installed, everything works as it should. E.g Here is my compilation and execution message (both server and client)
And the client.
I guess you have already tried using -lenet or pkg-config command as above and you have successfully compiled the code. Above that, I can’t really say where the problem lies in case of execution of the program.
December 5, 2013 at 5:42 am |
Hi Pramod Nepal ,
Thanks for a quick reply.
I have installed ENET as directed on its official website.
As I am new to this and less aware with linux linking etc I am giving you some more details.
* In my machine : /user/local/lib/ on this path different files like libnet.a, libnet.la, libnet.so and etc are there.
* /usr/local/lib/pkgconfig/ there is one libnet.pc file.
I think the only issue is I am facing is linking while compilation of code.
Can you please guide me using information above I have provided how to compile using linking of enet library.
December 6, 2013 at 2:13 pm |
I think, I understand your problem now.
To install enet, you should use the package manager of the distribution you are using. In case of ubuntu (I think ubuntu 12.04 and above) the command to install enet is
sudo apt-get install libenet-dev
From your comments, it seems you are trying to install using the source code. Unless, you learn more about linking and installing library using specific parameters, use above command to install enet.
Now, lets talk about your command to compile the code.
With -c switch you are only creating object files rather than the final binary. Also, you are using -I switch to include the headers while compilation. It has nothing to do with how the executable code is going to be produced. If I inspect server file (binary) after compilation (gcc -o server server.c -lenet), I get following output.
$ ldd server
linux-vdso.so.1 => (0x00007fff707bd000)
libenet.so.2 => /usr/lib/x86_64-linux-gnu/libenet.so.2 (0x00007f76cce68000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f76ccaa0000)
/lib64/ld-linux-x86-64.so.2 (0x00007f76cd099000)
December 7, 2014 at 5:21 pm |
Hi .
Thank you for that example code .
I made some mods on it , to allow a single program to be client or server with the command line.
But the most important change ( and fix ? ) is about your offtrack use of broadcast(…) .
After some tries , i had success with : ( on windows , vstudio9)
in server loop :
…
…
case ENET_EVENT_TYPE_RECEIVE:
…
…
/* Tell all clients about this message */
//enet_host_broadcast (server, 0, event.packet);
int i;
for(i=0;ipeerCount;i++)
enet_peer_send (&server->peers[i], 0,event.packet);
So i don’t use a timer as you suggested , but i loop inside the peers.
As a result , all clients receive the message ( update occurs after leaving the blocking gets() of “SAY” … )
Thx !
September 19, 2015 at 4:35 am |
Hi,
Thanks for tutorial.
It work as expected, however the server event for ENET_EVENT_TYPE_CONNECT did’t get triggered until the client send the first packet. Do you know why ?
Thanks
March 29, 2016 at 10:21 am |
Great thanks! I was also having troubles to understand the tutorial from the ENet website. Now it is clear for me 🙂
June 3, 2020 at 8:19 pm |
[…] had a look here https://neppramod.wordpress.com/2012/08/12/enet-tutorial/, maybe I need to use “localhost” as a string, because of some form of DNS lookup in […]
June 11, 2020 at 9:21 pm |
I was able to re-run above example in a fresh ubuntu installation 20.04. I installed libenet-dev gcc and pkgconf and was able to compile and run it. I am guessing you are trying to run it in a different context.
June 12, 2020 at 7:33 am
Potentially, yes. I tried to compile on a mac. My investigations are currently very amateurish still; I’m not very familiar in working with C++. However I think I’ve stumbled on progress in a different direction.
Libraries like ENet are written presumably so that all the hidden complexities of writing networking code are just taken care of, so I could probably find myself stumbling soon. However, I’ll see how far I get first, before looking at these higher level networking libraries again.
June 4, 2020 at 8:10 pm |
[…] I attempted to emulate the steps in https://neppramod.wordpress.com/2012/08/12/enet-tutorial/ in order to connect from the C++ program to my nodejs […]