diff options
Diffstat (limited to 'src/UdpSocket.cpp')
-rw-r--r-- | src/UdpSocket.cpp | 455 |
1 files changed, 455 insertions, 0 deletions
diff --git a/src/UdpSocket.cpp b/src/UdpSocket.cpp new file mode 100644 index 0000000..f3b2ca0 --- /dev/null +++ b/src/UdpSocket.cpp @@ -0,0 +1,455 @@ +/* + Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Her Majesty the + Queen in Right of Canada (Communications Research Center Canada) + */ +/* + This file is part of CRC-DabMux. + + CRC-DabMux is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + CRC-DabMux is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with CRC-DabMux. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "UdpSocket.h" + +#include <iostream> +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> + +#ifdef TRACE_ON +# ifndef TRACE_CLASS +# define TRACE_CLASS(class, func) cout <<"-" <<(class) <<"\t(" <<this <<")::" <<(func) <<endl +# define TRACE_STATIC(class, func) cout <<"-" <<(class) <<"\t(static)::" <<(func) <<endl +# endif +#else +# ifndef TRACE_CLASS +# define TRACE_CLASS(class, func) +# define TRACE_STATIC(class, func) +# endif +#endif + + +/// Must be call once before doing any operation on sockets +int UdpSocket::init() +{ +#ifdef _WIN32 + WSADATA wsaData; + WORD wVersionRequested = wVersionRequested = MAKEWORD( 2, 2 ); + + int res = WSAStartup( wVersionRequested, &wsaData ); + if (res) { + setInetError("Can't initialize winsock"); + return -1; + } +#endif + return 0; +} + + +/// Must be call once before leaving application +int UdpSocket::clean() +{ +#ifdef _WIN32 + int res = WSACleanup(); + if (res) { + setInetError("Can't initialize winsock"); + return -1; + } +#endif + return 0; +} + + +/** + * Two step constructor. Create must be called prior to use this + * socket. + */ +UdpSocket::UdpSocket() : + listenSocket(INVALID_SOCKET) +{ + TRACE_CLASS("UdpSocket", "UdpSocket()"); +} + + +/** + * One step constructor. + * @param port The port number on which the socket will be bind + * @param name The IP address on which the socket will be bind. + * It is used to bind the socket on a specific interface if + * the computer have many NICs. + */ +UdpSocket::UdpSocket(int port, char *name) : + listenSocket(INVALID_SOCKET) +{ + TRACE_CLASS("UdpSocket", "UdpSocket(int, char*)"); + create(port, name); +} + + +/** + * This functin set blocking mode. The socket can be blocking or not, + * depending of the parametre. By default, the socket is blocking. + * @param block If true, set the socket blocking, otherwise set non-blocking + * @return 0 if ok + * -1 if error + */ +int UdpSocket::setBlocking(bool block) +{ +#ifdef _WIN32 + unsigned long res = block ? 0 : 1; + if (ioctlsocket(listenSocket, FIONBIO, &res) != 0) { + setInetError("Can't change blocking state of socket"); + return -1; + } + return 0; +#else + int res; + if (block) + res = fcntl(listenSocket, F_SETFL, 0); + else + res = fcntl(listenSocket, F_SETFL, O_NONBLOCK); + if (res == SOCKET_ERROR) { + setInetError("Can't change blocking state of socket"); + return -1; + } + return 0; +#endif +} + + +/** + * Two step initializer. This function must be called after the constructor + * without argument as been called. + * @param port The port number on which the socket will be bind + * @param name The IP address on which the socket will be bind. + * It is used to bind the socket on a specific interface if + * the computer have many NICs. + * @return 0 if ok + * -1 if error + */ +int UdpSocket::create(int port, char *name) +{ + TRACE_CLASS("UdpSocket", "create(int, char*)"); + if (listenSocket != INVALID_SOCKET) + closesocket(listenSocket); + address.setAddress(name); + address.setPort(port); + if ((listenSocket = socket(PF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) { + setInetError("Can't create socket"); + return -1; + } + reuseopt_t reuse = 1; + if (setsockopt(listenSocket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) + == SOCKET_ERROR) { + setInetError("Can't reuse address"); + return -1; + } + if (bind(listenSocket, address.getAddress(), sizeof(sockaddr_in)) == SOCKET_ERROR) { + setInetError("Can't bind socket"); + closesocket(listenSocket); + listenSocket = INVALID_SOCKET; + return -1; + } + return 0; +} + + +/// Destructor +UdpSocket::~UdpSocket() { + TRACE_CLASS("UdpSocket", "~UdpSocket()"); + if (listenSocket != INVALID_SOCKET) + closesocket(listenSocket); +} + + +/** + * Receive an UDP packet. + * @param packet The packet that will receive data. The address will be set + * to the source address. + * @return 0 if ok, -1 if error + */ +int UdpSocket::receive(UdpPacket &packet) +{ + TRACE_CLASS("UdpSocket", "receive(UdpPacket)"); + socklen_t addrSize; + addrSize = sizeof(*packet.getAddress().getAddress()); + int ret = recvfrom(listenSocket, packet.getData(), packet.getSize() - packet.getOffset(), 0, + packet.getAddress().getAddress(), &addrSize); + if (ret == SOCKET_ERROR) { + packet.setLength(0); +#ifndef _WIN32 + if (errno == EAGAIN) + return 0; +#endif + setInetError("Can't receive UDP packet"); + return -1; + } + packet.setLength(ret); + if (ret == (long)packet.getSize()) { + packet.setSize(packet.getSize() << 1); + } + return 0; +} + + +/** + * Send an UDP packet. + * @param packet The UDP packet to be sent. It includes the data and the + * destination address + * return 0 if ok, -1 if error + */ +int UdpSocket::send(UdpPacket &packet) +{ +#ifdef DUMP + TRACE_CLASS("UdpSocket", "send(UdpPacket)"); +#endif + int ret = sendto(listenSocket, packet.getData(), packet.getLength(), 0, + packet.getAddress().getAddress(), sizeof(*packet.getAddress().getAddress())); + if (ret == SOCKET_ERROR +#ifndef _WIN32 + && errno != ECONNREFUSED +#endif + ) { + setInetError("Can't send UDP packet"); + return -1; + } + return 0; +} + + +/** + * Must be called to receive data on a multicast address. + * @param groupname The multica +st address to join. + * @return 0 if ok, -1 if error + */ +int UdpSocket::joinGroup(char* groupname) +{ + TRACE_CLASS("UdpSocket", "joinGroup(char*)"); +#ifdef _WIN32 + ip_mreq group; +#else + ip_mreqn group; +#endif + if ((group.imr_multiaddr.s_addr = inet_addr(groupname)) == INADDR_NONE) { + setInetError(groupname); + return -1; + } + if (!IN_MULTICAST(ntohl(group.imr_multiaddr.s_addr))) { + setInetError("Not a multicast address"); + return -1; + } +#ifdef _WIN32 + group.imr_interface.s_addr = 0; + if (setsockopt(listenSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&group, sizeof(group)) + == SOCKET_ERROR) { + setInetError("Can't join multicast group"); + return -1; + } +#else + group.imr_address.s_addr = htons(INADDR_ANY);; + group.imr_ifindex = 0; + if (setsockopt(listenSocket, SOL_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group)) + == SOCKET_ERROR) { + setInetError("Can't join multicast group"); + } +#endif + return 0; +} + + +/** + * Constructs an UDP packet. + * @param initSize The initial size of the data buffer + */ +UdpPacket::UdpPacket(unsigned int initSize) : + dataBuf(new char[initSize]), + length(0), + size(initSize), + offset(0) +{ + TRACE_CLASS("UdpPacket", "UdpPacket(unsigned int)"); + if (dataBuf == NULL) + size = 0; +} + + +/// Destructor +UdpPacket::~UdpPacket() +{ + TRACE_CLASS("UdpPacket", "~UdpPacket()"); + if (dataBuf != NULL) { + delete []dataBuf; + dataBuf = NULL; + } +} + + +/** + * Changes size of the data buffer size. \a Length + \a offset data will be copied + * in the new buffer. + * @warning The pointer to data will be changed + * @param newSize The new data buffer size + */ +void UdpPacket::setSize(unsigned newSize) +{ + TRACE_CLASS("UdpPacket", "setSize(unsigned)"); + char *tmp = new char[newSize]; + if (length > newSize) + length = newSize; + if (tmp) { + memcpy(tmp, dataBuf, length); + delete []dataBuf; + dataBuf = tmp; + size = newSize; + } +} + + +/** + * Give the pointer to data. It is ajusted with the \a offset. + * @warning This pointer change. when the \a size of the buffer and the \a offset change. + * @return The pointer + */ +char *UdpPacket::getData() +{ + return dataBuf + offset; +} + + +/** + * Add some data at the end of data buffer and adjust size. + * @param data Pointer to the data to add + * @param size Size in bytes of new data + */ +void UdpPacket::addData(void *data, unsigned size) +{ + if (length + size > this->size) { + setSize(this->size << 1); + } + memcpy(dataBuf + length, data, size); + length += size; +} + + +/** + * Returns the length of useful data. Data before the \a offset are ignored. + * @return The data length + */ +unsigned long UdpPacket::getLength() +{ + return length - offset; +} + + +/** + * Returns the size of the data buffer. + * @return The data buffer size + */ +unsigned long UdpPacket::getSize() +{ + return size; +} + + +/** + * Returns the offset value. + * @return The offset value + */ +unsigned long UdpPacket::getOffset() +{ + return offset; +} + + +/** + * Sets the data length value. Data before the \a offset are ignored. + * @param len The new length of data + */ +void UdpPacket::setLength(unsigned long len) +{ + length = len + offset; +} + + +/** + * Sets the data offset. Data length is ajusted to ignore data before the \a offset. + * @param val The new data offset. + */ +void UdpPacket::setOffset(unsigned long val) +{ + offset = val; + if (offset > length) + length = offset; +} + + +/** + * Returns the UDP address of the data. + * @return The UDP address + */ +InetAddress &UdpPacket::getAddress() +{ + return address; +} + +/* +WSAEINTR +WSAEBADF +WSAEACCES +WSAEFAULT +WSAEINVAL +WSAEMFILE +WSAEWOULDBLOCK +WSAEINPROGRESS +WSAEALREADY +WSAENOTSOCK +WSAEDESTADDRREQ +WSAEMSGSIZE +WSAEPROTOTYPE +WSAENOPROTOOPT +WSAEPROTONOSUPPORT +WSAESOCKTNOSUPPORT +WSAEOPNOTSUPP +WSAEPFNOSUPPORT +WSAEAFNOSUPPORT +WSAEADDRINUSE +WSAEADDRNOTAVAIL +WSAENETDOWN +WSAENETUNREACH +WSAENETRESET +WSAECONNABORTED +WSAECONNRESET +WSAENOBUFS +WSAEISCONN +WSAENOTCONN +WSAESHUTDOWN +WSAETOOMANYREFS +WSAETIMEDOUT +WSAECONNREFUSED +WSAELOOP +WSAENAMETOOLONG +WSAEHOSTDOWN +WSAEHOSTUNREACH +WSAENOTEMPTY +WSAEPROCLIM +WSAEUSERS +WSAEDQUOT +WSAESTALE +WSAEREMOTE +WSAEDISCON +WSASYSNOTREADY +WSAVERNOTSUPPORTED +WSANOTINITIALISED +*/ |