飞鸽传书

飞鸽传书数据封包和拆包

局域网聊天软件中,飞鸽传书是唯一的绿色软件,除了传送文件的性能表现突出以外,它的用户交互界面还参考了许多著名即时通讯软件,操作起来十分简单,飞鸽传书用户可立即上手。因此它一再成为各大软件站推荐的精品软件之一,同时也受多许多人的喜爱。

在上篇《飞鸽传书基类及公共类的定义》中介绍过,所有受到的数据包,经过系统的预处理后,都会得到一个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);
        }
 

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



文章来自: 本站原创
引用通告: 查看所有引用 | 我要引用此文章
Tags: 飞鸽传书 数据包
相关日志:
评论: 0 | 引用: 0 | 查看次数: -
发表评论
昵 称:
密 码: 游客发言不需要密码.
内 容:
验证码: 验证码
选 项:
虽然发表评论不用注册,但是为了保护您的发言权,建议您注册帐号.
行业方案 产品概览 服务支持 客户 合作伙伴 新闻和活动 关于我们
主页 联系我们 网站地图 服务条款 隐私声明
Copyright © 2008 FEIM Studios. All rights reserved. 粤ICP备07067380号