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


    在上篇《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);
            }

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

    主要研究技术:代码生成工具、会员管理系统、客户关系管理软件、病人资料管理软件、Visio二次开发、酒店管理系统、仓库管理系统等共享软件开发
    专注于Winform开发框架/混合式开发框架Web开发框架Bootstrap开发框架微信门户开发框架的研究及应用
      转载请注明出处:
    撰写人:伍华聪  http://www.iqidi.com 
        
  • 相关阅读:
    第一篇unity
    C#相关知识小结
    必须知道的八大种排序算法【java实现】
    JAVA八大排序算法
    二进制、八进制、十进制、十六进制之间的转换
    八大排序算法
    JSONArray数据转换成java List
    使用json-lib的JSONObject.toBean( )时碰到的日期属性转换的问题
    一探前端开发中的JS调试技巧
    SpringMVC注解说明
  • 原文地址:https://www.cnblogs.com/wuhuacong/p/1623019.html
Copyright © 2020-2023  润新知