Added implementation of UDP connection 29/189129/11
authorKrzysztof Wieclaw <k.wieclaw@samsung.com>
Thu, 13 Sep 2018 14:41:27 +0000 (16:41 +0200)
committerKrzysztof Wieclaw <k.wieclaw@samsung.com>
Fri, 21 Sep 2018 11:13:02 +0000 (13:13 +0200)
Change-Id: I171df47023f7642957146522264a9668efdc6619
Signed-off-by: Krzysztof Wieclaw <k.wieclaw@samsung.com>
CMakeLists.txt
inc/udp_connection.h
src/udp_connection.c [new file with mode: 0644]

index fea0e79..9e5aac8 100644 (file)
@@ -29,6 +29,7 @@ SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -pie")
 INCLUDE_DIRECTORIES(${PROJECT_ROOT_DIR}/inc)
 
 ADD_EXECUTABLE(${PROJECT_NAME}
+       ${PROJECT_ROOT_DIR}/src/udp_connection.c
        ${PROJECT_ROOT_DIR}/src/config.c
        ${PROJECT_ROOT_DIR}/src/app.c
        ${PROJECT_ROOT_DIR}/src/log.c
index e2d41dd..dd8ff98 100644 (file)
@@ -40,22 +40,15 @@ typedef struct udp_connection udp_connection_t;
 udp_connection_t *udp_connection_create(int port);
 
 /**
- * @brief Sets the receiver of sent data.
- * @param[in] connection UDP connection object.
- * @param[in] address IP address of receiver.
- * @param[in] port Port of receiver to send data on.
- * @return 0 on success, -1 otherwise.
- */
-int udp_connection_set_receiver(udp_connection_t *connection, const char *address, int port);
-
-/**
- * @brief Sends data to set receiver.
+ * @brief Sends data on given address.
  * @param[in] connection UDP connection object.
  * @param[in] data Data to be sent.
  * @param[in] size Size in bytes of data pointed by data pointer.
+ * @param[in] address IP address of receiver.
+ * @param[in] port Port of receiver.
  * @return 0 on success, -1 otherwise.
  */
-int udp_connection_send(udp_connection_t *connection, const char *data, unsigned int size);
+int udp_connection_send(udp_connection_t *connection, const char *data, unsigned short int size, const char *address, int port);
 
 /**
  * @brief Sets callback for receiving data.
diff --git a/src/udp_connection.c b/src/udp_connection.c
new file mode 100644 (file)
index 0000000..381f2d4
--- /dev/null
@@ -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);
+}