From 7f1b28bd6fc4ed4542ec7787b8c250929dbec2b4 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 4 Feb 2014 18:20:40 +0100 Subject: [PATCH] Updated/added Socket::UDPConnection documentation, added support for binding to a port and for receiving data. --- lib/socket.cpp | 97 +++++++++++++++++++++++++++++++++++++++++++++----- lib/socket.h | 28 ++++++++------- 2 files changed, 105 insertions(+), 20 deletions(-) diff --git a/lib/socket.cpp b/lib/socket.cpp index 6fb08f61..0b4dac6e 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -899,7 +899,9 @@ int Socket::Server::getSocket(){ } /// Create a new UDP Socket. -/// \param nonblock Whether the socket should be nonblocking. +/// Will attempt to create an IPv6 UDP socket, on fail try a IPV4 UDP socket. +/// If both fail, prints an DLVL_FAIL debug message. +/// \param nonblock Whether the socket should be nonblocking. Socket::UDPConnection::UDPConnection(bool nonblock){ sock = socket(AF_INET6, SOCK_DGRAM, 0); if (sock == -1){ @@ -912,11 +914,16 @@ Socket::UDPConnection::UDPConnection(bool nonblock){ down = 0; destAddr = 0; destAddr_size = 0; + data = 0; + data_size = 0; + data_len = 0; if (nonblock){ setBlocking(!nonblock); } } //Socket::UDPConnection UDP Contructor +/// Copies a UDP socket, re-allocating local copies of any needed structures. +/// The data/data_size/data_len variables are *not* copied over. Socket::UDPConnection::UDPConnection(const UDPConnection & o){ sock = socket(AF_INET6, SOCK_DGRAM, 0); if (sock == -1){ @@ -936,8 +943,12 @@ Socket::UDPConnection::UDPConnection(const UDPConnection & o){ destAddr = 0; destAddr_size = 0; } + data = 0; + data_size = 0; + data_len = 0; } +/// Closes the UDP socket, cleans up any memory allocated by the socket. Socket::UDPConnection::~UDPConnection(){ if (sock != -1){ errno = EINTR; @@ -949,9 +960,19 @@ Socket::UDPConnection::~UDPConnection(){ free(destAddr); destAddr = 0; } + if (data){ + free(data); + data = 0; + } } +/// Stores the properties of the reiving end of this UDP socket. +/// This will be the receiving end for all SendNow calls. void Socket::UDPConnection::SetDestination(std::string destIp, uint32_t port){ + if (destAddr){ + free(destAddr); + destAddr = 0; + } destAddr = malloc(sizeof(struct sockaddr_in6)); if (destAddr){ destAddr_size = sizeof(struct sockaddr_in6); @@ -979,27 +1000,87 @@ void Socket::UDPConnection::SetDestination(std::string destIp, uint32_t port){ DEBUG_MSG(DLVL_FAIL, "Could not set destination for UDP socket: %s:%d", destIp.c_str(), port); }//Socket::UDPConnection SetDestination +/// Sets the socket to be blocking if the parameters is true. +/// Sets the socket to be non-blocking otherwise. void Socket::UDPConnection::setBlocking(bool blocking){ if (sock >= 0){ setFDBlocking(sock, blocking); } } -void Socket::UDPConnection::SendNow(const std::string & data){ - SendNow(data.c_str(), data.size()); +/// Sends a UDP datagram using the buffer sdata. +/// This function simply calls SendNow(const char*, size_t) +void Socket::UDPConnection::SendNow(const std::string & sdata){ + SendNow(sdata.c_str(), sdata.size()); } -void Socket::UDPConnection::SendNow(const char* data){ - int len = strlen(data); - SendNow(data, len); +/// Sends a UDP datagram using the buffer sdata. +/// sdata is required to be NULL-terminated. +/// This function simply calls SendNow(const char*, size_t) +void Socket::UDPConnection::SendNow(const char* sdata){ + int len = strlen(sdata); + SendNow(sdata, len); } -void Socket::UDPConnection::SendNow(const char * data, size_t len){ +/// Sends a UDP datagram using the buffer sdata of length len. +/// Does not do anything if len < 1. +/// Prints an DLVL_FAIL level debug message if sending failed. +void Socket::UDPConnection::SendNow(const char * sdata, size_t len){ if (len < 1){return;} - int r = sendto(sock, data, len, 0, (sockaddr*)destAddr, destAddr_size); + int r = sendto(sock, sdata, len, 0, (sockaddr*)destAddr, destAddr_size); if (r > 0){ up += r; }else{ DEBUG_MSG(DLVL_FAIL, "Could not send UDP data through %d: %s", sock, strerror(errno)); } } + +/// Bind to a port number, returning the bound port. +/// Attempts to bind over IPv6 first. +/// If it fails, attempts to bind over IPv4. +/// If that fails too, gives up and returns zero. +/// Prints a debug message at DLVL_FAIL level if binding failed. +/// \return Actually bound port number, or zero on error. +int Socket::UDPConnection::bind(int port){ + struct sockaddr_in6 s6; + s6.sin6_family = AF_INET6; + s6.sin6_addr = in6addr_any; + if (port){s6.sin6_port = htons(port);} + int r = ::bind(sock, (sockaddr*)&s6, sizeof(s6)); + if (r == 0){return ntohs(s6.sin6_port);} + + struct sockaddr_in s4; + s4.sin_family = AF_INET; + s4.sin_addr.s_addr = INADDR_ANY; + if (port){s4.sin_port = htons(port);} + r = ::bind(sock, (sockaddr*)&s4, sizeof(s4)); + if (r == 0){return ntohs(s4.sin_port);} + + DEBUG_MSG(DLVL_FAIL, "Could not bind UDP socket to port %d", port); + return 0; +} + +/// Attempt to receive a UDP packet. +/// This will automatically allocate or resize the internal data buffer if needed. +/// If a packet is received, it will be placed in the "data" member, with it's length in "data_len". +/// \return True if a packet was received, false otherwise. +bool Socket::UDPConnection::Receive(){ + int r = recvfrom(sock, data, data_size, MSG_PEEK | MSG_TRUNC, 0, 0); + if (data_size < (unsigned int)r){ + data = (char*)realloc(data, r); + if (data){ + data_size = r; + }else{ + data_size = 0; + } + } + r = recvfrom(sock, data, data_size, 0, 0, 0); + if (r > 0){ + down += r; + data_len = r; + return true; + }else{ + data_len = 0; + return false; + } +} diff --git a/lib/socket.h b/lib/socket.h index 474262b7..d01175e8 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -123,20 +123,24 @@ namespace Socket { private: int sock; ///< Internally saved socket number. std::string remotehost;///< Stores remote host address - void * destAddr; - unsigned int destAddr_size; - unsigned int up; - unsigned int down; + void * destAddr;///< Destination address pointer. + unsigned int destAddr_size;///< Size of the destination address pointer. + unsigned int up;///< Amount of bytes transferred up. + unsigned int down;///< Amount of bytes transferred down. + unsigned int data_size;///< The size in bytes of the allocated space in the data pointer. public: - UDPConnection(const UDPConnection & o); ///< Copy constructor - UDPConnection(bool nonblock = false); ///< Create new UDP socket + char * data;///< Holds the last received packet. + unsigned int data_len; ///< The size in bytes of the last received packet. + UDPConnection(const UDPConnection & o); + UDPConnection(bool nonblock = false); ~UDPConnection(); - void setBlocking(bool blocking); ///< Set this socket to be blocking (true) or nonblocking (false). - void SetDestination(std::string hostname, uint32_t port); ///< Sets the hostname and port destination for this UDP socket. - void Close();///< Closes the local socket - void SendNow(const std::string & data); ///< Will not buffer anything but always send right away. Blocks. - void SendNow(const char * data); ///< Will not buffer anything but always send right away. Blocks. - void SendNow(const char * data, size_t len); ///< Will not buffer anything but always send right away. Blocks. + int bind(int port); + void setBlocking(bool blocking); + void SetDestination(std::string hostname, uint32_t port); + bool Receive(); + void SendNow(const std::string & data); + void SendNow(const char * data); + void SendNow(const char * data, size_t len); }; }