200 lines
5.3 KiB
C++
200 lines
5.3 KiB
C++
#ifndef SOCKET_H
|
|
#define SOCKET_H
|
|
|
|
#include <sys/socket.h>
|
|
#include <vector>
|
|
#include <errno.h>
|
|
#include <netinet/in.h>
|
|
|
|
#include "NetworkAddress.h"
|
|
#include "../exception.h"
|
|
|
|
class SocketUDP {
|
|
|
|
private:
|
|
|
|
static constexpr int MAX_DATAGRAM_SIZE = 64*1024;
|
|
|
|
bool bound = false;
|
|
|
|
public:
|
|
|
|
/** ctor */
|
|
SocketUDP() : handle(0) {
|
|
|
|
// create socket
|
|
handle = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (handle == -1) {throw Exception("error while creating socket");}
|
|
|
|
}
|
|
|
|
/** dtor */
|
|
~SocketUDP() {
|
|
close();
|
|
}
|
|
|
|
/**
|
|
* @brief bind the socket to receive all datagrams sent to the given (local) port
|
|
* a localPort of 0 will let the OS determine a free one.
|
|
* @param localPort the local endpoint for datagrams sent from a remote
|
|
*/
|
|
void bind(const uint16_t localPort) {
|
|
|
|
if (bound) {throw Exception("socket already bound!");}
|
|
|
|
// local endpoint AF_INET struct
|
|
struct sockaddr_in srvAddr;
|
|
memset((char*)&srvAddr, 0, sizeof(srvAddr));
|
|
srvAddr.sin_family = AF_INET;
|
|
srvAddr.sin_addr.s_addr = htonl(INADDR_ANY); // every interface
|
|
srvAddr.sin_port = htons(localPort);
|
|
|
|
// bind the socket to the given port
|
|
int ret = ::bind(handle, (struct sockaddr*) &srvAddr, sizeof(srvAddr));
|
|
if (ret < 0) {throw Exception("error while binding socket");}
|
|
|
|
// mark as bound
|
|
bound = true;
|
|
|
|
}
|
|
|
|
/** close the socket */
|
|
void close() {
|
|
|
|
// cleanup
|
|
if (handle) {
|
|
::close(handle);
|
|
handle = 0;
|
|
}
|
|
|
|
}
|
|
|
|
// /** is the socket currently closed? */
|
|
// bool isClosed() const {
|
|
// return handle == 0;
|
|
// }
|
|
|
|
// /** allow to broadcast packets using this socket? */
|
|
// void allowBroadcast(const bool allow) {
|
|
|
|
// #if defined(__GNUC__)
|
|
// const int broadcastEnable = (allow) ? (1) : (0);
|
|
// const int ret = setsockopt(handle, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable));
|
|
// if (ret < 0) { throw SocketException("error while enabling broadcast", errno); }
|
|
// #elif defined(_WIN32)
|
|
// ;
|
|
// #endif
|
|
|
|
// }
|
|
|
|
/**
|
|
* @brief send a datagram to the given destination address
|
|
* @param data the data to send
|
|
* @param len the length of the data to send (max 64k!)
|
|
* @param addr the destination address
|
|
*/
|
|
void sendDatagram(const uint8_t* data, uint32_t len, const NetworkAddress& addr) {
|
|
|
|
// sanity check
|
|
if (!bound) {throw Exception("bind() the socket first!");}
|
|
|
|
// ensure max datagram size
|
|
if (len > MAX_DATAGRAM_SIZE) {throw Exception("max datagram size is " + std::to_string(MAX_DATAGRAM_SIZE)+ " bytes!");}
|
|
|
|
// ensure correct destination address
|
|
if (!addr.isValidTargetPort()) {throw Exception("the given destination address has no valid port number");}
|
|
if (!addr.isValidTargetHost()) {throw Exception("the given destination address has no valid hostname");}
|
|
|
|
// send datagram to the given destionation
|
|
const struct sockaddr_in& sockAddr = addr.getAsSocketAddress();
|
|
const int options = 0;
|
|
const int res = sendto(handle, (const char*)data, len, options, (struct sockaddr*) &sockAddr, sizeof(sockaddr));
|
|
|
|
// check
|
|
if (res < 0) {throw Exception("error while sending datagram");}
|
|
|
|
}
|
|
|
|
/**
|
|
* @brief send a datagram to the given destination address
|
|
* @param data the data to send (max 64k!)
|
|
* @param addr the destination address
|
|
*/
|
|
void sendDatagram(const std::vector<uint8_t>& data, const NetworkAddress& addr) {
|
|
sendDatagram(data.data(), (uint32_t) data.size(), addr);
|
|
}
|
|
|
|
void sendDatagram(const std::string& data, const NetworkAddress& addr) {
|
|
sendDatagram((const uint8_t*)data.data(), data.length(), addr);
|
|
}
|
|
|
|
// /**
|
|
// * @brief send the given datagram to its internal destination address
|
|
// * @param d the datagram to send
|
|
// */
|
|
// void sendDatagram(const Datagram& d) {
|
|
// sendDatagram(d.getData(), d.getLength(), d.getAddress());
|
|
// }
|
|
|
|
|
|
// /**
|
|
// * @brief convenience function for receiveDatagram(d) which returns a new
|
|
// * datagram instead of reusing an existing one.
|
|
// * @return a newly created datagram holding the received message
|
|
// */
|
|
// DefaultDatagram receiveDatagram() {
|
|
// DefaultDatagram dd;
|
|
// receiveDatagram(dd);
|
|
// return dd;
|
|
// }
|
|
|
|
// /**
|
|
// * @brief this method will block until a new datagram is received on the bound
|
|
// * port of the underlying socket. the received data will be stored within the
|
|
// * given datagram
|
|
// * @param d the buffer to store the received datagram to
|
|
// */
|
|
// void receiveDatagram(Datagram& d) {
|
|
|
|
// const int flags = 0;
|
|
// const int maxSize = MAX_DATAGRAM_SIZE;
|
|
|
|
// // sanity check
|
|
// if (!bound) {throw SocketException("bind() the socket first!");}
|
|
|
|
// // ensure the target buffer may hold the largest possible datagram
|
|
// d.ensureSpace(maxSize);
|
|
|
|
// // the datagrams sender will be stored here
|
|
// struct sockaddr_in senderAddr;
|
|
// socklen_t senderLength = sizeof(senderAddr);
|
|
|
|
// // receive datagram from socket and store sender information
|
|
// int len = recvfrom(handle, (char*) d.getDataWriteable(), maxSize, flags, (struct sockaddr*) &senderAddr, &senderLength);
|
|
// if (len < 0) {throw SocketException("error while receiving datagram", errno);}
|
|
|
|
// // store senders network-address in the datagram
|
|
// NetworkAddress na(senderAddr);
|
|
// d.setAddress(na);
|
|
|
|
// // inform buffer about the actual size of the datagram
|
|
// d.setLength( (uint32_t) len );
|
|
|
|
// }
|
|
|
|
|
|
|
|
private:
|
|
|
|
/** the socket handle */
|
|
int handle;
|
|
|
|
/** the local address the socket is bound to */
|
|
NetworkAddress localAddress;
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif // SOCKET_H
|