Socket開發探秘--數據封包和拆包

作者: 伍華聰  來源: 博客園  發布時間: 2009-12-14 09:31  閱讀: 2805 次  推薦: 0   原文鏈接   [收藏]  

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

 

以上的是實體類的基類,它封裝了數據的拆包和封包過程,只需要在子類代碼中指定協議頭就可以了。子類的代碼如下所示。

代碼
    /// 
    /// 測試請求
    
/// 
    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);
        }

 

我編寫的測試例子中,實體類的繼承圖如下所示。

0
0
 
標簽:Socket開發
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()