• 艾伟_转载:Socket开发探秘数据封包和拆包 狼人:


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

    代码
        /// 
        
    /// 测试数据的实体类信息
        
    ///  
        public class TestDataRequest
        {
            
    #region MyRegion

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

            
    #endregion

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

            
    /// 
            
    /// 转换Socket接收到的信息为对象信息
            
    /// 
            
    /// Socket接收到的信息
            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++];
                } 
            }

            
    /// 
            
    /// 转换对象为Socket发送格式的字符串
            
    /// 
            
    /// 
            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()
            {
            }

            
    /// 
            
    /// 转换Socket接收到的信息为对象信息
            
    /// 
            
    /// Socket接收到的信息
            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);
                        }
                    }
                }
            }

            
    /// 
            
    /// 转换对象为Socket发送格式的字符串
            
    /// 
            
    /// 
            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;
            }
        }

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

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

            
    /// 
            
    /// 请求序列
            
    /// 
            public string Seq;

            
    /// 
            
    /// 用户帐号
            
    /// 
            public string UserId;

            
    /// 
            
    /// 用户密码
            
    /// 
            public string Password;

            
    /// 
            
    /// 消息时间
            
    /// 
            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;
            }

            
    /// 
            
    /// 转换Socket接收到的信息为对象信息
            
    /// 
            
    /// Socket接收到的信息
            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);
            }

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

  • 相关阅读:
    位运算之巧解
    牛客练习赛28
    最大素因子
    hdu 4135 Co-prime(容斥定理入门)
    NYOJ #21 三个水杯(bfs)
    牛客国庆集训派对Day_7
    牛客国庆集训派对Day_4~6
    牛客国庆集训派对Day_1~3
    [POJ2336]Ferry Loading II
    [BZOJ1131][POI2008]Sta
  • 原文地址:https://www.cnblogs.com/waw/p/2157029.html
Copyright © 2020-2023  润新知