前面發表過兩篇隨筆:《Socket開發探秘--基類及公共類的定義》和《Socket開發探秘-- 數據封包和拆包》,介紹了Socket方面的開發。本文繼續探討使用Json格式來作為Socket收 發協議方面的技術問題。
前面說到,收到的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);
}
}
}
/// <summary>
/// 轉換對象為Socket發送格式的字符串
/// </summary>
/// <returns></returns>
public override string ToString()
{
string data = "";
#region 普通按順序構造的代碼
//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('|');
#endregion
#region 按Json格式構造的代碼
data = JsonTools.ObjectToJson(this);
#endregion
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
{
/// <summary>
/// 從一個對象信息生成Json串
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static string ObjectToJson(object obj)
{
return JavaScriptConvert.SerializeObject(obj);
}
/// <summary>
/// 從一個Json串生成對象信息
/// </summary>
/// <param name="jsonString"></param>
/// <param name="objType"></param>
/// <returns></returns>
public static object JsonToObject(string jsonString, Type objType)
{
return JavaScriptConvert.DeserializeObject (jsonString, objType);
}
}
這樣就可以實現Json格式內容的發送和接受了。
使用測試客戶端對數據進行測試,並調用ToString()生成接受到的數據內容,查看具體的 內容,得到的效果如下所示。