Ecore_Con - Creating a server

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files
- Examples

Ecore_Con - Creating a server

In this example we are going to create a server that listens for connections from clients through a TCP port.

You can get the full source code at ecore_con_server_simple_example.c.

We begin our example in the main function, to demonstrate how to setup things, and then go to the callbacks that are needed for it to run properly.

In the `main` function, after initializing the libraries, we use ecore_con_server_add() to startup the server. Look at the reference documentation of this function: it supports many types of server, and we are going to use ECORE_CON_REMOTE_TCP (a TCP based server). Other arguments to this function are the address where we are listening on, the port, and a data pointer that will associate that data with the server:

main(void)
{
Ecore_Con_Server *svr;
Ecore_Con_Client *cl;
const Eina_List *clients, *l;
eina_init();
ecore_init();
ecore_con_init();
if (!(svr = ecore_con_server_add(ECORE_CON_REMOTE_TCP, "127.0.0.1", 8080, NULL)))
exit(1);

Notice that we are listening only on 127.0.0.1, which is the internal loopback interface. If the server needs to listening on all of its ips, use 0.0.0.0 instead.

We also need to set event handlers to be called when we receive any data from the clients, when a new client connects to our server, or when a client disconnects. These callbacks are:

ecore_event_handler_add(ECORE_CON_EVENT_CLIENT_ADD, (Ecore_Event_Handler_Cb)_add, NULL);
ecore_event_handler_add(ECORE_CON_EVENT_CLIENT_DEL, (Ecore_Event_Handler_Cb)_del, NULL);
ecore_event_handler_add(ECORE_CON_EVENT_CLIENT_DATA, (Ecore_Event_Handler_Cb)_data, NULL);

More details about what these callbacks do will be given later.

Now, before running the main loop, we also want to set some limits to our server. To avoid it to be overloaded with too many connections to handle, we are going to set a maximum of 3 clients connected at the same time. This number is used just to demonstrate the API. A good number to be used here would need to be determined by tests done on the server, to check the load supported by it.

Any other client trying to connect to this server, after the limit is reached, will wait until one of the connected clients disconnect and the server accepts the new connection.

Another important thing to do is setting a timeout, to avoid that a client hold a connection for too long without doing anything. This timeout will disconnect the idle client, allowing that other clients that may be waiting to connect finally can do it.

Then we just start the main loop:

ecore_con_server_timeout_set(svr, 10);
ecore_con_server_client_limit_set(svr, 3, 0);
ecore_main_loop_begin();

After exiting the main loop, we print the list of connected clients, and also free the data associated with each respective client. This data was previously associated using ecore_con_client_data_set():

clients = ecore_con_server_clients_get(svr);
printf("Clients connected to this server when exiting: %d\n",
eina_list_count(clients));
EINA_LIST_FOREACH(clients, l, cl)
{
printf("%s\n", ecore_con_client_ip_get(cl));
free(ecore_con_client_data_get(cl));
}

Then before exiting we show the total uptime of the server:

printf("Server was up for %0.3f seconds\n",
ecore_con_server_uptime_get(svr));

Now let's go back to the used callbacks.

The first callback, `_add`, is registered to the event ECORE_CON_EVENT_CLIENT_ADD, which will be called whenever a client connects to the server.

This callback will associate a data structure to this client, that will be used to count how many bytes were received from it. It also prints some info about the client, and send a welcome string to it. ecore_con_client_flush() is used to ensure that the string is sent immediately, instead of being buffered.

A timeout for idle specific for this client is also set, to demonstrate that it is independent of the general timeout of the server.

Before exiting, the callback will display a list of all clients still connected to this server. The code for this callback follows:

Eina_Bool
_add(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_Con_Event_Client_Add *ev)
{
char welcome[] = "hello! - sent from the server";
Ecore_Con_Server *srv;
Ecore_Con_Client *cl;
const Eina_List *clients, *l;
struct _Client *client = malloc(sizeof(*client));
client->sdata = 0;
printf("Client with ip %s, port %d, connected = %d!\n",
ecore_con_client_ip_get(ev->client),
ecore_con_client_port_get(ev->client),
ecore_con_client_connected_get(ev->client));
ecore_con_client_send(ev->client, welcome, sizeof(welcome));
ecore_con_client_flush(ev->client);
ecore_con_client_timeout_set(ev->client, 6);
ecore_con_client_data_set(ev->client, client);
srv = ecore_con_client_server_get(ev->client);
printf("Clients connected to this server:\n");
clients = ecore_con_server_clients_get(srv);
EINA_LIST_FOREACH(clients, l, cl)
printf("%s\n", ecore_con_client_ip_get(cl));
return ECORE_CALLBACK_RENEW;
}

The second callback is `_del`. It is associated with ECORE_CON_EVENT_CLIENT_DEL, and is called whenever a client disconnects from this server.

It will just print some information about the client, free the associated data structure, and call ecore_con_client_del() on it before exiting the callback. Here's its code:

Eina_Bool
_del(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_Con_Event_Client_Del *ev)
{
struct _Client *client;
if (!ev->client)
return ECORE_CALLBACK_RENEW;
client = ecore_con_client_data_get(ev->client);
printf("Lost client with ip %s!\n", ecore_con_client_ip_get(ev->client));
printf("Total data received from this client: %d\n", client->sdata);
printf("Client was connected for %0.3f seconds.\n",
ecore_con_client_uptime_get(ev->client));
if (client)
free(client);
ecore_con_client_del(ev->client);
return ECORE_CALLBACK_RENEW;
}

The last callback will print any data received by this server from its clients. It also increments the "bytes received" counter, sdata, in the data structure associated with this client. The callback code follows:

Eina_Bool
_data(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_Con_Event_Client_Data *ev)
{
char fmt[128];
struct _Client *client = ecore_con_client_data_get(ev->client);
snprintf(fmt, sizeof(fmt),
"Received %i bytes from client %s port %d:\n"
">>>>>\n"
"%%.%is\n"
">>>>>\n",
ev->size, ecore_con_client_ip_get(ev->client),
ecore_con_client_port_get(ev->client), ev->size);
printf(fmt, ev->data);
client->sdata += ev->size;
return ECORE_CALLBACK_RENEW;
}

The important parts of this example were described above. If you need to see the full source code for it, there's a link to the code in the beginning of this page.

This example will start a server and start accepting connections from clients, as demonstrated in the following diagram: Full size

Note
  This example contains a serious security flaw: it doesn't check for the size of data being received, thus allowing to the string to be exploited in some way. However, it is left like this to make the code simpler and just demonstrate the API usage.