efl_net_server_udp: initial UDP server.

This is the initial UDP server that works similarly to the TCP one,
however under the hood it's widely different since the socket is
reused for all "clients", thus needs a new Efl.Net.Server.Udp.Client
(Efl.Net.Socket) as Efl.Net.Socket.Udp exposes the fd and options such
as 'cork', which would interfere in other clients.

The main socket will read the packets and find an existing client to
feed it. If no client exists, then it will create one if not overr
limit. Since there is no kernel-queuing as done by listen()/accept(),
the 'no reject' case will just accept the client anyway.

Next commits will improve UDP server handling with some advanced
features:

 - join multicast groups
 - bind to a specific interface (SO_BINDTODEVICE)
 - block packets going out of local network (SO_DONTROUTE)
 - specify priorities (SO_PRIORITY)
This commit is contained in:
Gustavo Sverzut Barbieri 2016-10-21 13:24:02 -02:00
parent 3cb15c17e0
commit 7493368e54
9 changed files with 739 additions and 2 deletions

View File

@ -18,6 +18,8 @@ ecore_con_eolian_files = \
lib/ecore_con/efl_net_server.eo \
lib/ecore_con/efl_net_server_fd.eo \
lib/ecore_con/efl_net_server_tcp.eo \
lib/ecore_con/efl_net_server_udp.eo \
lib/ecore_con/efl_net_server_udp_client.eo \
lib/ecore_con/ecore_con_eet_base.eo \
lib/ecore_con/ecore_con_eet_server_obj.eo \
lib/ecore_con/ecore_con_eet_client_obj.eo \
@ -83,7 +85,9 @@ lib/ecore_con/efl_net_dialer_http.c \
lib/ecore_con/efl_net_dialer_websocket.c \
lib/ecore_con/efl_net_server.c \
lib/ecore_con/efl_net_server_fd.c \
lib/ecore_con/efl_net_server_tcp.c
lib/ecore_con/efl_net_server_tcp.c \
lib/ecore_con/efl_net_server_udp.c \
lib/ecore_con/efl_net_server_udp_client.c
EXTRA_DIST2 += lib/ecore_con/ecore_con_legacy.c

View File

@ -408,6 +408,7 @@ EFL_CALLBACKS_ARRAY_DEFINE(server_cbs,
static const char * protocols[] = {
"tcp",
"udp",
NULL
};
@ -501,6 +502,7 @@ main(int argc, char **argv)
}
if (strcmp(protocol, "tcp") == 0) cls = EFL_NET_SERVER_TCP_CLASS;
else if (strcmp(protocol, "udp") == 0) cls = EFL_NET_SERVER_UDP_CLASS;
else
{
fprintf(stderr, "ERROR: unsupported protocol: %s\n", protocol);
@ -524,6 +526,8 @@ main(int argc, char **argv)
if (cls == EFL_NET_SERVER_TCP_CLASS)
efl_net_server_tcp_ipv6_only_set(server, ipv6_only);
else if (cls == EFL_NET_SERVER_UDP_CLASS)
efl_net_server_udp_ipv6_only_set(server, ipv6_only);
/* an explicit call to efl_net_server_serve() after the object is
* constructed allows for more complex setup, such as interacting

View File

@ -17,6 +17,8 @@
#include "efl_net_socket_udp.eo.h"
#include "efl_net_dialer_udp.eo.h"
#include "efl_net_server_udp.eo.h"
#include "efl_net_server_udp_client.eo.h"
#include "efl_net_http_types.eot.h"

View File

@ -380,6 +380,9 @@ void ecore_con_mempool_shutdown(void);
#undef GENERIC_ALLOC_FREE_HEADER
void _efl_net_server_udp_init(Eo *client, int fd, const struct sockaddr *addr, socklen_t addrlen, const char *str);
void _efl_net_server_udp_client_feed(Eo *client, Eina_Rw_Slice slice);
Eina_Bool efl_net_ip_port_fmt(char *buf, int buflen, const struct sockaddr *addr);
/**

View File

@ -0,0 +1,357 @@
#define EFL_NET_SERVER_UDP_PROTECTED 1
#define EFL_NET_SERVER_FD_PROTECTED 1
#define EFL_NET_SERVER_PROTECTED 1
#define EFL_NET_SOCKET_PROTECTED 1
#define EFL_IO_WRITER_PROTECTED 1
#define EFL_LOOP_FD_PROTECTED 1
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "Ecore.h"
#include "Ecore_Con.h"
#include "ecore_con_private.h"
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_UDP_H
# include <netinet/udp.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#ifdef HAVE_EVIL
# include <Evil.h>
#endif
#include <sys/ioctl.h>
#define MY_CLASS EFL_NET_SERVER_UDP_CLASS
typedef struct _Efl_Net_Server_Udp_Data
{
Ecore_Thread *resolver;
Eina_Hash *clients; /* addr (string) -> client (Efl.Net.Server.Udp.Client) */
Eina_Bool ipv6_only;
} Efl_Net_Server_Udp_Data;
EOLIAN Efl_Object *
_efl_net_server_udp_efl_object_constructor(Eo *o, Efl_Net_Server_Udp_Data *pd)
{
pd->ipv6_only = 0xff;
pd->clients = eina_hash_string_superfast_new(NULL);
return efl_constructor(efl_super(o, MY_CLASS));
}
EOLIAN void
_efl_net_server_udp_efl_object_destructor(Eo *o, Efl_Net_Server_Udp_Data *pd)
{
if (pd->resolver)
{
ecore_thread_cancel(pd->resolver);
pd->resolver = NULL;
}
efl_destructor(efl_super(o, MY_CLASS));
if (pd->clients)
{
eina_hash_free(pd->clients);
pd->clients = NULL;
}
}
static Eina_Error
_efl_net_server_udp_resolved_bind(Eo *o, Efl_Net_Server_Udp_Data *pd, const struct addrinfo *addr)
{
Eina_Error err = 0;
char buf[INET6_ADDRSTRLEN + sizeof("[]:65536")];
socklen_t addrlen = addr->ai_addrlen;
int fd, r;
efl_net_server_fd_family_set(o, addr->ai_family);
fd = efl_net_socket4(addr->ai_family, addr->ai_socktype, addr->ai_protocol,
efl_net_server_fd_close_on_exec_get(o));
if (fd < 0)
{
ERR("socket(%d, %d, %d): %s",
addr->ai_family, addr->ai_socktype, addr->ai_protocol,
strerror(errno));
return errno;
}
efl_loop_fd_set(o, fd);
/* apply pending value BEFORE bind() */
if (addr->ai_family == AF_INET6)
{
if (pd->ipv6_only == 0xff)
efl_net_server_udp_ipv6_only_get(o); /* fetch & sync */
else
efl_net_server_udp_ipv6_only_set(o, pd->ipv6_only);
}
r = bind(fd, addr->ai_addr, addrlen);
if (r < 0)
{
err = errno;
efl_net_ip_port_fmt(buf, sizeof(buf), addr->ai_addr);
DBG("bind(%d, %s): %s", fd, buf, strerror(errno));
goto error;
}
if (getsockname(fd, addr->ai_addr, &addrlen) != 0)
{
ERR("getsockname(%d): %s", fd, strerror(errno));
goto error;
}
else if (efl_net_ip_port_fmt(buf, sizeof(buf), addr->ai_addr))
efl_net_server_address_set(o, buf);
DBG("fd=%d serving at %s", fd, buf);
efl_net_server_serving_set(o, EINA_TRUE);
return 0;
error:
efl_net_server_fd_family_set(o, AF_UNSPEC);
efl_loop_fd_set(o, -1);
close(fd);
return err;
}
static void
_efl_net_server_udp_resolved(void *data, const char *host EINA_UNUSED, const char *port EINA_UNUSED, const struct addrinfo *hints EINA_UNUSED, struct addrinfo *result, int gai_error)
{
Eo *o = data;
Efl_Net_Server_Udp_Data *pd = efl_data_scope_get(o, MY_CLASS);
const struct addrinfo *addr;
Eina_Error err;
pd->resolver = NULL;
efl_ref(o); /* we're emitting callbacks then continuing the workflow */
if (gai_error)
{
err = EFL_NET_SERVER_ERROR_COULDNT_RESOLVE_HOST;
goto end;
}
for (addr = result; addr != NULL; addr = addr->ai_next)
{
err = _efl_net_server_udp_resolved_bind(o, pd, addr);
if (err == 0) break;
}
freeaddrinfo(result);
end:
if (err) efl_event_callback_call(o, EFL_NET_SERVER_EVENT_ERROR, &err);
efl_unref(o);
}
EOLIAN static Eina_Error
_efl_net_server_udp_efl_net_server_serve(Eo *o, Efl_Net_Server_Udp_Data *pd, const char *address)
{
char *str;
const char *host, *port;
struct addrinfo hints = {
.ai_socktype = SOCK_DGRAM,
.ai_protocol = IPPROTO_UDP,
.ai_family = AF_UNSPEC,
};
EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
str = strdup(address);
if (!efl_net_ip_port_split(str, &host, &port))
{
free(str);
return EINVAL;
}
if (!port) port = "0";
if (strchr(host, ':')) hints.ai_family = AF_INET6;
pd->resolver = efl_net_ip_resolve_async_new(host, port, &hints,
_efl_net_server_udp_resolved, o);
free(str);
EINA_SAFETY_ON_NULL_RETURN_VAL(pd->resolver, EINVAL);
return 0;
}
static Efl_Callback_Array_Item *_efl_net_server_udp_client_cbs(void);
static void
_efl_net_server_udp_client_event_closed(void *data, const Efl_Event *event)
{
Eo *server = data;
Eo *client = event->object;
Efl_Net_Server_Udp_Data *pd = efl_data_scope_get(server, MY_CLASS);
efl_event_callback_array_del(client, _efl_net_server_udp_client_cbs(), server);
efl_net_server_clients_count_set(server, efl_net_server_clients_count_get(server) - 1);
eina_hash_del(pd->clients, efl_net_socket_address_remote_get(client), client);
if (efl_parent_get(client) == server)
efl_parent_set(client, NULL);
}
EFL_CALLBACKS_ARRAY_DEFINE(_efl_net_server_udp_client_cbs,
{ EFL_IO_CLOSER_EVENT_CLOSED, _efl_net_server_udp_client_event_closed });
static size_t
_udp_datagram_size_query(int fd)
{
#ifdef _WIN32
unsigned long size;
if (ioctlsocket(fd, FIONREAD, &size) == 0)
return size;
#else
int size;
if (ioctl(fd, FIONREAD, &size) == 0)
return size;
#endif
return 8 * 1024;
}
EOLIAN static void
_efl_net_server_udp_efl_net_server_fd_process_incoming_data(Eo *o, Efl_Net_Server_Udp_Data *pd)
{
unsigned int count, limit;
Eina_Bool reject_excess;
struct sockaddr_storage addr;
Eo *client;
int fd;
socklen_t addrlen = sizeof(addr);
char str[INET6_ADDRSTRLEN + sizeof("[]:65536")] = "";
char *buf;
size_t buflen;
ssize_t r;
Eina_Rw_Slice slice;
fd = efl_loop_fd_get(o);
buflen = _udp_datagram_size_query(fd);
buf = malloc(buflen);
EINA_SAFETY_ON_NULL_RETURN(buf);
r = recvfrom(fd, buf, buflen, 0, (struct sockaddr *)&addr, &addrlen);
if (r < 0)
{
Eina_Error err = efl_net_socket_error_get();
ERR("recvfrom(%d, %p, %zu, 0, %p, %d): %s", fd, buf, buflen, &addr, addrlen, eina_error_msg_get(err));
free(buf);
efl_event_callback_call(o, EFL_NET_SERVER_EVENT_ERROR, &err);
return;
}
slice = (Eina_Rw_Slice){.mem = buf, .len = r };
efl_net_ip_port_fmt(str, sizeof(str), (struct sockaddr *)&addr);
client = eina_hash_find(pd->clients, str);
if (client)
{
_efl_net_server_udp_client_feed(client, slice);
return;
}
count = efl_net_server_clients_count_get(o);
efl_net_server_clients_limit_get(o, &limit, &reject_excess);
if ((limit > 0) && (count >= limit))
{
if (reject_excess)
{
free(buf);
efl_event_callback_call(o, EFL_NET_SERVER_EVENT_CLIENT_REJECTED, str);
return;
}
}
client = efl_add(EFL_NET_SERVER_UDP_CLIENT_CLASS, o,
efl_event_callback_array_add(efl_added, _efl_net_server_udp_client_cbs(), o),
efl_io_closer_close_on_destructor_set(efl_added, EINA_TRUE),
efl_net_socket_address_local_set(efl_added, efl_net_server_address_get(o)),
_efl_net_server_udp_init(efl_added, fd, (const struct sockaddr *)&addr, addrlen, str),
efl_io_writer_can_write_set(efl_added, EINA_TRUE));
if (!client)
{
ERR("could not create client object for %s", str);
free(buf);
efl_event_callback_call(o, EFL_NET_SERVER_EVENT_CLIENT_REJECTED, str);
return;
}
if (!eina_hash_direct_add(pd->clients, efl_net_socket_address_remote_get(client), client))
{
ERR("could not create client object for %s", str);
free(buf);
efl_del(client);
efl_event_callback_call(o, EFL_NET_SERVER_EVENT_CLIENT_REJECTED, str);
return;
}
efl_net_server_clients_count_set(o, efl_net_server_clients_count_get(o) + 1);
efl_event_callback_call(o, EFL_NET_SERVER_EVENT_CLIENT_ADD, client);
if (efl_ref_get(client) == 1) /* users must take a reference themselves */
{
DBG("client %s was not handled, closing it...",
efl_net_socket_address_remote_get(client));
free(buf);
efl_del(client);
return;
}
_efl_net_server_udp_client_feed(client, slice);
}
EOLIAN void
_efl_net_server_udp_ipv6_only_set(Eo *o, Efl_Net_Server_Udp_Data *pd, Eina_Bool ipv6_only)
{
Eina_Bool old = pd->ipv6_only;
int fd = efl_loop_fd_get(o);
int value = ipv6_only;
pd->ipv6_only = ipv6_only;
if (fd < 0) return;
if (efl_net_server_fd_family_get(o) != AF_INET6) return;
#ifdef IPV6_V6ONLY
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &value, sizeof(value)) < 0)
{
ERR("could not set socket=%d IPV6_V6ONLY=%d: %s", fd, value, strerror(errno));
pd->ipv6_only = old;
}
#endif
}
EOLIAN Eina_Bool
_efl_net_server_udp_ipv6_only_get(Eo *o EINA_UNUSED, Efl_Net_Server_Udp_Data *pd)
{
#ifdef IPV6_V6ONLY
int fd = efl_loop_fd_get(o);
int value = 0;
socklen_t size = sizeof(value);
if (fd < 0) goto end;
if (efl_net_server_fd_family_get(o) != AF_INET6) goto end;
if (getsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &value, &size) < 0)
{
WRN("getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY): %s", fd, strerror(errno));
goto end;
}
pd->ipv6_only = !!value;
end:
#endif
return pd->ipv6_only;
}
#include "efl_net_server_udp.eo.c"

View File

@ -0,0 +1,42 @@
class Efl.Net.Server.Udp (Efl.Net.Server.Fd) {
[[A UDP server.
@since 1.19
]]
methods {
@property ipv6_only {
[[Whenever IPv6 listen address will accept only same-family clients or will allow IPv4 to connect as well.
Since Linux 2.4.21, Windows Vista and MacOS X these
control whenever a server that did bind to an IPv6
address will accept only IPv6 clients or will also
accept IPv4 by automatically converting them in an IPv6
address, allowing a single socket to handle both
protocols.
If an IPv6 address was used in @Efl.Net.Server.address,
this property is $false and an IPv4 connects, then an
address such as [::ffff:IPv4]:PORT will be used, such as
[::ffff:192.168.0.2]:1234, where the IPv4 address can be
extracted.
If an IPv4 address was used in @Efl.Net.Server.address,
this has no effect.
Systems can configure their default value, usually true
(allows only IPv6 clients).
]]
values {
ipv6_only: bool;
}
}
}
implements {
Efl.Object.constructor;
Efl.Object.destructor;
Efl.Net.Server.serve;
Efl.Net.Server.Fd.process_incoming_data;
}
}

View File

@ -0,0 +1,291 @@
#define EFL_NET_SERVER_UDP_CLIENT_PROTECTED 1
#define EFL_IO_READER_PROTECTED 1
#define EFL_IO_WRITER_PROTECTED 1
#define EFL_IO_CLOSER_PROTECTED 1
#define EFL_NET_SOCKET_PROTECTED 1
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "Ecore.h"
#include "Ecore_Con.h"
#include "ecore_con_private.h"
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_EVIL
# include <Evil.h>
#endif
#define MY_CLASS EFL_NET_SERVER_UDP_CLIENT_CLASS
typedef struct _Efl_Net_Server_Udp_Client_Packet
{
EINA_INLIST;
Eina_Rw_Slice slice;
} Efl_Net_Server_Udp_Client_Packet;
static void
_efl_net_server_udp_client_packet_free(Efl_Net_Server_Udp_Client_Packet *pkt)
{
free(pkt->slice.mem);
free(pkt);
}
typedef struct _Efl_Net_Server_Udp_Client_Data
{
Eina_Stringshare *address_local;
Eina_Stringshare *address_remote;
Eina_Inlist *packets;
struct sockaddr *addr_remote;
socklen_t addr_remote_len;
int fd;
Eina_Bool close_on_destructor;
Eina_Bool eos;
Eina_Bool can_read;
Eina_Bool can_write;
} Efl_Net_Server_Udp_Client_Data;
EOLIAN static Efl_Object *
_efl_net_server_udp_client_efl_object_finalize(Eo *o, Efl_Net_Server_Udp_Client_Data *pd)
{
o = efl_finalize(efl_super(o, MY_CLASS));
EINA_SAFETY_ON_NULL_RETURN_VAL(pd->addr_remote, NULL);
return o;
}
static void
_efl_net_server_udp_client_cleanup(Efl_Net_Server_Udp_Client_Data *pd)
{
Efl_Net_Server_Udp_Client_Packet *pkt;
pd->fd = -1;
EINA_INLIST_FREE(pd->packets, pkt)
{
pd->packets = eina_inlist_remove(pd->packets, EINA_INLIST_GET(pkt));
_efl_net_server_udp_client_packet_free(pkt);
}
}
EOLIAN static void
_efl_net_server_udp_client_efl_object_destructor(Eo *o, Efl_Net_Server_Udp_Client_Data *pd)
{
if (efl_io_closer_close_on_destructor_get(o) &&
(!efl_io_closer_closed_get(o)))
efl_io_closer_close(o);
efl_destructor(efl_super(o, MY_CLASS));
_efl_net_server_udp_client_cleanup(pd);
eina_stringshare_replace(&pd->address_local, NULL);
eina_stringshare_replace(&pd->address_remote, NULL);
free(pd->addr_remote);
pd->addr_remote = NULL;
pd->addr_remote_len = 0;
}
void
_efl_net_server_udp_init(Eo *o, int fd, const struct sockaddr *addr, socklen_t addrlen, const char *str)
{
Efl_Net_Server_Udp_Client_Data *pd = efl_data_scope_get(o, MY_CLASS);
pd->fd = fd;
pd->addr_remote = malloc(addrlen);
EINA_SAFETY_ON_NULL_RETURN(pd->addr_remote);
memcpy(pd->addr_remote, addr, addrlen);
pd->addr_remote_len = addrlen;
efl_net_socket_address_remote_set(o, str);
}
void
_efl_net_server_udp_client_feed(Eo *o, Eina_Rw_Slice slice)
{
Efl_Net_Server_Udp_Client_Data *pd = efl_data_scope_get(o, MY_CLASS);
Efl_Net_Server_Udp_Client_Packet *pkt;
pkt = malloc(sizeof(Efl_Net_Server_Udp_Client_Packet));
EINA_SAFETY_ON_NULL_GOTO(pkt, error);
pkt->slice = slice;
pd->packets = eina_inlist_append(pd->packets, EINA_INLIST_GET(pkt));
efl_io_reader_can_read_set(o, EINA_TRUE);
return;
error:
free(slice.mem);
}
EOLIAN static Eina_Error
_efl_net_server_udp_client_efl_io_closer_close(Eo *o, Efl_Net_Server_Udp_Client_Data *pd)
{
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EBADF);
efl_io_writer_can_write_set(o, EINA_FALSE);
efl_io_reader_can_read_set(o, EINA_FALSE);
efl_io_reader_eos_set(o, EINA_TRUE);
_efl_net_server_udp_client_cleanup(pd);
efl_event_callback_call(o, EFL_IO_CLOSER_EVENT_CLOSED, NULL);
return 0;
}
EOLIAN static Eina_Error
_efl_net_server_udp_client_efl_io_reader_read(Eo *o, Efl_Net_Server_Udp_Client_Data *pd, Eina_Rw_Slice *rw_slice)
{
Efl_Net_Server_Udp_Client_Packet *pkt;
EINA_SAFETY_ON_NULL_RETURN_VAL(rw_slice, EINVAL);
if (!pd->packets)
{
rw_slice->len = 0;
rw_slice->mem = NULL;
efl_io_reader_can_read_set(o, EINA_FALSE);
return EAGAIN;
}
pkt = EINA_INLIST_CONTAINER_GET(pd->packets, Efl_Net_Server_Udp_Client_Packet);
pd->packets = eina_inlist_remove(pd->packets, pd->packets);
*rw_slice = eina_rw_slice_copy(*rw_slice, eina_rw_slice_slice_get(pkt->slice));
_efl_net_server_udp_client_packet_free(pkt);
efl_io_reader_can_read_set(o, !!pd->packets);
return 0;
}
EOLIAN static Eina_Error
_efl_net_server_udp_client_efl_io_writer_write(Eo *o EINA_UNUSED, Efl_Net_Server_Udp_Client_Data *pd, Eina_Slice *ro_slice, Eina_Slice *remaining)
{
ssize_t r;
EINA_SAFETY_ON_NULL_RETURN_VAL(ro_slice, EINVAL);
if (pd->fd < 0) goto error;
do
{
r = sendto(pd->fd, ro_slice->mem, ro_slice->len, 0, pd->addr_remote, pd->addr_remote_len);
if (r < 0)
{
Eina_Error err = efl_net_socket_error_get();
if (err == EINTR) continue;
if (remaining) *remaining = *ro_slice;
ro_slice->len = 0;
ro_slice->mem = NULL;
return err;
}
}
while (r < 0);
if (remaining)
{
remaining->len = ro_slice->len - r;
remaining->bytes = ro_slice->bytes + r;
}
ro_slice->len = r;
return 0;
error:
if (remaining) *remaining = *ro_slice;
ro_slice->len = 0;
ro_slice->mem = NULL;
return EINVAL;
}
EOLIAN static Eina_Bool
_efl_net_server_udp_client_efl_io_reader_can_read_get(Eo *o EINA_UNUSED, Efl_Net_Server_Udp_Client_Data *pd)
{
return pd->can_read;
}
EOLIAN static void
_efl_net_server_udp_client_efl_io_reader_can_read_set(Eo *o, Efl_Net_Server_Udp_Client_Data *pd, Eina_Bool can_read)
{
EINA_SAFETY_ON_TRUE_RETURN(pd->fd < 0);
if (pd->can_read == can_read) return;
pd->can_read = can_read;
efl_event_callback_call(o, EFL_IO_READER_EVENT_CAN_READ_CHANGED, NULL);
}
EOLIAN static Eina_Bool
_efl_net_server_udp_client_efl_io_reader_eos_get(Eo *o EINA_UNUSED, Efl_Net_Server_Udp_Client_Data *pd)
{
return pd->eos;
}
EOLIAN static void
_efl_net_server_udp_client_efl_io_reader_eos_set(Eo *o, Efl_Net_Server_Udp_Client_Data *pd, Eina_Bool is_eos)
{
EINA_SAFETY_ON_TRUE_RETURN(pd->fd < 0);
if (pd->eos == is_eos) return;
pd->eos = is_eos;
if (is_eos)
efl_event_callback_call(o, EFL_IO_READER_EVENT_EOS, NULL);
}
EOLIAN static Eina_Bool
_efl_net_server_udp_client_efl_io_writer_can_write_get(Eo *o EINA_UNUSED, Efl_Net_Server_Udp_Client_Data *pd)
{
return pd->can_write;
}
EOLIAN static void
_efl_net_server_udp_client_efl_io_writer_can_write_set(Eo *o, Efl_Net_Server_Udp_Client_Data *pd, Eina_Bool can_write)
{
EINA_SAFETY_ON_TRUE_RETURN(pd->fd < 0);
if (pd->can_write == can_write) return;
pd->can_write = can_write;
efl_event_callback_call(o, EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, NULL);
}
EOLIAN static Eina_Bool
_efl_net_server_udp_client_efl_io_closer_closed_get(Eo *o EINA_UNUSED, Efl_Net_Server_Udp_Client_Data *pd)
{
return pd->fd < 0;
}
EOLIAN static void
_efl_net_server_udp_client_efl_io_closer_close_on_destructor_set(Eo *o EINA_UNUSED, Efl_Net_Server_Udp_Client_Data *pd, Eina_Bool close_on_destructor)
{
pd->close_on_destructor = close_on_destructor;
}
EOLIAN static Eina_Bool
_efl_net_server_udp_client_efl_io_closer_close_on_destructor_get(Eo *o EINA_UNUSED, Efl_Net_Server_Udp_Client_Data *pd)
{
return pd->close_on_destructor;
}
EOLIAN static void
_efl_net_server_udp_client_efl_net_socket_address_local_set(Eo *o EINA_UNUSED, Efl_Net_Server_Udp_Client_Data *pd, const char *address)
{
eina_stringshare_replace(&pd->address_local, address);
}
EOLIAN static const char *
_efl_net_server_udp_client_efl_net_socket_address_local_get(Eo *o EINA_UNUSED, Efl_Net_Server_Udp_Client_Data *pd)
{
return pd->address_local;
}
EOLIAN static void
_efl_net_server_udp_client_efl_net_socket_address_remote_set(Eo *o EINA_UNUSED, Efl_Net_Server_Udp_Client_Data *pd, const char *address)
{
eina_stringshare_replace(&pd->address_remote, address);
}
EOLIAN static const char *
_efl_net_server_udp_client_efl_net_socket_address_remote_get(Eo *o EINA_UNUSED, Efl_Net_Server_Udp_Client_Data *pd)
{
return pd->address_remote;
}
#include "efl_net_server_udp_client.eo.c"

View File

@ -0,0 +1,30 @@
class Efl.Net.Server.Udp.Client (Efl.Object, Efl.Net.Socket) {
[[A UDP client child of Efl.Net.Server.Udp
Unlike connection protocols such as TCP or Local, UDP doesn't
create extra sockets for its "clients". Then this thin class
will provide the required socket interface on top of the server
internal socket.
Given this limitation, some features such as 'cork' (used to
coalesce multiple writes() into a single datagram) are not
available since it could interfere with other clients.
@since 1.19
]]
implements {
Efl.Object.finalize;
Efl.Object.destructor;
Efl.Io.Closer.close;
Efl.Io.Closer.closed.get;
Efl.Io.Closer.close_on_destructor;
Efl.Io.Reader.can_read;
Efl.Io.Reader.eos;
Efl.Io.Reader.read;
Efl.Io.Writer.write;
Efl.Io.Writer.can_write;
Efl.Net.Socket.address_local;
Efl.Net.Socket.address_remote;
}
}

View File

@ -2,7 +2,11 @@ class Efl.Net.Socket.Udp (Efl.Net.Socket.Fd) {
[[A base UDP socket.
This is the common class and takes an existing FD, usually
created by an dialer or server.
created by an dialer.
Since for the server 'clients' there is no accepted connection
it will reuse the same file decriptor and to avoid it being
closed, another class Efl.Net.Server.Udp.Client is used instead.
@since 1.19
]]