Skip to content
UDP_Protocol.cpp 7.56 KiB
Newer Older
#include <iostream>
#include <string>
#include <sys/socket.h>
Valerio Pastore's avatar
Valerio Pastore committed
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <regex>
#include <limits>
#include <algorithm>
Valerio Pastore's avatar
Valerio Pastore committed
#include <ctime>
#include <iomanip>
Valerio Pastore's avatar
Valerio Pastore committed

#include <UDP_Protocol.h>

Valerio Pastore's avatar
Valerio Pastore committed
using namespace inaf::oasbo::Receivers;
Valerio Pastore's avatar
Valerio Pastore committed

astri's avatar
astri committed
UDPProtocol::UDPProtocol() :
Valerio Pastore's avatar
Valerio Pastore committed
		UDPProtocol(std::string("localhost"), 9003) {
astri's avatar
astri committed
UDPProtocol::UDPProtocol(std::string ip, int port) :
		srv_sock(-1), ip(ip), port(port) {
	memset(&cliaddr, 0, sizeof(cliaddr));
	memset(&srvaddr, 0, sizeof(srvaddr));
	// Filling server information
	srvaddr.sin_family = AF_INET; // IPv4
	srvaddr.sin_addr.s_addr = inet_addr(ip.c_str());
	srvaddr.sin_port = htons(this->port);
Valerio Pastore's avatar
Valerio Pastore committed

UDPProtocol::~UDPProtocol() {
	closeConnectionToClient();
}

Valerio Pastore's avatar
Valerio Pastore committed
int UDPProtocol::receiveAtLeastNbytes(uint8_t *buff, int n_bytes,
		int max_size) {
	int bytercv = 0;
	socklen_t len = sizeof(cliaddr);
Valerio Pastore's avatar
Valerio Pastore committed
	while (bytercv < n_bytes && this->checkIncomingPackets) {
		int rcv = recvfrom(srv_sock, &buff[bytercv], max_size + 1, // +1 to recognized if there are more bytes than expected
Valerio Pastore's avatar
Valerio Pastore committed
		0, (struct sockaddr*) &cliaddr, &len);

		bytercv += rcv;
Valerio Pastore's avatar
Valerio Pastore committed
		if (rcv < 0) {
			return -1;
Valerio Pastore's avatar
.  
Valerio Pastore committed
	if (!this->checkIncomingPackets) { // interrupted by closeConnectionToClient before receiving all the N bytes.
Valerio Pastore's avatar
Valerio Pastore committed
		return -1;
	}
	return bytercv;
}

Valerio Pastore's avatar
.  
Valerio Pastore committed
int UDPProtocol::receiveFromClient(Packets::BasePacket &pack) {
Valerio Pastore's avatar
Valerio Pastore committed
	bool headerFlag = false; // tells if the header has been received
Valerio Pastore's avatar
Valerio Pastore committed
	uint headerSize = pack.getHeaderSize();
	uint packSize = pack.getPacketStructureByteSize();
	uint tailSize = pack.getTailSize();
Valerio Pastore's avatar
Valerio Pastore committed
	uint payloadSize = 0;
	uint totPacketSize = 0;
	ssize_t to_be_received = 0;
Valerio Pastore's avatar
Valerio Pastore committed
	uint8_t *buff = new uint8_t[(packSize + headerSize) * headerSize]; // * headerSize to avoid overflow
	int tot_byte_rcv = 0;
Valerio Pastore's avatar
Valerio Pastore committed
	while (this->checkIncomingPackets) {
astri's avatar
astri committed
		while (!headerFlag) { // until the header has not received
Valerio Pastore's avatar
.  
Valerio Pastore committed
			int rcv = receiveAtLeastNbytes(buff, headerSize, packSize); // receive at least headerSize byte, maximum packSize for each udp rcv.
Valerio Pastore's avatar
Valerio Pastore committed
			if (rcv < 0) {
Valerio Pastore's avatar
.  
Valerio Pastore committed
				delete[] buff;
Valerio Pastore's avatar
Valerio Pastore committed
				return -1;
astri's avatar
astri committed
			tot_byte_rcv += rcv;
Valerio Pastore's avatar
Valerio Pastore committed
			pack.copyToMemory(buff, headerSize); //copy the header into packet to be able to read it.
			if (!pack.hasRecognizedHeader()) { // reset buffer and exit
Valerio Pastore's avatar
Valerio Pastore committed
				resetPacket(pack, headerSize);
Valerio Pastore's avatar
.  
Valerio Pastore committed
				delete[] buff;
Valerio Pastore's avatar
Valerio Pastore committed
				return -1;
			}
			headerFlag = true;
Valerio Pastore's avatar
Valerio Pastore committed
			payloadSize = pack.getPayloadSize();
			totPacketSize = headerSize + payloadSize + tailSize;
			to_be_received = totPacketSize - tot_byte_rcv; // Calculate how much is still left to read
		}

Valerio Pastore's avatar
Valerio Pastore committed
		if (to_be_received == 0) { // whole packet has been received, we can return.
Valerio Pastore's avatar
.  
Valerio Pastore committed
			pack.copyToMemory(&buff[headerSize], tot_byte_rcv - headerSize,
					headerSize); // copy the buffer into packet except already copied header
Valerio Pastore's avatar
.  
Valerio Pastore committed
			delete[] buff;
			return tot_byte_rcv;
		}
Valerio Pastore's avatar
.  
Valerio Pastore committed
		if (to_be_received < 0) {// error,received more bytes then expected, we can return.
Valerio Pastore's avatar
Valerio Pastore committed
			resetPacket(pack, tot_byte_rcv);
Valerio Pastore's avatar
.  
Valerio Pastore committed
			delete[] buff;
Valerio Pastore's avatar
Valerio Pastore committed
			return -1;
Valerio Pastore's avatar
Valerio Pastore committed

		// go ahead until the rest of the packet received or connection terminates or another header is received.
		uint8_t *tmp_buff = new uint8_t[packSize + headerSize]; // maximum receivable in receiveAtLeastNBytes
		int rcv = receiveAtLeastNbytes(tmp_buff, headerSize, packSize);
astri's avatar
astri committed
		if (rcv == -1) {
Valerio Pastore's avatar
.  
Valerio Pastore committed
			delete[] buff;
			delete[] tmp_buff;
Valerio Pastore's avatar
Valerio Pastore committed
			resetPacket(pack, tot_byte_rcv);
			return -1;
		}
Valerio Pastore's avatar
Valerio Pastore committed

		// copy the content of the buffer into a vector to check if it is a header. If it is,
Valerio Pastore's avatar
.  
Valerio Pastore committed
		// save it and discard previous data, otherwise append to current buff.
astri's avatar
astri committed
		std::vector<uint8_t> vec;
		std::copy(tmp_buff, tmp_buff + headerSize, std::back_inserter(vec));
		if (pack.isRecognizedHeader(vec)) { //another header received, save it and discard previous data.
			std::memset(buff, 0, tot_byte_rcv);
			std::memcpy(buff, tmp_buff, rcv);
			headerFlag = true;
Valerio Pastore's avatar
Valerio Pastore committed
			pack.copyToMemory(buff, headerSize);
Valerio Pastore's avatar
Valerio Pastore committed
			payloadSize = pack.getPayloadSize();
			totPacketSize = headerSize + payloadSize + tailSize;
			to_be_received = totPacketSize - rcv;
astri's avatar
astri committed
			tot_byte_rcv = rcv;
		} else { // append to current buff
			tot_byte_rcv += rcv;
			std::memcpy(&buff[tot_byte_rcv - rcv], tmp_buff, rcv);
			to_be_received -= rcv;
Valerio Pastore's avatar
.  
Valerio Pastore committed
		delete[] tmp_buff;
Valerio Pastore's avatar
.  
Valerio Pastore committed
	return -1; // connection interrupted by closeConnectionToClient
Valerio Pastore's avatar
Valerio Pastore committed
}

int UDPProtocol::connectToClient() {
	// Creating socket file descriptor
	if ((srv_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
Valerio Pastore's avatar
Valerio Pastore committed
		time_t now = time(nullptr);
		std::cerr << "[" << std::put_time(localtime(&now), "%Y-%m-%d %H:%M:%S")
				<< "]\t[UDP Receiver]\t" << "socket creation failed"
				<< std::endl;
Valerio Pastore's avatar
Valerio Pastore committed
		return -1;
	}

	// Set timeout to the socket
	struct timeval tv;
Valerio Pastore's avatar
Valerio Pastore committed
	tv.tv_sec = 1;
Valerio Pastore's avatar
Valerio Pastore committed
	tv.tv_usec = 0;
Valerio Pastore's avatar
Valerio Pastore committed
	if (setsockopt(srv_sock, SOL_SOCKET, SO_RCVTIMEO, (const char*) &tv,
			sizeof tv) < 0) {
		time_t now = time(nullptr);
		std::cerr << "[" << std::put_time(localtime(&now), "%Y-%m-%d %H:%M:%S")
Valerio Pastore's avatar
.  
Valerio Pastore committed
				<< "]\t[UDP Receiver]\t" << "Error setting timeout to socket"
				<< std::endl;
Valerio Pastore's avatar
Valerio Pastore committed
		return -1;
	}
Valerio Pastore's avatar
Valerio Pastore committed

	// Bind the socket with the server address
Valerio Pastore's avatar
Valerio Pastore committed
	if (bind(srv_sock, (const struct sockaddr*) &srvaddr, sizeof(srvaddr))
			< 0) {
		time_t now = time(nullptr);
		std::cerr << "[" << std::put_time(localtime(&now), "%Y-%m-%d %H:%M:%S")
				<< "]\t[UDP Receiver]\t" << "bind failed" << std::endl;
Valerio Pastore's avatar
Valerio Pastore committed
		return -1;
	}

	return srv_sock;
int UDPProtocol::closeConnectionToClient() {
Valerio Pastore's avatar
Valerio Pastore committed
	checkIncomingPackets = false;
	if (srv_sock != -1) {
		::close(srv_sock);
		srv_sock = -1;
	return 1;
}
bool UDPProtocol::isConnectedToClient() const {
	return srv_sock != -1;
}
Valerio Pastore's avatar
.  
Valerio Pastore committed
void UDPProtocol::resetPacket(Packets::BasePacket &pack, int bytes) {
	uint8_t *buff = new uint8_t[bytes];
	std::memset(buff, 0, bytes);
astri's avatar
astri committed
	int toBeReset = std::min(
			static_cast<int>(pack.getPacketStructureByteSize()), bytes);
Valerio Pastore's avatar
Valerio Pastore committed
	pack.copyToMemory(buff, toBeReset);
Valerio Pastore's avatar
.  
Valerio Pastore committed
	delete[] buff;
Valerio Pastore's avatar
Valerio Pastore committed
std::string UDPProtocol::getHost() {
	return ip + ":" + std::to_string(port);
Valerio Pastore's avatar
Valerio Pastore committed
void UDPProtocol::setHost(std::string host) {
	std::string ip = { };
	int port = 0;
Valerio Pastore's avatar
Valerio Pastore committed
	if (split_ip_port(host, ip, port)) {
		setIp(ip);
		setPort(port);
Valerio Pastore's avatar
Valerio Pastore committed
	} else {
		time_t now = time(nullptr);
		std::cerr << "[" << std::put_time(localtime(&now), "%Y-%m-%d %H:%M:%S")
				<< "]\t[UDP Receiver]\t"
				<< "invalid IP address and port format: " << host << std::endl;
Valerio Pastore's avatar
Valerio Pastore committed
void UDPProtocol::setIp(std::string ip) {
Valerio Pastore's avatar
Valerio Pastore committed
	std::regex pattern("^[\\w.-]+$");
	// Check if the input string matches the expected format
	bool ok = true;
	if (!std::regex_match(ip, pattern)) {
Valerio Pastore's avatar
Valerio Pastore committed
		time_t now = time(nullptr);
		std::cerr << "[" << std::put_time(localtime(&now), "%Y-%m-%d %H:%M:%S")
				<< "]\t[UDP Receiver]\t" << "invalid IP address: " << ip
Valerio Pastore's avatar
Valerio Pastore committed
				<< ", setting to localhost" << std::endl;
		ok = false;
Valerio Pastore's avatar
Valerio Pastore committed
		this->ip = std::string("localhost");
Valerio Pastore's avatar
Valerio Pastore committed
	if (ok)
		this->ip = ip;

Valerio Pastore's avatar
Valerio Pastore committed
	this->host = this->ip + ":" + std::to_string(this->port);
Valerio Pastore's avatar
Valerio Pastore committed
void UDPProtocol::setPort(int port) {
	if (port > 1023 && port < 65535)
		this->port = port;
Valerio Pastore's avatar
Valerio Pastore committed
	else {
		time_t now = time(nullptr);
		std::cerr << "[" << std::put_time(localtime(&now), "%Y-%m-%d %H:%M:%S")
				<< "]\t[UDP Receiver]\t" << "invalid port: " << port
				<< ", setting to 9003" << std::endl;
		this->port = 9003;
	}
Valerio Pastore's avatar
Valerio Pastore committed
	this->host = this->ip + ":" + std::to_string(this->port);
}

bool UDPProtocol::split_ip_port(const std::string &ip_port,
Valerio Pastore's avatar
Valerio Pastore committed
		std::string &ip_address, int &port) {
	// Regex pattern to match IP address and port in the format "xxx.xxx.xxx.xxx:xxxx"
Valerio Pastore's avatar
Valerio Pastore committed
	std::regex pattern("^[\\w.-]+:(\\d{1,5})$");
	// Check if the input string matches the expected format
	if (!std::regex_match(ip_port, pattern)) {
		return false;
Valerio Pastore's avatar
Valerio Pastore committed

	// Split the input string into IP address and port
	int colon_pos = ip_port.find(":");
	ip_address = ip_port.substr(0, colon_pos);
Valerio Pastore's avatar
Valerio Pastore committed
	port = std::stoi(ip_port.substr(colon_pos + 1));
	return true;