Files
pip/libs/main/io_devices/pipeer.h
2026-03-12 14:46:57 +03:00

442 lines
20 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! \~\file pipeer.h
//! \~\ingroup IO
//! \~\brief
//! \~english Peer-to-peer network node
//! \~russian Узел одноранговой сети
/*
PIP - Platform Independent Primitives
Peer - named I/O ethernet node, forming self-organized peering network
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PIPEER_H
#define PIPEER_H
#include "pidiagnostics.h"
#include "piethernet.h"
//! \~\ingroup IO
//! \~\brief
//! \~english Named network peer built on top of %PIIODevice.
//! \~russian Именованный сетевой пир, построенный поверх %PIIODevice.
//! \~\details
//! \~english
//! The class discovers peers, routes packets by peer name and can expose a trusted-peer stream through inherited \a read() and \a write().
//! \~russian
//! Класс обнаруживает пиры, маршрутизирует пакеты по имени пира и может предоставлять поток trusted-peer через унаследованные \a read() и
//! \a write().
class PIP_EXPORT PIPeer: public PIIODevice {
PIIODEVICE(PIPeer, "peer");
private:
class PeerData;
public:
//! \~english Constructs a peer node with local name "name".
//! \~russian Создает пиринговый узел с локальным именем "name".
explicit PIPeer(const PIString & name = PIString());
//! \~english Destroys the peer node.
//! \~russian Уничтожает пиринговый узел.
virtual ~PIPeer();
//! \~\ingroup IO
//! \~\brief
//! \~english Public information about a discovered peer.
//! \~russian Общедоступная информация об обнаруженном пире.
class PIP_EXPORT PeerInfo {
friend class PIPeer;
BINARY_STREAM_FRIEND(PIPeer::PeerInfo);
public:
//! \~english Constructs an empty peer description.
//! \~russian Создает пустое описание пира.
PeerInfo() {
dist = sync = cnt = 0;
trace = -1;
was_update = false;
_data = 0;
}
//! \~english Destroys the peer description.
//! \~russian Уничтожает описание пира.
~PeerInfo() {}
//! \~\ingroup IO
//! \~\brief
//! \~english Network address of a peer endpoint.
//! \~russian Сетевой адрес конечной точки пира.
struct PIP_EXPORT PeerAddress {
//! \~english Constructs a peer address with address and netmask.
//! \~russian Создает адрес пира с адресом и маской сети.
PeerAddress(const PINetworkAddress & a = PINetworkAddress(), const PINetworkAddress & m = PINetworkAddress("255.255.255.0"));
//! \~english Returns whether this address has a valid measured ping.
//! \~russian Возвращает, есть ли для этого адреса валидный измеренный ping.
bool isAvailable() const { return ping > 0; }
//! \~english Peer address.
//! \~russian Адрес пира.
PINetworkAddress address;
//! \~english Netmask for the address.
//! \~russian Маска сети для адреса.
PINetworkAddress netmask;
//! \~english Last measured ping in milliseconds, or a negative value if unknown.
//! \~russian Последний измеренный ping в миллисекундах, либо отрицательное значение если он неизвестен.
double ping; // ms
//! \~english Returns whether a ping request is currently pending.
//! \~russian Показывает, ожидается ли сейчас ответ на ping-запрос.
bool wait_ping;
//! \~english Timestamp of the last ping request or reply.
//! \~russian Временная метка последнего ping-запроса или ответа.
PISystemTime last_ping;
};
//! \~english Peer name.
//! \~russian Имя пира.
PIString name;
//! \~english Known addresses of the peer.
//! \~russian Известные адреса пира.
PIVector<PeerAddress> addresses;
//! \~english Distance in hops from the local peer (0 for direct neighbours).
//! \~russian Расстояние в хопах от локального пира (0 для прямых соседей).
int dist;
//! \~english Names of direct neighbours for this peer.
//! \~russian Имена прямых соседей этого пира.
PIStringList neighbours;
//! \~english Returns whether the peer is a direct neighbour.
//! \~russian Возвращает, является ли пир прямым соседом.
bool isNeighbour() const { return dist == 0; }
//! \~english Returns the best known ping in milliseconds.
//! \~russian Возвращает наилучший известный ping в миллисекундах.
int ping() const;
//! \~english Returns the fastest known address of the peer.
//! \~russian Возвращает самый быстрый известный адрес пира.
PINetworkAddress fastestAddress() const;
protected:
void addNeighbour(const PIString & n);
void addNeighbours(const PIStringList & l);
void removeNeighbour(const PIString & n);
void resetPing();
void init();
void destroy();
int sync, cnt, trace;
bool was_update;
PISystemTime time;
PeerData * _data;
};
BINARY_STREAM_FRIEND(PIPeer::PeerInfo);
//! \~english Sends byte array "data" to peer "to".
//! \~russian Отправляет массив байт "data" пиру "to".
//! \~\details
//! \~english Sends the specified byte array to the peer identified by name using the most efficient route.
//! \~russian Отправляет указанный байтовый массив пиру, идентифицируемому по имени, используя наиболее эффективный маршрут.
bool send(const PIString & to, const PIByteArray & data) { return send(to, data.data(), data.size_s()); }
//! \~english Sends string "data" to peer "to".
//! \~russian Отправляет строку "data" пиру "to".
//! \~\details
//! \~english Sends the specified string to the peer identified by name using the most efficient route.
//! \~russian Отправляет указанную строку пиру, идентифицируемому по имени, используя наиболее эффективный маршрут.
bool send(const PIString & to, const PIString & data) { return send(to, data.data(), data.size_s()); }
//! \~english Sends raw buffer to peer "to".
//! \~russian Отправляет сырой буфер пиру "to".
bool send(const PIString & to, const void * data, int size);
//! \~english Sends byte array "data" to peer described by "to".
//! \~russian Отправляет массив байт "data" пиру, описанному в "to".
bool send(const PeerInfo & to, const PIByteArray & data) { return send(to.name, data.data(), data.size_s()); }
//! \~english Sends string "data" to peer described by "to".
//! \~russian Отправляет строку "data" пиру, описанному в "to".
bool send(const PeerInfo & to, const PIString & data) { return send(to.name, data.data(), data.size_s()); }
//! \~english Sends raw buffer to peer described by "to".
//! \~russian Отправляет сырой буфер пиру, описанному в "to".
bool send(const PeerInfo & to, const void * data, int size) { return send(to.name, data, size); }
//! \~english Sends byte array "data" to peer pointer "to".
//! \~russian Отправляет массив байт "data" пиру по указателю "to".
bool send(const PeerInfo * to, const PIByteArray & data);
//! \~english Sends string "data" to peer pointer "to".
//! \~russian Отправляет строку "data" пиру по указателю "to".
bool send(const PeerInfo * to, const PIString & data);
//! \~english Sends raw buffer to peer pointer "to".
//! \~russian Отправляет сырой буфер пиру по указателю "to".
bool send(const PeerInfo * to, const void * data, int size);
//! \~english Sends byte array "data" to all known peers.
//! \~russian Отправляет массив байт "data" всем известным пирам.
void sendToAll(const PIByteArray & data);
//! \~english Sends string "data" to all known peers.
//! \~russian Отправляет строку "data" всем известным пирам.
void sendToAll(const PIString & data);
//! \~english Sends raw buffer to all known peers.
//! \~russian Отправляет сырой буфер всем известным пирам.
void sendToAll(const void * data, int size);
//! \~english Returns whether multicast reception is active.
//! \~russian Возвращает, активно ли получение multicast-пакетов.
bool isMulticastReceive() const { return !eths_mcast.isEmpty(); }
//! \~english Returns whether broadcast reception is active.
//! \~russian Возвращает, активно ли получение broadcast-пакетов.
bool isBroadcastReceive() const { return !eths_bcast.isEmpty(); }
//! \~english Returns service-channel diagnostics.
//! \~russian Возвращает диагностику служебного канала.
PIDiagnostics & diagnosticService() { return diag_s; }
//! \~english Returns payload-channel diagnostics.
//! \~russian Возвращает диагностику канала данных.
PIDiagnostics & diagnosticData() { return diag_d; }
//! \~english Returns all currently known peers.
//! \~russian Возвращает всех известных на данный момент пиров.
const PIVector<PIPeer::PeerInfo> & allPeers() const { return peers; }
//! \~english Returns whether a peer with name "name" is known.
//! \~russian Возвращает, известен ли пир с именем "name".
bool isPeerExists(const PIString & name) const { return getPeerByName(name) != 0; }
//! \~english Returns peer information by name, or null if absent.
//! \~russian Возвращает информацию о пире по имени, либо null если пир не найден.
const PeerInfo * getPeerByName(const PIString & name) const { return peers_map.value(name, 0); }
//! \~english Returns information about the local peer.
//! \~russian Возвращает информацию о локальном пире.
const PeerInfo & selfInfo() const { return self_info; }
//! \~english Returns routing map used to reach known peers.
//! \~russian Возвращает карту маршрутов, используемую для доступа к известным пирам.
const PIMap<PIString, PIVector<PeerInfo *>> & _peerMap() const { return addresses_map; }
//! \~english Rebuilds the peer network state and restarts discovery sockets.
//! \~russian Перестраивает состояние сети пиров и перезапускает сокеты обнаружения.
void reinit();
//! \~english Locks the peer list for manual external access.
//! \~russian Блокирует список пиров для внешнего ручного доступа.
void lock() { peers_mutex.lock(); }
//! \~english Unlocks the peer list after external access.
//! \~russian Снимает блокировку списка пиров после внешнего доступа.
void unlock() { peers_mutex.unlock(); }
//! \~english Changes local peer name and updates related diagnostics names.
//! \~russian Изменяет имя локального пира и обновляет связанные диагностические имена.
void changeName(const PIString & new_name);
//! \~english Returns trusted peer name used by inherited \a read() and \a write().
//! \~russian Возвращает имя доверенного пира, используемое унаследованными \a read() и \a write().
const PIString & trustPeerName() const { return trust_peer; }
//! \~english Sets trusted peer name for inherited \a read() and \a write().
//! \~russian Устанавливает имя доверенного пира для унаследованных \a read() и \a write().
void setTrustPeerName(const PIString & peer_name) { trust_peer = peer_name; }
//! \~english Sets TCP server address used for peer discovery fallback.
//! \~russian Устанавливает адрес TCP-сервера, используемого как резервный канал обнаружения пиров.
void setTcpServerIP(const PIString & ip);
//! \~english Returns size of the next buffered payload from the trusted peer stream.
//! \~russian Возвращает размер следующей буферизованной полезной нагрузки из trusted-peer потока.
ssize_t bytesAvailable() const override;
//! \events
//! \{
//! \fn void dataReceivedEvent(const PIString & from, const PIByteArray & data)
//! \~english Raised when payload data is delivered from peer "from".
//! \~russian Вызывается, когда полезные данные доставлены от пира "from".
EVENT2(dataReceivedEvent, const PIString &, from, const PIByteArray &, data);
//! \fn void peerConnectedEvent(const PIString & name)
//! \~english Raised when a new peer becomes available.
//! \~russian Вызывается, когда становится доступен новый пир.
EVENT1(peerConnectedEvent, const PIString &, name);
//! \fn void peerDisconnectedEvent(const PIString & name)
//! \~english Raised when a known peer disappears from the network.
//! \~russian Вызывается, когда известный пир исчезает из сети.
EVENT1(peerDisconnectedEvent, const PIString &, name);
//! \}
// bool lockedEth() const {return eth_mutex.isLocked();}
// bool lockedPeers() const {return peers_mutex.isLocked();}
// bool lockedMBcasts() const {return mc_mutex.isLocked();}
// bool lockedSends() const {return send_mutex.isLocked();}
// bool lockedMCSends() const {return send_mc_mutex.isLocked();}
protected:
//! \~english Reimplement to handle incoming payload data.
//! \~russian Переопределите для обработки входящих полезных данных.
virtual void dataReceived(const PIString & from, const PIByteArray & data) { ; }
//! \~english Reimplement to react to peer appearance.
//! \~russian Переопределите для реакции на появление пира.
virtual void peerConnected(const PIString & name) { ; }
//! \~english Reimplement to react to peer disappearance.
//! \~russian Переопределите для реакции на исчезновение пира.
virtual void peerDisconnected(const PIString & name) { ; }
EVENT_HANDLER2(bool, dataRead, const uchar *, readed, ssize_t, size);
EVENT_HANDLER2(bool, mbcastRead, const uchar *, readed, ssize_t, size);
void destroy();
private:
EVENT_HANDLER1(void, timerEvent, int, delim);
EVENT_HANDLER2(bool, sendInternal, const PIString &, to, const PIByteArray &, data);
EVENT_HANDLER2(void, dtReceived, const PIString &, from, const PIByteArray &, data);
EVENT_HANDLER1(void, newTcpClient, PIEthernet *, client);
EVENT_HANDLER(void, tcpClientReconnect);
bool hasPeer(const PIString & name);
bool removePeer(const PIString & name);
void removeNeighbour(const PIString & name);
void addPeer(const PeerInfo & pd);
void sendPeerInfo(const PeerInfo & info);
void sendPeerRemove(const PIString & peer);
void sendSelfInfo() { sendPeerInfo(self_info); }
void sendSelfRemove() { sendPeerRemove(self_info.name); }
void syncPeers();
void checkNetwork();
void initNetwork();
void buildMap();
void initEths(PIStringList al);
void initMBcasts(PIStringList al);
void destroyEths();
void destroyMBcasts();
void sendMBcast(const PIByteArray & ba);
void pingNeighbours();
void addToRemoved(const PeerInfo & pi) { removed[pi.name] = PIPair<int, PISystemTime>(pi.cnt, pi.time); }
bool isRemoved(const PeerInfo & pi) const { return (removed.value(pi.name) == PIPair<int, PISystemTime>(pi.cnt, pi.time)); }
bool openDevice() override;
bool closeDevice() override;
PIString constructFullPathDevice() const override;
void configureFromFullPathDevice(const PIString & full_path) override;
PIPropertyStorage constructVariantDevice() const override;
void configureFromVariantDevice(const PIPropertyStorage & d) override;
ssize_t readDevice(void * read_to, ssize_t max_size) override;
ssize_t writeDevice(const void * data, ssize_t size) override;
void interrupt() override;
DeviceInfoFlags deviceInfoFlags() const override { return PIIODevice::Reliable; }
PeerInfo * quickestPeer(const PIString & to);
bool sendToNeighbour(PeerInfo * peer, const PIByteArray & ba);
inline static bool isPeerRecent(const PeerInfo & my, const PeerInfo & income) {
return (my.cnt < income.cnt) || (my.time < income.time);
}
// 1 - new peer, 2 - remove peer, 3 - sync peers, 4 - data, 5 - ping request, 6 - ping reply
// Data packet: 4, from, to, ticks, data_size, data
protected:
bool inited__; // for internal use
private:
PIVector<PIEthernet *> eths_traffic, eths_mcast, eths_bcast;
PIEthernet::InterfaceList prev_ifaces;
PIEthernet eth_send, eth_lo, eth_tcp_srv, eth_tcp_cli;
PITimer sync_timer;
PeerInfo self_info;
PIVector<PeerInfo> peers;
PIMap<PIString, PeerInfo *> peers_map;
PIMap<PIString, PIVector<PeerInfo *>> addresses_map; // map {"to" = list of nearest peers}
PIMap<PIString, PIPair<int, PISystemTime>> removed;
PIDiagnostics diag_s, diag_d;
bool destroyed, no_timer;
PIString trust_peer;
PIString server_ip;
mutable PIMutex read_buffer_mutex;
PIQueue<PIByteArray> read_buffer;
int read_buffer_size;
std::atomic_bool iterrupted = {false};
PIMutex mc_mutex, eth_mutex, peers_mutex, send_mutex, send_mc_mutex;
};
inline PICout operator<<(PICout c, const PIPeer::PeerInfo::PeerAddress & v) {
c.space();
c << "PeerAddress(" << v.address << ", " << v.netmask << ", " << v.ping << ")";
return c;
}
inline PICout operator<<(PICout c, const PIPeer::PeerInfo & v) {
c.space();
c << "PeerInfo(" << v.name << ", " << v.dist << ", " << v.addresses << ")";
return c;
}
//! \relatesalso PIBinaryStream
//! \~english Store operator.
//! \~russian Оператор сохранения.
BINARY_STREAM_WRITE(PIPeer::PeerInfo::PeerAddress) {
s << v.address << v.netmask << v.ping;
return s;
}
//! \relatesalso PIBinaryStream
//! \~english Restore operator.
//! \~russian Оператор извлечения.
BINARY_STREAM_READ(PIPeer::PeerInfo::PeerAddress) {
s >> v.address >> v.netmask >> v.ping;
return s;
}
//! \relatesalso PIBinaryStream
//! \~english Store operator.
//! \~russian Оператор сохранения.
BINARY_STREAM_WRITE(PIPeer::PeerInfo) {
s << v.name << v.addresses << v.dist << v.neighbours << v.cnt << v.time;
return s;
}
//! \relatesalso PIBinaryStream
//! \~english Restore operator.
//! \~russian Оператор извлечения.
BINARY_STREAM_READ(PIPeer::PeerInfo) {
s >> v.name >> v.addresses >> v.dist >> v.neighbours >> v.cnt >> v.time;
return s;
}
#endif // PIPEER_H