龙仪的家

导航

<2006年5月>
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

随笔分类

文章分类

收藏夹

随笔档案

文章档案

统计

我的常用网址

代码库之十一 - 流入流出包

这个类是在我原来的项目经理写的代码基础上改进而成,在此对他表示诚挚的感谢。好,这个类的作用就是在网络传送的时候,给外部提供一个简单的处理数据方式,如果你的数据都很小,比如都是int8或者int16类型的东西,那我建议你不要用这个了,因为冗余太大(当然如果你的带宽很大,不在乎冗余的话尽管用,还是很方便的),对于每个数据对象有个标签,对于一个包的头尾也有标签,对象标签标志这个数据是什么类型,包标签标志这个类的大小,每个标签是4个字节,一个字节表示类型,其余3个字节表示长度,如果你的包大于16777216字节就不能用这个类了,要做修改才行,这个类的好处就是使用起来比较简单。大家的支持是我的动力!
/////////////////////////////////////////H//////////////////////////////////////////////

#ifndef LGLIB_PACKET_H
#define LGLIB_PACKET_H

#if _MSC_VER > 1000
#pragma once
#endif

#include <string>
using namespace std;

namespace lglib
{
 

 #define MAX_PACKET_SIZE 2097152// 1024*1024*2 //the max data length

 //datatype defination
 typedef enum _PACKETTAGTYPE
 {

  UINT8 = 0, //int8
  UINT16,  //int16
  UINT32,  //int32
  STRING,  //string
  STRUCT,  //struct
  PDATA,  //pure data ,you can convert to anything
  PacketHead, //Packet Head Tag
  PacketEnd, //Packet End Tag

  UnKnownType,//type error
  Exceed  //exceed the buffer
  
 } PacketTagType;

//建立包头
#define MAKEPACKETTAG(HI,LOW) \
 ( (uint32) ( ( ( (uint8) HI ) << 24 )  | LOW ) )
#define GETTYPE(x) \
 ( (PacketTagType) ( x >> 24 ) )
#define GETVAL(x) \
 ( (uint32) ( ( x << 8 ) >> 8 ) )
 
 /************************************************************************/
 /*   注意!!!:包最大定义为2M,不能超过此大小                             */
 /*   Attention!!!:The Max Packet size is 2M,Do not exceed this value!  */
 /************************************************************************/

 /************************************************************************/
 /* 数据包基类                                                           */
 /* Packet Base Class                                                    */
 /************************************************************************/
 class lg_Packet
 {
 public:
  virtual cpstr GetData() = 0;  // 获得数据
  virtual uint32 GetTotalSize() = 0;  // 获得包的实际大小
  virtual void Reset() = 0;   // 重置
 };
 /************************************************************************/
 /* 数据包流入虚基类 : 处理已有的数据缓冲区                              */
 /************************************************************************/
 class lg_InPacket
  : public lg_Packet

 {
 public:
  
  virtual lg_InPacket& operator>>( uint8& byte )  = 0;
  virtual lg_InPacket& operator>>( uint16& word )  = 0;
  virtual lg_InPacket& operator>>( uint32& dword ) = 0;
  virtual lg_InPacket& operator>>( cpstr& cstr )  = 0;
  virtual lg_InPacket& operator>>( string& str )  = 0;
  virtual cpstr ReadData( uint32& size )    = 0;
  virtual PacketTagType PreReadTag()     = 0;
  
 protected:

  virtual uint32 ReadTag(PacketTagType type)   = 0;
 };

 
 /************************************************************************/
 /* 数据包流入实现类 : 处理已有的数据缓冲区                              */
 /************************************************************************/

 class __LGLIB__ lg_DataInPacket : public lg_InPacket
 {
 protected:
  // 数据
  cpstr data;      // 数据
  int32 datalen;     // 数据长度
  cpstr cursor;     // 指针

 public:
  // 构造
  lg_DataInPacket( );

 public:
  // 方法
  const char* GetData()  { return data; }
  uint32  GetTotalSize() { return datalen; }
  
  virtual int32 SetData(cpstr d, int32 len);
  void Reset();

  virtual lg_InPacket& operator>>( uint8& byte );
  virtual lg_InPacket& operator>>( uint16& word );
  virtual lg_InPacket& operator>>( uint32& dword );
  virtual lg_InPacket& operator>>( cpstr& cstr );
  lg_InPacket& operator>>( string& str);
  const void* ReadStruct(uint32& size);
  const char* ReadData( uint32& size );

  virtual bool IsEnd();
  virtual PacketTagType PreReadTag();
 protected:
  virtual uint32 ReadTag(PacketTagType type);
 };
 /************************************************************************/
 /* 数据包流出虚基类 : 生成新的数据缓冲区                                */
 /************************************************************************/
 class lg_OutPacket
  : public lg_Packet
 {
 public:
  virtual lg_OutPacket& operator<<( uint8 byte )   = 0;
  virtual lg_OutPacket& operator<<( uint16 word )   = 0;
  virtual lg_OutPacket& operator<<( uint32 dword )  = 0;
  virtual lg_OutPacket& operator<<( cpstr cstr )   = 0;
  virtual lg_OutPacket& operator<<( const string& str ) = 0;
  virtual void WriteData( cpstr Buf, uint32 size )  = 0;
 private:
  virtual void WriteTag(PacketTagType type,uint32 Len) = 0;
 };

 /************************************************************************/
 /* 数据包流出实现类 : 生成新的数据缓冲区                              */
 /************************************************************************/
 class __LGLIB__ lg_DataOutPacket :
  public lg_OutPacket
 {
 protected:
  // 数据
  char* data;  // 数据
  char* cursor;  // 指针
  uint32 m_nTotalLen; // 整个缓冲长度
  uint32 m_nIniSize;
 public:
  // 构造
  lg_DataOutPacket();
  //析构
  ~lg_DataOutPacket();

 public:
  //设定缓冲大小,默认申请2M
  void InitData(uint32 IniSize = 0);
  void Reset();
  // 方法
  //虚基类方法 :取得缓冲头指针
  const char* GetData();
  //虚基类方法 :取得缓冲数据大小
  uint32  GetTotalSize();
  lg_OutPacket& operator<<( const string& str )
  {
   return operator<<( str.c_str() );
  }

  virtual lg_OutPacket& operator<<( uint8 byte );
  virtual lg_OutPacket& operator<<( uint16 word );
  virtual lg_OutPacket& operator<<( uint32 dword );
  virtual lg_OutPacket& operator<<( cpstr cstr );
  virtual void WriteData( cpstr Buf, uint32 size );
  virtual void WriteStruct(cpstr Buf,uint32 size);
 private:
  int32   SetCursor( int32 off );
  void   UpdateHead(uint32 size);
  virtual void WriteTag(PacketTagType type,uint32 Len);
 };
}
#endif

////////////////////////////////////////////////////////CPP////////////////////////////////////////////////

#include "stdafx.h"
#include "lg_Packet.h"
#include "lg_Exception.h"

//using namespace std;

namespace lglib
{
/************************************************************************/
/*  CPP::lg_DataOutPacket                                               */
/************************************************************************/
 lg_DataOutPacket::lg_DataOutPacket()
  : data(NULL) , cursor(NULL) ,m_nTotalLen(0),m_nIniSize(0)
 {

 }

 lg_DataOutPacket::~lg_DataOutPacket()
 {
  delete [] data;
  data = NULL;
  cursor = NULL;
 }
 
 //构造之后必须调用此函数,否则无法正常运行
 void lg_DataOutPacket::InitData(uint32 IniSize)
 {
  if(data != NULL){
   delete [] data;
   data = NULL;
   cursor = NULL;
  }
  if(IniSize == 0){
   data = new char[MAX_PACKET_SIZE];
   m_nIniSize = MAX_PACKET_SIZE;
  }else{
   m_nIniSize = IniSize;
   data = new char[IniSize + 1];//+ 1避免指向无效指针
  }
  cursor = data;
  //数据长度的初始值
  m_nTotalLen = sizeof(PacketTagType)*2;

  WriteTag(PacketHead,m_nTotalLen);//cursor 移位

 }

 //虚基类方法 :取得缓冲头指针
 const char* lg_DataOutPacket::GetData()
 {
  if(NULL == cursor)
   throw lg_Exception("lg_DataOutPacket::GetData()","缓冲区未设置",LG_BUFFER_NOT_INIT);
  return data;
 }

 //虚基类方法 :取得缓冲数据大小
 uint32 lg_DataOutPacket::GetTotalSize()
 {
  if(NULL == cursor)
   throw lg_Exception("lg_DataOutPacket::GetSize()","缓冲区未设置",LG_BUFFER_NOT_INIT);

  return m_nTotalLen;
 }

 //给重复利用这个对象提供可能
 void lg_DataOutPacket::Reset()
 {
  if(NULL == cursor)
   throw lg_Exception("lg_DataOutPacket::SetCursor()","缓冲区未设置",LG_BUFFER_NOT_INIT);
  cursor = data;
  m_nTotalLen = sizeof(PacketTagType)*2;
  WriteTag(PacketHead,m_nTotalLen);//cursor 移位
 }
 int32 lg_DataOutPacket::SetCursor( int32 off )
 {
  if(NULL == cursor)
   throw lg_Exception("lg_DataOutPacket::SetCursor()","缓冲区未设置",LG_BUFFER_NOT_INIT);
  
  int32 old = static_cast<int32>( cursor - data );
  cursor = data + off;
  return old;
 }
 void lg_DataOutPacket::UpdateHead(uint32 size)
 {
  uint32 dword = MAKEPACKETTAG(PacketHead,size);
  *( uint32 *)data = htonl(dword);
 }
 //本类方法
 void lg_DataOutPacket::WriteTag(PacketTagType type,uint32 Len)
 {
  char* EndPos = data + m_nIniSize;

//  if(cursor > EndPos - sizeof( DataTag ))
//   throw lg_Exception("void lg_DataOutPacket::WriteTag","无法容纳数据头",LG_BUFFER_LEN_LIMIT);

  if ( cursor > EndPos - sizeof( int32 ) - Len)
   throw lg_Exception("void lg_DataOutPacket::WriteTag","无法容纳数据内容",LG_BUFFER_LEN_LIMIT);

  uint32 dword = MAKEPACKETTAG(type,Len);

  *( uint32 *)cursor = htonl(dword);
  
  if(type != PacketEnd){
   cursor += sizeof(uint32);
   if(type != PacketHead)
    m_nTotalLen += sizeof(uint32);
  }
 }
 lg_OutPacket& lg_DataOutPacket::operator <<( uint8 byte )
 {
  if(NULL == cursor)
   throw lg_Exception("lg_DataOutPacket::operator <<( uint8 byte )","缓冲区未设置",LG_BUFFER_NOT_INIT);

  WriteTag(UINT8,sizeof(byte));
  *( uint8* )cursor = byte;
  cursor += sizeof(byte);
  m_nTotalLen+=sizeof(byte);
  WriteTag(PacketEnd,m_nTotalLen);
  UpdateHead(m_nTotalLen);
  return (*this);
 }

 lg_OutPacket& lg_DataOutPacket::operator <<( uint16 word )
 {
  if(NULL == cursor)
   throw lg_Exception("lg_DataOutPacket::operator <<( uint16 word )","缓冲区未设置",LG_BUFFER_NOT_INIT);

  WriteTag(UINT16,sizeof(word));
  *( uint16*)cursor = htons(word);
  cursor += sizeof(word);
  m_nTotalLen+=sizeof(word);
  WriteTag(PacketEnd,m_nTotalLen);
  UpdateHead(m_nTotalLen);
  return (*this);
 }

 lg_OutPacket& lg_DataOutPacket::operator <<( uint32 dword )
 {
  if(NULL == cursor)
   throw lg_Exception("lg_DataOutPacket::operator <<( uint32 dword )","缓冲区未设置",LG_BUFFER_NOT_INIT);

  WriteTag(UINT32,sizeof(dword));
  *( uint32 *)cursor = htonl(dword);
  cursor += sizeof(dword);
  m_nTotalLen+=sizeof(dword);
  WriteTag(PacketEnd,m_nTotalLen);
  UpdateHead(m_nTotalLen);
  return (*this);
 }
 lg_OutPacket& lg_DataOutPacket::operator <<( cpstr cstr )
 {
  if(NULL == cursor)
   throw lg_Exception("lg_DataOutPacket::operator <<( cpstr cstr )","缓冲区未设置",LG_BUFFER_NOT_INIT);

  int32 len = static_cast<uint32>( strlen( cstr ) + 1 );
  WriteTag(STRING,len);
  strcpy( cursor, cstr );
  cursor += len;
  m_nTotalLen+=len;
  WriteTag(PacketEnd,m_nTotalLen);
  UpdateHead(m_nTotalLen);
  return (*this);
 }
 void lg_DataOutPacket::WriteStruct(cpstr Buf,uint32 size)
 {
  if(NULL == cursor)
   throw lg_Exception("lg_DataOutPacket::WriteData( cpstr Buf, int32 size )","缓冲区未设置",LG_BUFFER_NOT_INIT);

  WriteTag(STRUCT,size);
  memcpy( cursor, Buf, size );
  cursor += size;
  m_nTotalLen+=size;
  WriteTag(PacketEnd,m_nTotalLen);
  UpdateHead(m_nTotalLen);
 }
 void lg_DataOutPacket::WriteData( cpstr Buf, uint32 size )
 {
  if(NULL == cursor)
   throw lg_Exception("lg_DataOutPacket::WriteData( cpstr Buf, int32 size )","缓冲区未设置",LG_BUFFER_NOT_INIT);

  WriteTag(PDATA,size);
  memcpy( cursor, Buf, size );
  cursor += size;
  m_nTotalLen+=size;
  WriteTag(PacketEnd,m_nTotalLen);
  UpdateHead(m_nTotalLen);
 }
/************************************************************************/
/*  CPP::lg_DataInPacket                                                */
/************************************************************************/
 lg_DataInPacket::lg_DataInPacket() : data(NULL),cursor(NULL),datalen(0)
 {

 }
 
 bool lg_DataInPacket::IsEnd()
 {
  if(PreReadTag() == PacketEnd)
   return true;
  return false;
//  const char* EndPos = data + datalen;
//  if(cursor >= EndPos)
//   return false;
//  return true;
 }
 int32 lg_DataInPacket::SetData(cpstr d, int32 len)
 {
  data = cursor = d;
  datalen = len;
  //指定位置是否有开始标志
  if(PreReadTag() != PacketHead){
   data = cursor = NULL;
   datalen = 0;
   return 0;//抛弃此数据
  }
  uint32 BufLenHead = ReadTag(PacketHead);
  //所提供的缓冲太小
  if(len < BufLenHead){
   data = cursor = NULL;
   datalen = 0;
   return -1;//缓冲不够,保留数据
  }
  cursor = data;
  cursor = cursor + BufLenHead - sizeof(uint32);

  //指定位置没有结束标志
  if(PreReadTag() != PacketEnd){
   data = cursor = NULL;
   datalen = 0;
   return 0;//抛弃此数据
  }
  uint32 BufLenEnd = ReadTag(PacketEnd);
  //前后缓冲长度不一致
  if(BufLenEnd != BufLenHead){
   data = cursor = NULL;
   datalen = 0;
   return 0;//头尾不一致,抛弃数据
  }
  cursor = d + sizeof(uint32);
  datalen = BufLenEnd;

  return BufLenEnd;
 }
 void lg_DataInPacket::Reset()
 {
  data = cursor = NULL;
  datalen = 0;
 }
 //预读类型并不改变cursor指针
 PacketTagType lg_DataInPacket::PreReadTag()
 {
  if(NULL == cursor)
   throw lg_Exception("lg_DataInPacket::PreReadTag()","缓冲区未设置",LG_BUFFER_NOT_INIT);

  const char* EndPos = data + datalen;
  if ( cursor <= EndPos - sizeof( uint32 ))
  {
   uint32 dword = ntohl( *( uint32* )cursor );
   PacketTagType type = GETTYPE(dword);
   if(type >= UINT8&&type < UnKnownType){

    return type;

   }else{

    return UnKnownType;

   }
  }
  return Exceed;
 }
 uint32 lg_DataInPacket::ReadTag(PacketTagType type)
 {
  if(NULL == cursor)
   throw lg_Exception("lg_DataInPacket::ReadTag(PacketTagType type)","缓冲区未设置",LG_BUFFER_NOT_INIT);
  
  const char* EndPos = data + datalen;
  if ( cursor <= EndPos - sizeof( uint32 ))
  {
   uint32 dword = ntohl( *( uint32* )cursor );
   PacketTagType dtype = GETTYPE(dword);
   if(dtype != type)
    throw lg_Exception("int32 lg_DataInPacket::ReadTag","类型不匹配",LG_TYPE_DISMATCH);
   
   cursor += sizeof(uint32);
   uint32 Len = GETVAL(dword);

   if( Len <= 0 || Len >= MAX_PACKET_SIZE )
    throw lg_Exception("int32 lg_DataInPacket::ReadTag","非法的数据长度",LG_BUFFER_TOO_LONG);

   //头尾不做此判断
   if(type != PacketHead && type != PacketEnd){
    if(cursor > EndPos - Len)
     throw lg_Exception("int32 lg_DataInPacket::ReadTag","无法容纳数据内容",LG_BUFFER_LEN_LIMIT);
   }

   return Len;
  }else{
   throw lg_Exception("int32 lg_DataInPacket::ReadTag","到达尾部",LG_BUFFER_LEN_LIMIT);
  }
  return 0;
 }
 
 lg_InPacket& lg_DataInPacket::operator >>( uint8& byte )
 {
  if(NULL == cursor)
   throw lg_Exception("lg_DataInPacket::operator >>( uint8& byte )","缓冲区未设置",LG_BUFFER_NOT_INIT);

  ReadTag(UINT8);
  byte = *( uint8 *)cursor;
  cursor += sizeof(byte);
  return (*this);
 }

 lg_InPacket& lg_DataInPacket::operator >>( uint16& word )
 {
  if(NULL == cursor)
   throw lg_Exception("lg_DataInPacket::operator >>( uint16& word )","缓冲区未设置",LG_BUFFER_NOT_INIT);

  ReadTag(UINT16);
  word = ntohs( *( uint16 *)cursor );
  cursor += sizeof(word);
  return (*this);
 }

 lg_InPacket& lg_DataInPacket::operator >>( uint32& dword )
 {
  if(NULL == cursor)
   throw lg_Exception("lg_DataInPacket::operator >>( uint32& dword )","缓冲区未设置",LG_BUFFER_NOT_INIT);

  ReadTag(UINT32);
  dword = ntohl( *( uint32* )cursor );
  cursor += sizeof(dword);
  return (*this);
 }

 lg_InPacket& lg_DataInPacket::operator >>( cpstr& cstr )
 {
  if(NULL == cursor)
   throw lg_Exception("lg_DataInPacket::operator >>( cpstr& cstr )","缓冲区未设置",LG_BUFFER_NOT_INIT);

  int32 len = ReadTag(STRING);

  if ( cursor <= data + datalen - len && !cursor[len - 1] )
  {
   cstr = cursor;
   //memcpy(str,cursor,len);
   cursor += len;
  }
  else
  {
   throw lg_Exception("lg_InPacket& lg_DataInPacket::operator >>( cpstr& str )","字符串尾错误",LG_STRING_END_ERROR);
  }
  return (*this);
 }

 lg_InPacket& lg_DataInPacket::operator >>( string& str )
 {
  cpstr p;
  operator >>( p );
  str = p;
  return (*this);
 }
 const void* lg_DataInPacket::ReadStruct(uint32& size)
 {
  if(NULL == cursor)
   throw lg_Exception("lg_DataInPacket::ReadData( int32& size )","缓冲区未设置",LG_BUFFER_NOT_INIT);

  int32 len = ReadTag(STRUCT);
  size = len;
  const void * p = cursor;
  cursor += len;
  return p;
 }
 const char* lg_DataInPacket::ReadData( uint32& size )
 {
  if(NULL == cursor)
   throw lg_Exception("lg_DataInPacket::ReadData( int32& size )","缓冲区未设置",LG_BUFFER_NOT_INIT);

  int32 len = ReadTag(PDATA);
  size = len;
  const char * p = cursor;
  cursor += len;
  return p;
 }
}

posted on 2006-05-08 09:57 龙仪 阅读(1705) 评论(5)  编辑 收藏

评论

# re: 代码库之十一 - 流入流出包 2006-05-08 14:16 清风雨

没有细看,初步感觉和我的一个packet设计思路很类似。只不过,我没有用operator重载,而是用函数(所以,我的不能叫stream,只能叫packet)。
关于这个packet的设计,并不独立,如果还是根据自己的网络,设计适合自己的packet比较好,而且工作量也不大。
不过,我很倾向和强烈建议写网络的设计一个pakcet的好。

# re: 代码库之十一 - 流入流出包 2006-05-08 14:47 hpho

偶又来JJYY几句:
很多形式差不多的代码在重复, PacketTagType使得整体设计没多少多态可言. 感觉虚的operator<<和operator>>没什么意义, 因为若有一个新类需要用这个来序列作传送就必须修改基类.

# to:hpho 2006-05-09 01:15 龙仪

首先感谢大家的支持,也许我孤陋寡闻了,我还没在网上看到有谁共享这种代码,如果你那里有请贴上来,大家取长补短嘛:),至于多态,我不太明白,实际上网络传输的就是那几种数据格式吧。我承认虚operator有些罗嗦,但只是部分忠于原著而已:-P

# to:清风雨 2006-05-09 01:17 龙仪

能否将你写的packet类贴上来,大家互相学习嘛:)

# re: 龙仪 2006-05-09 13:19 清风雨

hpho是说你应该去掉那些operator<<的virtual修饰(我倒一直没有注意,只是随便扫了一下)。

我的packet只支持几种基本类型、字符串、数组(我没有用内存块的概念),也不允许扩展,在我的blog里有。所以,其实,和你的接口基乎一样,内部实现上可能我们不同。

标题  
姓名  
主页
验证码 *
内容   
  登录  使用高级评论  Top
[使用Ctrl+Enter键可以直接提交]