Socket开发探秘--数据封包和拆包

转载:https://www.cnblogs.com/wuhuacong/archive/2009/12/13/1623019.html

在上篇《 Socket开发探秘–基类及公共类的定义 》中介绍过,所有受到的数据包,经过系统的预处理后,都会得到一个PreData的数据实体,该实体包含了协议头、协议内容和所属用户的ID。PreData是定义了一个标准的协议数据格式,包含了协议关键字、协议内容、用户标识的内容。
前面说了,我们数据是通过实体类作为载体的,我们知道,收到的Socket数据经过粗略的解析后,就是PreData类型的数据,这个是通用的数据格式,我们需要进一步处理才能转化为所能认识的数据对象(实体类对象),同样,我们发送数据的时候,内容部分肯定是按照一定协议规则串联起来的数据,那么我们就需要把实体转化为发送的数据格式。综上所述,我们通过实体类,必须实现数据的发送和读取的转换。
复制代码
代码
     ///   <summary>
     ///  测试数据的实体类信息
     ///   </summary>
     public   class  TestDataRequest
    {
         #region  MyRegion

         ///   <summary>
         ///  请求序列
         ///   </summary>
         public   string  seq;
         ///   <summary>
         ///  用户帐号
         ///   </summary>
         public   string  userid;
         ///   <summary>
         ///  用户密码
         ///   </summary>
         public   string  psw;

         #endregion

         public  TestDataRequest( string  seq,  string  userid,  string  psw)
        {
             this .seq  =  seq;
             this .userid  =  userid;
             this .psw  =  psw;
        }
         public  TestDataRequest()
        {
        }

         ///   <summary>
         ///  转换Socket接收到的信息为对象信息
         ///   </summary>
         ///   <param name=”data”> Socket接收到的信息 </param>
         public  TestDataRequest( string  data)
        {
             string [] dataArray  =   null ;
            dataArray  =  NetStringUtil.UnPack(data);
             if  (dataArray  !=   null   &&  dataArray.Length  >   0 )
            {
                TestDataRequest newAnswerData  =   new  TestDataRequest();
                 int  i  =   0 ;
                 this .seq  =  dataArray[i ++ ];
                 this .userid  =  dataArray[i ++ ];
                 this .psw  =  dataArray[i ++ ];
            } 
        }

         ///   <summary>
         ///  转换对象为Socket发送格式的字符串
         ///   </summary>
         ///   <returns></returns>
         public   override   string  ToString()
        {
             string  data  =   “” ;
            data  =   this .seq  +   ” | ”   +   this .userid  +   ” | ”   +   this .psw.ToString();
            data  =  NetStringUtil.PackSend(DataTypeKey.TestDataRequest, data);
             return  data;
        }
复制代码

  

以上把数据的处理放在了实体类中进行封包和拆包,是一种比较好的做法,但是由于数据的封包拆包是一个繁琐的过程,代码重复性比较多,而且也容易出错。

这里设计了一个基类,来改进这种方式的数据处理,我们把所有对数据的拆包和封包,利用反射机制,减少我们的代码量,提高代码的优雅性。

复制代码
代码
     public   class  BaseEntity
    {
         protected   string  HeaderKey;

         public  BaseEntity()
        {
        }

         ///   <summary>
         ///  转换Socket接收到的信息为对象信息
         ///   </summary>
         ///   <param name=”data”> Socket接收到的信息 </param>
         public  BaseEntity( string  data)
        {
             string [] dataArray  =   null ;
            dataArray  =  NetStringUtil.UnPack(data);
             if  (dataArray  !=   null   &&  dataArray.Length  >   0 )
            {
                 int  i  =   0 ;
                FieldInfo[] fieldArray  =  ReflectionUtil.GetFields( this );
                 if  (fieldArray  ==   null   ||  dataArray.Length  !=  fieldArray.Length)
                {
                     throw   new  ArgumentException( ” 收到的信息和字段信息不一致 ” );
                }

                 if  (fieldArray  !=   null )
                {
                     foreach  (FieldInfo info  in  fieldArray)
                    {
                         string  strValue  =  dataArray[i ++ ];
                        ReflectionUtil.SetField( this , info.Name, strValue);
                    }
                }
            }
        }

         ///   <summary>
         ///  转换对象为Socket发送格式的字符串
         ///   </summary>
         ///   <returns></returns>
         public   override   string  ToString()
        {
             string  data  =   “” ;
            FieldInfo[] fieldArray  =  ReflectionUtil.GetFields( this );
            StringBuilder sb  =   new  StringBuilder();
             if  (fieldArray  !=   null )
            {
                 foreach  (FieldInfo info  in  fieldArray)
                {
                    sb.Append(ReflectionUtil.GetField( this , info.Name));
                    sb.Append( ” | ” );
                }
            }

            data  =  sb.ToString().Trim( ’ | ’ );
             if  ( string .IsNullOrEmpty(HeaderKey))
            {
                 throw   new  ArgumentNullException( ” DataTypeKey ” ,  ” 实体类未指定协议类型 ” );
            }
            data  =  NetStringUtil.PackSend(HeaderKey, data);
             return  data;
        }
    }
复制代码

 

以上的是实体类的基类,它封装了数据的拆包和封包过程,只需要在子类代码中指定协议头就可以了。子类的代码如下所示。

 

代码
     ///   <summary>
     ///  测试请求
     ///   </summary>
     public   class  TestDataRequest : BaseEntity
    {
         #region  字段信息

         ///   <summary>
         ///  请求序列
         ///   </summary>
         public   string  Seq;

         ///   <summary>
         ///  用户帐号
         ///   </summary>
         public   string  UserId;

         ///   <summary>
         ///  用户密码
         ///   </summary>
         public   string  Password;

         ///   <summary>
         ///  消息时间
         ///   </summary>
         public  DateTime CreateDate  =  DateTime.Now;

         #endregion

         public  TestDataRequest()
        {
             this .HeaderKey  =  DataTypeKey.TestDataRequest;
        }

         public  TestDataRequest( string  seq,  string  userid,  string  psw)
        {
             this .Seq  =  seq;
             this .UserId  =  userid;
             this .Password  =  psw;
             this .HeaderKey  =  DataTypeKey.TestDataRequest;
        }

         ///   <summary>
         ///  转换Socket接收到的信息为对象信息
         ///   </summary>
         ///   <param name=”data”> Socket接收到的信息 </param>
         public  TestDataRequest( string  data) :  base (data)
        {
             this .HeaderKey  =  DataTypeKey.TestDataRequest;
        }
    }

 

 下面的代码是收到数据包,利用实体类构造函数,解析为实体类的操作,以及构造实体类,通过ToString()方式把实体类信息转化为可以发送的数据包的操作。

 

复制代码
代码
         private   void  TestDataHandle(PreData data)
        {
            TestDataRequest request  =   new  TestDataRequest(data.Content);
            Log.WriteInfo( string .Format( ” ############{0} ” , request.ToString()));

            TestDataAnswerData answerData  =   new  TestDataAnswerData(request.Seq, request.UserId, request.Password);
            ShopClientManager.This.AddSend(data.UserId, answerData.ToString(),  true );
        }
复制代码

 

我编写的测试例子中,实体类的继承图如下所示。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值