diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/udp_connection.c | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/src/udp_connection.c b/src/udp_connection.c new file mode 100644 index 0000000..381f2d4 --- /dev/null +++ b/src/udp_connection.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * + * Licensed under the Flora License, Version 1.1 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "udp_connection.h" +#include <gio/gio.h> +#include <stdlib.h> +#include "log.h" +#define MESSAGE_IN_BUF_SIZE 512 + +struct udp_connection { + GSocket *socket; + udp_receive_cb receive_cb; + GIOChannel *channel; + guint watch_id; + GError *error; +}; + +static gboolean _channel_ready_cb(GIOChannel *source, GIOCondition cond, gpointer data); +static void _connection_release_resources(udp_connection_t *connection); + +udp_connection_t *udp_connection_create(int port) +{ + udp_connection_t *connection = (udp_connection_t*) calloc(sizeof(udp_connection_t), 1); + if(connection == NULL) { + _E("Failed to calloc allocate memory"); + return NULL; + } + + connection->socket = g_socket_new(G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &(connection->error)); + if(!connection->socket) { + _E("Failed to create socket"); + _connection_release_resources(connection); + return NULL; + } + _D("Socket created"); + + GInetAddress *inet_addr = g_inet_address_new_any(G_SOCKET_FAMILY_IPV4); + if(!inet_addr) { + _E("Failed to obtain inet any address"); + _connection_release_resources(connection); + return NULL; + } + gchar *address_str = g_inet_address_to_string(inet_addr); + _D("Obtained address: %s", address_str); + + GInetSocketAddress *socket_addr = (GInetSocketAddress*) g_inet_socket_address_new(inet_addr, port); + g_object_unref(inet_addr); + if(!socket_addr) { + _E("Failed to get socket address"); + g_free(address_str); + _connection_release_resources(connection); + return NULL; + } + + gboolean res = g_socket_bind(connection->socket, (GSocketAddress*) socket_addr, TRUE, &(connection->error)); + g_object_unref(socket_addr); + if(!res) { + _E("Failed to bind socket: %s", connection->error->message); + g_free(address_str); + _connection_release_resources(connection); + return NULL; + } + _D("Bound socket to %s:%d", address_str, port); + g_socket_set_blocking(connection->socket, FALSE); + + int socket_fd = g_socket_get_fd(connection->socket); + connection->channel = g_io_channel_unix_new(socket_fd); + connection->watch_id = g_io_add_watch(connection->channel, G_IO_IN, _channel_ready_cb, connection); + g_io_channel_unref(connection->channel); + + g_free(address_str); + return connection; +} + +int udp_connection_send(udp_connection_t *connection, const char *data, unsigned short int size, const char *address, int port) +{ + GIOCondition cond = g_socket_condition_check(connection->socket, G_IO_OUT); + if(cond != G_IO_OUT) { + _E("Failed to send data - socket is not G_IO_OUT"); + return -1; + } + + GInetSocketAddress *receiver_address = (GInetSocketAddress*) g_inet_socket_address_new_from_string(address, port); + if(!receiver_address) { + _E("Failed to obtain socket address from %s:%d", address, port); + return -1; + } + + GError *error = NULL; + gssize wr_size = g_socket_send_to(connection->socket, (GSocketAddress*) receiver_address, data, size, NULL, &error); + if(wr_size != size) { + _E("Error sending data to %s:%d - sent only %d", address, port, wr_size); + g_object_unref(receiver_address); + g_error_free(error); + return -1; + } + g_object_unref(receiver_address); + _D("Sent %d bytes", size); + return 0; +} + +void udp_connection_set_receive_cb(udp_connection_t *connection, udp_receive_cb callback) +{ + connection->receive_cb = callback; +} + +void udp_connection_destroy(udp_connection_t *connection) +{ + _connection_release_resources(connection); +} + +static gboolean _channel_ready_cb(GIOChannel *source, GIOCondition cond, gpointer data) +{ + udp_connection_t *connection = (udp_connection_t*) data; + GError *error = NULL; + GInetSocketAddress *socket_address = NULL; + gchar buffer[MESSAGE_IN_BUF_SIZE]; + + gssize size = g_socket_receive_from(connection->socket, (GSocketAddress**) &socket_address, buffer, MESSAGE_IN_BUF_SIZE, NULL, &error); + if(size < 0) { + _E("Cannot read data from socket"); + g_error_free(error); + return FALSE; + } + + if(size == MESSAGE_IN_BUF_SIZE) { + _W("Packet dropped due to its size"); + g_error_free(error); + return TRUE; + } + + if(size == 0) { + _W("No data to read"); + return TRUE; + } + + GInetAddress *address= g_inet_socket_address_get_address(socket_address); + if(!address) { + _E("Failed to obtain the address of received message"); + return TRUE; + } + + gchar *address_str = g_inet_address_to_string(address); + if(!address_str) { + _E("Failed to obtain the address in text of received message"); + g_object_unref(address); + return TRUE; + } + + int port = g_inet_socket_address_get_port(socket_address); + if(connection->receive_cb) { + connection->receive_cb(buffer, size, address_str, port); + } + + g_free(address_str); + g_object_unref(socket_address); + + return TRUE; +} + +static void _connection_release_resources(udp_connection_t *connection) +{ + if(!connection) { + return; + } + + if(connection->error) { + g_error_free(connection->error); + } + + if(connection->socket) { + g_socket_close(connection->socket, NULL); + } + + free(connection); +} |