Split internal parts from connection.h

This commit is contained in:
sfan5 2024-03-07 15:10:52 +01:00
parent dfba79f8ff
commit 24f2c38093
5 changed files with 562 additions and 522 deletions

@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <cerrno> #include <cerrno>
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include "connection.h" #include "connection_internal.h"
#include "serialization.h" #include "serialization.h"
#include "log.h" #include "log.h"
#include "porting.h" #include "porting.h"

@ -32,95 +32,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <vector> #include <vector>
#include <map> #include <map>
#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
class NetworkPacket; class NetworkPacket;
namespace con namespace con
@ -129,336 +40,19 @@ namespace con
class ConnectionReceiveThread; class ConnectionReceiveThread;
class ConnectionSendThread; class ConnectionSendThread;
typedef enum MTProtocols { enum MTProtocols {
MTP_PRIMARY, MTP_PRIMARY,
MTP_UDP, MTP_UDP,
MTP_MINETEST_RELIABLE_UDP MTP_MINETEST_RELIABLE_UDP
} MTProtocols;
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) enum rate_stat_type {
{ CUR_DL_RATE,
if (totest > base) AVG_DL_RATE,
{ CUR_INC_RATE,
if ((totest - base) > (SEQNUM_MAX/2)) AVG_INC_RATE,
return false; CUR_LOSS_RATE,
AVG_LOSS_RATE,
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));
}
static inline float CALC_DTIME(u64 lasttime, u64 curtime)
{
float value = ( curtime - lasttime) / 1000.0;
return MYMAX(MYMIN(value,0.1),0.0);
}
/*
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
};
typedef std::shared_ptr<BufferedPacket> BufferedPacketPtr;
// This adds the base headers to the data and makes a packet out of it
BufferedPacketPtr makePacket(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:
ReliablePacketBuffer() = default;
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
};
struct ConnectionCommand;
typedef std::shared_ptr<ConnectionCommand> ConnectionCommandPtr;
// 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 Peer; class Peer;
@ -480,16 +74,54 @@ private:
Peer *m_peer = nullptr; Peer *m_peer = nullptr;
}; };
class Connection; /*
Connection
*/
typedef enum { enum ConnectionEventType {
CUR_DL_RATE, CONNEVENT_NONE,
AVG_DL_RATE, CONNEVENT_DATA_RECEIVED,
CUR_INC_RATE, CONNEVENT_PEER_ADDED,
AVG_INC_RATE, CONNEVENT_PEER_REMOVED,
CUR_LOSS_RATE, CONNEVENT_BIND_FAILED,
AVG_LOSS_RATE, };
} rate_stat_type;
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 { class Peer {
public: public:
@ -615,102 +247,7 @@ class Peer {
u64 m_last_timeout_check; u64 m_last_timeout_check;
}; };
class UDPPeer final : public Peer class UDPPeer;
{
public:
friend class PeerHelper;
friend class ConnectionReceiveThread;
friend class ConnectionSendThread;
friend class Connection;
UDPPeer(u16 a_id, Address a_address, Connection* connection);
virtual ~UDPPeer() = default;
void PutReliableSendCommand(ConnectionCommandPtr &c,
unsigned int max_packet_size) override;
bool getAddress(MTProtocols type, Address& toset) override;
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);
};
/*
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_) {}
};
class PeerHandler;
class Connection class Connection
{ {

@ -0,0 +1,499 @@
/*
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
/********************************************/
/* may only be included from in src/network */
/********************************************/
#include "connection.h"
#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);
}
/*
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(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:
ReliablePacketBuffer() = default;
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(u16 id, Address address, Connection *connection);
virtual ~UDPPeer() = default;
void PutReliableSendCommand(ConnectionCommandPtr &c,
unsigned int max_packet_size) override;
bool getAddress(MTProtocols type, Address& toset) override;
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);
};
}

@ -20,9 +20,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once #pragma once
/********************************************/
/* may only be included from in src/network */
/********************************************/
#include <cassert> #include <cassert>
#include "threading/thread.h" #include "threading/thread.h"
#include "connection.h" #include "connection_internal.h"
namespace con namespace con
{ {

@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "porting.h" #include "porting.h"
#include "settings.h" #include "settings.h"
#include "util/serialize.h" #include "util/serialize.h"
#include "network/connection.h" #include "network/connection_internal.h"
#include "network/networkpacket.h" #include "network/networkpacket.h"
#include "network/socket.h" #include "network/socket.h"