字节流和字节顺序

字节流和字节顺序,第1张

介于要设计游戏服务器和客户端逻辑,现在需要普及一下我在代码中用到的一些数据结构,这一篇讲一下字节流--- ByteStream。


字节流就是自己实现的一个数据存储区,可对基础数据和其他特定数据进行序列化和反序列化。


用与玩家数据或者其他服务器数据的落地保存。


也可用于客户端和服务器,、服务器和服务器之间的通信。


里面也添加了对字节顺序的处理逻辑。


如果是通信的数据包可以启动字节顺序转换。


需要注意的一点是我对输入的每一个元素只拷贝元素内容,没有存储其类型,所以在字节流输出的时候没有办法做类型校验,这样对于结构经常变化的数据集合不是很友好,所以如果在通信层面用的话,对于不是经常变化结构的数据集合使用这种方法好于protobuf, 对于经常变化的数据集合可以使用protobuf, 但是我的服务器实现里面是模块化封装,底层的数据结构不会发生改变,所有会用ByteStream, 而 protobuf 会给上层业务层面使用。


贴一下代码:

1:字节顺序代码:

// 字节顺序转换

#ifndef EndianConverter_h__
#define EndianConverter_h__

#include 

namespace SCore
{

	// 未知
#define ENDAIN_UNKNOW 0

// 大端 (网络字节顺序)
#define ENDIAN_BIG 1	

// 小端 (本地字节顺序)
#define ENDIAN_LITTLE 2


// 每一个包含头文件的cpp 都会生成 endian_struct ,目前只有一个文件包含
static union endian_struct
{
	char buf[sizeof(int)];
	int i;
} g_endian_t = { {(char)ENDIAN_LITTLE , (char)3, (char)5, (char)ENDIAN_BIG } };

// 本机字节顺序
#define LOCAL_ENDIAN (g_endian_t.i & 0xff)

// 字节顺序反转
template void EndianReverse(char* value)
{
	if (value == (char*)0 || N <= 1)
		return;

	for (int i = 0; i < N / 2; ++i)
	{
		std::swap(value[i], value[N - i - 1]);
	}
}

// 字节顺序转换
template void EndianConverter(T* value)
{
	EndianReverse((char*)value);
}

// 网络字节顺序转换成本机字节顺序
template void NetEndianToLocal(T* value)
{
	if (ENDIAN_BIG == LOCAL_ENDIAN)
		return;
	EndianReverse((char*)value);
}

// 本机字节顺序转换成网络字节顺序
template void LocalEndianToNet(T* value)
{
	if (ENDIAN_BIG == LOCAL_ENDIAN)
		return;
	EndianReverse((char*)value);
}

// 顺序转换
template 
void ENDIAN_SWAP(char* ptr)
{
	for (int i = 0; i < N / 2; ++i)
	{
	    std::swap(ptr[i], ptr[N - i - 1]);
	}
}
}

2:字节流代码:


// 字节流
#ifndef ByteStream_h__
#define ByteStream_h__

#include 
#include 
#include 
#ifndef _WIN32
#include 
#endif

#include "Net/EndianConverter.h"

namespace SCore
{

#define INIT_BYTE_SIZE 128
	class ByteStream
	{
		typedef std::vector ByteData;
		typedef char* ByteStreamChar;
		typedef wchar_t* ByteStreamWChar;

	public:
		ByteStream(size_t initSize = INIT_BYTE_SIZE, bool bEndian = false)
		{
			m_data.reserve(initSize);
			m_nMaxSize = initSize;
			m_nUseSize = 0;
			m_nReadOffset = 0;
			m_bEndian = bEndian;
			*this << m_bEndian;
		}

		ByteStream(const ByteStream& cpy) :
			m_nMaxSize(cpy.m_nMaxSize),
			m_nReadOffset(cpy.m_nReadOffset),
			m_nUseSize(cpy.m_nUseSize)
		{
			m_data.reserve(m_nMaxSize);
			memcpy(m_data.data(), cpy.m_data.data(), m_nUseSize);
		}

		ByteStream(const char* cs, size_t len)
		{
			m_nMaxSize = 0;
			m_nUseSize = 0;
			m_nReadOffset = 0;
			LoadData(cs, len);
		}


		// 获取流数据
		const char* GetData()
		{
			return m_data.data();
		}

		// 获取流数据大小
		size_t GetDataLen()
		{
			return m_nUseSize;
		}

		// 加载数据
		void LoadData(const char* ptr, size_t len)
		{
			m_nUseSize = 0;
			Append(ptr, len);
			ResetReadOffset();
		}

		// 重置读偏移
		void ResetReadOffset()
		{
			m_nReadOffset = 0;
			*this >> m_bEndian;
		}

		// 清理
		void Clear()
		{
			m_nUseSize = 0;
		}

	public:
		// 输入
		template
		ByteStream& operator<<(const T& t)
		{
			if (m_bEndian)
			{
				T tmp = t;
				LocalEndianToNet(&tmp);
				Append((const char*)&tmp, sizeof(t));
				return *this;
			}

			Append((const char*)&t, sizeof(t));
			return *this;
		}

        // 特例重载
		ByteStream& operator<<(const char* t)
		{
			*this << std::string(t);
			return *this;
		}

		ByteStream& operator<<(const wchar_t* t)
		{
			*this << std::wstring(t);
			return *this;
		}

        // 特例化模板
		template<>
		ByteStream& operator<<(const ByteStreamChar& t)
		{
			*this << std::string(t);
			return *this;
		}

		template<>
		ByteStream& operator<<(const ByteStreamWChar& t)
		{
			*this << std::wstring(t);
			return *this;
		}

		template<>
		ByteStream& operator<<(const std::string& t)
		{
			Append(t.c_str(), t.length() + 1);
			return *this;
		}

		template<>
		ByteStream& operator<<(const std::wstring& t)
		{
			Append((const char*)t.c_str(), (t.length() + 1) * sizeof(wchar_t));
			return *this;
		}

		// 输出
		template
		ByteStream& operator>>(T& t)
		{
			if (m_nUseSize - m_nReadOffset < sizeof(t))
			{
				assert(0 && "read byte stream faild...");// ERROR
				return *this;
			}

			memcpy(&t, m_data.data() + m_nReadOffset, sizeof(t));
			m_nReadOffset += sizeof(t);

			if (m_bEndian)
			{
				NetEndianToLocal(&t);
			}

			return *this;
		}

        // 特例化模板
		template<>
		ByteStream& operator>>(std::string& t)
		{
			size_t len = strlen(m_data.data() + m_nReadOffset);
			if (len >= m_nUseSize - m_nReadOffset)
			{
				assert(0 && "read byte stream faild...");// ERROR
				return *this;
			}

			t = (m_data.data() + m_nReadOffset);
			m_nReadOffset += (len + 1);
			return *this;
		}

		template<>
		ByteStream& operator>>(std::wstring& t)
		{
			size_t len = (strlen(m_data.data() + m_nReadOffset) + 1) * sizeof(wchar_t);
			if (len >= m_nUseSize - m_nReadOffset)
			{
				assert(0 && "read byte stream faild...");// ERROR
				return *this;
			}

			t = (wchar_t*)(m_data.data() + m_nReadOffset);
			m_nReadOffset += len;
			return *this;
		}

	private:
		// 追加写
		void Append(const char* ptr, size_t len)
		{
			assert(ptr && len > 0);
			if (!ptr || len == 0)
			{
				return;
			}

			if (m_nMaxSize - m_nUseSize < len)
			{
				// 扩充
				size_t newSize = m_nMaxSize + (m_nMaxSize >> 1);
				if (newSize < len + m_nUseSize)
				{
					newSize = len + m_nUseSize;
				}

				m_data.reserve(newSize);
				m_nMaxSize = newSize;
			}

			memcpy(m_data.data() + m_nUseSize, ptr, len);
			m_nUseSize += len;

		}

	private:

		ByteData m_data;
		size_t m_nMaxSize;
		size_t m_nUseSize;
		size_t m_nReadOffset;	// 读取偏移	
		bool m_bEndian;
	};
}

#endif // ByteStream_h__

// 简单使用示例:

// 通信包使用ByteStream:

void SendMsg()
{
    // 定义零时变量,初始化buffer大小128, 开启字节顺序转换
    ByteStream bs(128, true);

    // 要发送的数据
    time_t now = g_ServerCore.GetNow();
    int id = 1;
    std::string name("xxxxxxx");
    long long data = 2;
  

    // 输入数据
	bs << now;
    bs << id << name << data;

    // 发包
    // void send(int msg_type, const char* msg, size_t msg_len);
    #define msg_type_test 2    // 定义消息类型,这里都是演示
	pSession->Send(msg_type_test , bs.GetData(), bs.GetDataLen());
}

// 收包:
void OnRecvMsg(MsgPacket* msg)
{
	assert(msg);

    // msg->msg : 上面发送的bs.GetData()
    // msg->len  : 上面发送的bs.GetDataLen()
    ByteStream bs(msg->msg, msg->len);

    // 数据输出
	time_t now;
	int id = 1;
    std::string name;
    long long data;
	bs >> now >> id >> name >> data;
    // TODO
}

// 用于数据存储是一样的使用方式,只不过不需要字节顺序转换
ByteStream bs;    // 这样定义就可以,

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/578331.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-11
下一篇 2022-04-11

发表评论

登录后才能评论

评论列表(0条)

保存