天天看點

Socket開發探秘--基于Json格式的資料協定收發

前面說到,收到的Socket資料經過粗略的解析後,就是PreData類型的資料,這個是通用的資料格式,我們需要進一步處理才能轉化為所能認識的資料對象(實體類對象),同樣,我們發送資料的時候,内容部分肯定是按照一定協定規則串聯起來的資料,那麼我們就需要把實體轉化為發送的資料格式。綜上所述,我們通過實體類,必須實作資料的發送和讀取的轉換。 

由于資料的封包拆包是一個繁瑣的過程,代碼重複性比較多,而且也容易出錯。前面介紹過設計一個基類,我們把所有對資料的拆包和封包,利用反射機制,減少我們的代碼量,提高代碼的優雅性。 但是後來有人建議,可能使用Json格式的資料内容可能更好,确實,如果是采用以|分割符号的内容,有一個缺點,就是資料内容比較難懂(有時候我們還是需要分析資料包的),Json會更易讀一些。 另外,使用Json可以脫離字段順序的關系,可以向後相容一些曆史的協定,例如首次定義的協定有字段A、B,後來伺服器更新,更新增加支援C、D,舊的用戶端可以和新的用戶端并存,增加了相容性。

是以我在此基礎上優化一下代碼,使其支援Json格式的資料發送,其實由于之前的代碼封裝的還算比較好,是以修改為Json格式的協定内容,隻需要修改BaseEntity中幾行代碼即可實作,下面貼出修改代碼的前後對比(注釋掉的代碼是原來的代碼):

    public class BaseEntity

    {

        protected string HeaderKey;

        public BaseEntity()

        {

        }

        /// <summary>

        /// 轉換Socket接收到的資訊為對象資訊

        /// </summary>

        /// <param name="data">Socket接收到的資訊</param>

        public BaseEntity(string data)

            #region 普通按順序構造的代碼

            //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);

            //        }

            //} 

            #endregion

            //Json格式轉換後的内容,肯定是小于或者等于實體類的内容

            //因為對象要相容曆史的Json内容,通過反射以最小的成員來指派

            BaseEntity obj = JsonTools.JsonToObject(data, this.GetType()) as BaseEntity;

            if (obj != null)

            {

                FieldInfo[] fieldArray = ReflectionUtil.GetFields(obj);

                foreach (FieldInfo info in fieldArray)

                {

                    object value = ReflectionUtil.GetField(obj, info.Name);

                    ReflectionUtil.SetField(this, info.Name, value);

                }                

            }

        /// 轉換對象為Socket發送格式的字元串

        /// <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('|'); 

            #region 按Json格式構造的代碼

            data = JsonTools.ObjectToJson(this);

            if (string.IsNullOrEmpty(HeaderKey))

                throw new ArgumentNullException("DataTypeKey", "實體類未指定協定類型");

            data = NetStringUtil.PackSend(HeaderKey, data);

            return data;

    }

JsonTools是一個Json的輔助類,負責Json内容的解析的,由于我的項目是采用C#2.0的,是以Json操作采用了Newtonsoft.Json.dll類庫,如果是C#3.5的,采用系統内置類庫就可以了。

    /// <summary>

    /// Json處理類

    /// </summary>

    public class JsonTools

        /// 從一個對象資訊生成Json串

        /// <param name="obj"></param>

        public static string ObjectToJson(object obj)

            return JavaScriptConvert.SerializeObject(obj);

        /// 從一個Json串生成對象資訊

        /// <param name="jsonString"></param>

        /// <param name="objType"></param>

        public static object JsonToObject(string jsonString, Type objType)

            return JavaScriptConvert.DeserializeObject(jsonString, objType);

這樣就可以實作Json格式内容的發送和接受了。

使用測試用戶端對資料進行測試,并調用ToString()生成接受到的資料内容,檢視具體的内容,得到的效果如下所示。