基于UDP的网络同步框架

一.UDP协议简介

UDP(User Datagram Protocol)是一种无连接的传输层协议,属于OSI模型中的传输层。其核心特点如下:

  • 1.无连接: 无需三次握手建立连接,直接发送数据报(datagram),适合快速传输但对可靠性要求不高的场景。
  • 2.不可靠传输: 不保证数据顺序、不确认接收、不重传丢失包,因此可能出现丢包、乱序或重复包。
  • 3.低开销: 协议头仅8字节(源/目的端口、长度、校验和),远小于TCP的20字节,传输效率高。
  • 4.支持广播/多播: 可一对多发送数据,适合实时音视频流、在线游戏状态同步等场景。
  • 5.应用场景: 实时音视频(如WebRTC)、在线游戏、DNS查询、传感器数据传输等。

二.UDP套接字封装

网络地址封装实现:
SocketAddress.h

#pragma once

class SocketAddress
{
public:
	SocketAddress(uint32_t inAddress, uint16_t inPort)
	{
		//使用IPV4协议
		GetAsSockAddrIn()->sin_family = AF_INET;
		//主机字节序转换为网络字节序
		GetAsSockAddrIn()->sin_addr.S_un.S_addr = htonl(inAddress);//ip地址
		GetAsSockAddrIn()->sin_port = htons(inPort);//端口
	}
	SocketAddress(const sockaddr& inSockAddr)
	{
		//复制inSockAddr到成员mSockAddr
		memcpy(&mSockAddr, &inSockAddr, sizeof(sockaddr));
	}

	SocketAddress()
	{
		//使用IPV4协议
		GetAsSockAddrIn()->sin_family = AF_INET;
		//IP地址设置为INADDR_ANY(表示接受所有可用的网络接口)
		GetIP4Ref() = INADDR_ANY;
		//设置端口号
		GetAsSockAddrIn()->sin_port = 0;
	}
	//比较地址族 端口号 IP地址
	bool operator==(const SocketAddress& inOther) const noexcept
	{
		if (this == &inOther) return true;
		return (mSockAddr.sa_family == AF_INET &&
			GetAsSockAddrIn()->sin_port == inOther.GetAsSockAddrIn()->sin_port) &&
			(GetIP4Ref() == inOther.GetIP4Ref());
	}

	//计算SocketAddress对象哈希值
	size_t GetHash() const noexcept
	{
		auto hash_combine = [](size_t seed, auto v) {
			return seed ^ (std::hash<decltype(v)>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2));
			};

		size_t hash = std::hash<uint32_t>()(GetIP4Ref());
		hash = hash_combine(hash, ntohs(GetAsSockAddrIn()->sin_port));
		hash = hash_combine(hash, mSockAddr.sa_family);
		return hash;
	}

	uint32_t GetSize() const { return sizeof(sockaddr); }
	string ToString() const;
private:
	friend class UDPSocket;
	friend class TCPSocket;
	//存储socket地址信息
	sockaddr mSockAddr;
	//获取IPV4地址引用
#if _WIN32
	uint32_t& GetIP4Ref() { return *reinterpret_cast<uint32_t*>(&GetAsSockAddrIn()->sin_addr.S_un.S_addr); }
	const uint32_t& GetIP4Ref()	const { return *reinterpret_cast<const uint32_t*>(&GetAsSockAddrIn()->sin_addr.S_un.S_addr); }
#else
	uint32_t& GetIP4Ref() { return GetAsSockAddrIn()->sin_addr.s_addr; }
	const uint32_t& GetIP4Ref()	const { return GetAsSockAddrIn()->sin_addr.s_addr; }
#endif
	//获取指向sockaddr_in结构体的指针 方便访问和操作sockaddr结构体中的IPv4地址和端口信息
	sockaddr_in* GetAsSockAddrIn()
	{
		return reinterpret_cast<sockaddr_in*>(&mSockAddr);
	}
	const sockaddr_in* GetAsSockAddrIn() const
	{
		return reinterpret_cast<const sockaddr_in*>(&mSockAddr);
	}
};

typedef shared_ptr<SocketAddress> SocketAddressPtr;

/*
为自定义类型 SocketAddress 提供哈希计算功能,
使其可以作为无序容器(如 std::unordered_map、std::unordered_set)的键
*/
namespace std {
	template<>
	struct hash<SocketAddress> {
		size_t operator()(const SocketAddress& sa) const noexcept {
			return sa.GetHash();
		}
	};
}

SocketAddress.cpp


#include"MalouPCH.h"

string SocketAddress::ToString() const
{
#if _WIN32
	const sockaddr_in* s = GetAsSockAddrIn();
	wchar_t destinationBuffer[128];
	if (s->sin_family == AF_INET)
	{
		snprintf((char*)destinationBuffer, sizeof(destinationBuffer),
			"%d.%d.%d.%d",
			s->sin_addr.s_addr & 0xFF,
			(s->sin_addr.s_addr >> 8) & 0xFF,
			(s->sin_addr.s_addr >> 16) & 0xFF,
			(s->sin_addr.s_addr >> 24) & 0xFF);
	}
	else
	{
		InetNtop(s->sin_family, const_cast<in_addr*>(&s->sin_addr), destinationBuffer, sizeof(destinationBuffer));
	}
	return StringUtil::Sprintf("%s:%d",
		destinationBuffer,
		ntohs(s->sin_port));
#else
	//not implement on mac for now...
	return string("not implemented on mac for now");
#endif
}

UDPSocket.h

#pragma once
#include"SocketAddress.h"

class UDPSocket
{
public:
	~UDPSocket();
	/*
	将套接字绑定到指定地址和端口
	@param InToAddress IP地址和端口信息
	@return 成功返回 NO_ERROR,失败返回错误码
	*/
	int Bind(const SocketAddress& InToAddress);
	/*
	向指定地址发送UDP数据包
	@param InToSend  要发送的数据缓冲区
	@param InLen 数据长度
	@param 目标地址信息
	@return 成功: 实际发送的字节数;失败: 返回负的错误码
	*/
	int SendTo(const void* InToSend, int InLen, const SocketAddress& InToAddress);
	/*
	接收UDP数据包并获取发送方地址
	@param InToReceive 接收数据缓冲区
	@param InLen 缓冲区长度
	@param OutFromAddress 输出参数,存储发送方地址
	@return
	成功:
		返回接收的字节数
	失败:
		1.非阻塞模式下无数据: 返回0;
		2.连接重置: 返回 -WSAECONNRESET;
		3.其他错误: 返回负的错误码。
	*/
	int ReceiveFrom(void* InToReceive, int InLen, SocketAddress& OutFromAddress);
	/*
	设置套接字为阻塞或非阻塞模式
	@param InShouldBeNonBlocking
	@return 成功: NO_ERROR;失败: 错误码
	*/
	int SetNonBlockingMode(bool InShouldBeNonBlocking);
private:
	friend class SocketUtil;
	UDPSocket(SOCKET InSocket) :mSocket(InSocket) {}
	SOCKET mSocket;
};
typedef shared_ptr<UDPSocket> UDPSocketPtr;

UDPSocket.cpp

#include"MalouPCH.h"

UDPSocket::~UDPSocket()
{
	//跨平台处理 关闭socket
#if _WIN32
	closesocket(mSocket);
#else
	close(mSocket);
#endif
}

int UDPSocket::Bind(const SocketAddress& InToAddress)
{
	int err = bind(mSocket, &InToAddress.mSockAddr, InToAddress.GetSize());
	if (0 != err)
	{
		SocketUtil::ReportError("UDPSokcet::Bind");
		return SocketUtil::GetLastError();
	}
	return NO_ERROR;
}

int UDPSocket::SendTo(const void* InToSend, int InLen, const SocketAddress& InToAddress)
{
	int byteSentCount = sendto(mSocket, static_cast<const char*>(InToSend), InLen, 0, &InToAddress.mSockAddr, InToAddress.GetSize());
	if (byteSentCount <= 0)
	{
		SocketUtil::ReportError("UDPSocket::SendTo");
		return -SocketUtil::GetLastError();
	}
	return byteSentCount;
}

int UDPSocket::ReceiveFrom(void* InToReceive, int InLen, SocketAddress& OutFromAddress)
{
	socklen_t fromLength = OutFromAddress.GetSize();
	int readByteCount = recvfrom(mSocket, static_cast<char*>(InToReceive), InLen, 0, &OutFromAddress.mSockAddr, &fromLength);
	if (readByteCount > 0)
	{
		return readByteCount;
	}
	else
	{
		int err = SocketUtil::GetLastError();
		if (WSAEWOULDBLOCK == err)
		{
			return 0;
		}
		else if (WSAECONNRESET == err)
		{
			LOG("Connection reset from %s", OutFromAddress.ToString().c_str());
			return -WSAECONNRESET;
		}
		else
		{
			SocketUtil::ReportError("UDPSocket::ReceiveFrom");
			return -err;
		}
	}
}

int UDPSocket::SetNonBlockingMode(bool InShouldBeNonBlocking)
{
#if _WIN32
	u_long arg = InShouldBeNonBlocking ? 1 : 0;
	int result = ioctlsocket(mSocket, FIONBIO, &arg);
#else
	int flags = fcntl(mSocket, F_GETFL, 0);
	flags = InShouldBeNonBlocking ? (flags | O_NONBLOCK) : (flags & ~O_NONBLOCK);
	int result = fcntl(mSocket, F_SETFL, flags);
#endif
	if (SOCKET_ERROR == result)
	{
		SocketUtil::ReportError("UDPSocket::SetNonBlockingMode");
		return SocketUtil::GetLastError();
	}
	else
	{
		return NO_ERROR;
	}
}

三.基于bit流的网络数据读写类

MemoryBitStream.h

#pragma once

/*
* 浮点数转换定点数
* @param InNumber 输入的浮点数
* @param InMin 数值范围的最小值
* @param InPrecision 量化精度(步长)
* @return uint32_t
*/
inline uint32_t ConvertToFixed(float InNumber, float InMin, float InPrecision)
{
	return static_cast<int>((InNumber - InMin) / InPrecision);
}
/*
* 定点数转换浮点数
* @param InNumber 输入的整数
* @param InMin 数值范围的最小值
* @param InPrecision 量化精度(步长)
* @return float
*/
inline float ConvertFromFixed(uint32_t InNumber, float InMin, float InPrecision)
{
	return InNumber * InPrecision + InMin;
}

/*输出bit流*/
class OutputMemoryBitStream
{
public:
	OutputMemoryBitStream() :
		mBitHead(0),
		mBuffer(nullptr)
	{
		ReallocBuffer(1500 * 8);
	}
	/*释放缓冲区内存*/
	~OutputMemoryBitStream() { delete mBuffer; }
	/*
	* 写入指定bit位数据
	* @param InData 待写入数据 单字节
	* @param InBitCount 写入长度
	*/
	void WriteBits(uint8_t InData, uint32_t InBitCount);
	/*
	* 写入指定bit位数据
	* @param InData 待写入数据
	* @param InBitCount 写入长度
	*/
	void WriteBits(const void* InData, uint32_t InBitCount);

	/*获取缓冲区指针*/
	const char* GetBufferPtr() const { return mBuffer; }

	/*获取缓冲区bit位置偏移*/
	uint32_t GetBitLength() const { return mBitHead; }

	/*获取缓冲区字节位置偏移*/
	uint32_t GetByteLength() const { return (mBitHead + 7) >> 3; }

	/*
	* 按字节长度写入数据
	* @param InData 待写入数据
	* @param InByteCount 字节长度
	*/
	void WriteBytes(const void* InData, uint32_t InByteCount)
	{
		WriteBits(InData, InByteCount << 3);
	}

	template<typename T>
	void Write(T InData, uint32_t InBitCount = sizeof(T) * 8)
	{
		static_assert(std::is_arithmetic<T>::value ||
			std::is_enum<T>::value,
			"Generic Write only supports primitive data types");
		WriteBits(&InData, InBitCount);
	}
	/*写入bool类型数据*/
	void Write(bool InData) { WriteBits(&InData, 1); }

	/*写入Vector3类型数据*/
	void Write(const Vector3& InVector);

	/*写入Quaternion类型数据*/
	void Write(const Quaternion& InQuat);

	/*写入string类型数据*/
	void Write(const string& InString)
	{
		uint32_t elementCount = static_cast<uint32_t>(InString.size());
		Write(elementCount);
		for (const auto& element : InString)
		{
			Write(element);
		}
	}

private:
	/*分配缓冲区内存*/
	void ReallocBuffer(uint32_t InNewBitLength);
private:

	/*缓冲区指针*/
	char* mBuffer;

	/*缓冲区位偏移*/
	uint32_t mBitHead;

	/*缓冲区容量大小*/
	uint32_t mBitCapacity;
};

/*输入bit流*/
class InputMemoryBitStream
{
public:
	InputMemoryBitStream(char* InBuffer, uint32_t InBitCount) :
		mBuffer(InBuffer),
		mBitCapacity(InBitCount),
		mBitHead(0),
		mIsBufferOwner(false) {
	}

	InputMemoryBitStream(const InputMemoryBitStream& InOther) :
		mBitCapacity(InOther.mBitCapacity),
		mBitHead(InOther.mBitHead),
		mIsBufferOwner(true)
	{
		int byteCount = (mBitCapacity + 7) / 8;
		mBuffer = static_cast<char*>(new char[byteCount]);
		memcpy(mBuffer, InOther.mBuffer, byteCount);
	}
	~InputMemoryBitStream()
	{
		if (mIsBufferOwner) delete mBuffer;
	}
	const char* GetBufferPtr()const { return mBuffer; }
	uint32_t GetRemainingBitCount()const { return mBitCapacity - mBitHead; }
	void ReadBits(uint8_t& OutData, uint32_t InBitCount);
	void ReadBits(void* OutData, uint32_t InBitCount);
	void ReadBytes(void* OutData, uint32_t InByteCount) { ReadBits(OutData, InByteCount << 3); }

	template<typename T>
	void Read(T& InData, uint32_t InBitCount = sizeof(T) * 8)
	{
		static_assert(std::is_arithmetic< T >::value ||
			std::is_enum< T >::value,
			"Generic Read only supports primitive data types");
		ReadBits(&InData, InBitCount);
	}
	void Read(uint32_t& OutData, uint32_t InBitCount = 32) { ReadBits(&OutData, InBitCount); }
	void Read(int& OutData, uint32_t InBitCount = 32) { ReadBits(&OutData, InBitCount); }
	void Read(float& OutData) { ReadBits(&OutData, 32); }
	void Read(uint16_t& OutData, uint32_t InBitCount = 16) { ReadBits(&OutData, InBitCount); }
	void Read(int16_t& OutData, uint32_t InBitCount = 16) { ReadBits(&OutData, InBitCount); }
	void Read(uint8_t& OutData, uint32_t InBitCount = 8) { ReadBits(&OutData, InBitCount); }
	void Read(bool& OutData) { ReadBits(&OutData, 1); }
	void Read(string& InString)
	{
		uint32_t elementCount;
		Read(elementCount);
		InString.resize(elementCount);
		for (auto& element : InString)
		{
			Read(element);
		}
	}
	void ResetToCapacity(uint32_t InByteCount)
	{
		mBitCapacity = InByteCount << 3;
		mBitHead = 0;
	}
	void Read(Quaternion& OutQuat);
	void Read(Vector3& OutVector);
private:
	/*缓冲区指针*/
	char* mBuffer;

	/*缓冲区位偏移*/
	uint32_t mBitHead;

	/*缓冲区容量大小*/
	uint32_t mBitCapacity;

	bool mIsBufferOwner;
};

MemoryBitStream.cpp

#include"MalouPCH.h"

void OutputMemoryBitStream::ReallocBuffer(uint32_t InNewBitLength)
{
	if (mBuffer == nullptr)
	{
		//分配内存 一个字节8位 所以要右移3位计算字节数
		mBuffer = new char[InNewBitLength >> 3];
		//初始化内存为0
		memset(mBuffer, 0, InNewBitLength >> 3);
	}
	else
	{
		//分配临时内存
		char* tempBuffer = new char[InNewBitLength >> 3];
		//初始化内存为0
		memset(tempBuffer, 0, InNewBitLength >> 3);
		//拷贝旧数据到新的内存区
		memcpy(tempBuffer, mBuffer, mBitCapacity >> 3);
		//释放旧缓冲区
		delete mBuffer;
		//更新指针指向新缓冲区
		mBuffer = tempBuffer;
	}
	//更新容量
	mBitCapacity = InNewBitLength;
}

void OutputMemoryBitStream::WriteBits(uint8_t InData, uint32_t InBitCount)
{
	//计算写入后的新头指针位置
	uint32_t nextBitHead = mBitHead + InBitCount;
	//大于当前容量时 更新缓冲区大小
	if (nextBitHead > mBitCapacity)
	{
		ReallocBuffer(std::max(mBitCapacity * 2, nextBitHead));
	}
	//计算字节偏移 = mBitHead / 8
	uint32_t byteOffset = mBitHead >> 3;
	//计算位偏移  = mBitHead % 8
	uint32_t bitOffset = mBitHead & 0x7;
	//构造位掩码 保护不需要修改的位
	//0xff = 1111 1111 如bitOffset=3左移3位再取反 生成0000 0111
	uint8_t currentMask = ~(0xff << bitOffset);
	//按位操作保留原值未覆盖部分,写入新数据
	//例:bitOffset=3 还剩5位可以写入数据 
	//mBuffer[byteOffset] = 0000 0111 & currentMask 0000 0111 = 0000 0111不修改原有数据
	//InData<<bitOffset计算新数据中当前字节可以被写入的数据,例如InData=1111 1111,
	//则InData<<bitOffset=1111 1000;与先前的结果进行|运算 填满当前字节。
	mBuffer[byteOffset] = (mBuffer[byteOffset] & currentMask) | (InData << bitOffset);
	//处理跨字节写入
	//计算当前字节剩余可写入数据的位数
	uint32_t bitsFreeThisByte = 8 - bitOffset;
	//如果剩余空间不足,将多余位写入下一个字节
	//例如:bitOffset=3 时写入8位数据,需要5位在当前字节,3位在下一字节
	if (bitsFreeThisByte < InBitCount)
	{
		//右移取得当前字节存储完后剩余的数据 存入下一字节
		mBuffer[byteOffset + 1] = InData >> bitsFreeThisByte;
	}
	//更新头指针
	mBitHead = nextBitHead;
}

void OutputMemoryBitStream::WriteBits(const void* InData, uint32_t InBitCount)
{
	const char* srcByte = static_cast<const char*>(InData);
	//循环一次写入8个bit到缓冲区
	while (InBitCount > 8)
	{
		WriteBits(*srcByte, 8);
		//更新源数组指针
		++srcByte;
		//更新源数据长度
		InBitCount -= 8;
	}
	//写入剩余不足8个bit的数据
	if (InBitCount > 0)
	{
		WriteBits(*srcByte, InBitCount);
	}
}

void OutputMemoryBitStream::Write(const Vector3& InVector)
{
	Write(InVector.X);
	Write(InVector.Y);
	Write(InVector.Z);
}

void OutputMemoryBitStream::Write(const Quaternion& InQuat)
{
	float precision = 2.f / 65535.f;
	Write(ConvertToFixed(InQuat.X, -1.f, precision), 16);
	Write(ConvertToFixed(InQuat.Y, -1.f, precision), 16);
	Write(ConvertToFixed(InQuat.Z, -1.f, precision), 16);
	Write(InQuat.W < 0);
}

void InputMemoryBitStream::ReadBits(uint8_t& OutData, uint32_t InBitCount)
{
	//计算字节偏移 = mBitHead / 8
	uint32_t byteOffset = mBitHead >> 3;
	//计算单字节位偏移 = mBitHead % 8
	uint32_t bitOffset = mBitHead & 0x7;
	//从当前字节读取数据,并右移去除低位不需要的比特
	OutData = static_cast<uint8_t>(mBuffer[byteOffset]) >> bitOffset;
	//处理跨字节读取
	uint8_t bitsFreeThisByte = 8 - bitOffset;
	if (bitsFreeThisByte < InBitCount)
	{
		//如果当前字节剩余位数不足,从下一个字节读取高位数据 
		//通过位或操作(|=)合并两部分数据
		OutData |= static_cast<uint8_t>(mBuffer[byteOffset + 1]) << bitsFreeThisByte;
	}
	//生成掩码保留指定比特数 清除超出InBitCount的高位数据
	OutData &= (~(0xff << InBitCount));
	//更新读取位置
	mBitHead += InBitCount;
}

void InputMemoryBitStream::ReadBits(void* OutData, uint32_t InBitCount)
{
	uint8_t* destByte = reinterpret_cast<uint8_t*>(OutData);
	while (InBitCount > 8)
	{
		ReadBits(*destByte, 8);
		++destByte;
		InBitCount -= 8;
	}
	if (InBitCount > 0)
	{
		ReadBits(*destByte, InBitCount);
	}
}

void InputMemoryBitStream::Read(Quaternion& OutQuat)
{
	float precision = 2.f / 65536.f;
	uint32_t f = 0;
	Read(f, 16);
	OutQuat.X = ConvertFromFixed(f, -1.f, precision);
	Read(f, 16);
	OutQuat.Y = ConvertFromFixed(f, -1.f, precision);
	Read(f, 16);
	OutQuat.Z = ConvertFromFixed(f, -1.f, precision);
	OutQuat.W = sqrtf(1.f -
		(OutQuat.X * OutQuat.X +
			OutQuat.Y * OutQuat.Y +
			OutQuat.Z * OutQuat.Z));
	bool isNegative;
	Read(isNegative);
	if (isNegative)
	{
		OutQuat.W *= -1;
	}
}

void InputMemoryBitStream::Read(Vector3& OutVector)
{
	Read(OutVector.X);
	Read(OutVector.Y);
	Read(OutVector.Z);
}

四.网络管理器

基类 NetworkManager.h

typedef unordered_map<int, GameObjectPtr> IntToGameObjectMap;

class NetworkManager
{
public:
    //静态常量标记数据包的类型
	static const uint32_t kHelloCC = 'HELO';
	static const uint32_t kWelcomeCC = 'WLCM';
	static const uint32_t kStateCC = 'STAT';
	static const uint32_t kInputCC = 'INPT';
	static const int kMaxPacketsPerFrameCount = 10;

	NetworkManager();
	virtual ~NetworkManager();
	bool Init(uint16_t InPort);
    /*处理收到的所有数据包*/
	void ProcessIncomingPackets();
    /*处理数据包*/
	virtual void ProcessPacket(InputMemoryBitStream& InInputStream, const SocketAddress& InFromAddress) = 0;
    /*处理连接重置*/
	virtual void HandleConnectionReset(const SocketAddress& inFromAddress) { (void)inFromAddress; }
    /*发送数据包*/
	void SendPacket(const OutputMemoryBitStream& inOutputStream, const SocketAddress& inFromAddress);

	const WeightedTimedMovingAverage& GetBytesReceivedPerSecond() const { return mBytesReceivedPerSecond; }
	const WeightedTimedMovingAverage& GetBytesSentPerSecond() const { return mBytesSentPerSecond; }

    //模拟丢包和延迟
	void SetDropPacketChance(float inChance) { mDropPacketChance = inChance; }
	float GetDropPacketChance() const { return mDropPacketChance; }
	void SetSimulatedLatency(float inLatency) { mSimulatedLatency = inLatency; }
	float GetSimulatedLatency() const { return mSimulatedLatency; }

	inline GameObjectPtr GetGameObject(int inNetworkId) const;
    /*添加网络id和游戏对象映射*/
	void AddToNetworkIdToGameObjectMap(GameObjectPtr inGameObject);
    /*移除网络id和游戏对象映射*/
	void RemoveFromNetworkIdToGameObjectMap(GameObjectPtr inGameObject);
protected:
	IntToGameObjectMap mNetworkIdToGameObjectMap;
private:
    /*处理收到的包*/
	class ReceivedPacket
	{
	public:
		ReceivedPacket(float inReceivedTime, InputMemoryBitStream& inInputMemoryBitStream, const SocketAddress& inAddress);
        /*获取发包方地址*/
		const SocketAddress& GetFromAddress()	const { return mFromAddress; }
        /*获取收包的时间戳*/
		float GetReceivedTime()	const { return mReceivedTime; }
        /*获取数据包缓冲区*/
		InputMemoryBitStream& GetPacketBuffer() { return mPacketBuffer; }
	private:
        /*收包时间*/
		float mReceivedTime;
        /*数据包缓冲区*/
		InputMemoryBitStream mPacketBuffer;
        /*发包方地址*/
		SocketAddress mFromAddress;
	};
    /*更新每秒发送字节数统计的工具函数 统计网络吞吐量*/
	void UpdateBytesSentLastFrame();
    /*读取收到的数据包并加入到队列中*/
	void ReadIncomingPacketsIntoQueue();
    /*处理队列中的数据包*/
	void ProcessQueuedPackets();
    /*数据包队列*/
	queue<ReceivedPacket,list<ReceivedPacket>>	mPacketQueue;

	UDPSocketPtr mSocket;
	WeightedTimedMovingAverage	mBytesReceivedPerSecond;
	WeightedTimedMovingAverage	mBytesSentPerSecond;
	int mBytesSentThisFrame;
	float mDropPacketChance;
	float mSimulatedLatency;
};

inline GameObjectPtr NetworkManager::GetGameObject(int inNetworkId) const
{
	auto gameObjectIt = mNetworkIdToGameObjectMap.find(inNetworkId);
	if (gameObjectIt != mNetworkIdToGameObjectMap.end())
	{
		return gameObjectIt->second;
	}
	else
	{
		return GameObjectPtr();
	}
}

NetworkManager.cpp

#include"MalouPCH.h"

NetworkManager::NetworkManager():
	mBytesSentThisFrame(0),
	mDropPacketChance(0.f),
	mSimulatedLatency(0.f)
{
}

NetworkManager::~NetworkManager()
{
}

bool NetworkManager::Init(uint16_t InPort)
{
	mSocket = SocketUtil::CreateUDPSocket(INET);
	SocketAddress ownAddress(INADDR_ANY, InPort);
	mSocket->Bind(ownAddress);

	LOG("Initializing NetworkManager at port %d", InPort);

	mBytesReceivedPerSecond = WeightedTimedMovingAverage(1.f);
	mBytesSentPerSecond = WeightedTimedMovingAverage(1.f);

	if (mSocket == nullptr) return false;
	if (mSocket->SetNonBlockingMode(true) != NO_ERROR) return false;
	return true;
}
void NetworkManager::ProcessIncomingPackets()
{
	ReadIncomingPacketsIntoQueue();
	ProcessQueuedPackets();
	UpdateBytesSentLastFrame();
}

void NetworkManager::SendPacket(const OutputMemoryBitStream& InOutputStream, const SocketAddress& InFromAddress)
{
	int sentByteCount = mSocket->SendTo(InOutputStream.GetBufferPtr(), InOutputStream.GetByteLength(), InFromAddress);
	if (sentByteCount > 0)
	{
		mBytesSentThisFrame += sentByteCount;
	}
}

void NetworkManager::ProcessQueuedPackets()
{
	//遍历队列
    while (!mPacketQueue.empty())
	{
		ReceivedPacket& nextPacket = mPacketQueue.front();//获取队列前端的引用
        //检查当前时间(Timing::Instance.GetTimef())是否已经超过数据包的预定接收时间 模拟真实网络延迟 同时保持时序正确性
		if (Timing::Instance.GetTimef() > nextPacket.GetReceivedTime())
		{
            //处理数据包
            ProcessPacket(nextPacket.GetPacketBuffer(), nextPacket.GetFromAddress());//纯虚函数由客户端和服务端分别自定义处理逻辑
			mPacketQueue.pop();//从队列中移除
		}
		else
		{
			break;
		}
	}
}
void NetworkManager::UpdateBytesSentLastFrame()
{
	if (mBytesSentThisFrame > 0)
	{
		mBytesSentPerSecond.UpdatePerSecond(static_cast<float>(mBytesSentThisFrame));
		mBytesSentThisFrame = 0;
	}
}

void NetworkManager::ReadIncomingPacketsIntoQueue()
{
	char packetMem[1500];//缓冲区存储收到的数据包,以太网MTU通常为1500个字节 
	int packetSize = sizeof(packetMem);//设置为缓冲区大小
	InputMemoryBitStream inputSteam(packetMem, packetSize * 8);//输入bit流对象 处理接收到的数据
	SocketAddress fromAddress;//存储发送方地址信息
	int receivePackedCount = 0;//接收计数器
	int totalReadByteCount = 0;//总共读取的字节数
    //使用while循环,限制每帧最多处理kMaxPacketsPerFrameCount个数据包
	while (receivePackedCount < kMaxPacketsPerFrameCount)
	{
		int readByteCount = mSocket->ReceiveFrom(packetMem, packetSize, fromAddress);//读取数据
		if (0 == readByteCount)
		{
			break;
		}
		else if (-WSAECONNRESET == readByteCount)//连接重置错误
		{
			HandleConnectionReset(fromAddress);
		}
		else if (readByteCount > 0)
		{
			inputSteam.ResetToCapacity(readByteCount);//重置输入流容量
			++receivePackedCount;//增加接收计数器
			totalReadByteCount += readByteCount;//增加读取的总字节数
            //模拟丢包
			if (MalouMath::GetRandomFloat() >= mDropPacketChance)
			{
				//计算模拟接收时间
                float simulatedReceiveTime = Timing::Instance.GetTimef() + mSimulatedLatency;
                //加入队列
				mPacketQueue.emplace(simulatedReceiveTime, inputSteam, fromAddress);
			}
			else
			{
				LOG("Dropped packet!", 0);
			}
		}
		else
		{
			LOG("UCatch Error!", 0);
		}

	}
	if (totalReadByteCount > 0)
	{
		//如果有接收到数据,更新每秒接收字节数的统计
        mBytesReceivedPerSecond.UpdatePerSecond(static_cast<float>(totalReadByteCount));
	}
}

void NetworkManager::AddToNetworkIdToGameObjectMap(GameObjectPtr InGameObject)
{
	mNetworkIdToGameObjectMap[InGameObject->GetNetworkId()] = InGameObject;
}
void NetworkManager::RemoveFromNetworkIdToGameObjectMap(GameObjectPtr InGameObject)
{
	mNetworkIdToGameObjectMap.erase(InGameObject->GetNetworkId());
}

NetworkManager::ReceivedPacket::ReceivedPacket(float InReceivedTime, InputMemoryBitStream& IoInputMemoryBitStream, const SocketAddress& InAddress):
	mReceivedTime(InReceivedTime),
	mFromAddress(InAddress),
	mPacketBuffer(IoInputMemoryBitStream)
{}


服务器网络管理器 NetworkManagerServer.h



class NetworkManagerServer : public NetworkManager
{
public:

	static NetworkManagerServer* Instance;
    /*初始化单例并启动服务器*/
	static bool StaticInit(uint16_t Port);
    /*处理已认证客户端的数据包*/
	virtual void ProcessPacket(InputMemoryBitStream& InputStream, const SocketAddress& FromAddress) override;
    /*处理客户端连接重置*/
	virtual void HandleConnectionReset(const SocketAddress& FromAddress) override;
    /*发送所有待发数据包*/
	void SendOutgoingPackets();
    /*检测无响应客户端*/
	void CheckForDisconnects();
    /*注册需要同步的游戏对象*/
	void RegisterGameObject(GameObjectPtr GameObject);
    /*注册并返回智能指针包裹的对象*/
	inline GameObjectPtr RegisterAndReturn(GameObject* GameObject);
    /*取消游戏对象注册*/
	void UnregisterGameObject(GameObject* GameObject);
    /*标记对象状态需要同步*/
	void SetStateDirty(int NetworkId, uint32_t DirtyState);
	void RespawnCats();
    /*获取玩家客户端代理*/
	ClientProxyPtr GetClientProxy(int PlayerId) const;

private:

	NetworkManagerServer();
    /*处理新客户端连接包*/
	void HandlePacketFromNewClient(InputMemoryBitStream& InputStream, const SocketAddress& FromAddress);
    /*处理已认证客户端的数据包*/
	void ProcessPacket(ClientProxyPtr ClientProxy, InputMemoryBitStream& InputStream);
    /*发送欢迎信息给新客户端*/
	void SendWelcomePacket(ClientProxyPtr ClientProxy);
    /*向所有客户端发送状态更新*/
	void UpdateAllClients();
    /*序列化游戏世界状态 通常用于客户端刚连接时的完整状态初始化*/
	void AddWorldStateToPacket(OutputMemoryBitStream& OutputStream);
    /*序列化记分板状态*/
	void AddScoreBoardStateToPacket(OutputMemoryBitStream& OutputStream);
    /*向特定客户端发送状态包*/
	void SendStatePacketToClient(ClientProxyPtr ClientProxy);
	void WriteLastMoveTimestampIfDirty(OutputMemoryBitStream& OutputStream, ClientProxyPtr ClientProxy);
    /*处理玩家输入指令*/
	void HandleInputPacket(ClientProxyPtr ClientProxy, InputMemoryBitStream& InputStream);
    /*清理断开连接的客户端*/
	void HandleClientDisconnected(ClientProxyPtr ClientProxy);
    /*分配新网络对象ID*/
	int GetNewNetworkId();

    //玩家ID到客户端代理的映射
	unordered_map<int, ClientProxyPtr> mPlayerIdToClientMap;
    //网络地址到客户端代理的映射
	unordered_map<SocketAddress, ClientProxyPtr> mAddressToClientMap;
    //新玩家ID分配计数器
	int mNewPlayerId;
    //新网络对象ID分配计数器
	int mNewNetworkId;
    //上次状态包发送时间
	float mTimeOfLastSatePacket;
    //状态包发送间隔(秒)
	float mTimeBetweenStatePackets;
    //客户端超时断开阈值
	float mClientDisconnectTimeout;
};


inline GameObjectPtr NetworkManagerServer::RegisterAndReturn(GameObject* GameObject)
{
	GameObjectPtr toRet(GameObject);
	RegisterGameObject(toRet);
	return toRet;
}

NetworkManagerServer.cpp

#include"MalouServerPCH.h"

NetworkManagerServer* NetworkManagerServer::Instance;

NetworkManagerServer::NetworkManagerServer() :
	mNewPlayerId(1),
	mNewNetworkId(1),
	mTimeBetweenStatePackets(0.033f),
	mClientDisconnectTimeout(3.f)
{
}

bool NetworkManagerServer::StaticInit(uint16_t Port)
{
	Instance = new NetworkManagerServer();
	return Instance->Init(Port);
}

void NetworkManagerServer::HandleConnectionReset(const SocketAddress& FromAddress)
{
	//通过网络地址查找客户端代理并处理连接重置
    auto it = mAddressToClientMap.find(FromAddress);
	if (it != mAddressToClientMap.end())
	{
		HandleClientDisconnected(it->second);
	}
}

void NetworkManagerServer::ProcessPacket(InputMemoryBitStream& InputStream, const SocketAddress& FromAddress)
{
    //通过网络地址查找客户端代理
	auto it = mAddressToClientMap.find(FromAddress);
	if (it == mAddressToClientMap.end())//没找到 视为新加入的客户端
	{
		HandlePacketFromNewClient(InputStream, FromAddress);
	}
	else
	{
		ProcessPacket((*it).second, InputStream);
	}
}


void NetworkManagerServer::ProcessPacket(ClientProxyPtr ClientProxy, InputMemoryBitStream& InputStream)
{
	//更新指定代理上次处理数据包的时间
    ClientProxy->UpdateLastPacketTime();
    //读取数据包类型
	uint32_t packetType;
	InputStream.Read(packetType);

	switch (packetType)
	{
	case kHelloCC://首次连接
		SendWelcomePacket(ClientProxy);
		break;
	case kInputCC://玩家输入
        //获取数据包管理器并读取状态数据
		if (ClientProxy->GetDeliveryNotificationManager().ReadAndProcessState(InputStream))
		{
			HandleInputPacket(ClientProxy, InputStream);
		}
		break;
	default:
		LOG("Unknown packet type received from %s", ClientProxy->GetSocketAddress().ToString().c_str());
		break;
	}
}


void NetworkManagerServer::HandlePacketFromNewClient(InputMemoryBitStream& InputStream, const SocketAddress& FromAddress)
{
	//读取数据包类型
    uint32_t packetType;
	InputStream.Read(packetType);

	if (packetType == kHelloCC)//首次连接
	{
		//读取玩家名字
        string name;
		InputStream.Read(name);
        //创建新的客户端代理并加入映射
		ClientProxyPtr newClientProxy = std::make_shared< ClientProxy >(FromAddress, name, mNewPlayerId++);
		mAddressToClientMap[FromAddress] = newClientProxy;
		mPlayerIdToClientMap[newClientProxy->GetPlayerId()] = newClientProxy;

		static_cast<Server*> (Engine::Instance.get())->HandleNewClient(newClientProxy);
        //发送欢迎数据包
		SendWelcomePacket(newClientProxy);
        //同步当前游戏中的对象到新客户端
		for (const auto& pair : mNetworkIdToGameObjectMap)
		{
			newClientProxy->GetReplicationManagerServer().ReplicateCreate(pair.first, pair.second->GetAllStateMask());
		}
	}
	else
	{
		LOG("Bad incoming packet from unknown client at socket %s", FromAddress.ToString().c_str());
	}
}

void NetworkManagerServer::SendWelcomePacket(ClientProxyPtr ClientProxy)
{
	OutputMemoryBitStream welcomePacket;
    //写入数据包类型常量和分配的玩家id
	welcomePacket.Write(kWelcomeCC);
	welcomePacket.Write(ClientProxy->GetPlayerId());

	LOG("Server Welcoming, new client '%s' as player %d", ClientProxy->GetName().c_str(), ClientProxy->GetPlayerId());

	SendPacket(welcomePacket, ClientProxy->GetSocketAddress());
}

void NetworkManagerServer::RespawnCats()
{
	for (auto it = mAddressToClientMap.begin(), end = mAddressToClientMap.end(); it != end; ++it)
	{
		ClientProxyPtr clientProxy = it->second;

		clientProxy->RespawnCatIfNecessary();
	}
}

void NetworkManagerServer::SendOutgoingPackets()
{
	float time = Timing::Instance.GetTimef();
    //遍历地址代理映射
	for (auto it = mAddressToClientMap.begin(), end = mAddressToClientMap.end(); it != end; ++it)
	{
		//获取客户端代理对象
        ClientProxyPtr clientProxy = it->second;
        //处理超时数据包
		clientProxy->GetDeliveryNotificationManager().ProcessTimedOutPackets();
        //检查客户端移动状态藏标记 如果命中则同步到指定客户端
		if (clientProxy->IsLastMoveTimestampDirty())
		{
			SendStatePacketToClient(clientProxy);
		}
	}
}

void NetworkManagerServer::UpdateAllClients()
{
	for (auto it = mAddressToClientMap.begin(), end = mAddressToClientMap.end(); it != end; ++it)
	{
		it->second->GetDeliveryNotificationManager().ProcessTimedOutPackets();

		SendStatePacketToClient(it->second);
	}
}

void NetworkManagerServer::SendStatePacketToClient(ClientProxyPtr ClientProxy)
{
	OutputMemoryBitStream statePacket;
    //写入包类型数据
	statePacket.Write(kStateCC);
    //可靠UDP传输处理 返回InFlightPacket用于跟踪数据包状态
	InFlightPacket* ifp = ClientProxy->GetDeliveryNotificationManager().WriteState(statePacket);
    //写入玩家的移动数据
	WriteLastMoveTimestampIfDirty(statePacket, ClientProxy);
    //写入计分板数据
	AddScoreBoardStateToPacket(statePacket);
    //创建同步数据传输对象
	ReplicationManagerTransmissionData* rmtd = new ReplicationManagerTransmissionData(&ClientProxy->GetReplicationManagerServer());
	//写入所有脏状态的对象数据
    ClientProxy->GetReplicationManagerServer().Write(statePacket, rmtd);
    //将同步数据附加到可靠传输包
	ifp->SetTransmissionData('RPLM', TransmissionDataPtr(rmtd));

	SendPacket(statePacket, ClientProxy->GetSocketAddress());
}

void NetworkManagerServer::WriteLastMoveTimestampIfDirty(OutputMemoryBitStream& OutputStream, ClientProxyPtr ClientProxy)
{
	//检查客户端最后处理移动时间戳是否有更新
    bool isTimestampDirty = ClientProxy->IsLastMoveTimestampDirty();
	OutputStream.Write(isTimestampDirty);
	if (isTimestampDirty)
	{
		//写入服务器已处理的最后移动指令时间戳
        OutputStream.Write(ClientProxy->GetUnprocessedMoveList().GetLastMoveTimestamp());
        //写入后重置脏标记
		ClientProxy->SetIsLastMoveTimestampDirty(false);
	}
}

void NetworkManagerServer::AddWorldStateToPacket(OutputMemoryBitStream& OutputStream)
{
	const auto& gameObjects = World::Instance->GetGameObjects();

	OutputStream.Write(gameObjects.size());

	for (GameObjectPtr gameObject : gameObjects)
	{
		OutputStream.Write(gameObject->GetNetworkId());//写入网络id
		OutputStream.Write(gameObject->GetClassId());//写入类型id
		gameObject->Write(OutputStream, 0xffffffff);//写入完整状态(全量同步)
	}
}

void NetworkManagerServer::AddScoreBoardStateToPacket(OutputMemoryBitStream& OutputStream)
{
	ScoreBoardManager::Instance->Write(OutputStream);
}


int NetworkManagerServer::GetNewNetworkId()
{
	int toRet = mNewNetworkId++;
	if (mNewNetworkId < toRet)//越界检查
	{
		LOG("Network ID Wrap Around!!! You've been playing way too long...", 0);
	}

	return toRet;

}

void NetworkManagerServer::HandleInputPacket(ClientProxyPtr ClientProxy, InputMemoryBitStream& InputStream)
{
	//读取指令数量
    uint32_t moveCount = 0;
	InputStream.Read(moveCount, 2);//2位可表示0-3个指令,适合高频小包

    //指令循环处理
	Move move;
	for (; moveCount > 0; --moveCount)
	{
		if (move.Read(InputStream))
		{
			if (ClientProxy->GetUnprocessedMoveList().AddMoveIfNew(move))//添加新指令到待处理队列
			{
				ClientProxy->SetIsLastMoveTimestampDirty(true);//设置时间戳变化标志,触发后续确认
			}
		}
	}
}

ClientProxyPtr NetworkManagerServer::GetClientProxy(int PlayerId) const
{
	auto it = mPlayerIdToClientMap.find(PlayerId);
	if (it != mPlayerIdToClientMap.end())
	{
		return it->second;
	}

	return nullptr;
}

void NetworkManagerServer::CheckForDisconnects()
{
    //保存断开连接的客户端代理
    vector<ClientProxyPtr> clientsToDC;
    //计算从客户端搜到数据包的时间间隔
	float minAllowedLastPacketFromClientTime = Timing::Instance.GetTimef() - mClientDisconnectTimeout;
    //遍历网络地址到客户端代理的映射 检查收包的时间间隔 若小于上值 则将改代理加入clientsToDC
	for (const auto& pair : mAddressToClientMap)
	{
		if (pair.second->GetLastPacketFromClientTime() < minAllowedLastPacketFromClientTime)
		{
			clientsToDC.push_back(pair.second);
		}
	}
    //遍历所有断开的代理 处理断开逻辑
	for (ClientProxyPtr client : clientsToDC)
	{
		HandleClientDisconnected(client);
	}
}

void NetworkManagerServer::HandleClientDisconnected(ClientProxyPtr ClientProxy)
{
	//移除相关映射
    mPlayerIdToClientMap.erase(ClientProxy->GetPlayerId());
	mAddressToClientMap.erase(ClientProxy->GetSocketAddress());
	static_cast<Server*> (Engine::Instance.get())->HandleLostClient(ClientProxy);
    //所有玩家都断开连接停止服务器
	if (mAddressToClientMap.empty())
	{
		Engine::Instance->SetShouldKeepRunning(false);
	}
}

void NetworkManagerServer::RegisterGameObject(GameObjectPtr GameObject)
{
	//分配并设置gameobject网络id
    int newNetworkId = GetNewNetworkId();
	GameObject->SetNetworkId(newNetworkId);
    //添加id到gameobject的映射
	mNetworkIdToGameObjectMap[newNetworkId] = GameObject;
    //遍历网络地址到客户端代理的映射 同步创建命令
	for (const auto& pair : mAddressToClientMap)
	{
		pair.second->GetReplicationManagerServer().ReplicateCreate(newNetworkId, GameObject->GetAllStateMask());
	}
}


void NetworkManagerServer::UnregisterGameObject(GameObject* inGameObject)
{
	int networkId = inGameObject->GetNetworkId();
	mNetworkIdToGameObjectMap.erase(networkId);

	for (const auto& pair : mAddressToClientMap)
	{
		pair.second->GetReplicationManagerServer().ReplicateDestroy(networkId);
	}
}

void NetworkManagerServer::SetStateDirty(int NetworkId, uint32_t DirtyState)
{
	for (const auto& pair : mAddressToClientMap)
	{
		pair.second->GetReplicationManagerServer().SetStateDirty(NetworkId, DirtyState);
	}
}

客户端网络管理器 NetworkManagerClient.h

class NetworkManagerClient : public NetworkManager
{
	enum NetworkClientState
	{
		NCS_Uninitialized,
		NCS_SayingHello,
		NCS_Welcomed
	};

public:
	static NetworkManagerClient* Instance;
	static void	StaticInit(const SocketAddress& inServerAddress, const string& inName);
	void SendOutgoingPackets();
	virtual void ProcessPacket(InputMemoryBitStream& inInputStream, const SocketAddress& inFromAddress) override;
	const WeightedTimedMovingAverage& GetAvgRoundTripTime()	const { return mAvgRoundTripTime; }
	float GetRoundTripTime() const { return mAvgRoundTripTime.GetValue(); }
	int GetPlayerId() const { return mPlayerId; }
	float GetLastMoveProcessedByServerTimestamp() const { return mLastMoveProcessedByServerTimestamp; }
private:
	NetworkManagerClient();
	void Init(const SocketAddress& inServerAddress, const string& inName);
	void UpdateSayingHello();
	void SendHelloPacket();
	void HandleWelcomePacket(InputMemoryBitStream& inInputStream);
	void HandleStatePacket(InputMemoryBitStream& inInputStream);
	void ReadLastMoveProcessedOnServerTimestamp(InputMemoryBitStream& inInputStream);
	void HandleGameObjectState(InputMemoryBitStream& inInputStream);
	void HandleScoreBoardState(InputMemoryBitStream& inInputStream);
	void UpdateSendingInputPacket();
	void SendInputPacket();
	void DestroyGameObjectsInMap(const IntToGameObjectMap& inObjectsToDestroy);
	DeliveryNotificationManager	mDeliveryNotificationManager;
	ReplicationManagerClient mReplicationManagerClient;
	SocketAddress mServerAddress;
	NetworkClientState mState;
	float mTimeOfLastHello;
	float mTimeOfLastInputPacket;
	string mName;
	int	mPlayerId;
	float mLastMoveProcessedByServerTimestamp;
	WeightedTimedMovingAverage mAvgRoundTripTime;
	float mLastRoundTripTime;
};

NetworkManagerClient.cpp

#include"MalouClientPCH.h"

NetworkManagerClient* NetworkManagerClient::Instance;

namespace
{
	const float kTimeBetweenHellos = 1.f;
	const float kTimeBetweenInputPackets = 0.033f;
}

NetworkManagerClient::NetworkManagerClient() :
	mState(NCS_Uninitialized),
	mDeliveryNotificationManager(true, false),
	mLastRoundTripTime(0.f)
{
}

void NetworkManagerClient::StaticInit(const SocketAddress& ServerAddress, const string& Name)
{
	Instance = new NetworkManagerClient();
	return Instance->Init(ServerAddress, Name);
}

void NetworkManagerClient::Init(const SocketAddress& ServerAddress, const string& Name)
{
	NetworkManager::Init(7779);

	mServerAddress = ServerAddress;
	mState = NCS_SayingHello;
	mTimeOfLastHello = 0.f;
	mName = Name;
	mAvgRoundTripTime = WeightedTimedMovingAverage(1.f);
}

void NetworkManagerClient::ProcessPacket(InputMemoryBitStream& InputStream, const SocketAddress& FromAddress)
{
	uint32_t packetType;
	InputStream.Read(packetType);
	switch (packetType)
	{
	case kWelcomeCC:
		HandleWelcomePacket(InputStream);
		break;
	case kStateCC:
		if (mDeliveryNotificationManager.ReadAndProcessState(InputStream))
		{
			HandleStatePacket(InputStream);
		}
		break;
	}
}


void NetworkManagerClient::SendOutgoingPackets()
{
	switch (mState)
	{
	case NCS_SayingHello:
		UpdateSayingHello();
		break;
	case NCS_Welcomed:
		UpdateSendingInputPacket();
		break;
	}
}

void NetworkManagerClient::UpdateSayingHello()
{
	float time = Timing::Instance.GetTimef();

	if (time > mTimeOfLastHello + kTimeBetweenHellos)
	{
		SendHelloPacket();
		mTimeOfLastHello = time;
	}
}

void NetworkManagerClient::SendHelloPacket()
{
	OutputMemoryBitStream helloPacket;

	helloPacket.Write(kHelloCC);
	helloPacket.Write(mName);

	SendPacket(helloPacket, mServerAddress);
}

void NetworkManagerClient::HandleWelcomePacket(InputMemoryBitStream& InputStream)
{
	if (mState == NCS_SayingHello)
	{
		int playerId;
		InputStream.Read(playerId);
		mPlayerId = playerId;
		mState = NCS_Welcomed;
		LOG("'%s' was welcomed on client as player %d", mName.c_str(), mPlayerId);
	}
}


void NetworkManagerClient::HandleStatePacket(InputMemoryBitStream& InputStream)
{
	if (mState == NCS_Welcomed)
	{
		ReadLastMoveProcessedOnServerTimestamp(InputStream);

		HandleScoreBoardState(InputStream);

		mReplicationManagerClient.Read(InputStream);
	}
}

void NetworkManagerClient::ReadLastMoveProcessedOnServerTimestamp(InputMemoryBitStream& InputStream)
{
	bool isTimestampDirty;
	InputStream.Read(isTimestampDirty);
	if (isTimestampDirty)
	{
		InputStream.Read(mLastMoveProcessedByServerTimestamp);

		float rtt = Timing::Instance.GetFrameStartTime() - mLastMoveProcessedByServerTimestamp;
		mLastRoundTripTime = rtt;
		mAvgRoundTripTime.Update(rtt);
		InputManager::Instance->GetMoveList().RemovedProcessedMoves(mLastMoveProcessedByServerTimestamp);
	}
}

void NetworkManagerClient::HandleGameObjectState(InputMemoryBitStream& InputStream)
{
	IntToGameObjectMap	objectsToDestroy = mNetworkIdToGameObjectMap;
	int stateCount;
	InputStream.Read(stateCount);
	if (stateCount > 0)
	{
		for (int stateIndex = 0; stateIndex < stateCount; ++stateIndex)
		{
			int networkId;
			uint32_t fourCC;
			InputStream.Read(networkId);
			InputStream.Read(fourCC);
			GameObjectPtr go;
			auto itGO = mNetworkIdToGameObjectMap.find(networkId);
			if (itGO == mNetworkIdToGameObjectMap.end())
			{
				go = GameObjectRegistry::Instance->CreateGameObject(fourCC);
				go->SetNetworkId(networkId);
				AddToNetworkIdToGameObjectMap(go);
			}
			else
			{
				go = itGO->second;
			}

			go->Read(InputStream);
			objectsToDestroy.erase(networkId);
		}
	}
	DestroyGameObjectsInMap(objectsToDestroy);
}

void NetworkManagerClient::HandleScoreBoardState(InputMemoryBitStream& InputStream)
{
	ScoreBoardManager::Instance->Read(InputStream);
}

void NetworkManagerClient::DestroyGameObjectsInMap(const IntToGameObjectMap& inObjectsToDestroy)
{
	for (auto& pair : inObjectsToDestroy)
	{
		pair.second->SetDoesWantToDie(true);
		mNetworkIdToGameObjectMap.erase(pair.first);
	}
}


void NetworkManagerClient::UpdateSendingInputPacket()
{
	float time = Timing::Instance.GetTimef();

	if (time > mTimeOfLastInputPacket + kTimeBetweenInputPackets)
	{
		SendInputPacket();
		mTimeOfLastInputPacket = time;
	}
}

void NetworkManagerClient::SendInputPacket()
{
	const MoveList& moveList = InputManager::Instance->GetMoveList();

	if (moveList.HasMoves())
	{
		OutputMemoryBitStream inputPacket;

		inputPacket.Write(kInputCC);

		mDeliveryNotificationManager.WriteState(inputPacket);

		int moveCount = moveList.GetMoveCount();
		int firstMoveIndex = moveCount - 3;
		if (firstMoveIndex < 3)
		{
			firstMoveIndex = 0;
		}
		auto move = moveList.begin() + firstMoveIndex;

		inputPacket.Write(moveCount - firstMoveIndex, 2);

		for (; firstMoveIndex < moveCount; ++firstMoveIndex, ++move)
		{
			move->Write(inputPacket);
		}
		SendPacket(inputPacket, mServerAddress);
	}
}

五.可靠UDP实现

使用Ack机制实现可靠的UDP传输,它的基本原理如下:发送方会在每个数据包中写入一个唯一的序列号,接收方在接收到数据包后,会用该序列号构建Ack数据并且写入到要发送的数据包中,原发送方再收到新的数据包后会检查其中的Ack信息,来判断接收方是否正常接收到数据包,如果没有则会触发重传或者一些其他的逻辑。

核心组件 DeliveryNotificationManager.h

/*
* 可靠数据包传输管理器
* 核心功能
  1.数据包序列编号:为每个发出的数据包分配唯一序列号
  2.ACK确认机制:接收方确认已收到的数据包
  3.丢包检测与处理:通过超时机制检测丢包
  4.传输统计:记录成功/丢失/发送的数据包数量
*/
class DeliveryNotificationManager
{
public:
	DeliveryNotificationManager(bool ShouldSendAcks, bool ShouldProcessAcks);
	~DeliveryNotificationManager();

    /*核心流程方法 发送数据包时调用*/
	inline InFlightPacket* WriteState(OutputMemoryBitStream& OutputStream);
    /*核心流程方法 接收数据包时调用*/
	inline bool ReadAndProcessState(InputMemoryBitStream& InputStream);

    /*检查超时未确认的数据包 触发丢包处理逻辑*/
	void ProcessTimedOutPackets();
    //统计获取方法
	uint32_t GetDroppedPacketCount() const { return mDroppedPacketCount; }
	uint32_t GetDeliveredPacketCount() const { return mDeliveredPacketCount; }
	uint32_t GetDispatchedPacketCount()	const { return mDispatchedPacketCount; }

	const deque<InFlightPacket>& GetInFlightPackets()	const { return mInFlightPackets; }
private:
    //序列号处理
	InFlightPacket* WriteSequenceNumber(OutputMemoryBitStream& OutputStream);
    /*剥离控制序列号 后续逻辑直接处理ack信息*/
	bool ProcessSequenceNumber(InputMemoryBitStream& InputStream);

    //ACK处理
	void WriteAckData(OutputMemoryBitStream& OutputStream);
	void ProcessAcks(InputMemoryBitStream& InputStream);
		 
	//状态管理	 
	void AddPendingAck(PacketSequenceNumber SequenceNumber);
	void HandlePacketDeliveryFailure(const InFlightPacket& FlightPacket);
	void HandlePacketDeliverySuccess(const InFlightPacket& FlightPacket);

    //下一个发出的数据包序列号
	PacketSequenceNumber mNextOutgoingSequenceNumber;
    //预期接收的下一个序列号
	PacketSequenceNumber mNextExpectedSequenceNumber;
    //已发送但未确认的数据包队列 按发送时间排序的队列
	deque<InFlightPacket> mInFlightPackets;
    //待发送的ACK确认范围队列
	deque<AckRange>	mPendingAcks;
    /*
    非对称可靠性需求:
    某些应用可能只需要单向可靠性。例如:
        服务器→客户端:需要可靠传输(如状态更新)
        客户端→服务器:可以容忍丢包(如输入指令)
    混合可靠性模式:
        允许某些通道启用可靠性,其他通道保持不可靠传输
    */
    //是否应发送ACK确认
	bool mShouldSendAcks;
    //是否处理收到的ACK
	bool mShouldProcessAcks;

    //成功交付的数据包计数
	uint32_t mDeliveredPacketCount;
    //丢失的数据包计数
	uint32_t mDroppedPacketCount;
    //已发送的数据包计数
	uint32_t mDispatchedPacketCount;
};

inline InFlightPacket* DeliveryNotificationManager::WriteState(OutputMemoryBitStream& OutputStream)
{
	InFlightPacket* toRet = WriteSequenceNumber(OutputStream);//写入序列号到数据流
    //如果需要发送ACK,写入ACK数据
	if (mShouldSendAcks)
	{
		WriteAckData(OutputStream);
	}
	return toRet;//返回InFlightPacket对象用于跟踪
}

inline bool	DeliveryNotificationManager::ReadAndProcessState(InputMemoryBitStream& InputStream)
{
	bool toRet = ProcessSequenceNumber(InputStream);//处理收到的序列号(检测丢包/乱序)
    //如果需要处理ACK,解析ACK数据
	if (mShouldProcessAcks)
	{
		ProcessAcks(InputStream);
	}
	return toRet;
}

DeliveryNotificationManager.cpp

#include "MalouPCH.h"

namespace
{
	const float kDelayBeforeAckTimeOut = 0.5f;
}

DeliveryNotificationManager::DeliveryNotificationManager(bool ShouldSendAcks, bool ShouldProcessAcks):
	mNextOutgoingSequenceNumber(0),
	mNextExpectedSequenceNumber(0),
	mShouldSendAcks(ShouldSendAcks),
	mShouldProcessAcks(ShouldProcessAcks),
	mDeliveredPacketCount(0),
	mDroppedPacketCount(0),
	mDispatchedPacketCount(0)
{}

DeliveryNotificationManager::~DeliveryNotificationManager()
{
	//LOG("DNM destructor. Delivery rate %d%%, Drop rate %d%%",
	//	(100 * mDeliveredPacketCount) / mDispatchedPacketCount,
	//	(100 * mDroppedPacketCount) / mDispatchedPacketCount);
}

void DeliveryNotificationManager::ProcessTimedOutPackets()
{
	//计算超时时间点
    float timeoutTime = Timing::Instance.GetTimef() - kDelayBeforeAckTimeOut;

	while (!mInFlightPackets.empty())
	{
		const auto& nextInFlightPacket = mInFlightPackets.front();

		//该包已发出超过0.5秒仍未收到ACK → 网络可能已丢失该包
        if (nextInFlightPacket.GetTimeDispatched() < timeoutTime)
		{
			HandlePacketDeliveryFailure(nextInFlightPacket);
			mInFlightPackets.pop_front();
		}
		else//否则终止检查(队列按时间排序)
		{
			break;
		}
	}
}

InFlightPacket* DeliveryNotificationManager::WriteSequenceNumber(OutputMemoryBitStream& OutputStream)
{
	//分配并写入递增的序列号
    PacketSequenceNumber sequenceNumber = mNextOutgoingSequenceNumber++;
	OutputStream.Write(sequenceNumber);

    //增加发送计数器
	++mDispatchedPacketCount;
    //如果需要处理ACK,创建InFlightPacket跟踪包
	if (mShouldProcessAcks)
	{
		mInFlightPackets.emplace_back(sequenceNumber);

		return &mInFlightPackets.back();
	}
	else
	{
		return nullptr;
	}
}

void DeliveryNotificationManager::WriteAckData(OutputMemoryBitStream& OutputStream)
{
	//检查有待确认的ack
    bool hasAcks = (mPendingAcks.size() > 0);
    //写入是否有ACK的标志
	OutputStream.Write(hasAcks);
    //写入最早的ACK范围数据
	if (hasAcks)
	{
		mPendingAcks.front().Write(OutputStream);
		mPendingAcks.pop_front();
	}
}

bool DeliveryNotificationManager::ProcessSequenceNumber(InputMemoryBitStream& InputStream)
{
	//创建并读取序列号
    PacketSequenceNumber sequenceNumber;
	InputStream.Read(sequenceNumber);

	if (sequenceNumber == mNextExpectedSequenceNumber)//预期序列号 正常接收
	{
		mNextExpectedSequenceNumber = sequenceNumber + 1;//更新预期值
		if (mShouldSendAcks)
		{
			AddPendingAck(sequenceNumber);
		}
		return true;
	}
	else if (sequenceNumber < mNextExpectedSequenceNumber)//旧序列号 重复包忽略
	{
		return false;
	}
	else if (sequenceNumber > mNextExpectedSequenceNumber)//新序列号 检测到丢包,跳过缺失包
	{
		mNextExpectedSequenceNumber = sequenceNumber + 1;
		if (mShouldSendAcks)
		{
			AddPendingAck(sequenceNumber);
		}
		return true;
	}
	return false;
}


void DeliveryNotificationManager::ProcessAcks(InputMemoryBitStream& InputStream)
{
	//创建并读取ACK状态
    bool hasAcks;
	InputStream.Read(hasAcks);
    //存在ACK
	if (hasAcks)
	{
		//创建并读取ACK范围数据
        AckRange ackRange;
		ackRange.Read(InputStream);

		PacketSequenceNumber nextAckdSequenceNumber = ackRange.GetStart();//获取范围起始索引
		uint32_t onePastAckdSequenceNumber = nextAckdSequenceNumber + ackRange.GetCount();//获取范围大小
		while (nextAckdSequenceNumber < onePastAckdSequenceNumber && !mInFlightPackets.empty())
		{
			const auto& nextInFlightPacket = mInFlightPackets.front();
			PacketSequenceNumber nextInFlightPacketSequenceNumber = nextInFlightPacket.GetSequenceNumber();

			if (nextInFlightPacketSequenceNumber < nextAckdSequenceNumber)//已确认包之前的包 → 视为丢包
			{
				auto copyOfInFlightPacket = nextInFlightPacket;
				mInFlightPackets.pop_front();
				HandlePacketDeliveryFailure(copyOfInFlightPacket);
			}
			else if (nextInFlightPacketSequenceNumber == nextAckdSequenceNumber)//正好是确认的包 → 成功处理
			{
				HandlePacketDeliverySuccess(nextInFlightPacket);
				mInFlightPackets.pop_front();
				++nextAckdSequenceNumber;
			}
			else if (nextInFlightPacketSequenceNumber > nextAckdSequenceNumber)//确认包之后的包 → 跳过
			{
				++nextAckdSequenceNumber;
			}
		}
	}
}

void DeliveryNotificationManager::AddPendingAck(PacketSequenceNumber SequenceNumber)
{
	//尝试扩展最后一个ACK范围,优化ACK数据量(合并连续序列号)
    if (mPendingAcks.size() == 0 || !mPendingAcks.back().ExtendIfShould(SequenceNumber))
	{
        //如果不能扩展则新建ACK范围
        
        //这里使用了c++11的新特性 原位构造函数,其特点是
        //直接在容器内存中构造元素
        //不需要预先创建临时对象
        //参数会完美转发给 T 的构造函数
        mPendingAcks.emplace_back(SequenceNumber);
	}
}

void DeliveryNotificationManager::HandlePacketDeliveryFailure(const InFlightPacket& FlightPacket)
{
	++mDroppedPacketCount;//增加丢包计数
	FlightPacket.HandleDeliveryFailure(this);//回调包对象的失败处理
}

void DeliveryNotificationManager::HandlePacketDeliverySuccess(const InFlightPacket& FlightPacket)
{
	++mDeliveredPacketCount;//增加成功计数
	FlightPacket.HandleDeliverySuccess(this);//回调包对象的成功处理
}

辅助类 AckRange.h



/*
* 高效批量确认网络数据包辅助类
* 用于优化可靠UDP协议中的ACK确认机制
* 核心功能
    1.表示连续的ACK范围:记录一个连续的已接收数据包序列
    2.范围扩展:动态合并相邻的序列号
    3.序列化/反序列化:支持网络传输
*应用场景示例
    包5(创建范围[5])
    包6(扩展为[5,6]) 
    包8(创建新范围[8])
    包7(扩展为[5,6,7],合并后[5,6,7,8])
    最终只需发送一个ACK范围:start=5, count=4
*/
class AckRange
{
public:
	AckRange():mStart(0),mCount(0){}
	AckRange(PacketSequenceNumber Start):mStart(0),mCount(1){}
    /*扩展ACK范围 自动合并相邻包,减少需要发送的ACK数量*/
	inline bool ExtendIfShould(PacketSequenceNumber SequenceNumber);
	PacketSequenceNumber GetStart()const { return mStart; }
	uint32_t GetCount()const { return mCount; }
	void Write(OutputMemoryBitStream& OutputStream) const;
	void Read(InputMemoryBitStream& InputStream);
private:
    //连续ACK范围的起始序列号
	PacketSequenceNumber mStart;
    //连续ACK的数量
	uint32_t mCount;
};

inline bool AckRange::ExtendIfShould(PacketSequenceNumber SequenceNumber)
{
	if (SequenceNumber == mStart + mCount)
	{
		++mCount;
		return true;
	}
	return false;
}

AckRange.cpp

#include"MalouPCH.h"

void AckRange::Write(OutputMemoryBitStream& OutputStream) const
{
	OutputStream.Write(mStart);
	bool hasCount = mCount > 1;
	OutputStream.Write(hasCount);
	if (hasCount)
	{
		uint32_t countMinusOne = mCount - 1;
		uint8_t countToAck = countMinusOne > 25 ? 25 : static_cast<uint8_t>(countMinusOne);
		OutputStream.Write(countToAck);
	}
}

void AckRange::Read(InputMemoryBitStream& InputStream)
{
	InputStream.Read(mStart);
	bool hasCount;
	InputStream.Read(hasCount);
	if (hasCount)
	{
		uint8_t countMinusOne;
		InputStream.Read(countMinusOne);
		mCount = countMinusOne + 1;
	}
	else
	{
		mCount = 1;
	}
}

辅助类 InFlightPacket.h


class DeliveryNotificationManager;

using PacketSequenceNumber = uint16_t;
/*
可靠UDP实现的核心组件
用于跟踪已发送但未确认的网络数据包的类
核心功能
    1.记录在途数据包:跟踪已发送但未收到ACK确认的数据包
    2.传输数据管理:存储与数据包关联的传输数据
    3.传输结果处理:提供成功/失败的回调接口
*/
class InFlightPacket
{
public:
	InFlightPacket(PacketSequenceNumber SequenceNumber);
    /*获取包序列号*/
	PacketSequenceNumber GetSequenceNumber() const { return mSequenceNumber; }
    /*获取包发送时间*/
	float GetTimeDispatched() const { return mTimeDispatched; }
    /*存储附加数据*/
	void SetTransmissionData(int Key, TransmissionDataPtr TransmissionData)
	{
		mTransmissionDataMap[Key] = TransmissionData;
	}
    /*获取附加数据*/
	const TransmissionDataPtr GetTransmissionData(int Key) const
	{
		auto it = mTransmissionDataMap.find(Key);
		if (it != mTransmissionDataMap.end())
		{
			return it->second;
		}
		return nullptr;
	}
    /*传输失败回调*/
	void HandleDeliveryFailure(DeliveryNotificationManager* DeliveryNotificationManager) const; 
    /*传输成功回调*/
	void HandleDeliverySuccess(DeliveryNotificationManager* DeliveryNotificationManager) const;
private:
    //数据包唯一序列号
	PacketSequenceNumber mSequenceNumber;
    //数据包发送时间戳
	float mTimeDispatched;
    //存储与包关联的传输数据
	unordered_map<int,TransmissionDataPtr>	mTransmissionDataMap; 
};

InFlightPacket.cpp

#include"MalouPCH.h"

InFlightPacket::InFlightPacket(PacketSequenceNumber SequenceNumber) :
	mSequenceNumber(SequenceNumber),
	mTimeDispatched(Timing::Instance.GetTimef())
{
}


void InFlightPacket::HandleDeliveryFailure(DeliveryNotificationManager* DeliveryNotificationManager) const
{
	for (const auto& pair : mTransmissionDataMap)
	{
		pair.second->HandleDeliveryFailure(DeliveryNotificationManager);
	}
}

void InFlightPacket::HandleDeliverySuccess(DeliveryNotificationManager* DeliveryNotificationManager) const
{
	for (const auto& pair : mTransmissionDataMap)
	{
		pair.second->HandleDeliverySuccess(DeliveryNotificationManager);
	}
}

六.同步管理器

同步命令封装 ReplicationCommand.h

#pragma once
/*
* 网络同步操作类型枚举
*/
enum ReplicationAction
{
	RA_Create,
	RA_Update,
	RA_Destroy,
	RA_RPC,
	RA_MAX
};

class ReplicationManagerTransmissionData;

struct ReplicationCommand
{
public:

    ReplicationCommand() {}
	ReplicationCommand(uint32_t InitialDirtyState) :
		mAction(RA_Create),
		mDirtyState(InitialDirtyState) {}

	void HandleCreateAckd() { if (mAction == RA_Create) { mAction = RA_Update; } }
	void AddDirtyState(uint32_t State) { mDirtyState |= State; }
	void SetDestroy() { mAction = RA_Destroy; }

	bool HasDirtyState() const { return (mAction == RA_Destroy) || (mDirtyState != 0); }

	ReplicationAction GetAction() const { return mAction; }
	uint32_t GetDirtyState() const { return mDirtyState; }
	inline void	ClearDirtyState(uint32_t StateToClear);

	void Write(OutputMemoryBitStream& OutputStream, int NetworkId, ReplicationManagerTransmissionData* TransactionData);
	void Read(InputMemoryBitStream& InputStream, int NetworkId);

private:
	uint32_t mDirtyState;
	ReplicationAction mAction;
};

inline void	 ReplicationCommand::ClearDirtyState(uint32_t StateToClear)
{
	mDirtyState &= ~StateToClear;

	if (mAction == RA_Destroy)
	{
		mAction = RA_Update;
	}
}

服务器同步管理器 ReplicationManagerServer.h

/*
* 游戏服务器端对象同步管理器
* 管理游戏对象的网络复制(创建/更新/销毁)和状态同步
*/
class ReplicationManagerServer
{
public:
    /*同步创建操作*/
	void ReplicateCreate(int NetworkId, uint32_t InitialDirtyState);
    /*同步销毁操作*/
	void ReplicateDestroy(int NetworkId);
    /*设置状态脏标记*/
	void SetStateDirty(int NetworkId, uint32_t DirtyState);
    /*处理创建ACK包*/
	void HandleCreateAckd(int NetworkId);
    /*停止同步指定对象*/
	void RemoveFromReplication(int NetworkId);
    /*将待同步操作写入网络包*/
	void Write(OutputMemoryBitStream& OutputStream, ReplicationManagerTransmissionData* TransmissinData);
private:

    /*序列化创建操作*/
	uint32_t WriteCreateAction(OutputMemoryBitStream& OutputStream, int NetworkId, uint32_t DirtyState);
    /*序列化状态更新操作*/
	uint32_t WriteUpdateAction(OutputMemoryBitStream& OutputStream, int NetworkId, uint32_t DirtyState);
    /*序列化销毁操作*/
	uint32_t WriteDestroyAction(OutputMemoryBitStream& OutputStream, int NetworkId, uint32_t DirtyState);

	unordered_map<int, ReplicationCommand>	mNetworkIdToReplicationCommand;

};

ReplicationManagerServer.cpp

#include"MalouServerPCH.h"

void ReplicationManagerServer::ReplicateCreate(int NetworkId, uint32_t InitialDirtyState)
{
	mNetworkIdToReplicationCommand[NetworkId] = ReplicationCommand(InitialDirtyState);
}

void ReplicationManagerServer::ReplicateDestroy(int NetworkId)
{
	mNetworkIdToReplicationCommand[NetworkId].SetDestroy();
}

void ReplicationManagerServer::RemoveFromReplication(int NetworkId)
{
	mNetworkIdToReplicationCommand.erase(NetworkId);
}

void ReplicationManagerServer::SetStateDirty(int NetworkId, uint32_t DirtyState)
{
	mNetworkIdToReplicationCommand[NetworkId].AddDirtyState(DirtyState);
}

void ReplicationManagerServer::HandleCreateAckd(int NetworkId)
{
	mNetworkIdToReplicationCommand[NetworkId].HandleCreateAckd();
}

void ReplicationManagerServer::Write(OutputMemoryBitStream& OutputStream, ReplicationManagerTransmissionData* TransmissinData)
{
	for (auto& pair : mNetworkIdToReplicationCommand)
	{
		ReplicationCommand& replicationCommand = pair.second;
		if (replicationCommand.HasDirtyState())
		{
			int networkId = pair.first;

			OutputStream.Write(networkId);

			ReplicationAction action = replicationCommand.GetAction();
			OutputStream.Write(action, 2);

			uint32_t writtenState = 0;
			uint32_t dirtyState = replicationCommand.GetDirtyState();

			switch (action)
			{
			case RA_Create:
				writtenState = WriteCreateAction(OutputStream, networkId, dirtyState);
				break;
			case RA_Update:
				writtenState = WriteUpdateAction(OutputStream, networkId, dirtyState);
				break;
			case RA_Destroy:
				writtenState = WriteDestroyAction(OutputStream, networkId, dirtyState);
				break;
			}
            //记录已发送的同步操作
			TransmissinData->AddTransmission(networkId, action, writtenState);

			replicationCommand.ClearDirtyState(writtenState);

		}
	}
}


uint32_t ReplicationManagerServer::WriteCreateAction(OutputMemoryBitStream& OutputStream, int NetworkId, uint32_t DirtyState)
{
	GameObjectPtr gameObject = NetworkManagerServer::Instance->GetGameObject(NetworkId);
	OutputStream.Write(gameObject->GetClassId());
	return gameObject->Write(OutputStream, DirtyState);
}

uint32_t ReplicationManagerServer::WriteUpdateAction(OutputMemoryBitStream& OutputStream, int NetworkId, uint32_t DirtyState)
{
	GameObjectPtr gameObject = NetworkManagerServer::Instance->GetGameObject(NetworkId);
	
	uint32_t writtenState = gameObject->Write(OutputStream, DirtyState);

	return writtenState;
}

uint32_t ReplicationManagerServer::WriteDestroyAction(OutputMemoryBitStream& OutputStream, int NetworkId, uint32_t DirtyState)
{
	(void)OutputStream;
	(void)NetworkId;
	(void)DirtyState;
	return DirtyState;
}

客户端同步管理器 ReplicationManagerClient.h

class ReplicationManagerClient
{
public:
	void Read(InputMemoryBitStream& InputStream);
private:
	void ReadAndDoCreateAction(InputMemoryBitStream& InputStream, int NetworkId);
	void ReadAndDoUpdateAction(InputMemoryBitStream& InputStream, int NetworkId);
	void ReadAndDoDestroyAction(InputMemoryBitStream& InputStream, int NetworkId);
};

ReplicationManagerClient.cpp

#include"MalouClientPCH.h"
#include <cassert>

void ReplicationManagerClient::Read(InputMemoryBitStream& InputStream)
{
	while (InputStream.GetRemainingBitCount() >= 32)
	{
		int networkId; InputStream.Read(networkId);
		uint8_t action; InputStream.Read(action, 2);
		switch (action)
		{
		case RA_Create:
			ReadAndDoCreateAction(InputStream, networkId);
			break;
		case RA_Update:
			ReadAndDoUpdateAction(InputStream, networkId);
			break;
		case RA_Destroy:
			ReadAndDoDestroyAction(InputStream, networkId);
			break;
		}
	}
}

void ReplicationManagerClient::ReadAndDoCreateAction(InputMemoryBitStream& InputStream, int inNetworkId)
{
	uint32_t fourCCName;
	InputStream.Read(fourCCName);
	GameObjectPtr gameObject = NetworkManagerClient::Instance->GetGameObject(inNetworkId);
	if (!gameObject)
	{
		gameObject = GameObjectRegistry::Instance->CreateGameObject(fourCCName);
		gameObject->SetNetworkId(inNetworkId);
		NetworkManagerClient::Instance->AddToNetworkIdToGameObjectMap(gameObject);

		assert(gameObject->GetClassId() == fourCCName);
	}
	gameObject->Read(InputStream);
}

void ReplicationManagerClient::ReadAndDoUpdateAction(InputMemoryBitStream& InputStream, int inNetworkId)
{
	GameObjectPtr gameObject = NetworkManagerClient::Instance->GetGameObject(inNetworkId);
	gameObject->Read(InputStream);
}

void ReplicationManagerClient::ReadAndDoDestroyAction(InputMemoryBitStream& InputStream, int inNetworkId)
{
	GameObjectPtr gameObject = NetworkManagerClient::Instance->GetGameObject(inNetworkId);
	if (gameObject)
	{
		gameObject->SetDoesWantToDie(true);
		NetworkManagerClient::Instance->RemoveFromNetworkIdToGameObjectMap(gameObject);
	}
}

七.游戏对象网络同步的传输数据处理

可靠数据传输回调机制的抽象基类 TransmissionData.h

class DeliveryNotificationManager;

/*
* 可靠数据传输回调机制的抽象基类
* 用于在网络包传输成功或失败时执行特定操作
* 核心设计目的
    1.提供传输结果回调接口:当数据包被确认送达或丢失时触发相应处理
    2.支持多态行为:允许派生类实现不同的传输后处理逻辑
    3.生命周期管理:通过智能指针自动管理内存
*/
class TransmissionData
{
public:
	virtual void HandleDeliveryFailure(DeliveryNotificationManager* inDeliveryNotificationManager) const = 0;
	virtual void HandleDeliverySuccess(DeliveryNotificationManager* inDeliveryNotificationManager) const = 0;
};
using TransmissionDataPtr = shared_ptr<TransmissionData>;

数据处理类 ReplicationManagerTransmissionData.h


class ReplicationManagerServer;

/*
* 游戏对象网络同步的传输数据处理类
* 处理游戏世界中对象的创建、状态更新和销毁的可靠传输
*/
class ReplicationManagerTransmissionData : public TransmissionData
{
public:
	ReplicationManagerTransmissionData(ReplicationManagerServer* ReplicationManagerServer) :
		mReplicationManagerServer(ReplicationManagerServer)
	{}
    /*单个同步操作类*/
	class ReplicationTransmission
	{
	public:
		ReplicationTransmission(int NetworkId, ReplicationAction Action, uint32_t State) :
			mNetworkId(NetworkId),
			mAction(Action),
			mState(State)
		{}
		int GetNetworkId() const { return mNetworkId; }
		ReplicationAction GetAction() const { return mAction; }
		uint32_t GetState() const { return mState; }

	private:
        //对象网络id
		int mNetworkId;
        //操作类型(创建/更新/销毁)
		ReplicationAction mAction;
        //对象状态
		uint32_t mState;
	};
    /*添加同步操作*/
	void AddTransmission(int NetworkId, ReplicationAction Action, uint32_t State);

    /*传输结果处理*/
	virtual void HandleDeliveryFailure(DeliveryNotificationManager* DeliveryNotificationManager) const override;
	virtual void HandleDeliverySuccess(DeliveryNotificationManager* DeliveryNotificationManager) const override;


private:
    /*对象创建失败处理*/
	void HandleCreateDeliveryFailure(int NetworkId) const;
    /*状态更新失败处理*/
	void HandleUpdateStateDeliveryFailure(int NetworkId, uint32_t State, DeliveryNotificationManager* DeliveryNotificationManager) const;
	/*对象销毁失败处理*/
    void HandleDestroyDeliveryFailure(int NetworkId) const;
    /*对象创建成功处理*/
	void HandleCreateDeliverySuccess(int NetworkId) const;
    /*对象销毁成功处理*/
	void HandleDestroyDeliverySuccess(int NetworkId) const;
    //服务器同步管理器指针
	ReplicationManagerServer* mReplicationManagerServer;
    //保存同步操作
	vector<ReplicationTransmission> mTransmissions;
};
using ReplicationManagerTransmissionDataPtr = shared_ptr<ReplicationManagerTransmissionData>;

ReplicationManagerTransmissionData.cpp

#include"MalouServerPCH.h"

void ReplicationManagerTransmissionData::AddTransmission(int NetworkId, ReplicationAction Action, uint32_t State)
{
	mTransmissions.emplace_back(NetworkId, Action, State);
}

void ReplicationManagerTransmissionData::HandleDeliveryFailure(DeliveryNotificationManager* DeliveryNotificationManager) const
{
	for (const ReplicationTransmission& rt : mTransmissions)
	{
		int networkId = rt.GetNetworkId();
		switch (rt.GetAction())
		{
		case RA_Create:
			HandleCreateDeliveryFailure(networkId);
			break;
		case RA_Update:
			HandleUpdateStateDeliveryFailure(networkId, rt.GetState(), DeliveryNotificationManager);
			break;
		case RA_Destroy:
			HandleDestroyDeliveryFailure(networkId);
			break;
		}
	}
}

void ReplicationManagerTransmissionData::HandleDeliverySuccess(DeliveryNotificationManager* DeliveryNotificationManager) const
{
	for (const ReplicationTransmission& rt : mTransmissions)
	{
		switch (rt.GetAction())
		{
		case RA_Create:
			HandleCreateDeliverySuccess(rt.GetNetworkId());
			break;
		case RA_Destroy:
			HandleDestroyDeliverySuccess(rt.GetNetworkId());
			break;
		}
	}
}

void ReplicationManagerTransmissionData::HandleCreateDeliveryFailure(int NetworkId) const
{
	GameObjectPtr gameObject = NetworkManagerServer::Instance->GetGameObject(NetworkId);
	if (gameObject)
	{
		mReplicationManagerServer->ReplicateCreate(NetworkId, gameObject->GetAllStateMask());
	}
}

void ReplicationManagerTransmissionData::HandleDestroyDeliveryFailure(int NetworkId) const
{
	mReplicationManagerServer->ReplicateDestroy(NetworkId);
}

void ReplicationManagerTransmissionData::HandleUpdateStateDeliveryFailure(int NetworkId, uint32_t State, DeliveryNotificationManager* DeliveryNotificationManager) const
{
	if (NetworkManagerServer::Instance->GetGameObject(NetworkId))//检查对象的有效性
	{
		//遍历所有已发送但未确认的数据包,
        for (const auto& inFlightPacket : DeliveryNotificationManager->GetInFlightPackets())
		{
			ReplicationManagerTransmissionDataPtr rmtdp = std::static_pointer_cast<ReplicationManagerTransmissionData>(inFlightPacket.GetTransmissionData('RPLM'));

			//状态冲突检测
            for (const ReplicationTransmission& otherRT : rmtdp->mTransmissions)
			{
				/*
                * 使用位掩码清除已经被其他在途包包含的状态位
                * 例如:
                    失败的状态:0b00001111 (表示需要同步4个属性)
                    在途包的状态:0b00000011
                    结果状态:0b00001100 (只保留未被覆盖的属性)
                */
                State &= ~otherRT.GetState();
			}
		}
        //只有当剩余状态不为0时 重新标记需要同步的状态
		if (State)
		{
			mReplicationManagerServer->SetStateDirty(NetworkId, State);
		}
	}
}

void ReplicationManagerTransmissionData::HandleCreateDeliverySuccess(int NetworkId) const
{
	mReplicationManagerServer->HandleCreateAckd(NetworkId);
}

void ReplicationManagerTransmissionData::HandleDestroyDeliverySuccess(int NetworkId) const
{
	mReplicationManagerServer->RemoveFromReplication(NetworkId);
}

八.服务器客户端代理

单个客户端连接的代理类,管理客户端相关的所有网络状态和数据
ClientProxy.h

/*
* 单个客户端连接的代理类
* 管理客户端相关的所有网络状态和数据
* 核心职责
    1.客户端连接管理:维护客户端网络地址和连接状态
    2.输入处理:缓存和处理客户端输入指令
    3.可靠传输:管理数据包的可靠传输和确认
    4.对象同步:控制客户端可见的游戏对象同步
    5.玩家状态:维护玩家专属的游戏状态
* 
*/
class ClientProxy
{
public:

	ClientProxy(const SocketAddress& SocketAddress, const string& Name, int PlayerId);
    /*获取客户端网络地址*/
	const SocketAddress& GetSocketAddress()	const { return mSocketAddress; }
    /*获取玩家ID*/
	int	GetPlayerId() const { return mPlayerId; }
    /*获取玩家昵称*/
	const string& GetName() const { return mName; }
    /*设置输入状态*/
	void SetInputState(const InputState& InputState) { mInputState = InputState; }
    /*获取输入状态*/
	const InputState& GetInputState() const { return mInputState; }
    /*更新最后通信时间*/
	void UpdateLastPacketTime();
    /*获取最后通信时间*/
	float GetLastPacketFromClientTime()	const { return mLastPacketFromClientTime; }
    /*获取可靠传输管理器*/
	DeliveryNotificationManager& GetDeliveryNotificationManager() { return mDeliveryNotificationManager; }
    /*获取对象同步管理器*/
	ReplicationManagerServer& GetReplicationManagerServer() { return mReplicationManagerServer; }
    /*获取未处理的移动指令列表*/
	const MoveList& GetUnprocessedMoveList() const { return mUnprocessedMoveList; }
    /*获取未处理的移动指令列表*/
	MoveList& GetUnprocessedMoveList() { return mUnprocessedMoveList; }
    /*设置时间戳脏标记*/
	void SetIsLastMoveTimestampDirty(bool IsDirty) { mIsLastMoveTimestampDirty = IsDirty; }
    /*获取时间戳脏标记*/
	bool IsLastMoveTimestampDirty()	const { return mIsLastMoveTimestampDirty; }
	void HandleCatDied();
	void RespawnCatIfNecessary();

private:
    //可靠传输管理器
	DeliveryNotificationManager	mDeliveryNotificationManager;
    //对象同步管理器
	ReplicationManagerServer mReplicationManagerServer;
    //客户端网络地址
	SocketAddress mSocketAddress;
    //玩家昵称
	string mName;
    //玩家唯一ID
	int	mPlayerId;
    //当前输入状态
	InputState mInputState;
    //最后收到数据包的时间
	float mLastPacketFromClientTime;
    //复活倒计时
	float mTimeToRespawn;
    //待处理的移动指令队列
	MoveList mUnprocessedMoveList;
    //时间戳脏标记
	bool mIsLastMoveTimestampDirty;
};

using ClientProxyPtr = shared_ptr<ClientProxy>;

ClientProxy.cpp

#include"MalouServerPCH.h"

namespace
{
	const float kRespawnDelay = 3.f;
}

ClientProxy::ClientProxy(const SocketAddress& SocketAddress, const string& Name, int PlayerId) :
	mSocketAddress(SocketAddress),
	mName(Name),
	mPlayerId(PlayerId),
	mDeliveryNotificationManager(false, true),
	mIsLastMoveTimestampDirty(false),
	mTimeToRespawn(0.f)
{
	UpdateLastPacketTime();
}


void ClientProxy::UpdateLastPacketTime()
{
	mLastPacketFromClientTime = Timing::Instance.GetTimef();
}

void ClientProxy::HandleCatDied()
{
	mTimeToRespawn = Timing::Instance.GetFrameStartTime() + kRespawnDelay;
}

void ClientProxy::RespawnCatIfNecessary()
{
	if (mTimeToRespawn != 0.f && Timing::Instance.GetFrameStartTime() > mTimeToRespawn)
	{
		static_cast<Server*> (Engine::Instance.get())->SpawnCatForPlayer(mPlayerId);
		mTimeToRespawn = 0.f;
	}
}

九.类结构图

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值