//! \~\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 . */ #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 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 & 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> & _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(pi.cnt, pi.time); } bool isRemoved(const PeerInfo & pi) const { return (removed.value(pi.name) == PIPair(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 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 peers; PIMap peers_map; PIMap> addresses_map; // map {"to" = list of nearest peers} PIMap> removed; PIDiagnostics diag_s, diag_d; bool destroyed, no_timer; PIString trust_peer; PIString server_ip; mutable PIMutex read_buffer_mutex; PIQueue 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