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:
parent
3cb15c17e0
commit
7493368e54
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
@ -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"
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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"
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
]]
|
||||
|
|
Loading…
Reference in New Issue