c#中序列化和反序列化又称之为串行化,能够使运行的中的数据结构及数据能够长时间保存起来,用以后面的使用。本文也就实际项目来说明。
从初步建立TCP连接,到发送验证数据包数据,这边就需要向服务器发送数据。发送的数据是验证信息,就简单来说,如果是一个字符串,这里面我们用TCP可以直接写入流就行了:
//客户端
string content = "发送的数据";
Byte[] bytSend = Encoding.UTF8.GetBytes(content);
ntwStream.Write(bytSend, 0, bytSend.Length);
而相对应的服务器接收数据应该是这样:
//服务器
listener.Listen(0);
Socket socket = listener.Accept();
NetworkStream ntwStream = new NetworkStream(socket);
StreamReader strmReader = new StreamReader(ntwStream);
strmReader.ReadToEnd()
这种传输数据结构相对很简单的TCP可以直接传输即可,但是对于相对复杂的结构的数据,类的实例,需要的数据有int,string,还有文件列表List<>等等,那么简单数据传输就不适用了(也可以简单传输,但是解析相对困难),这时候就需要序列化(后面有复杂结构示例),下面就以验证数据包来说明,数据包包括登录名和密码
//在要序列化的类上面加上[Serializable]声明这个类是可以序列化的
[Serializable]
public class LoginInfo
{//登入数据包.
public String strUID = null;//用户名
public String strPWD = null;//密码.
public Int32 iState = 0;//状态值.
}
客户端发送验证数据包
//客户端
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter mBinaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
try
{
//连接服务器
ClientTcp.Connect(System.Net.IPAddress.Parse(strIPAddr),iPort);
//发送验证数据包
LoginInfo mLoginInfo = new LoginInfo();
mLoginInfo.strPWD = "password";
mLoginInfo.strUID = "administrator";
mLoginInfo.iState = bIsShowListMsg ? 100 : 0;
mBinaryFormatter.Serialize(ClientTcp.GetStream(), mLoginInfo);
object oNetData = mBinaryFormatter.Deserialize(ClientTcp.GetStream());
ReturnInfoValue mReturnValue = oNetData as ReturnInfoValue;
if (mReturnValue == null) return null;
if (mReturnValue.iReturnValue == VideoStreamCommon.NetPacket.RIV_ERROE) return null;
return ClientTcp;
}
catch (System.Exception error)
{
MessageBox.Show("创建TCP发生异常,异常信息:" + error.Message);
}
服务器接收验证包
try
{
listenerTcp.Start();
do
{
TcpClient mClientTcp = listenerTcp.AcceptTcpClient();
if (mClientTcp == null) break;
oNetData = mBinaryFormatter.Deserialize(mThreadParam.ClientTcp.GetStream());
if (bIsCheckClient == false)
{//先验证数据.
int iTepValue = CheckClientInfo(oNetData);
if (iTepValue == 2)
{//创建消息窗口.
AsyncSetWorkThreadParam(ref mThreadParam, 1001, true);//添加线消息窗口列表.
}
bIsCheckClient = (iTepValue != 0);
if (bIsCheckClient) continue;//验证数据成功.
else break; //验证数据失败.
}
} while (true);
}
catch (System.Exception ep)
{//系统异常.
ep.Message.ToString();
AsyncAddListMessage("端口被占用:" + strIPAddr, 0);
}
验证数据函数
///
/// 验证连接是否合法.
///
///
///
public int CheckClientInfo(object param)
{
LoginInfo mLoginInfo = param as LoginInfo;
if (mLoginInfo == null) return 0;
if(mLoginInfo.strUID == "administrator" && mLoginInfo.strPWD == "password")
{
return 1;
}
else
{
return 0;
}
}
当程序功能化之后,每个人负责不同模块,这就会涉及到创建一个进程,然后向该进程传递必要的参数,如果参数中数据结构比较复杂也得使用序列化,调用进程序列化文件后,被调用进程再解析该文件,实现数据传递。下面上代码:
[Serializable]
public class Param
{
public List channelList;//通道列表
public string fileid;
public int isFullDiskPlay;
public string strIP;
public string strPort;
public Param();
[Serializable]
public class CHANNEL_INFO
{
public List fileList;
public int id;
public string name;
public CHANNEL_INFO();
}
[Serializable]
public class FILE_INFO
{
public DateTime dtBegin;
public DateTime dtEnd;
public string name;
public FILE_INFO();
}
public bool SeveToFile(string strFileName)
{
try
{
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream(strFileName, FileMode.Create, FileAccess.Write, FileShare.None);
formatter.Serialize(stream, this);
stream.Close();
return true;
}
catch (System.Exception ex)
{
return false;
}
}
public static Param LoadFromFile(string strFileName)
{
try
{
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream(strFileName, FileMode.Open, FileAccess.Read,
FileShare.Read);
VideoStreamParam_Hik video = formatter.Deserialize(stream) as
VideoStreamParam_Hik;
stream.Close();
return video;
}
catch (System.Exception ex)
{
return null;
}
}
}
下面是调用部分
//序列化文件名称(GUID名称)
strFileName = Guid.NewGuid().ToString() + ".bin";
//保存到bin文件
if (!videomodel.SeveToFile(strFileName))
{
MessageBox.Show("测试失败");
return;
}
//开启一个进程
System.Diagnostics.Process PlayStream = new System.Diagnostics.Process();
PlayStream.StartInfo.UseShellExecute = false;
PlayStream.StartInfo.Arguments = strFileName;
PlayStream.StartInfo.CreateNoWindow = true;
PlayStream.StartInfo.FileName = AppDomain.CurrentDomain.BaseDirectory + "test.exe";
PlayStream.Start();
最后是调用解析部分,该部分再test.exe中
///
/// 从文件中反序列化出所需要的数据
///
/// 文件名(完全路径)
/// 全程记录表
///
public bool LoadFromFile(string strFileName, ref Param param)
{
try
{
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream(strFileName, FileMode.Open, FileAccess.Read,
FileShare.Read);
param = new Param();
param = (Param)formatter.Deserialize(stream);
stream.Close();
return true;
}
catch (System.Exception ex)
{
MessageBox.Show("反序列化发生异常!,异常信息:" + ex.Message);
return false;
}
}
上面反序列化的实例和传递的部分是一样的,这样就可以使用其中的参数了。
最后还要说明一下,序列化和反序列化的的类应该使用公用的类,即定义一个两个程序公用的类,都调用该公共的dll,而不能定义两个相同的类结构,不然在反序列化中不能匹配,转化出错。