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);
Ecore_Con_Client
struct _Ecore_Con_Client Ecore_Con_Client
Used to provide legacy API/ABI compatibility with non-Eo applications.
Definition Ecore_Con.h:308
ecore_con_init
ECORE_CON_API int ecore_con_init(void)
Initializes the Ecore_Con library.
Definition ecore_con.c:68
Ecore_Con_Server
struct _Ecore_Con_Server Ecore_Con_Server
Used to provide legacy API/ABI compatibility with non-Eo applications.
Definition Ecore_Con.h:294
ecore_con_server_add
ECORE_CON_API Ecore_Con_Server * ecore_con_server_add(Ecore_Con_Type type, const char *name, int port, const void *data)
Creates a server to listen for connections.
Definition ecore_con_legacy.c:1696
ecore_init
EAPI int ecore_init(void)
Sets up connections, signal handlers, sockets etc.
Definition ecore.c:230
Eina_List
struct _Eina_List Eina_List
Type for a generic double linked list.
Definition eina_list.h:304
eina_init
EINA_API int eina_init(void)
Initializes the Eina library.
Definition eina_main.c:291

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);
ECORE_CON_EVENT_CLIENT_ADD
ECORE_CON_API int ECORE_CON_EVENT_CLIENT_ADD
A client has connected to the server.
Definition ecore_con_legacy.c:157
ECORE_CON_EVENT_CLIENT_DEL
ECORE_CON_API int ECORE_CON_EVENT_CLIENT_DEL
A client has disconnected from the server.
Definition ecore_con_legacy.c:158
ECORE_CON_EVENT_CLIENT_DATA
ECORE_CON_API int ECORE_CON_EVENT_CLIENT_DATA
A client connected to the server has sent data.
Definition ecore_con_legacy.c:166
Ecore_Event_Handler_Cb
Eina_Bool(* Ecore_Event_Handler_Cb)(void *data, int type, void *event)
A callback used by the main loop to handle events of a specified type.
Definition Ecore_Common.h:603
ecore_event_handler_add
Ecore_Event_Handler * ecore_event_handler_add(int type, Ecore_Event_Handler_Cb func, const void *data)
Adds an event handler.
Definition ecore_events.c:13

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();
ecore_con_server_client_limit_set
ECORE_CON_API void ecore_con_server_client_limit_set(Ecore_Con_Server *svr, int client_limit, char reject_excess_clients)
Sets a limit on the number of clients that can be handled concurrently by the given server,...
Definition ecore_con_legacy.c:2267
ecore_con_server_timeout_set
ECORE_CON_API void ecore_con_server_timeout_set(Ecore_Con_Server *svr, double timeout)
Sets the default time after which an inactive client will be disconnected.
Definition ecore_con_legacy.c:2284
ecore_main_loop_begin
void ecore_main_loop_begin(void)
Runs the application main loop.
Definition ecore_main.c:1311

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));
}
ecore_con_client_ip_get
ECORE_CON_API const char * ecore_con_client_ip_get(const Ecore_Con_Client *cl)
Gets the IP address of a client that has connected.
Definition ecore_con_legacy.c:739
ecore_con_client_data_get
ECORE_CON_API void * ecore_con_client_data_get(Ecore_Con_Client *cl)
Retrieves the data associated with the given client.
Definition ecore_con_legacy.c:732
ecore_con_server_clients_get
ECORE_CON_API const Eina_List * ecore_con_server_clients_get(const Ecore_Con_Server *svr)
Retrieves the current list of clients.
Definition ecore_con_legacy.c:2277
EINA_LIST_FOREACH
#define EINA_LIST_FOREACH(list, l, _data)
Definition for the macro to iterate over a list.
Definition eina_list.h:1415

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));
ecore_con_server_uptime_get
ECORE_CON_API double ecore_con_server_uptime_get(const Ecore_Con_Server *svr)
Checks how long a server has been connected.
Definition ecore_con_legacy.c:2405

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;
ecore_con_client_timeout_set
ECORE_CON_API void ecore_con_client_timeout_set(Ecore_Con_Client *cl, double timeout)
Sets the time after which the client will be disconnected when inactive.
Definition ecore_con_legacy.c:697
ecore_con_client_server_get
ECORE_CON_API Ecore_Con_Server * ecore_con_client_server_get(const Ecore_Con_Client *cl)
The server the client is connected to.
Definition ecore_con_legacy.c:753
ecore_con_client_send
ECORE_CON_API int ecore_con_client_send(Ecore_Con_Client *cl, const void *data, int size)
Sends the given data to the given client.
Definition ecore_con_legacy.c:668
ecore_con_client_port_get
ECORE_CON_API int ecore_con_client_port_get(const Ecore_Con_Client *cl)
Returns the port that the client has connected to.
Definition ecore_con_legacy.c:746
ecore_con_client_flush
ECORE_CON_API void ecore_con_client_flush(Ecore_Con_Client *cl)
Flushes all pending data to the given client.
Definition ecore_con_legacy.c:767
ecore_con_client_connected_get
ECORE_CON_API Eina_Bool ecore_con_client_connected_get(const Ecore_Con_Client *cl)
Returns whether the client is still connected.
Definition ecore_con_legacy.c:690
ecore_con_client_data_set
ECORE_CON_API void ecore_con_client_data_set(Ecore_Con_Client *cl, const void *data)
Sets the data associated with the given client to data.
Definition ecore_con_legacy.c:724
Ecore_Con_Event_Client_Add
struct _Ecore_Con_Event_Client_Add Ecore_Con_Event_Client_Add
Used as the data param for the corresponding event.
Definition Ecore_Con.h:335
ECORE_CALLBACK_RENEW
#define ECORE_CALLBACK_RENEW
Return value to keep a callback.
Definition Ecore_Common.h:153
Eina_Bool
unsigned char Eina_Bool
Type to mimic a boolean.
Definition eina_types.h:527
EINA_UNUSED
#define EINA_UNUSED
Used to indicate that a function parameter is purposely unused.
Definition eina_types.h:339
_Ecore_Con_Event_Client_Add::client
Ecore_Con_Client * client
the client that connected
Definition Ecore_Con.h:443
}

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;
Ecore_Con_Event_Client_Del
struct _Ecore_Con_Event_Client_Del Ecore_Con_Event_Client_Del
Used as the data param for the corresponding event.
Definition Ecore_Con.h:348
_Ecore_Con_Event_Client_Del::client
Ecore_Con_Client * client
the client that was lost
Definition Ecore_Con.h:462
client = ecore_con_client_data_get(ev->client);
printf("Lost client with ip %s!\n", ecore_con_client_ip_get(ev->client));
if (client)
{
printf("Total data received from this client: %d\n", client->sdata);
free(client);
}

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:

printf("Client was connected for %0.3f seconds.\n",
ecore_con_client_uptime_get(ev->client));
ecore_con_client_del(ev->client);
return ECORE_CALLBACK_RENEW;
ecore_con_client_uptime_get
ECORE_CON_API double ecore_con_client_uptime_get(const Ecore_Con_Client *cl)
Checks how long a client has been connected.
Definition ecore_con_legacy.c:760
ecore_con_client_del
ECORE_CON_API void * ecore_con_client_del(Ecore_Con_Client *cl)
Closes the connection and free memory allocated to the given client.
Definition ecore_con_legacy.c:711
}

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:

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.