mirror of
https://github.com/luanti-org/luanti.git
synced 2025-12-25 07:45:27 +01:00
Move network protocol implementation behind an interface
This commit is contained in:
1667
src/network/mtp/impl.cpp
Normal file
1667
src/network/mtp/impl.cpp
Normal file
File diff suppressed because it is too large
Load Diff
325
src/network/mtp/impl.h
Normal file
325
src/network/mtp/impl.h
Normal file
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
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 "network/connection.h"
|
||||
#include "network/socket.h"
|
||||
#include "constants.h"
|
||||
#include "util/pointer.h"
|
||||
#include "util/container.h"
|
||||
#include "util/numeric.h"
|
||||
#include "porting.h"
|
||||
#include "network/networkprotocol.h"
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
namespace con
|
||||
{
|
||||
|
||||
class ConnectionReceiveThread;
|
||||
class ConnectionSendThread;
|
||||
|
||||
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 IPeer {
|
||||
public:
|
||||
friend class PeerHelper;
|
||||
|
||||
virtual ~Peer() {
|
||||
MutexAutoLock usage_lock(m_exclusive_access_mutex);
|
||||
FATAL_ERROR_IF(m_usage != 0, "Reference counting failure");
|
||||
}
|
||||
|
||||
void Drop();
|
||||
|
||||
virtual void PutReliableSendCommand(ConnectionCommandPtr &c,
|
||||
unsigned int max_packet_size) {};
|
||||
|
||||
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) :
|
||||
IPeer(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 IConnection
|
||||
{
|
||||
public:
|
||||
friend class ConnectionSendThread;
|
||||
friend class ConnectionReceiveThread;
|
||||
|
||||
Connection(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);
|
||||
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
|
||||
525
src/network/mtp/internal.h
Normal file
525
src/network/mtp/internal.h
Normal file
@@ -0,0 +1,525 @@
|
||||
/*
|
||||
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 "network/mtp/impl.h"
|
||||
|
||||
// Constant that differentiates the protocol from random data and other protocols
|
||||
#define PROTOCOL_ID 0x4f457403
|
||||
|
||||
#define MAX_UDP_PEERS 65535
|
||||
|
||||
/*
|
||||
=== NOTES ===
|
||||
|
||||
A packet is sent through a channel to a peer with a basic header:
|
||||
Header (7 bytes):
|
||||
[0] u32 protocol_id
|
||||
[4] session_t sender_peer_id
|
||||
[6] u8 channel
|
||||
sender_peer_id:
|
||||
Unique to each peer.
|
||||
value 0 (PEER_ID_INEXISTENT) is reserved for making new connections
|
||||
value 1 (PEER_ID_SERVER) is reserved for server
|
||||
these constants are defined in constants.h
|
||||
channel:
|
||||
Channel numbers have no intrinsic meaning. Currently only 0, 1, 2 exist.
|
||||
*/
|
||||
#define BASE_HEADER_SIZE 7
|
||||
#define CHANNEL_COUNT 3
|
||||
|
||||
/*
|
||||
Packet types:
|
||||
|
||||
CONTROL: This is a packet used by the protocol.
|
||||
- When this is processed, nothing is handed to the user.
|
||||
Header (2 byte):
|
||||
[0] u8 type
|
||||
[1] u8 controltype
|
||||
controltype and data description:
|
||||
CONTROLTYPE_ACK
|
||||
[2] u16 seqnum
|
||||
CONTROLTYPE_SET_PEER_ID
|
||||
[2] session_t peer_id_new
|
||||
CONTROLTYPE_PING
|
||||
- There is no actual reply, but this can be sent in a reliable
|
||||
packet to get a reply
|
||||
CONTROLTYPE_DISCO
|
||||
*/
|
||||
enum ControlType : u8 {
|
||||
CONTROLTYPE_ACK = 0,
|
||||
CONTROLTYPE_SET_PEER_ID = 1,
|
||||
CONTROLTYPE_PING = 2,
|
||||
CONTROLTYPE_DISCO = 3,
|
||||
};
|
||||
|
||||
/*
|
||||
ORIGINAL: This is a plain packet with no control and no error
|
||||
checking at all.
|
||||
- When this is processed, it is directly handed to the user.
|
||||
Header (1 byte):
|
||||
[0] u8 type
|
||||
*/
|
||||
//#define TYPE_ORIGINAL 1
|
||||
#define ORIGINAL_HEADER_SIZE 1
|
||||
|
||||
/*
|
||||
SPLIT: These are sequences of packets forming one bigger piece of
|
||||
data.
|
||||
- When processed and all the packet_nums 0...packet_count-1 are
|
||||
present (this should be buffered), the resulting data shall be
|
||||
directly handed to the user.
|
||||
- If the data fails to come up in a reasonable time, the buffer shall
|
||||
be silently discarded.
|
||||
- These can be sent as-is or atop of a RELIABLE packet stream.
|
||||
Header (7 bytes):
|
||||
[0] u8 type
|
||||
[1] u16 seqnum
|
||||
[3] u16 chunk_count
|
||||
[5] u16 chunk_num
|
||||
*/
|
||||
//#define TYPE_SPLIT 2
|
||||
|
||||
/*
|
||||
RELIABLE: Delivery of all RELIABLE packets shall be forced by ACKs,
|
||||
and they shall be delivered in the same order as sent. This is done
|
||||
with a buffer in the receiving and transmitting end.
|
||||
- When this is processed, the contents of each packet is recursively
|
||||
processed as packets.
|
||||
Header (3 bytes):
|
||||
[0] u8 type
|
||||
[1] u16 seqnum
|
||||
|
||||
*/
|
||||
//#define TYPE_RELIABLE 3
|
||||
#define RELIABLE_HEADER_SIZE 3
|
||||
#define SEQNUM_INITIAL 65500
|
||||
#define SEQNUM_MAX 65535
|
||||
|
||||
namespace con
|
||||
{
|
||||
|
||||
|
||||
enum PacketType : u8 {
|
||||
PACKET_TYPE_CONTROL = 0,
|
||||
PACKET_TYPE_ORIGINAL = 1,
|
||||
PACKET_TYPE_SPLIT = 2,
|
||||
PACKET_TYPE_RELIABLE = 3,
|
||||
PACKET_TYPE_MAX
|
||||
};
|
||||
|
||||
inline bool seqnum_higher(u16 totest, u16 base)
|
||||
{
|
||||
if (totest > base)
|
||||
{
|
||||
if ((totest - base) > (SEQNUM_MAX/2))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((base - totest) > (SEQNUM_MAX/2))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool seqnum_in_window(u16 seqnum, u16 next,u16 window_size)
|
||||
{
|
||||
u16 window_start = next;
|
||||
u16 window_end = ( next + window_size ) % (SEQNUM_MAX+1);
|
||||
|
||||
if (window_start < window_end) {
|
||||
return ((seqnum >= window_start) && (seqnum < window_end));
|
||||
}
|
||||
|
||||
|
||||
return ((seqnum < window_end) || (seqnum >= window_start));
|
||||
}
|
||||
|
||||
inline float CALC_DTIME(u64 lasttime, u64 curtime)
|
||||
{
|
||||
float value = (curtime - lasttime) / 1000.0f;
|
||||
return MYMAX(MYMIN(value, 0.1f), 0.0f);
|
||||
}
|
||||
|
||||
/* Exceptions */
|
||||
|
||||
class NotFoundException : public BaseException
|
||||
{
|
||||
public:
|
||||
NotFoundException(const char *s) : BaseException(s) {}
|
||||
};
|
||||
|
||||
class ProcessedSilentlyException : public BaseException
|
||||
{
|
||||
public:
|
||||
ProcessedSilentlyException(const char *s) : BaseException(s) {}
|
||||
};
|
||||
|
||||
class ProcessedQueued : public BaseException
|
||||
{
|
||||
public:
|
||||
ProcessedQueued(const char *s) : BaseException(s) {}
|
||||
};
|
||||
|
||||
class IncomingDataCorruption : public BaseException
|
||||
{
|
||||
public:
|
||||
IncomingDataCorruption(const char *s) : BaseException(s) {}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Struct for all kinds of packets. Includes following data:
|
||||
BASE_HEADER
|
||||
u8[] packet data (usually copied from SharedBuffer<u8>)
|
||||
*/
|
||||
struct BufferedPacket {
|
||||
BufferedPacket(u32 a_size)
|
||||
{
|
||||
m_data.resize(a_size);
|
||||
data = &m_data[0];
|
||||
}
|
||||
|
||||
DISABLE_CLASS_COPY(BufferedPacket)
|
||||
|
||||
u16 getSeqnum() const;
|
||||
|
||||
inline size_t size() const { return m_data.size(); }
|
||||
|
||||
u8 *data; // Direct memory access
|
||||
float time = 0.0f; // Seconds from buffering the packet or re-sending
|
||||
float totaltime = 0.0f; // Seconds from buffering the packet
|
||||
u64 absolute_send_time = -1;
|
||||
Address address; // Sender or destination
|
||||
unsigned int resend_count = 0;
|
||||
|
||||
private:
|
||||
std::vector<u8> m_data; // Data of the packet, including headers
|
||||
};
|
||||
|
||||
|
||||
// This adds the base headers to the data and makes a packet out of it
|
||||
BufferedPacketPtr makePacket(const Address &address, const SharedBuffer<u8> &data,
|
||||
u32 protocol_id, session_t sender_peer_id, u8 channel);
|
||||
|
||||
// Depending on size, make a TYPE_ORIGINAL or TYPE_SPLIT packet
|
||||
// Increments split_seqnum if a split packet is made
|
||||
void makeAutoSplitPacket(const SharedBuffer<u8> &data, u32 chunksize_max,
|
||||
u16 &split_seqnum, std::list<SharedBuffer<u8>> *list);
|
||||
|
||||
// Add the TYPE_RELIABLE header to the data
|
||||
SharedBuffer<u8> makeReliablePacket(const SharedBuffer<u8> &data, u16 seqnum);
|
||||
|
||||
struct IncomingSplitPacket
|
||||
{
|
||||
IncomingSplitPacket(u32 cc, bool r):
|
||||
chunk_count(cc), reliable(r) {}
|
||||
|
||||
IncomingSplitPacket() = delete;
|
||||
|
||||
float time = 0.0f; // Seconds from adding
|
||||
u32 chunk_count;
|
||||
bool reliable; // If true, isn't deleted on timeout
|
||||
|
||||
bool allReceived() const
|
||||
{
|
||||
return (chunks.size() == chunk_count);
|
||||
}
|
||||
bool insert(u32 chunk_num, SharedBuffer<u8> &chunkdata);
|
||||
SharedBuffer<u8> reassemble();
|
||||
|
||||
private:
|
||||
// Key is chunk number, value is data without headers
|
||||
std::map<u16, SharedBuffer<u8>> chunks;
|
||||
};
|
||||
|
||||
/*
|
||||
A buffer which stores reliable packets and sorts them internally
|
||||
for fast access to the smallest one.
|
||||
*/
|
||||
|
||||
class ReliablePacketBuffer
|
||||
{
|
||||
public:
|
||||
bool getFirstSeqnum(u16 &result);
|
||||
|
||||
BufferedPacketPtr popFirst();
|
||||
BufferedPacketPtr popSeqnum(u16 seqnum);
|
||||
void insert(BufferedPacketPtr &p_ptr, u16 next_expected);
|
||||
|
||||
void incrementTimeouts(float dtime);
|
||||
u32 getTimedOuts(float timeout);
|
||||
// timeout relative to last resend
|
||||
std::vector<ConstSharedPtr<BufferedPacket>> getResend(float timeout, u32 max_packets);
|
||||
|
||||
void print();
|
||||
bool empty();
|
||||
u32 size();
|
||||
|
||||
|
||||
private:
|
||||
typedef std::list<BufferedPacketPtr>::iterator FindResult;
|
||||
|
||||
FindResult findPacketNoLock(u16 seqnum);
|
||||
|
||||
std::list<BufferedPacketPtr> m_list;
|
||||
|
||||
u16 m_oldest_non_answered_ack;
|
||||
|
||||
std::mutex m_list_mutex;
|
||||
};
|
||||
|
||||
/*
|
||||
A buffer for reconstructing split packets
|
||||
*/
|
||||
|
||||
class IncomingSplitBuffer
|
||||
{
|
||||
public:
|
||||
~IncomingSplitBuffer();
|
||||
|
||||
/*
|
||||
Returns a reference counted buffer of length != 0 when a full split
|
||||
packet is constructed. If not, returns one of length 0.
|
||||
*/
|
||||
SharedBuffer<u8> insert(BufferedPacketPtr &p_ptr, bool reliable);
|
||||
|
||||
void removeUnreliableTimedOuts(float dtime, float timeout);
|
||||
|
||||
private:
|
||||
// Key is seqnum
|
||||
std::map<u16, IncomingSplitPacket*> m_buf;
|
||||
|
||||
std::mutex m_map_mutex;
|
||||
};
|
||||
|
||||
enum ConnectionCommandType{
|
||||
CONNCMD_NONE,
|
||||
CONNCMD_SERVE,
|
||||
CONNCMD_CONNECT,
|
||||
CONNCMD_DISCONNECT,
|
||||
CONNCMD_DISCONNECT_PEER,
|
||||
CONNCMD_SEND,
|
||||
CONNCMD_SEND_TO_ALL,
|
||||
CONCMD_ACK,
|
||||
CONCMD_CREATE_PEER,
|
||||
CONNCMD_RESEND_ONE
|
||||
};
|
||||
|
||||
// This is very similar to ConnectionEvent
|
||||
struct ConnectionCommand
|
||||
{
|
||||
const ConnectionCommandType type;
|
||||
Address address;
|
||||
session_t peer_id = PEER_ID_INEXISTENT;
|
||||
u8 channelnum = 0;
|
||||
Buffer<u8> data;
|
||||
bool reliable = false;
|
||||
bool raw = false;
|
||||
|
||||
DISABLE_CLASS_COPY(ConnectionCommand);
|
||||
|
||||
static ConnectionCommandPtr serve(Address address);
|
||||
static ConnectionCommandPtr connect(Address address);
|
||||
static ConnectionCommandPtr disconnect();
|
||||
static ConnectionCommandPtr disconnect_peer(session_t peer_id);
|
||||
static ConnectionCommandPtr resend_one(session_t peer_id);
|
||||
static ConnectionCommandPtr send(session_t peer_id, u8 channelnum, NetworkPacket *pkt, bool reliable);
|
||||
static ConnectionCommandPtr ack(session_t peer_id, u8 channelnum, const Buffer<u8> &data);
|
||||
static ConnectionCommandPtr createPeer(session_t peer_id, const Buffer<u8> &data);
|
||||
|
||||
private:
|
||||
ConnectionCommand(ConnectionCommandType type_) :
|
||||
type(type_) {}
|
||||
|
||||
static ConnectionCommandPtr create(ConnectionCommandType type);
|
||||
};
|
||||
|
||||
/* maximum window size to use, 0xFFFF is theoretical maximum. don't think about
|
||||
* touching it, the less you're away from it the more likely data corruption
|
||||
* will occur
|
||||
*/
|
||||
#define MAX_RELIABLE_WINDOW_SIZE 0x8000
|
||||
/* starting value for window size */
|
||||
#define START_RELIABLE_WINDOW_SIZE 0x400
|
||||
/* minimum value for window size */
|
||||
#define MIN_RELIABLE_WINDOW_SIZE 0x40
|
||||
|
||||
class Channel
|
||||
{
|
||||
|
||||
public:
|
||||
u16 readNextIncomingSeqNum();
|
||||
u16 incNextIncomingSeqNum();
|
||||
|
||||
u16 getOutgoingSequenceNumber(bool& successful);
|
||||
u16 readOutgoingSequenceNumber();
|
||||
bool putBackSequenceNumber(u16);
|
||||
|
||||
u16 readNextSplitSeqNum();
|
||||
void setNextSplitSeqNum(u16 seqnum);
|
||||
|
||||
// This is for buffering the incoming packets that are coming in
|
||||
// the wrong order
|
||||
ReliablePacketBuffer incoming_reliables;
|
||||
// This is for buffering the sent packets so that the sender can
|
||||
// re-send them if no ACK is received
|
||||
ReliablePacketBuffer outgoing_reliables_sent;
|
||||
|
||||
//queued reliable packets
|
||||
std::queue<BufferedPacketPtr> queued_reliables;
|
||||
|
||||
//queue commands prior splitting to packets
|
||||
std::deque<ConnectionCommandPtr> queued_commands;
|
||||
|
||||
IncomingSplitBuffer incoming_splits;
|
||||
|
||||
Channel() = default;
|
||||
~Channel() = default;
|
||||
|
||||
void UpdatePacketLossCounter(unsigned int count);
|
||||
void UpdatePacketTooLateCounter();
|
||||
void UpdateBytesSent(unsigned int bytes,unsigned int packages=1);
|
||||
void UpdateBytesLost(unsigned int bytes);
|
||||
void UpdateBytesReceived(unsigned int bytes);
|
||||
|
||||
void UpdateTimers(float dtime);
|
||||
|
||||
float getCurrentDownloadRateKB()
|
||||
{ MutexAutoLock lock(m_internal_mutex); return cur_kbps; };
|
||||
float getMaxDownloadRateKB()
|
||||
{ MutexAutoLock lock(m_internal_mutex); return max_kbps; };
|
||||
|
||||
float getCurrentLossRateKB()
|
||||
{ MutexAutoLock lock(m_internal_mutex); return cur_kbps_lost; };
|
||||
float getMaxLossRateKB()
|
||||
{ MutexAutoLock lock(m_internal_mutex); return max_kbps_lost; };
|
||||
|
||||
float getCurrentIncomingRateKB()
|
||||
{ MutexAutoLock lock(m_internal_mutex); return cur_incoming_kbps; };
|
||||
float getMaxIncomingRateKB()
|
||||
{ MutexAutoLock lock(m_internal_mutex); return max_incoming_kbps; };
|
||||
|
||||
float getAvgDownloadRateKB()
|
||||
{ MutexAutoLock lock(m_internal_mutex); return avg_kbps; };
|
||||
float getAvgLossRateKB()
|
||||
{ MutexAutoLock lock(m_internal_mutex); return avg_kbps_lost; };
|
||||
float getAvgIncomingRateKB()
|
||||
{ MutexAutoLock lock(m_internal_mutex); return avg_incoming_kbps; };
|
||||
|
||||
u16 getWindowSize() const { return m_window_size; };
|
||||
|
||||
void setWindowSize(long size)
|
||||
{
|
||||
m_window_size = (u16)rangelim(size, MIN_RELIABLE_WINDOW_SIZE, MAX_RELIABLE_WINDOW_SIZE);
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex m_internal_mutex;
|
||||
u16 m_window_size = MIN_RELIABLE_WINDOW_SIZE;
|
||||
|
||||
u16 next_incoming_seqnum = SEQNUM_INITIAL;
|
||||
|
||||
u16 next_outgoing_seqnum = SEQNUM_INITIAL;
|
||||
u16 next_outgoing_split_seqnum = SEQNUM_INITIAL;
|
||||
|
||||
unsigned int current_packet_loss = 0;
|
||||
unsigned int current_packet_too_late = 0;
|
||||
unsigned int current_packet_successful = 0;
|
||||
float packet_loss_counter = 0.0f;
|
||||
|
||||
unsigned int current_bytes_transfered = 0;
|
||||
unsigned int current_bytes_received = 0;
|
||||
unsigned int current_bytes_lost = 0;
|
||||
float max_kbps = 0.0f;
|
||||
float cur_kbps = 0.0f;
|
||||
float avg_kbps = 0.0f;
|
||||
float max_incoming_kbps = 0.0f;
|
||||
float cur_incoming_kbps = 0.0f;
|
||||
float avg_incoming_kbps = 0.0f;
|
||||
float max_kbps_lost = 0.0f;
|
||||
float cur_kbps_lost = 0.0f;
|
||||
float avg_kbps_lost = 0.0f;
|
||||
float bpm_counter = 0.0f;
|
||||
|
||||
unsigned int rate_samples = 0;
|
||||
};
|
||||
|
||||
|
||||
class UDPPeer final : public Peer
|
||||
{
|
||||
public:
|
||||
|
||||
friend class PeerHelper;
|
||||
friend class ConnectionReceiveThread;
|
||||
friend class ConnectionSendThread;
|
||||
friend class Connection;
|
||||
|
||||
UDPPeer(session_t id, const Address &address, Connection *connection);
|
||||
virtual ~UDPPeer() = default;
|
||||
|
||||
void PutReliableSendCommand(ConnectionCommandPtr &c,
|
||||
unsigned int max_packet_size) override;
|
||||
|
||||
virtual const Address &getAddress() const override {
|
||||
return address;
|
||||
}
|
||||
|
||||
u16 getNextSplitSequenceNumber(u8 channel) override;
|
||||
void setNextSplitSequenceNumber(u8 channel, u16 seqnum) override;
|
||||
|
||||
SharedBuffer<u8> addSplitPacket(u8 channel, BufferedPacketPtr &toadd,
|
||||
bool reliable) override;
|
||||
|
||||
bool isTimedOut(float timeout, std::string &reason) override;
|
||||
|
||||
protected:
|
||||
/*
|
||||
Calculates avg_rtt and resend_timeout.
|
||||
rtt=-1 only recalculates resend_timeout
|
||||
*/
|
||||
void reportRTT(float rtt) override;
|
||||
|
||||
void RunCommandQueues(
|
||||
unsigned int max_packet_size,
|
||||
unsigned int maxtransfer);
|
||||
|
||||
float getResendTimeout()
|
||||
{ MutexAutoLock lock(m_exclusive_access_mutex); return resend_timeout; }
|
||||
|
||||
void setResendTimeout(float timeout)
|
||||
{ MutexAutoLock lock(m_exclusive_access_mutex); resend_timeout = timeout; }
|
||||
|
||||
bool Ping(float dtime, SharedBuffer<u8>& data) override;
|
||||
|
||||
Channel channels[CHANNEL_COUNT];
|
||||
bool m_pending_disconnect = false;
|
||||
private:
|
||||
// This is changed dynamically
|
||||
float resend_timeout = 0.5;
|
||||
|
||||
bool processReliableSendCommand(
|
||||
ConnectionCommandPtr &c_ptr,
|
||||
unsigned int max_packet_size);
|
||||
};
|
||||
|
||||
}
|
||||
1394
src/network/mtp/threads.cpp
Normal file
1394
src/network/mtp/threads.cpp
Normal file
File diff suppressed because it is too large
Load Diff
190
src/network/mtp/threads.h
Normal file
190
src/network/mtp/threads.h
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
Copyright (C) 2017 celeron55, Loic Blot <loic.blot@unix-experience.fr>
|
||||
|
||||
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
|
||||
|
||||
/********************************************/
|
||||
/* may only be included from in src/network */
|
||||
/********************************************/
|
||||
|
||||
#include <cassert>
|
||||
#include "threading/thread.h"
|
||||
#include "network/mtp/internal.h"
|
||||
|
||||
namespace con
|
||||
{
|
||||
|
||||
class Connection;
|
||||
|
||||
struct OutgoingPacket
|
||||
{
|
||||
session_t peer_id;
|
||||
u8 channelnum;
|
||||
SharedBuffer<u8> data;
|
||||
bool reliable;
|
||||
bool ack;
|
||||
|
||||
OutgoingPacket(session_t peer_id_, u8 channelnum_, const SharedBuffer<u8> &data_,
|
||||
bool reliable_,bool ack_=false):
|
||||
peer_id(peer_id_),
|
||||
channelnum(channelnum_),
|
||||
data(data_),
|
||||
reliable(reliable_),
|
||||
ack(ack_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class ConnectionSendThread : public Thread
|
||||
{
|
||||
|
||||
public:
|
||||
friend class UDPPeer;
|
||||
|
||||
ConnectionSendThread(unsigned int max_packet_size, float timeout);
|
||||
|
||||
void *run();
|
||||
|
||||
void Trigger();
|
||||
|
||||
void setParent(Connection *parent)
|
||||
{
|
||||
assert(parent != NULL); // Pre-condition
|
||||
m_connection = parent;
|
||||
}
|
||||
|
||||
void setPeerTimeout(float peer_timeout) { m_timeout = peer_timeout; }
|
||||
|
||||
private:
|
||||
void runTimeouts(float dtime, u32 peer_packet_quota);
|
||||
void resendReliable(Channel &channel, const BufferedPacket *k, float resend_timeout);
|
||||
void rawSend(const BufferedPacket *p);
|
||||
bool rawSendAsPacket(session_t peer_id, u8 channelnum,
|
||||
const SharedBuffer<u8> &data, bool reliable);
|
||||
|
||||
void processReliableCommand(ConnectionCommandPtr &c);
|
||||
void processNonReliableCommand(ConnectionCommandPtr &c);
|
||||
void serve(Address bind_address);
|
||||
void connect(Address address);
|
||||
void disconnect();
|
||||
void disconnect_peer(session_t peer_id);
|
||||
void send(session_t peer_id, u8 channelnum, const SharedBuffer<u8> &data);
|
||||
void sendReliable(ConnectionCommandPtr &c);
|
||||
void sendToAll(u8 channelnum, const SharedBuffer<u8> &data);
|
||||
void sendToAllReliable(ConnectionCommandPtr &c);
|
||||
|
||||
void sendPackets(float dtime, u32 peer_packet_quota);
|
||||
|
||||
void sendAsPacket(session_t peer_id, u8 channelnum, const SharedBuffer<u8> &data,
|
||||
bool ack = false);
|
||||
|
||||
void sendAsPacketReliable(BufferedPacketPtr &p, Channel *channel);
|
||||
|
||||
bool packetsQueued();
|
||||
|
||||
Connection *m_connection = nullptr;
|
||||
unsigned int m_max_packet_size;
|
||||
float m_timeout;
|
||||
std::queue<OutgoingPacket> m_outgoing_queue;
|
||||
Semaphore m_send_sleep_semaphore;
|
||||
|
||||
unsigned int m_iteration_packets_avaialble;
|
||||
unsigned int m_max_data_packets_per_iteration;
|
||||
unsigned int m_max_packets_requeued = 256;
|
||||
};
|
||||
|
||||
class ConnectionReceiveThread : public Thread
|
||||
{
|
||||
public:
|
||||
ConnectionReceiveThread();
|
||||
|
||||
void *run();
|
||||
|
||||
void setParent(Connection *parent)
|
||||
{
|
||||
assert(parent); // Pre-condition
|
||||
m_connection = parent;
|
||||
}
|
||||
|
||||
private:
|
||||
void receive(SharedBuffer<u8> &packetdata, bool &packet_queued);
|
||||
|
||||
// Returns next data from a buffer if possible
|
||||
// If found, returns true; if not, false.
|
||||
// If found, sets peer_id and dst
|
||||
bool getFromBuffers(session_t &peer_id, SharedBuffer<u8> &dst);
|
||||
|
||||
bool checkIncomingBuffers(
|
||||
Channel *channel, session_t &peer_id, SharedBuffer<u8> &dst);
|
||||
|
||||
/*
|
||||
Processes a packet with the basic header stripped out.
|
||||
Parameters:
|
||||
packetdata: Data in packet (with no base headers)
|
||||
peer_id: peer id of the sender of the packet in question
|
||||
channelnum: channel on which the packet was sent
|
||||
reliable: true if recursing into a reliable packet
|
||||
*/
|
||||
SharedBuffer<u8> processPacket(Channel *channel,
|
||||
const SharedBuffer<u8> &packetdata, session_t peer_id,
|
||||
u8 channelnum, bool reliable);
|
||||
|
||||
SharedBuffer<u8> handlePacketType_Control(Channel *channel,
|
||||
const SharedBuffer<u8> &packetdata, Peer *peer, u8 channelnum,
|
||||
bool reliable);
|
||||
SharedBuffer<u8> handlePacketType_Original(Channel *channel,
|
||||
const SharedBuffer<u8> &packetdata, Peer *peer, u8 channelnum,
|
||||
bool reliable);
|
||||
SharedBuffer<u8> handlePacketType_Split(Channel *channel,
|
||||
const SharedBuffer<u8> &packetdata, Peer *peer, u8 channelnum,
|
||||
bool reliable);
|
||||
SharedBuffer<u8> handlePacketType_Reliable(Channel *channel,
|
||||
const SharedBuffer<u8> &packetdata, Peer *peer, u8 channelnum,
|
||||
bool reliable);
|
||||
|
||||
struct PacketTypeHandler
|
||||
{
|
||||
SharedBuffer<u8> (ConnectionReceiveThread::*handler)(Channel *channel,
|
||||
const SharedBuffer<u8> &packet, Peer *peer, u8 channelnum,
|
||||
bool reliable);
|
||||
};
|
||||
|
||||
struct RateLimitHelper {
|
||||
u64 time = 0;
|
||||
int counter = 0;
|
||||
bool logged = false;
|
||||
|
||||
void tick() {
|
||||
u64 now = porting::getTimeS();
|
||||
if (time != now) {
|
||||
time = now;
|
||||
counter = 0;
|
||||
logged = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static const PacketTypeHandler packetTypeRouter[PACKET_TYPE_MAX];
|
||||
|
||||
Connection *m_connection = nullptr;
|
||||
|
||||
RateLimitHelper m_new_peer_ratelimit;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user