📄 trackernet.cpp
字号:
//=========== (C) Copyright 2000 Valve, L.L.C. All rights reserved. ===========
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose:
//=============================================================================
#include "PacketHeader.h"
#include "ReceiveMessage.h"
#include "Random.h"
#include "SendMessage.h"
#include "Sockets.h"
#include "Threads.h"
#include "ThreadedSocket.h"
#include "TrackerNET_Interface.h"
#include "TrackerProtocol.h"
#include "UtlLinkedList.h"
#include "UtlRBTree.h"
#include <math.h>
#include <stdarg.h>
#include <time.h>
#include <assert.h>
#include <stdio.h>
#pragma warning(disable: 4244) // warning C4244: 'argument' : conversion from 'int' to 'unsigned char', possible loss of data
// disabled for the CUtlLinkedList that uses an unsigned char as it's index
#pragma warning(disable: 4710) // warning C4710: function not expanded
// networking constants
static const float ACK_DELAY = 0.2f;
static const float RESEND_TIME = 1.5f; // time before resending an unacknowledged packet
static const int MAX_SEND_ATTEMPTS = 5; // max number of times a packet is sent before we fail
static const int MESSAGE_WINDOW = 16; // range of packets to hold in sliding message window
// used for mapping ip's to targets
struct TargetMapItem_t
{
CNetAddress netAddress;
int targetIndex;
};
//-----------------------------------------------------------------------------
// Purpose: implementation of the trackerNet interface
//-----------------------------------------------------------------------------
class CTrackerNET : public ITrackerNET
{
public:
CTrackerNET();
~CTrackerNET();
// sets up the networking thread and the port to listen on
bool Initialize(unsigned short portMin, unsigned short portMax);
// closes the network; if flush is true, then it blocks until the worker threads have completed
void Shutdown(bool flush);
void RunFrame();
// message receiving
IReceiveMessage *GetIncomingData(); // Gets any info has been received, returns NULL if no more
IReceiveMessage *GetFailedMessage();
void ReleaseMessage(IReceiveMessage *); // frees a received message
IBinaryBuffer *GetIncomingRawData(CNetAddress &address); // gets any info that has been received from out-of-band engine packets
IBinaryBuffer *CreateRawMessage();
void SendRawMessage(IBinaryBuffer *message, CNetAddress &address);
// message sending
ISendMessage *CreateMessage(int msgID);
ISendMessage *CreateMessage(int msgID, void const *pBuf, int bufSize);
ISendMessage *CreateReply(int msgID, IReceiveMessage *msgToReplyTo);
void SendMessage(ISendMessage *pMsg, Reliability_e state);
// network addresses
CNetAddress GetNetAddress(const char *stringAddress);
CNetAddress GetLocalAddress();
// thread API access
IThreads *GetThreadAPI();
int GetPort();
virtual void deleteThis()
{
delete this;
}
// returns the number of buffers current in the input and output queues
int BuffersInQueue();
int PeakBuffersInQueue();
int BytesSent();
int BytesReceived();
int BytesSentPerSecond();
int BytesReceivedPerSecond();
void SetWindowsEvent(unsigned long event);
private:
// connection creation
struct NetworkTarget_t;
int FindTarget(CNetAddress &address, int targetID = -1, int sequenceNumber = -1);
void InternalSendMessage(ISendMessage *pMsg, NetworkTarget_t *target, int sequenceNumber);
void ParseIncomingPacket(IBinaryBuffer *packet, CNetAddress netAddress, bool encrypted);
void CheckReliablePacketSending();
void CheckSendingAcknowledgements();
void UpdateSequence(NetworkTarget_t *target, int targetIndex, int incomingSequence);
void HandleOutOfSequencePacket(IBinaryBuffer *packet, packet_header_t *pHeader, NetworkTarget_t *target, CNetAddress &netAddress, bool encrypted);
int FindTargetIndexByAddress(CNetAddress &addr);
#ifdef _DEBUG
// debug utility function
void WriteToLog(const char *str, ...);
#else
void WriteToLog(const char *str, ...) {}
#endif
// pointers to our networking interfaces
IThreadedSocket *m_pThreadedSocket;
IThreads *m_pThreads;
ISockets *m_pSockets;
float m_flNextFrameTime; // next time at which we should Run a frame
unsigned long m_hEvent; // handle to event to signal
// holds a single sent message, in case it has to be resent
struct SentMessage_t
{
int sequenceNumber;
float resendTime; // time to resend (in seconds)
int resendAttempt; // the number of resends sent
int networkTargetHandle; // handle to the network target this was sent from
int networkTargetID;
ISendMessage *message;
};
// holds messages we are received, but not ready to use
struct FutureMessage_t
{
int sequenceNumber;
IReceiveMessage *message;
};
// target list
struct NetworkTarget_t
{
NetworkTarget_t() : m_MessageWindow(MESSAGE_WINDOW, 0) {}
CNetAddress netAddress;
int targetID; // unique identifier of this connection
int incomingSequence; // the highest valid ID received from the target
int incomingAcknowledged; // the highest valid ID received from the target that we have sent an acknowledgement to
int outgoingSequence; // the highest (most recent) packet ID sent
int outgoingAcknowledged; // the highest packet ID that has been ack'd by the target
// packets in the range (outgoingAcknowledged, outgoingSequence] will get auto-resent
float ackTime; // time at which the next ack can be sent
bool needAck; // true if an ack has been marked to be send
// float createTime; // time at which this connection was created (unused)
// float activityTime; // time at which there was last activity on this connection (unused for now)
CUtlLinkedList<FutureMessage_t, unsigned char> m_MessageWindow;
};
// index-based linked list of network targets - essentially safe handles to targets
CUtlLinkedList<NetworkTarget_t, int> m_TargetList;
int m_iHighestAckTarget;
// list of messages sent reliably, and may need to be resent
CUtlLinkedList<SentMessage_t, int> m_ReliableMessages;
// map to the targets
CUtlRBTree<TargetMapItem_t, int> m_TargetMap;
// holds the list of all failed messages
struct FailedMsg_t
{
IReceiveMessage *message;
};
CUtlLinkedList<FailedMsg_t, int> m_FailedMsgs;
// messages that we've received, in order
struct ReceivedMsg_t
{
IReceiveMessage *message;
};
CUtlLinkedList<ReceivedMsg_t, int> m_ReceivedMsgs;
};
EXPOSE_INTERFACE(CTrackerNET, ITrackerNET, TRACKERNET_INTERFACE_VERSION);
//-----------------------------------------------------------------------------
// Purpose: comparison function for TargetMapItem_t
//-----------------------------------------------------------------------------
bool TargetMapLessFunc(const TargetMapItem_t &t1, const TargetMapItem_t &t2)
{
return (t1.netAddress < t2.netAddress);
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CTrackerNET::CTrackerNET()
{
m_flNextFrameTime = 0.0f;
m_iHighestAckTarget = -1;
m_hEvent = 0;
m_TargetMap.SetLessFunc(TargetMapLessFunc);
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CTrackerNET::~CTrackerNET()
{
if (m_pThreadedSocket)
{
m_pThreadedSocket->deleteThis();
m_pThreadedSocket = NULL;
}
}
//-----------------------------------------------------------------------------
// Purpose: Sets up the networking
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CTrackerNET::Initialize(unsigned short portMin, unsigned short portMax)
{
// initialize threading
CreateInterfaceFn factory = Sys_GetFactoryThis();
m_pThreads = (IThreads *)factory(THREADS_INTERFACE_VERSION, NULL);
// initialize socket layer
m_pThreadedSocket = (IThreadedSocket *)factory(THREADEDSOCKET_INTERFACE_VERSION, NULL);
m_pSockets = m_pThreadedSocket->GetSocket();
if (!m_pSockets->InitializeSockets(portMin, portMax))
return false;
return true;
}
//-----------------------------------------------------------------------------
// Purpose: closes and shutsdown the network
// Input : flush - if flush is true, then it blocks until the worker threads have completed
//-----------------------------------------------------------------------------
void CTrackerNET::Shutdown(bool flush)
{
m_pThreadedSocket->Shutdown(flush);
m_pThreadedSocket->deleteThis();
m_pThreadedSocket = NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Runs a networking frame, handling reliable packet delivery
//-----------------------------------------------------------------------------
void CTrackerNET::RunFrame()
{
float time = (float)m_pThreads->GetTime();
// don't Run the frame more than 20 times per second
if (time < m_flNextFrameTime)
return;
m_flNextFrameTime = time + 0.05f;
// read in any new packets
IBinaryBuffer *recvBuf = NULL;
bool encrypted = false;
CNetAddress address;
// don't read too many packets in one frame
int packetsToRead = 10;
while (m_pThreadedSocket->GetNewMessage(&recvBuf, &address, encrypted) && packetsToRead--)
{
ParseIncomingPacket(recvBuf, address, encrypted);
}
// check to see if we need to send any ack packets in response to reliable messages we've received
CheckSendingAcknowledgements();
// see if any of our packets need to be resent
CheckReliablePacketSending();
}
//-----------------------------------------------------------------------------
// Purpose: Checks to see if we need to send any ack packets
// in response to reliable messages we've received
//-----------------------------------------------------------------------------
void CTrackerNET::CheckSendingAcknowledgements()
{
float time = (float)m_pThreads->GetTime();
// the target list is in order of ackTime's
while (m_TargetList.Count())
{
int targetIndex = m_TargetList.Head();
NetworkTarget_t &target = m_TargetList[targetIndex];
if (!target.needAck)
break; // target does not need acknowledging, we're at the end of the list
if (target.ackTime > time)
break; // this packet not yet ready to be acknowledged
if (target.incomingSequence > target.incomingAcknowledged)
{
// send an acknowledgement
ISendMessage *pMsg = CreateMessage(TMSG_ACK);
pMsg->SetNetAddress(target.netAddress);
SendMessage(pMsg, NET_UNRELIABLE);
}
// this target has been acknowledged, move to the end of the list
target.needAck = false;
m_TargetList.Unlink(targetIndex);
m_TargetList.LinkToTail(targetIndex);
// check to see if the highest ack target needs to be reset
if (targetIndex == m_iHighestAckTarget)
{
m_iHighestAckTarget = -1;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Checks to see if we should resend any reliable packets
//-----------------------------------------------------------------------------
void CTrackerNET::CheckReliablePacketSending()
{
float time = m_pThreads->GetTime();
// reliable packets are held in the m_ReliableMessages queue in the order that they were sent
// if a packet is resent, it is moved to the end of the queue
// packets are also checked at this time to see if they have been acknowledged
while (m_ReliableMessages.Count())
{
// get the first message
int index = m_ReliableMessages.Head();
// get the message
SentMessage_t &msg = m_ReliableMessages[index];
// check the resend time, if it's not time to resend then we have nothing more to do
if (msg.resendTime > time)
break;
// get the network target for this message
if (!m_TargetList.IsValidIndex(msg.networkTargetHandle)
|| !m_TargetList.IsInList(msg.networkTargetHandle)
|| m_TargetList[msg.networkTargetHandle].targetID != msg.networkTargetID)
{
// message's target has been removed, kill
if (msg.message)
{
msg.message->deleteThis();
}
m_ReliableMessages.Remove(index);
continue;
}
NetworkTarget_t &target = m_TargetList[msg.networkTargetHandle];
// check to see if it's already been acknowledged
if (msg.sequenceNumber <= target.outgoingAcknowledged)
{
// message has been acknowledged, kill
if (msg.message)
{
msg.message->deleteThis();
}
m_ReliableMessages.Remove(index);
// move onto next message
continue;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -