You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/CryEngine/CrySystem/ServiceNetwork.h

476 lines
13 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Service network implementation
#pragma once
//-----------------------------------------------------------------------------
#include "IServiceNetwork.h"
#include <AzCore/Socket/AzSocket_fwd.h>
class CServiceNetwork;
//-----------------------------------------------------------------------------
// General message buffer
class CServiceNetworkMessage
: public IServiceNetworkMessage
{
private:
void* m_pData;
uint32 m_id;
uint32 m_size;
int volatile m_refCount;
public:
CServiceNetworkMessage(const uint32 id, const uint32 size);
virtual ~CServiceNetworkMessage();
// IServiceNetworMessage interface
virtual uint32 GetId() const;
virtual uint32 GetSize() const;
virtual void* GetPointer();
virtual const void* GetPointer() const;
virtual struct IDataReadStream* CreateReader() const;
virtual void AddRef();
virtual void Release();
};
//-----------------------------------------------------------------------------
// General network TCP/IP connection
class CServiceNetworkConnection
: public IServiceNetworkConnection
{
public:
friend class CServiceNetworkListener;
// maximum size of a single message (0.5MB by default)
static const uint32 kMaximumMessageSize = 5 << 19;
// initialization message send period (ms)
static const uint64 kInitializationPerior = 1000;
// keep alive period (ms), by default every 2s
static const uint64 kKeepAlivePeriod = 2000;
// reconnection retries period (ms)
static const uint64 kReconnectTryPerior = 1000;
// timeout for assuming server side connection dead (reconnection timeout)
static const uint64 hReconnectTimeOut = 30 * 1000;
// communication time out (ms)
static const uint64 kTimeout = 5000;
// Type of endpoint
enum EEndpoint
{
// This is the server side of the connection (on the side of the listening socket)
eEndpoint_Server,
// This is the client side of the connection (we connected to the listening socket)
eEndpoint_Client,
};
// Internal state machine
enum EState
{
// Connection is initializing
eState_Initializing,
// Connection is valid
eState_Valid,
// Operation on the socket failed (we may need to reconnect)
eState_Lost,
// Connection is closed
eState_Closed,
};
// Command IDs, do not change the numerical values
enum ECommand
{
// Data block command
eCommand_Data = 1,
// Keep alive command
eCommand_KeepAlive = 2,
// Initialize communication channel (sent only once)
eCommand_Initialize = 3,
};
#pragma pack(push)
#pragma pack(1)
struct Header
{
uint8 m_cmd;
uint32 m_size;
void Swap();
};
struct InitHeader
{
uint8 m_cmd;
uint8 m_pad0;
uint8 m_pad1;
uint8 m_pad2;
uint32 m_tryCount;
uint64 m_guid0;
uint64 m_guid1;
void Swap();
};
#pragma pack(pop)
private:
CServiceNetwork* m_pManager;
// Type of endpoint (client/server)
EEndpoint m_endpointType;
// Connection state (internal)
EState m_state;
// Reference count (updated using CryInterlocked* functions)
int volatile m_refCount;
// Internal socket data
AZSOCKET m_socket;
// Local address
ServiceNetworkAddress m_localAddress;
// Remote connection address
ServiceNetworkAddress m_remoteAddress;
// Internal connection ID (unique)
CryGUID m_connectionID;
// Internal time counters
uint64 m_lastReconnectTime;
uint64 m_lastKeepAliveSendTime;
uint64 m_lastMessageReceivedTime;
uint64 m_lastInitializationSendTime;
uint32 m_reconnectTryCount;
// Statistics (updated from threads using CryIntelocked* functions)
volatile uint32 m_statsNumPacketsSend;
volatile uint32 m_statsNumPacketsReceived;
volatile uint32 m_statsNumDataSend;
volatile uint32 m_statsNumDataReceived;
// Queue of messages to send (thread access possible)
typedef CryMT::CLocklessPointerQueue< CServiceNetworkMessage > TSendQueue;
CServiceNetworkMessage* m_pSendedMessages;
TSendQueue m_pSendQueue;
uint32 m_messageDataSentSoFar;
volatile int m_sendQueueDataSize;
// Queue of received message
typedef CryMT::CLocklessPointerQueue< CServiceNetworkMessage > TReceiveQueue;
TReceiveQueue m_pReceiveQueue;
uint32 m_receiveQueueDataSize;
uint32 m_messageDataReceivedSoFar;
uint32 m_messageReceiveLength;
// Message being received "right now"
CServiceNetworkMessage* m_pCurrentReceiveMessage;
uint32 m_messageDummyReadLength;
// External request to close this connection was issued
bool m_bCloseRequested;
// Do not accept any new data for sending or receiving
bool m_bDisableCommunication;
public:
ILINE bool IsInitialized() const
{
return m_state != eState_Initializing;
}
ILINE bool IsSendingQueueEmpty() const
{
return m_pSendQueue.empty();
}
ILINE CServiceNetwork* GetManager() const
{
return m_pManager;
}
public:
CServiceNetworkConnection(
class CServiceNetwork* manager,
EEndpoint endpointType,
AZSOCKET socket,
const CryGUID& connectionID,
const ServiceNetworkAddress& localAddress,
const ServiceNetworkAddress& remoteAddress);
virtual ~CServiceNetworkConnection();
// IServiceNetworkConnection interface implementation
virtual const ServiceNetworkAddress& GetRemoteAddress() const;
virtual const ServiceNetworkAddress& GetLocalAddress() const;
virtual const CryGUID& GetGUID() const;
virtual bool IsAlive() const;
virtual uint32 GetMessageSendCount() const;
virtual uint32 GetMessageReceivedCount() const;
virtual uint64 GetMessageSendDataSize() const;
virtual uint64 GetMessageReceivedDataSize() const;
virtual bool SendMsg(IServiceNetworkMessage* message);
virtual IServiceNetworkMessage* ReceiveMsg();
virtual void FlushAndClose(const uint32 timeout);
virtual void FlushAndWait();
virtual void Close();
virtual void AddRef();
virtual void Release();
// All remote connections are updated on the client side
// This is called from service network update thread, try not to call by hand :)
void Update();
private:
void ProcessSendingQueue();
void ProcessReceivingQueue();
// Keep alive message handling
void ProcessKeepAlive();
void SendKeepAlive(const uint64 currentNetworkTime);
bool HandleTimeout(const uint64 currentNetworkTime);
// Handle the reconnection request
bool HandleReconnect(AZSOCKET socket, const uint32 tryCount);
// General send/receive functions with error handling.
// If socket error occurs the connection will be put in the lost state.
uint32 TrySend(const void* dataBuffer, uint32 dataSize, bool autoHandleErrors);
// Internal receive function with error handling
uint32 TryReceive(void* dataBuffer, uint32 dataSize, bool autoHandleErrors);
// Try to reconnect to the remote address
bool TryReconnect();
// Try to send the initialization header
bool TryInitialize();
// Low-level socket shutdown (hash way)
void Shutdown();
// Reset the connection (put in the lost state and reconnect)
void Reset();
};
//-----------------------------------------------------------------------------
// TCP/IP listener
class CServiceNetworkListener
: public IServiceNetworkListener
{
typedef CServiceNetworkConnection::InitHeader TInitHeader;
struct PendingConnection
{
// Connection socket
AZSOCKET m_socket;
// Initialization of initialization header received so far
uint32 m_dataReceivedSoFar;
// Initialization header
TInitHeader m_initHeader;
// Remote address (as returned from accept)
ServiceNetworkAddress m_remoteAddress;
};
protected:
// Owner (the manager)
CServiceNetwork* m_pManager;
// Reference count, updated using CryInterlocked* functions
int volatile m_refCount;
// Listening socket
AZSOCKET m_socket;
// Local address (usually has the IP in 127.0.0.1:port form)
ServiceNetworkAddress m_localAddress;
// Request to close this listener was received
bool m_closeRequestReceived;
// Pending connections (but not yet initialized)
typedef std::vector< PendingConnection* > TPendingConnectionList;
TPendingConnectionList m_pPendingConnections;
// All active connections spawned from this listener
typedef std::vector< CServiceNetworkConnection* > TConnectionList;
TConnectionList m_pLocalConnections;
// Access lock for the class members (thread safe)
CryMutex m_accessLock;
public:
ILINE CServiceNetwork* GetManager() const
{
return m_pManager;
}
public:
CServiceNetworkListener(CServiceNetwork* pManager, AZSOCKET socket, const ServiceNetworkAddress& address);
virtual ~CServiceNetworkListener();
void Update();
// IServiceNetworkListener interface implementation
virtual const ServiceNetworkAddress& GetLocalAddress() const;
virtual uint32 GetConnectionCount() const;
virtual IServiceNetworkConnection* Accept();
virtual bool IsAlive() const;
virtual void AddRef();
virtual void Release();
virtual void Close();
private:
void ProcessIncomingConnections();
void ProcessPendingConnections();
};
//-----------------------------------------------------------------------------
// TCP/IP manager for service connection channels
class CServiceNetwork
: public IServiceNetwork
, public CryRunnable
{
#if defined(AZ_RESTRICTED_PLATFORM)
#include AZ_RESTRICTED_FILE(ServiceNetwork_h)
#endif
protected:
struct ConnectionToClose
{
CServiceNetworkConnection* pConnection;
// timeout for forded close
uint64 maxWaitTime;
};
protected:
// Local listeners
typedef std::vector< CServiceNetworkListener* > TListenerArray;
TListenerArray m_pListeners;
// Local connections
typedef std::vector< CServiceNetworkConnection* > TConnectionArray;
TConnectionArray m_pConnections;
// Connections that are waiting for all of their data to be sent before closing
typedef std::vector< ConnectionToClose > TConnectionsToCloseArray;
TConnectionsToCloseArray m_connectionsToClose;
// We are running on threads, needed to sync the access to arrays
CryMutex m_accessMutex;
// Current network time (ms)
uint64 m_networkTime;
// Exit was requested
bool m_bExitRequested;
// Message verbose level
ICVar* m_pVerboseLevel;
// Thread
typedef CryThread< CServiceNetwork > TServiceNetworkThread;
TServiceNetworkThread* m_pThread;
// Buffer ID allocator (unique, incremented atomically using CryInterlockedIncrement)
volatile int m_bufferID;
// Random number generator for GUID creation
CRndGen m_guidGenerator;
// Send/Receive queue size limit
ICVar* m_pReceiveDataQueueLimit;
ICVar* m_pSendDataQueueLimit;
public:
ILINE const uint64 GetNetworkTime() const
{
return m_networkTime;
}
ILINE const CServiceNetwork* GetManager() const
{
return this;
}
ILINE const uint32 GetReceivedDataQueueLimit() const
{
return m_pReceiveDataQueueLimit->GetIVal();
}
ILINE const uint32 GetSendDataQueueLimit() const
{
return m_pSendDataQueueLimit->GetIVal();
}
public:
CServiceNetwork();
virtual ~CServiceNetwork();
// IServiceNetwork interface implementation
virtual void SetVerbosityLevel(const uint32 level);
virtual IServiceNetworkMessage* AllocMessageBuffer(const uint32 size);
virtual struct IDataWriteStream* CreateMessageWriter();
virtual struct IDataReadStream* CreateMessageReader(const void* pData, const uint32 dataSize);
virtual ServiceNetworkAddress GetHostAddress(const string& addressString, uint16 optionalPort = 0) const;
virtual IServiceNetworkListener* CreateListener(uint16 localPort);
virtual IServiceNetworkConnection* Connect(const ServiceNetworkAddress& remoteAddress);
// CryRunnable
virtual void Run();
virtual void Cancel();
// Register connection in the connection list (thread safe)
void RegisterConnection(CServiceNetworkConnection& con);
// Register connection for closing one all of the outgoing messages are sent
void RegisterForDeferredClose(CServiceNetworkConnection& con, const uint32 timeout);
// Debug print
#ifdef RELEASE
void Log([[maybe_unused]] const char* txt, ...) const {};
bool CheckVerbose([[maybe_unused]] const uint32 level) const { return false; }
#else
void Log(const char* txt, ...) const;
bool CheckVerbose(const uint32 level) const;
#endif
};
//-----------------------------------------------------------------------------