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

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

  

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

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

ExpandedBlockStart.gif 代码
     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;
        }
    }

 

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

 

ExpandedBlockStart.gif 代码
     ///   <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()方式把实体类信息转化为可以发送的数据包的操作。

 

ExpandedBlockStart.gif 代码
         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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值