ENet Tutorial

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.

Getting Started

However you approach later, you still need to have some of the basic constructs right before sending and receiving packets. This applies to both client and server.

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.

Enough talking, lets see this in action.
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);
        }
    }
}

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.

About these ads

8 Responses to “ENet Tutorial”

  1. thx Says:

    thx.

  2. Omar Says:

    what is the output of this command ( `pkg-config –libs libenet`) in your distribution? I’m using fedora and it’s not workng

  3. Bhavin Bansal Says:

    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

    • Pramod Nepal Says:

      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)

      => gcc -o server server.c `pkg-config --libs libenet`
      => ./server 
      (Server) We got a new connection from 100007f
      (Server) Message from client : Hi
      (Server) Message from client : How are you
      (null) disconnected.
      

      And the client.

      => gcc -o client client.c `pkg-config --libs libenet`
      client.c: In function ‘main’:
      client.c:71:9: warning: ‘gets’ is deprecated (declared at /usr/include/stdio.h:638) [-Wdeprecated-declarations]
               gets(message);
               ^
      => ls
      client  client.c  server  server.c
      => ./client 
      (Client) We got a new connection from 100007f
      Say > Hi
      (Client) Message from server : Hi
      Say > How are you
      (Client) Message from server : How are you
      Say > 
      

      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.

  4. Bhavin Bansal Says:

    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.

    • Pramod Nepal Says:

      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)

  5. ezee Says:

    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 !

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Follow

Get every new post delivered to your Inbox.