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 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;
}
}
{
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;
}
}
/// 測試請求
///
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);
}
{
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);
}
我編寫的測試例子中,實體類的繼承圖如下所示。
全站熱搜