efl_net_*_udp: make UDP usable, including multicast.

This was a huge work, but now UDP is usable as seen in the examples.

Instead of relying on 'connect()', just do 'sendto()' and 'recvfrom()'
as they are universal. Multicast address can only be connected in
IPv4, IPv6 wasn't working and I'm not sure the IPv4 is portable to
other platforms.

Dialer will auto-join multicast groups is the dialed address is
one. Multicast properties such as time to live (hops) and loopback can
be configured. When joining multicast groups, the local
address/interface can be configured by 'IP@IFACE' format, with
'@IFACE' being optional.

Dialers will now auto-bind, so it can receive data as dialers are
expected to be bi-directional. One can manually specify the binding
address if there is such need.

Since datagrams must be read in their full size, otherwise the
remaining bits are dropped, expose next_datagram_size_query() in both
Efl.Net.Socket.Udp and Efl.Net.Server.Udp.Client.

To finalize UDP for real we need to introduce an 'Efl_Net_Ip_Address'
structure to serve as both IPv4 and IPv6 and expose 'sendto()' and
'recvfrom()'. These will come later as this commit is already too big.
This commit is contained in:
Gustavo Sverzut Barbieri 2016-10-25 10:03:34 -02:00
parent b71a7a6a58
commit d4c6db251a
2 changed files with 91 additions and 16 deletions

View File

@ -12,15 +12,33 @@ static int needed_reads = 0;
static void
_connected(void *data EINA_UNUSED, const Efl_Event *event)
{
fprintf(stderr, "INFO: connected %s\n",
efl_net_dialer_address_dial_get(event->object));
fprintf(stderr,
"INFO: connected to '%s' (%s)\n"
"INFO: - local address=%s\n"
"INFO: - read-after-write=%d reads required\n"
"INFO: - cork=%hhu\n"
"INFO: - timeout_dial=%fs\n"
"INFO: - reuse address=%hhu\n"
"INFO: - reuse port=%hhu\n"
"INFO: - multicast TTL: %u\n"
"INFO: - multicast loopback: %u\n"
"INFO: - multicast groups:\n",
efl_net_dialer_address_dial_get(event->object),
efl_net_socket_address_remote_get(event->object),
efl_net_socket_address_local_get(event->object),
needed_reads,
efl_net_socket_udp_cork_get(event->object),
efl_net_dialer_timeout_dial_get(event->object),
efl_net_socket_udp_reuse_address_get(event->object),
efl_net_socket_udp_reuse_port_get(event->object),
efl_net_socket_udp_multicast_time_to_live_get(event->object),
efl_net_socket_udp_multicast_loopback_get(event->object));
}
static void
_can_read(void *data EINA_UNUSED, const Efl_Event *event)
{
char buf[1024];
Eina_Rw_Slice rw_slice = {.mem = buf, .len = sizeof(buf)};
Eina_Rw_Slice rw_slice;
Eina_Error err;
Eina_Bool can_read = efl_io_reader_can_read_get(event->object);
@ -33,9 +51,15 @@ _can_read(void *data EINA_UNUSED, const Efl_Event *event)
if (!needed_reads) return;
rw_slice.len = efl_net_socket_udp_next_datagram_size_query(event->object);
rw_slice.mem = malloc(rw_slice.len);
EINA_SAFETY_ON_NULL_RETURN(rw_slice.mem);
err = efl_io_reader_read(event->object, &rw_slice);
if (err)
{
free(rw_slice.mem);
if (err == EAGAIN) /* EAGAIN for spurious packets */
return;
fprintf(stderr, "ERROR: could not read: %s\n", eina_error_msg_get(err));
retval = EXIT_FAILURE;
ecore_main_loop_quit();
@ -50,6 +74,7 @@ _can_read(void *data EINA_UNUSED, const Efl_Event *event)
retval = EXIT_SUCCESS;
ecore_main_loop_quit();
}
free(rw_slice.mem);
}
static void
@ -149,10 +174,16 @@ static const Ecore_Getopt options = {
"Example of Efl_Net_Dialer_Udp usage, sending a message and receiving a reply\n",
EINA_FALSE,
{
ECORE_GETOPT_STORE_STR('b', "bind", "Bind to a particular address in the format IP:PORT."),
ECORE_GETOPT_STORE_TRUE('r', "read-after-write", "Do a read after writes are done."),
ECORE_GETOPT_STORE_TRUE('c', "cork", "use UDP_CORK around messages to generate a single datagram."),
ECORE_GETOPT_STORE_TRUE('R', "dont-route", "Do not route packets via a gateway."),
ECORE_GETOPT_STORE_DOUBLE('t', "connect-timeout", "timeout in seconds for the connection phase"),
ECORE_GETOPT_STORE_UINT(0, "multicast-ttl",
"Multicast time to live in number of hops from 0-255. Defaults to 1 (only local network)."),
ECORE_GETOPT_STORE_FALSE(0, "multicast-noloopback",
"Disable multicast loopback."),
ECORE_GETOPT_APPEND('M', "multicast-group", "Join a multicast group in the form 'IP@INTERFACE', with optional '@INTERFACE', where INTERFACE is the IP address of the interface to join the multicast.", ECORE_GETOPT_TYPE_STR),
ECORE_GETOPT_VERSION('V', "version"),
ECORE_GETOPT_COPYRIGHT('C', "copyright"),
ECORE_GETOPT_LICENSE('L', "license"),
@ -167,16 +198,25 @@ int
main(int argc, char **argv)
{
char *address = NULL;
char *bind_address = NULL;
Eina_List *mcast_groups = NULL, *lst;
char *str;
Eina_Bool cork = EINA_FALSE;
Eina_Bool do_read = EINA_FALSE;
Eina_Bool dont_route = EINA_FALSE;
unsigned mcast_ttl = 1;
Eina_Bool mcast_loopback = EINA_TRUE;
Eina_Bool quit_option = EINA_FALSE;
double timeout_dial = 30.0;
Ecore_Getopt_Value values[] = {
ECORE_GETOPT_VALUE_STR(bind_address),
ECORE_GETOPT_VALUE_BOOL(do_read),
ECORE_GETOPT_VALUE_BOOL(cork),
ECORE_GETOPT_VALUE_BOOL(dont_route),
ECORE_GETOPT_VALUE_DOUBLE(timeout_dial),
ECORE_GETOPT_VALUE_UINT(mcast_ttl),
ECORE_GETOPT_VALUE_BOOL(mcast_loopback),
ECORE_GETOPT_VALUE_LIST(mcast_groups),
/* standard block to provide version, copyright, license and help */
ECORE_GETOPT_VALUE_BOOL(quit_option), /* -V/--version quits */
@ -218,11 +258,19 @@ main(int argc, char **argv)
dialer = efl_add(EFL_NET_DIALER_UDP_CLASS, loop,
efl_name_set(efl_added, "dialer"),
efl_net_socket_udp_bind_set(efl_added, bind_address),
efl_net_socket_udp_cork_set(efl_added, cork),
efl_net_socket_udp_dont_route_set(efl_added, dont_route),
efl_net_socket_udp_reuse_address_set(efl_added, EINA_TRUE), /* optional, but nice for testing */
efl_net_socket_udp_reuse_port_set(efl_added, EINA_TRUE), /* optional, but nice for testing... not secure unless you know what you're doing */
efl_net_socket_udp_multicast_time_to_live_set(efl_added, mcast_ttl),
efl_net_socket_udp_multicast_loopback_set(efl_added, mcast_loopback),
efl_net_dialer_timeout_dial_set(efl_added, timeout_dial),
efl_event_callback_array_add(efl_added, dialer_cbs(), NULL));
EINA_LIST_FOREACH(mcast_groups, lst, str)
efl_net_socket_udp_multicast_join(dialer, str);
err = efl_net_dialer_dial(dialer, address);
if (err != 0)
{
@ -231,16 +279,6 @@ main(int argc, char **argv)
goto no_mainloop;
}
fprintf(stderr,
"INFO: dialed %s\n"
"INFO: - read-after-write=%hhu\n"
"INFO: - cork=%hhu\n"
"INFO: - timeout_dial=%fs\n",
efl_net_dialer_address_dial_get(dialer),
do_read,
efl_net_socket_udp_cork_get(dialer),
efl_net_dialer_timeout_dial_get(dialer));
if (do_read) needed_reads = cork ? 1 : 2;
ecore_main_loop_begin();
@ -251,6 +289,8 @@ main(int argc, char **argv)
efl_del(dialer);
end:
EINA_LIST_FREE(mcast_groups, str)
free(str);
ecore_con_shutdown();
ecore_shutdown();

View File

@ -443,12 +443,25 @@ _server_serving(void *data EINA_UNUSED, const Efl_Event *event)
}
else if (efl_class_get(event->object) == EFL_NET_SERVER_UDP_CLASS)
{
Eina_Iterator *it;
const char *str;
fprintf(stderr,
"UDP options:\n"
" - IPv6 only: %u\n"
" - don't route: %u\n",
" - don't route: %u\n"
" - multicast TTL: %u\n"
" - multicast loopback: %u\n"
" - multicast groups:\n",
efl_net_server_udp_ipv6_only_get(event->object),
efl_net_server_udp_dont_route_get(event->object));
efl_net_server_udp_dont_route_get(event->object),
efl_net_server_udp_multicast_time_to_live_get(event->object),
efl_net_server_udp_multicast_loopback_get(event->object));
it = efl_net_server_udp_multicast_groups_get(event->object);
EINA_ITERATOR_FOREACH(it, str)
fprintf(stderr, " * %s\n", str);
eina_iterator_free(it);
}
}
@ -496,6 +509,11 @@ static const Ecore_Getopt options = {
ECORE_GETOPT_CATEGORY("udp", "UDP options"),
ECORE_GETOPT_STORE_TRUE(0, "udp-dont-route",
"If true, datagrams won't be routed using a gateway, being restricted to the local network."),
ECORE_GETOPT_STORE_UINT(0, "udp-multicast-ttl",
"Multicast time to live in number of hops from 0-255. Defaults to 1 (only local network)."),
ECORE_GETOPT_STORE_FALSE(0, "udp-multicast-noloopback",
"Disable multicast loopback."),
ECORE_GETOPT_APPEND('M', "udp-multicast-group", "Join a multicast group in the form 'IP@INTERFACE', with optional '@INTERFACE', where INTERFACE is the IP address of the interface to join the multicast.", ECORE_GETOPT_TYPE_STR),
ECORE_GETOPT_CHOICE_METAVAR(0, NULL, "The server protocol.", "protocol",
protocols),
@ -514,10 +532,14 @@ main(int argc, char **argv)
const Efl_Class *cls;
char *protocol = NULL;
char *address = NULL;
Eina_List *udp_mcast_groups = NULL;
char *str;
unsigned int clients_limit = 0;
unsigned udp_mcast_ttl = 1;
Eina_Bool clients_reject_excess = EINA_FALSE;
Eina_Bool ipv6_only = EINA_TRUE;
Eina_Bool udp_dont_route = EINA_FALSE;
Eina_Bool udp_mcast_loopback = EINA_TRUE;
Eina_Bool quit_option = EINA_FALSE;
Ecore_Getopt_Value values[] = {
ECORE_GETOPT_VALUE_BOOL(echo),
@ -534,6 +556,9 @@ main(int argc, char **argv)
ECORE_GETOPT_VALUE_BOOL(quit_option), /* category: udp */
ECORE_GETOPT_VALUE_BOOL(udp_dont_route),
ECORE_GETOPT_VALUE_UINT(udp_mcast_ttl),
ECORE_GETOPT_VALUE_BOOL(udp_mcast_loopback),
ECORE_GETOPT_VALUE_LIST(udp_mcast_groups),
/* positional argument */
ECORE_GETOPT_VALUE_STR(protocol),
@ -600,8 +625,16 @@ main(int argc, char **argv)
efl_net_server_tcp_ipv6_only_set(server, ipv6_only);
else if (cls == EFL_NET_SERVER_UDP_CLASS)
{
const Eina_List *lst;
efl_net_server_udp_ipv6_only_set(server, ipv6_only);
efl_net_server_udp_dont_route_set(server, udp_dont_route);
efl_net_server_udp_multicast_time_to_live_set(server, udp_mcast_ttl);
efl_net_server_udp_multicast_loopback_set(server, udp_mcast_loopback);
EINA_LIST_FOREACH(udp_mcast_groups, lst, str)
efl_net_server_udp_multicast_join(server, str);
}
/* an explicit call to efl_net_server_serve() after the object is
@ -624,6 +657,8 @@ main(int argc, char **argv)
server = NULL;
end:
EINA_LIST_FREE(udp_mcast_groups, str)
free(str);
ecore_con_shutdown();
ecore_shutdown();