Socket开发探秘--基于Json格式的数据协议收发
收到的Socket数据经过粗略的解析后,就是PreData类型的数据,这个是通用的数据格式,我们需要进一步处理才能转化为所能认识的数据对象(实体类对象),同样,我们发送数据的时候,内容部分肯定是按照一定协议规则串联起来的数据,那么我们就需要把实体转化为发送的数据格式。综上所述,我们通过实体类,必须实现数据的发送和读取的转换。
由于数据的封包拆包是一个繁琐的过程,代码重复性比较多,而且也容易出错。前面介绍过设计一个基类,我们把所有对数据的拆包和封包,利用反射机制,减少我们的代码量,提高代码的优雅性。但是后来有人建议,可能使用Json格式的数据内容可能更好,确实,如果是采用以|分割符号的内容,有一个缺点,就是数据内容比较难懂(有时候我们还是需要分析数据包的),Json会更易读一些。另外,使用Json可以脱离字段顺序的关系,可以向后兼容一些历史的协议,例如首次定义的协议有字段A、B,后来服务器升级,升级增加支持C、D,旧的客户端可以和新的客户端并存,增加了兼容性。
因此我在此基础上优化一下代码,使其支持Json格式的数据发送,其实由于之前的代码封装的还算比较好,因此修改为Json格式的协议内容,只需要修改BaseEntity中几行代码即可实现,下面贴出修改代码的前后对比(注释掉的代码是原来的代码):
publicclassBaseEntity
{
protectedstringHeaderKey;
publicBaseEntity()
{
}
///<summary>
///转换Socket接收到的信息为对象信息
///</summary>
///<paramname="data">Socket接收到的信息</param>
publicBaseEntity(stringdata)
{
#region普通按顺序构造的代码
//string[]dataArray=null;
//dataArray=NetStringUtil.UnPack(data);
//if(dataArray!=null&&dataArray.Length>0)
//{
//inti=0;
//FieldInfo[]fieldArray=ReflectionUtil.GetFields(this);
//if(fieldArray==null||dataArray.Length!=fieldArray.Length)
//{
//thrownewArgumentException("收到的信息和字段信息不一致");
//}
//if(fieldArray!=null)
//{
//foreach(FieldInfoinfoinfieldArray)
//{
//stringstrValue=dataArray[i++];
//ReflectionUtil.SetField(this,info.Name,strValue);
//}
//}
//}
#endregion
//Json格式转换后的内容,肯定是小于或者等于实体类的内容
//因为对象要兼容历史的Json内容,通过反射以最小的成员来赋值
BaseEntityobj=JsonTools.JsonToObject(data,this.GetType())asBaseEntity;
if(obj!=null)
{
FieldInfo[]fieldArray=ReflectionUtil.GetFields(obj);
foreach(FieldInfoinfoinfieldArray)
{
objectvalue=ReflectionUtil.GetField(obj,info.Name);
ReflectionUtil.SetField(this,info.Name,value);
}
}
}
///<summary>
///转换对象为Socket发送格式的字符串
///</summary>
///<returns></returns>
publicoverridestringToString()
{
stringdata="";
#region普通按顺序构造的代码
//FieldInfo[]fieldArray=ReflectionUtil.GetFields(this);
//StringBuildersb=newStringBuilder();
//if(fieldArray!=null)
//{
//foreach(FieldInfoinfoinfieldArray)
//{
//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))
{
thrownewArgumentNullException("DataTypeKey","实体类未指定协议类型");
}
data=NetStringUtil.PackSend(HeaderKey,data);
returndata;
}
}
{
protectedstringHeaderKey;
publicBaseEntity()
{
}
///<summary>
///转换Socket接收到的信息为对象信息
///</summary>
///<paramname="data">Socket接收到的信息</param>
publicBaseEntity(stringdata)
{
#region普通按顺序构造的代码
//string[]dataArray=null;
//dataArray=NetStringUtil.UnPack(data);
//if(dataArray!=null&&dataArray.Length>0)
//{
//inti=0;
//FieldInfo[]fieldArray=ReflectionUtil.GetFields(this);
//if(fieldArray==null||dataArray.Length!=fieldArray.Length)
//{
//thrownewArgumentException("收到的信息和字段信息不一致");
//}
//if(fieldArray!=null)
//{
//foreach(FieldInfoinfoinfieldArray)
//{
//stringstrValue=dataArray[i++];
//ReflectionUtil.SetField(this,info.Name,strValue);
//}
//}
//}
#endregion
//Json格式转换后的内容,肯定是小于或者等于实体类的内容
//因为对象要兼容历史的Json内容,通过反射以最小的成员来赋值
BaseEntityobj=JsonTools.JsonToObject(data,this.GetType())asBaseEntity;
if(obj!=null)
{
FieldInfo[]fieldArray=ReflectionUtil.GetFields(obj);
foreach(FieldInfoinfoinfieldArray)
{
objectvalue=ReflectionUtil.GetField(obj,info.Name);
ReflectionUtil.SetField(this,info.Name,value);
}
}
}
///<summary>
///转换对象为Socket发送格式的字符串
///</summary>
///<returns></returns>
publicoverridestringToString()
{
stringdata="";
#region普通按顺序构造的代码
//FieldInfo[]fieldArray=ReflectionUtil.GetFields(this);
//StringBuildersb=newStringBuilder();
//if(fieldArray!=null)
//{
//foreach(FieldInfoinfoinfieldArray)
//{
//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))
{
thrownewArgumentNullException("DataTypeKey","实体类未指定协议类型");
}
data=NetStringUtil.PackSend(HeaderKey,data);
returndata;
}
}
JsonTools是一个Json的辅助类,负责Json内容的解析的,由于我的项目是采用C#2.0的,因此Json操作采用了Newtonsoft.Json.dll类库,如果是C#3.5的,采用系统内置类库就可以了。
///<summary>
///Json处理类
///</summary>
publicclassJsonTools
{
///<summary>
///从一个对象信息生成Json串
///</summary>
///<paramname="obj"></param>
///<returns></returns>
publicstaticstringObjectToJson(objectobj)
{
returnJavaScriptConvert.SerializeObject(obj);
}
///<summary>
///从一个Json串生成对象信息
///</summary>
///<paramname="jsonString"></param>
///<paramname="objType"></param>
///<returns></returns>
publicstaticobjectJsonToObject(stringjsonString,TypeobjType)
{
returnJavaScriptConvert.DeserializeObject(jsonString,objType);
}
}
///Json处理类
///</summary>
publicclassJsonTools
{
///<summary>
///从一个对象信息生成Json串
///</summary>
///<paramname="obj"></param>
///<returns></returns>
publicstaticstringObjectToJson(objectobj)
{
returnJavaScriptConvert.SerializeObject(obj);
}
///<summary>
///从一个Json串生成对象信息
///</summary>
///<paramname="jsonString"></param>
///<paramname="objType"></param>
///<returns></returns>
publicstaticobjectJsonToObject(stringjsonString,TypeobjType)
{
returnJavaScriptConvert.DeserializeObject(jsonString,objType);
}
}
这样就可以实现Json格式内容的发送和接受了。
使用测试客户端对数据进行测试,并调用ToString()生成接受到的数据内容,查看具体的内容,得到的效果如下所示。