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:
parent
11e4861082
commit
7871a4f4dd
11 changed files with 215 additions and 67 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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
21
source/Peer.cpp
Normal 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
30
source/Peer.hpp
Normal 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
15
source/RemotePeer.cpp
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
60
source/utils/network.cpp
Normal 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
19
source/utils/network.hpp
Normal 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);
|
||||
|
||||
|
||||
}
|
Loading…
Reference in a new issue