summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTobias Frust <tobiasfrust@Tobiass-MBP.fritz.box>2016-06-29 19:16:11 +0200
committerTobias Frust <tobiasfrust@Tobiass-MBP.fritz.box>2016-06-29 19:16:11 +0200
commit38b0d8ad024d11fa643934b0c56690b0e57c3e35 (patch)
treef5d53ff1d5c4d1a8efafd5ccdc40d799d7dcc9d5
parent5ecc4dde3731724f28d4629f8a563f30bb260617 (diff)
downloadods-38b0d8ad024d11fa643934b0c56690b0e57c3e35.tar.gz
ods-38b0d8ad024d11fa643934b0c56690b0e57c3e35.tar.bz2
ods-38b0d8ad024d11fa643934b0c56690b0e57c3e35.tar.xz
ods-38b0d8ad024d11fa643934b0c56690b0e57c3e35.zip
added UDP-Client and Server classes for data transfer via Ethernet
-rw-r--r--src/CMakeLists.txt5
-rw-r--r--src/UDPClient/UDPClient.cpp130
-rw-r--r--src/UDPClient/UDPClient.h37
-rw-r--r--src/UDPServer/UDPServer.cpp188
-rw-r--r--src/UDPServer/UDPServer.h39
-rw-r--r--src/main.cpp22
6 files changed, 414 insertions, 7 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 4c09b2e..746b901 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -31,8 +31,9 @@ include_directories(
${BOOST_INCLUDE_DIRS}
)
-file(GLOB SOURCES
- "*.cpp"
+set(SOURCES
+ "${CMAKE_SOURCE_DIR}/UDPClient/UDPClient.cpp"
+ "${CMAKE_SOURCE_DIR}/main.cpp"
)
add_executable(onlineDetectorSimulator ${SOURCES})
diff --git a/src/UDPClient/UDPClient.cpp b/src/UDPClient/UDPClient.cpp
index 640555f..75e81e1 100644
--- a/src/UDPClient/UDPClient.cpp
+++ b/src/UDPClient/UDPClient.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Tobias Frust
+ * http://linux.m2osw.com/c-implementation-udp-clientserver
*
* UDPSender.cpp
*
@@ -7,6 +7,134 @@
* Author: Tobias Frust
*/
+ // ========================= CLIENT =========================
+ /** \brief Initialize a UDP client object.
+ *
+ * This function initializes the UDP client object using the address and the
+ * port as specified.
+ *
+ * The port is expected to be a host side port number (i.e. 59200).
+ *
+ * The \p addr parameter is a textual address. It may be an IPv4 or IPv6
+ * address and it can represent a host name or an address defined with
+ * just numbers. If the address cannot be resolved then an error occurs
+ * and constructor throws.
+ *
+ * \note
+ * The socket is open in this process. If you fork() or exec() then the
+ * socket will be closed by the operating system.
+ *
+ * \warning
+ * We only make use of the first address found by getaddrinfo(). All
+ * the other addresses are ignored.
+ *
+ * \exception udp_client_server_runtime_error
+ * The server could not be initialized properly. Either the address cannot be
+ * resolved, the port is incompatible or not available, or the socket could
+ * not be created.
+ *
+ * \param[in] addr The address to convert to a numeric IP.
+ * \param[in] port The port number.
+ */
+#include "UDPClient.h"
+#include <string.h>
+#include <unistd.h>
+
+#ifndef SOCK_CLOEXEC
+#define SOCK_CLOEXEC 0
+#endif
+
+ UDPClient::UDPClient(const std::string& addr, int port)
+ : f_port(port)
+ , f_addr(addr){
+ char decimal_port[16];
+ snprintf(decimal_port, sizeof(decimal_port), "%d", f_port);
+ decimal_port[sizeof(decimal_port) / sizeof(decimal_port[0]) - 1] = '\0';
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ int r(getaddrinfo(addr.c_str(), decimal_port, &hints, &f_addrinfo));
+ if(r != 0 || f_addrinfo == NULL)
+ {
+ throw udp_client_server_runtime_error(("invalid address or port: \"" + addr + ":" + decimal_port + "\"").c_str());
+ }
+ f_socket = socket(f_addrinfo->ai_family, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
+ if(f_socket == -1)
+ {
+ freeaddrinfo(f_addrinfo);
+ throw udp_client_server_runtime_error(("could not create socket for: \"" + addr + ":" + decimal_port + "\"").c_str());
+ }
+ }
+
+ /** \brief Clean up the UDP client object.
+ *
+ * This function frees the address information structure and close the socket
+ * before returning.
+ */
+ UDPClient::~UDPClient()
+ {
+ freeaddrinfo(f_addrinfo);
+ close(f_socket);
+ }
+
+ /** \brief Retrieve a copy of the socket identifier.
+ *
+ * This function return the socket identifier as returned by the socket()
+ * function. This can be used to change some flags.
+ *
+ * \return The socket used by this UDP client.
+ */
+ int UDPClient::get_socket() const {
+ return f_socket;
+ }
+
+ /** \brief Retrieve the port used by this UDP client.
+ *
+ * This function returns the port used by this UDP client. The port is
+ * defined as an integer, host side.
+ *
+ * \return The port as expected in a host integer.
+ */
+ int UDPClient::get_port() const {
+ return f_port;
+ }
+
+ /** \brief Retrieve a copy of the address.
+ *
+ * This function returns a copy of the address as it was specified in the
+ * constructor. This does not return a canonalized version of the address.
+ *
+ * The address cannot be modified. If you need to send data on a different
+ * address, create a new UDP client.
+ *
+ * \return A string with a copy of the constructor input address.
+ */
+ std::string UDPClient::get_addr() const {
+ return f_addr;
+ }
+
+ /** \brief Send a message through this UDP client.
+ *
+ * This function sends \p msg through the UDP client socket. The function
+ * cannot be used to change the destination as it was defined when creating
+ * the udp_client object.
+ *
+ * The size must be small enough for the message to fit. In most cases we
+ * use these in Snap! to send very small signals (i.e. 4 bytes commands.)
+ * Any data we would want to share remains in the Cassandra database so
+ * that way we can avoid losing it because of a UDP message.
+ *
+ * \param[in] msg The message to send.
+ * \param[in] size The number of bytes representing this message.
+ *
+ * \return -1 if an error occurs, otherwise the number of bytes sent. errno
+ * is set accordingly on error.
+ */
+ int UDPClient::send(const char *msg, size_t size){
+ return sendto(f_socket, msg, size, 0, f_addrinfo->ai_addr, f_addrinfo->ai_addrlen);
+ }
diff --git a/src/UDPClient/UDPClient.h b/src/UDPClient/UDPClient.h
index a962fe9..f6cf0d6 100644
--- a/src/UDPClient/UDPClient.h
+++ b/src/UDPClient/UDPClient.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Tobias Frust
+ * http://linux.m2osw.com/c-implementation-udp-clientserver
*
* UDPSender.h
*
@@ -7,9 +7,38 @@
* Author: Tobias Frust
*/
-#ifndef UDPSENDER_H_
-#define UDPSENDER_H_
+#ifndef UDPCLIENT_H_
+#define UDPCLIENT_H_
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <stdexcept>
+#include <cstring>
+class udp_client_server_runtime_error : public std::runtime_error
+{
+public:
+ udp_client_server_runtime_error(const char *w) : std::runtime_error(w) {}
+};
-#endif /* UDPSENDER_H_ */
+
+class UDPClient {
+public:
+ UDPClient(const std::string& addr, int port);
+ ~UDPClient();
+
+ int get_socket() const;
+ int get_port() const;
+ std::string get_addr() const;
+
+ int send(const char *msg, size_t size);
+
+private:
+ int f_socket;
+ int f_port;
+ std::string f_addr;
+ struct addrinfo * f_addrinfo;
+};
+
+#endif /* UDPCLIENT_H_ */
diff --git a/src/UDPServer/UDPServer.cpp b/src/UDPServer/UDPServer.cpp
new file mode 100644
index 0000000..cd36647
--- /dev/null
+++ b/src/UDPServer/UDPServer.cpp
@@ -0,0 +1,188 @@
+/*
+ * http://linux.m2osw.com/c-implementation-udp-clientserver
+ *
+ * UDPServer.cpp
+ *
+ * Created on: 29.06.2016
+ * Author: Tobias Frust
+ */
+
+// ========================= SEVER =========================
+
+/** \brief Initialize a UDP server object.
+ *
+ * This function initializes a UDP server object making it ready to
+ * receive messages.
+ *
+ * The server address and port are specified in the constructor so
+ * if you need to receive messages from several different addresses
+ * and/or port, you'll have to create a server for each.
+ *
+ * The address is a string and it can represent an IPv4 or IPv6
+ * address.
+ *
+ * Note that this function calls connect() to connect the socket
+ * to the specified address. To accept data on different UDP addresses
+ * and ports, multiple UDP servers must be created.
+ *
+ * \note
+ * The socket is open in this process. If you fork() or exec() then the
+ * socket will be closed by the operating system.
+ *
+ * \warning
+ * We only make use of the first address found by getaddrinfo(). All
+ * the other addresses are ignored.
+ *
+ * \exception udp_client_server_runtime_error
+ * The udp_client_server_runtime_error exception is raised when the address
+ * and port combinaison cannot be resolved or if the socket cannot be
+ * opened.
+ *
+ * \param[in] addr The address we receive on.
+ * \param[in] port The port we receive from.
+ */
+UDPServer::UDPServer(const std::string& addr, int port)
+ : f_port(port)
+ , f_addr(addr)
+{
+ char decimal_port[16];
+ snprintf(decimal_port, sizeof(decimal_port), "%d", f_port);
+ decimal_port[sizeof(decimal_port) / sizeof(decimal_port[0]) - 1] = '\0';
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ int r(getaddrinfo(addr.c_str(), decimal_port, &hints, &f_addrinfo));
+ if(r != 0 || f_addrinfo == NULL)
+ {
+ throw udp_client_server_runtime_error(("invalid address or port for UDP socket: \"" + addr + ":" + decimal_port + "\"").c_str());
+ }
+ f_socket = socket(f_addrinfo->ai_family, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
+ if(f_socket == -1)
+ {
+ freeaddrinfo(f_addrinfo);
+ throw udp_client_server_runtime_error(("could not create UDP socket for: \"" + addr + ":" + decimal_port + "\"").c_str());
+ }
+ r = bind(f_socket, f_addrinfo->ai_addr, f_addrinfo->ai_addrlen);
+ if(r != 0)
+ {
+ freeaddrinfo(f_addrinfo);
+ close(f_socket);
+ throw udp_client_server_runtime_error(("could not bind UDP socket with: \"" + addr + ":" + decimal_port + "\"").c_str());
+ }
+}
+
+/** \brief Clean up the UDP server.
+ *
+ * This function frees the address info structures and close the socket.
+ */
+UDPServer::~UDPServer()
+{
+ freeaddrinfo(f_addrinfo);
+ close(f_socket);
+}
+
+/** \brief The socket used by this UDP server.
+ *
+ * This function returns the socket identifier. It can be useful if you are
+ * doing a select() on many sockets.
+ *
+ * \return The socket of this UDP server.
+ */
+int UDPServer::get_socket() const
+{
+ return f_socket;
+}
+
+/** \brief The port used by this UDP server.
+ *
+ * This function returns the port attached to the UDP server. It is a copy
+ * of the port specified in the constructor.
+ *
+ * \return The port of the UDP server.
+ */
+int UDPServer::get_port() const
+{
+ return f_port;
+}
+
+/** \brief Return the address of this UDP server.
+ *
+ * This function returns a verbatim copy of the address as passed to the
+ * constructor of the UDP server (i.e. it does not return the canonalized
+ * version of the address.)
+ *
+ * \return The address as passed to the constructor.
+ */
+std::string UDPServer::get_addr() const
+{
+ return f_addr;
+}
+
+/** \brief Wait on a message.
+ *
+ * This function waits until a message is received on this UDP server.
+ * There are no means to return from this function except by receiving
+ * a message. Remember that UDP does not have a connect state so whether
+ * another process quits does not change the status of this UDP server
+ * and thus it continues to wait forever.
+ *
+ * Note that you may change the type of socket by making it non-blocking
+ * (use the get_socket() to retrieve the socket identifier) in which
+ * case this function will not block if no message is available. Instead
+ * it returns immediately.
+ *
+ * \param[in] msg The buffer where the message is saved.
+ * \param[in] max_size The maximum size the message (i.e. size of the \p msg buffer.)
+ *
+ * \return The number of bytes read or -1 if an error occurs.
+ */
+int UDPServer::recv(char *msg, size_t max_size)
+{
+ return ::recv(f_socket, msg, max_size, 0);
+}
+
+/** \brief Wait for data to come in.
+ *
+ * This function waits for a given amount of time for data to come in. If
+ * no data comes in after max_wait_ms, the function returns with -1 and
+ * errno set to EAGAIN.
+ *
+ * The socket is expected to be a blocking socket (the default,) although
+ * it is possible to setup the socket as non-blocking if necessary for
+ * some other reason.
+ *
+ * This function blocks for a maximum amount of time as defined by
+ * max_wait_ms. It may return sooner with an error or a message.
+ *
+ * \param[in] msg The buffer where the message will be saved.
+ * \param[in] max_size The size of the \p msg buffer in bytes.
+ * \param[in] max_wait_ms The maximum number of milliseconds to wait for a message.
+ *
+ * \return -1 if an error occurs or the function timed out, the number of bytes received otherwise.
+ */
+int UDPServer::timed_recv(char *msg, size_t max_size, int max_wait_ms)
+{
+ fd_set s;
+ FD_ZERO(&s);
+ FD_SET(f_socket, &s);
+ struct timeval timeout;
+ timeout.tv_sec = max_wait_ms / 1000;
+ timeout.tv_usec = (max_wait_ms % 1000) * 1000;
+ int retval = select(f_socket + 1, &s, &s, &s, &timeout);
+ if(retval == -1)
+ {
+ // select() set errno accordingly
+ return -1;
+ }
+ if(retval > 0)
+ {
+ // our socket has data
+ return ::recv(f_socket, msg, max_size, 0);
+ }
+
+ // our socket has no data
+ errno = EAGAIN;
+ return -1;
+}
diff --git a/src/UDPServer/UDPServer.h b/src/UDPServer/UDPServer.h
new file mode 100644
index 0000000..7eef796
--- /dev/null
+++ b/src/UDPServer/UDPServer.h
@@ -0,0 +1,39 @@
+/*
+ * http://linux.m2osw.com/c-implementation-udp-clientserver
+ *
+ * UDPServer.h
+ *
+ * Created on: 29.06.2016
+ * Author: Tobias Frust
+ */
+
+#ifndef UDPSERVER_H_
+#define UDPSERVER_H_
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <stdexcept>
+#include <string>
+
+class UDPServer
+{
+public:
+ UDPServer(const std::string& addr, int port);
+ ~UDPServer();
+
+ int get_socket() const;
+ int get_port() const;
+ std::string get_addr() const;
+
+ int recv(char *msg, size_t max_size);
+ int timed_recv(char *msg, size_t max_size, int max_wait_ms);
+
+private:
+ int f_socket;
+ int f_port;
+ std::string f_addr;
+ struct addrinfo * f_addrinfo;
+};
+
+#endif /* UDPSERVER_H_ */
diff --git a/src/main.cpp b/src/main.cpp
index e69de29..2ff08b2 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -0,0 +1,22 @@
+#include "UDPClient/UDPClient.h"
+
+#include <iostream>
+#include <string>
+
+int main (int argc, char *argv[]){
+
+ std::cout << "Sending UDP packages: " << std::endl;
+
+ std::string address = "192.168.178.33";
+ int port = 1234;
+
+ UDPClient client = UDPClient(address, port);
+
+ std::string msg = "test";
+ std::size_t length{msg.size()};
+
+ client.send(msg.c_str(), length);
+
+ return 0;
+
+}