123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- /*
- Minetest
- Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
- 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 2.1 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, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
- #pragma once
- #include "irrlichttypes.h"
- #include "peerhandler.h"
- #include "socket.h"
- #include "constants.h"
- #include "util/pointer.h"
- #include "util/container.h"
- #include "util/numeric.h"
- #include "porting.h"
- #include "networkprotocol.h"
- #include <iostream>
- #include <vector>
- #include <map>
- class NetworkPacket;
- namespace con
- {
- class ConnectionReceiveThread;
- class ConnectionSendThread;
- enum rate_stat_type {
- CUR_DL_RATE,
- AVG_DL_RATE,
- CUR_INC_RATE,
- AVG_INC_RATE,
- CUR_LOSS_RATE,
- AVG_LOSS_RATE,
- };
- class Peer;
- // FIXME: Peer refcounting should generally be replaced by std::shared_ptr
- class PeerHelper
- {
- public:
- PeerHelper() = default;
- inline PeerHelper(Peer *peer) { *this = peer; }
- ~PeerHelper();
- PeerHelper& operator=(Peer *peer);
- inline Peer* operator->() const { return m_peer; }
- inline Peer* operator&() const { return m_peer; }
- inline bool operator!() { return !m_peer; }
- inline bool operator!=(std::nullptr_t) { return !!m_peer; }
- private:
- Peer *m_peer = nullptr;
- };
- /*
- Connection
- */
- enum ConnectionEventType {
- CONNEVENT_NONE,
- CONNEVENT_DATA_RECEIVED,
- CONNEVENT_PEER_ADDED,
- CONNEVENT_PEER_REMOVED,
- CONNEVENT_BIND_FAILED,
- };
- struct ConnectionEvent;
- typedef std::shared_ptr<ConnectionEvent> ConnectionEventPtr;
- // This is very similar to ConnectionCommand
- struct ConnectionEvent
- {
- const ConnectionEventType type;
- session_t peer_id = 0;
- Buffer<u8> data;
- bool timeout = false;
- Address address;
- // We don't want to copy "data"
- DISABLE_CLASS_COPY(ConnectionEvent);
- static ConnectionEventPtr create(ConnectionEventType type);
- static ConnectionEventPtr dataReceived(session_t peer_id, const Buffer<u8> &data);
- static ConnectionEventPtr peerAdded(session_t peer_id, Address address);
- static ConnectionEventPtr peerRemoved(session_t peer_id, bool is_timeout, Address address);
- static ConnectionEventPtr bindFailed();
- const char *describe() const;
- private:
- ConnectionEvent(ConnectionEventType type_) :
- type(type_) {}
- };
- struct ConnectionCommand;
- typedef std::shared_ptr<ConnectionCommand> ConnectionCommandPtr;
- struct BufferedPacket;
- typedef std::shared_ptr<BufferedPacket> BufferedPacketPtr;
- class Connection;
- class PeerHandler;
- class Peer {
- public:
- friend class PeerHelper;
- virtual ~Peer() {
- MutexAutoLock usage_lock(m_exclusive_access_mutex);
- FATAL_ERROR_IF(m_usage != 0, "Reference counting failure");
- }
- // Unique id of the peer
- const session_t id;
- void Drop();
- virtual void PutReliableSendCommand(ConnectionCommandPtr &c,
- unsigned int max_packet_size) {};
- virtual const Address &getAddress() const = 0;
- bool isPendingDeletion() const {
- MutexAutoLock lock(m_exclusive_access_mutex);
- return m_pending_deletion;
- }
- void ResetTimeout() {
- MutexAutoLock lock(m_exclusive_access_mutex);
- m_timeout_counter = 0;
- }
- bool isHalfOpen() const {
- MutexAutoLock lock(m_exclusive_access_mutex);
- return m_half_open;
- }
- void SetFullyOpen() {
- MutexAutoLock lock(m_exclusive_access_mutex);
- m_half_open = false;
- }
- virtual bool isTimedOut(float timeout, std::string &reason);
- unsigned int m_increment_packets_remaining = 0;
- virtual u16 getNextSplitSequenceNumber(u8 channel) { return 0; };
- virtual void setNextSplitSequenceNumber(u8 channel, u16 seqnum) {};
- virtual SharedBuffer<u8> addSplitPacket(u8 channel, BufferedPacketPtr &toadd,
- bool reliable)
- {
- FATAL_ERROR("unimplemented in abstract class");
- }
- virtual bool Ping(float dtime, SharedBuffer<u8>& data) { return false; };
- virtual float getStat(rtt_stat_type type) const {
- switch (type) {
- case MIN_RTT:
- return m_rtt.min_rtt;
- case MAX_RTT:
- return m_rtt.max_rtt;
- case AVG_RTT:
- return m_rtt.avg_rtt;
- case MIN_JITTER:
- return m_rtt.jitter_min;
- case MAX_JITTER:
- return m_rtt.jitter_max;
- case AVG_JITTER:
- return m_rtt.jitter_avg;
- }
- return -1;
- }
- protected:
- Peer(session_t id, const Address &address, Connection *connection) :
- id(id),
- m_connection(connection),
- address(address),
- m_last_timeout_check(porting::getTimeMs())
- {
- }
- virtual void reportRTT(float rtt) {};
- void RTTStatistics(float rtt,
- const std::string &profiler_id = "",
- unsigned int num_samples = 1000);
- bool IncUseCount();
- void DecUseCount();
- mutable std::mutex m_exclusive_access_mutex;
- bool m_pending_deletion = false;
- Connection *m_connection;
- // Address of the peer
- Address address;
- // Ping timer
- float m_ping_timer = 0.0f;
- private:
- struct rttstats {
- float jitter_min = FLT_MAX;
- float jitter_max = 0.0f;
- float jitter_avg = -1.0f;
- float min_rtt = FLT_MAX;
- float max_rtt = 0.0f;
- float avg_rtt = -1.0f;
- };
- rttstats m_rtt;
- float m_last_rtt = -1.0f;
- /*
- Until the peer has communicated with us using their assigned peer id
- the connection is considered half-open.
- During this time we inhibit re-sending any reliables or pings. This
- is to avoid spending too many resources on a potential DoS attack
- and to make sure Minetest servers are not useful for UDP amplificiation.
- */
- bool m_half_open = true;
- // current usage count
- unsigned int m_usage = 0;
- // Seconds from last receive
- float m_timeout_counter = 0.0f;
- u64 m_last_timeout_check;
- };
- class UDPPeer;
- class Connection
- {
- public:
- friend class ConnectionSendThread;
- friend class ConnectionReceiveThread;
- Connection(u32 protocol_id, u32 max_packet_size, float timeout, bool ipv6,
- PeerHandler *peerhandler);
- ~Connection();
- /* Interface */
- ConnectionEventPtr waitEvent(u32 timeout_ms);
- void putCommand(ConnectionCommandPtr c);
- void SetTimeoutMs(u32 timeout) { m_bc_receive_timeout = timeout; }
- void Serve(Address bind_addr);
- void Connect(Address address);
- bool Connected();
- void Disconnect();
- bool ReceiveTimeoutMs(NetworkPacket *pkt, u32 timeout_ms);
- void Receive(NetworkPacket *pkt);
- bool TryReceive(NetworkPacket *pkt);
- void Send(session_t peer_id, u8 channelnum, NetworkPacket *pkt, bool reliable);
- session_t GetPeerID() const { return m_peer_id; }
- Address GetPeerAddress(session_t peer_id);
- float getPeerStat(session_t peer_id, rtt_stat_type type);
- float getLocalStat(rate_stat_type type);
- u32 GetProtocolID() const { return m_protocol_id; };
- const std::string getDesc();
- void DisconnectPeer(session_t peer_id);
- protected:
- PeerHelper getPeerNoEx(session_t peer_id);
- session_t lookupPeer(const Address& sender);
- session_t createPeer(const Address& sender, int fd);
- UDPPeer* createServerPeer(const Address& sender);
- bool deletePeer(session_t peer_id, bool timeout);
- void SetPeerID(session_t id) { m_peer_id = id; }
- void doResendOne(session_t peer_id);
- void sendAck(session_t peer_id, u8 channelnum, u16 seqnum);
- std::vector<session_t> getPeerIDs()
- {
- MutexAutoLock peerlock(m_peers_mutex);
- return m_peer_ids;
- }
- u32 getActiveCount();
- UDPSocket m_udpSocket;
- // Command queue: user -> SendThread
- MutexedQueue<ConnectionCommandPtr> m_command_queue;
- void putEvent(ConnectionEventPtr e);
- void TriggerSend();
- bool ConnectedToServer()
- {
- return getPeerNoEx(PEER_ID_SERVER) != nullptr;
- }
- private:
- // Event queue: ReceiveThread -> user
- MutexedQueue<ConnectionEventPtr> m_event_queue;
- session_t m_peer_id = 0;
- u32 m_protocol_id;
- std::map<session_t, Peer *> m_peers;
- std::vector<session_t> m_peer_ids;
- std::mutex m_peers_mutex;
- std::unique_ptr<ConnectionSendThread> m_sendThread;
- std::unique_ptr<ConnectionReceiveThread> m_receiveThread;
- mutable std::mutex m_info_mutex;
- // Backwards compatibility
- PeerHandler *m_bc_peerhandler;
- u32 m_bc_receive_timeout = 0;
- bool m_shutting_down = false;
- };
- } // namespace
|