simplified some constructor syntax, made the "id" value of a peer not required for the program to work correctly (id will be removed later)

This commit is contained in:
study-faraphel 2024-11-17 14:21:49 +01:00
parent 11e4861082
commit 7871a4f4dd
11 changed files with 215 additions and 67 deletions

View file

@ -59,6 +59,11 @@ add_executable(M2-PT-DRP
source/packets/info/InfoPacketData.hpp
source/utils/time/Chrony.cpp
source/utils/time/Chrony.hpp
source/Peer.hpp
source/utils/network.cpp
source/utils/network.hpp
source/Peer.cpp
source/RemotePeer.cpp
)
target_include_directories(M2-PT-DRP PRIVATE
source

View file

@ -1,7 +1,7 @@
#pragma once
#include <list>
#include <memory>
#include <map>
#include <netdb.h>
#include "RemotePeer.hpp"
@ -13,14 +13,19 @@
*/
class Context {
public:
int socket = -1; /// current socket file descriptor, used to communicate
addrinfo* broadcastAddressInfo = nullptr; /// address used to broadcast messages
std::shared_ptr<RemotePeer> server = nullptr; /// peer currently used as the server
explicit Context() {
this->socket = -1;
this->broadcastAddressInfo = nullptr;
this->server = nullptr;
this->latestPeerDiscovery = std::chrono::high_resolution_clock::now();
}
int socket; /// current socket file descriptor, used to communicate
addrinfo* broadcastAddressInfo; /// address used to broadcast messages
std::shared_ptr<RemotePeer> server; /// peer currently used as the server
Peer me; /// information about our own machine
std::map<
std::uint32_t,
std::shared_ptr<RemotePeer>
> remotePeers {}; /// information about other machines
std::list<std::shared_ptr<RemotePeer>> remotePeers {}; /// information about other machines
std::chrono::high_resolution_clock::time_point latestPeerDiscovery; /// time of the latest discovered machine
};

View file

@ -57,7 +57,7 @@ Manager::Manager(const std::string& address, const std::string& port, const bool
throw std::runtime_error("Could not create the socket: " + std::string(strerror(errno)));
// allow IPv6 multicast loopback so that we can receive our own messages.
int socketLoopback = 1;
const int socketLoopback = 1;
if (setsockopt(
context.socket,
IPPROTO_IPV6,
@ -102,21 +102,8 @@ Manager::Manager(const std::string& address, const std::string& port, const bool
) < 0)
throw std::runtime_error("Could not bind to the address: " + std::string(strerror(errno)));
// generate a random identifier for ourselves
std::random_device randomDevice;
std::mt19937 randomGenerator(randomDevice());
std::uniform_int_distribution<std::uint32_t> distribution(
1,
std::numeric_limits<std::uint32_t>::max()
);
this->context.me.id = distribution(randomGenerator);
// TODO(Faraphel): should only be enabled in specific case.
this->context.me.serverEnabled = true;
// define the time of the latest discovery
this->context.latestPeerDiscovery = std::chrono::high_resolution_clock::now();
}
@ -177,8 +164,22 @@ void Manager::loopReceiver() {
continue;
// decrypt the packet
// TODO(Faraphel): handle exception ?
packetContent = packet.getContent();
// look for a saved peer with the same address
auto remotePeer = std::ranges::find_if(
this->context.remotePeers,
[&](const std::shared_ptr<RemotePeer>& remotePeer) { return
remotePeer->addressLength == fromAddressLength and
std::memcmp(&fromAddress, &remotePeer->address, fromAddressLength) == 0;
}
);
// if found, update the latest connection date
if (remotePeer != this->context.remotePeers.end()) {
(*remotePeer)->latestConnection = std::chrono::high_resolution_clock::now();
}
// get the corresponding event class
std::shared_ptr<drp::event::BaseEvent> event;
try {

21
source/Peer.cpp Normal file
View file

@ -0,0 +1,21 @@
#include "Peer.hpp"
Peer::Peer() {
this->id = randomDistribution(randomGenerator);
this->channel = 0;
this->serverEnabled = false;
this->status = drp::task::TaskType::UNDEFINED;
this->latencyAverage = std::chrono::high_resolution_clock::duration::max();
}
std::random_device Peer::randomDevice = std::random_device();
std::mt19937 Peer::randomGenerator = std::mt19937(randomDevice());
std::uniform_int_distribution<std::uint32_t> Peer::randomDistribution = std::uniform_int_distribution<std::uint32_t>(
1,
std::numeric_limits<std::uint32_t>::max()
);

30
source/Peer.hpp Normal file
View file

@ -0,0 +1,30 @@
#pragma once
#include <chrono>
#include <cstdint>
#include <random>
#include "behavior/tasks/types.hpp"
/**
* Contains common information about a certain peer.
*/
class Peer {
public:
explicit Peer();
std::uint32_t id;
bool serverEnabled;
drp::task::TaskType status;
std::uint8_t channel;
std::chrono::high_resolution_clock::duration latencyAverage {};
private:
// random
static std::random_device randomDevice;
static std::mt19937 randomGenerator;
static std::uniform_int_distribution<std::uint32_t> randomDistribution;
};

15
source/RemotePeer.cpp Normal file
View file

@ -0,0 +1,15 @@
#include "RemotePeer.hpp"
RemotePeer::RemotePeer(const sockaddr_storage& address, const socklen_t addressLength, const Peer& peer) {
this->address = address;
this->addressLength = addressLength;
this->information = peer;
this->latestConnection = std::chrono::system_clock::now();
this->latency = std::chrono::high_resolution_clock::duration::max();
}
void RemotePeer::update(const Peer& peer) {
this->information = peer;
}

View file

@ -3,35 +3,24 @@
#include <chrono>
#include <sys/socket.h>
#include "behavior/tasks/types.hpp"
// TODO(Faraphel): should be split in multiple files.
/**
* Contains common information about a certain peer.
*/
struct Peer {
std::uint32_t id = 0;
bool serverEnabled = false;
drp::task::TaskType status = drp::task::TaskType::UNDEFINED;
std::uint8_t channel = 0;
std::chrono::high_resolution_clock::duration latencyAverage = std::chrono::high_resolution_clock::duration::max();
};
#include "Peer.hpp"
/**
* Contains information about a distant peer.
*/
struct RemotePeer {
class RemotePeer {
public:
explicit RemotePeer(const sockaddr_storage& address, socklen_t addressLength, const Peer& peer);
void update(const Peer& peer);
// communication
sockaddr_storage address {};
socklen_t addressLength = 0;
socklen_t addressLength;
std::chrono::high_resolution_clock::duration latency = std::chrono::high_resolution_clock::duration::max();
std::chrono::high_resolution_clock::time_point latestConnection;
std::chrono::high_resolution_clock::duration latency{};
// information
Peer information;

View file

@ -19,29 +19,32 @@ void InfoEvent::handle(
// get the peer information
const auto packetData = packet::info::InfoPacketData::fromGeneric(content);
const Peer peer = packetData.peer;
const Peer packetPeer = packetData.peer;
// check if the peer address is already in the map
const auto iterator = context.remotePeers.find(peer.id);
const auto itRemotePeer = std::ranges::find_if(
context.remotePeers,
[&](const auto& remotePeer) {
return remotePeer->information.id == packetPeer.id;
}
);
std::shared_ptr<RemotePeer> remotePeer;
if (iterator == context.remotePeers.end()) {
// if not found, create a new remote peer.
if (itRemotePeer == context.remotePeers.end()) {
// if not found, create a new peer
remotePeer = std::make_shared<RemotePeer>();
remotePeer->address = fromAddress;
remotePeer->addressLength = fromAddressLength;
const auto remotePeer = std::make_shared<RemotePeer>(fromAddress, fromAddressLength, packetPeer);
// register it in the peer list
context.remotePeers.push_back(remotePeer);
// update the latest discovery time
context.latestPeerDiscovery = std::chrono::high_resolution_clock::now();
} else {
// get the peer
remotePeer = iterator->second;
const auto& remotePeer = *itRemotePeer;
// update the peer information
remotePeer->update(packetPeer);
}
// TODO(Faraphel): interpret the timestamp and calculate average ping
// save it in the peers list
remotePeer->information = peer;
context.remotePeers[peer.id] = remotePeer;
}

View file

@ -4,15 +4,16 @@
#include <chrono>
#include <cstring>
#include <iostream>
#include <numeric>
#include <thread>
#include <algorithm>
#include <sys/socket.h>
#include <ifaddrs.h>
#include "Context.hpp"
#include "packets/base/Packet.hpp"
#include "packets/base/SecurityMode.hpp"
#include "packets/search/SearchPacketData.hpp"
#include "utils/network.hpp"
namespace drp::task {
@ -20,22 +21,21 @@ namespace drp::task {
void UndefinedTask::handle(Context& context) {
std::cout << "[Task - Undefined] List of peers: " << std::endl;
for (const auto& peer : context.remotePeers)
for (const auto& remotePeer : context.remotePeers)
std::cout <<
"\tPeer(id=" << peer.second->information.id << ", " <<
"status=" << std::to_string(static_cast<std::uint8_t>(peer.second->information.status)) << ")" <<
"\tPeer(id=" << remotePeer->information.id << ", " <<
"status=" << std::to_string(static_cast<std::uint8_t>(remotePeer->information.status)) << ")" <<
std::endl;
// search if a server is available among the peer.
const auto& server = std::ranges::find_if(
context.remotePeers,
[&](const auto& peer) { return peer.second->information.status == TaskType::SERVER; }
[&](const auto& peer) { return peer->information.status == TaskType::SERVER; }
);
// if a server have been found, use it
if (server != context.remotePeers.end()) {
// if a server have been found, use it
context.server = server->second;
context.server = *server;
context.me.status = TaskType::CLIENT;
return;
}
@ -51,21 +51,21 @@ void UndefinedTask::handle(Context& context) {
// TODO(Faraphel): should use the machine with the lowest average ping
if (context.me.serverEnabled) {
// find the remote peer with the highest id that can be a server
const auto serverCandidate = std::ranges::max_element(
std::shared_ptr<RemotePeer> serverCandidate = *std::ranges::max_element(
context.remotePeers,
[&](auto& remotePeer1, auto& remotePeer2) {
return (
(remotePeer1.second->information.serverEnabled ? remotePeer1.first : 0) <
(remotePeer2.second->information.serverEnabled ? remotePeer2.first : 0)
(remotePeer1->information.serverEnabled ? remotePeer1->information.id : 0) <
(remotePeer2->information.serverEnabled ? remotePeer2->information.id : 0)
);
}
);
// check if we are this peer
if (context.me.id == serverCandidate->first) {
if (util::network::is_localhost(serverCandidate->address, serverCandidate->addressLength)) {
std::cout << "[Task - Undefined] Becoming server..." << std::endl;
// set ourselves as the server
context.server = serverCandidate->second;
context.server = serverCandidate;
context.me.status = TaskType::SERVER;
return;
}

60
source/utils/network.cpp Normal file
View file

@ -0,0 +1,60 @@
#include "network.hpp"
#include <cstring>
#include <ifaddrs.h>
#include <stdexcept>
#include <netinet/in.h>
namespace drp::util::network {
bool is_localhost(const sockaddr_storage& address, const socklen_t addressLength) {
// iterate through all the interfaces
ifaddrs* interfaces;
if (getifaddrs(&interfaces) == -1)
throw std::runtime_error("Could not get network interfaces.");
bool result = false;
for (const ifaddrs *interface = interfaces; interface; interface = interface->ifa_next) {
// if the interface family does not correspond to the server candidate, ignore it
if (interface->ifa_addr->sa_family != address.ss_family)
continue;
switch (address.ss_family) {
case AF_INET: {
const auto interfaceIpv4 = reinterpret_cast<const sockaddr_in*>(interface->ifa_addr);
const auto addressIpv4 = reinterpret_cast<const sockaddr_in*>(&address);
// the family have already been checked.
if (std::memcmp(&interfaceIpv4->sin_addr, &addressIpv4->sin_addr, sizeof(in_addr)) == 0)
result = true;
break;
}
case AF_INET6: {
const auto interfaceIpv6 = reinterpret_cast<const sockaddr_in6*>(interface->ifa_addr);
const auto addressIpv6 = reinterpret_cast<const sockaddr_in6*>(&address);
// the interface and the family have already been checked.
if (std::memcmp(&interfaceIpv6->sin6_addr, &addressIpv6->sin6_addr, sizeof(in6_addr)) == 0)
result = true;
break;
}
default:
throw std::runtime_error("Unknown address family.");
}
if (result == true)
break;
}
freeifaddrs(interfaces);
return result;
}
}

19
source/utils/network.hpp Normal file
View file

@ -0,0 +1,19 @@
#pragma once
#include <cstddef>
#include <sys/socket.h>
namespace drp::util::network {
/**
* Indicate if an address refer to the local machine.
* @param address the address to check.
* @param addressLength the length of the address.
* @return True if the address refer to ourselves, False otherwise.
*/
bool is_localhost(const sockaddr_storage& address, socklen_t addressLength);
}