Remoting中数据序列化

数据移动,序列化

      该文讲述通过网络传输序列化数据的两个类,BinaryFormatterSoapFormatter类。这些类可以将类的实例转化成字节流通过网络传输到远程系统,也可以转换回原来的数据。

一、     使用序列化类

序列化一个类,并通过网络传输需要三步:

1、将要序列化的类创建成一个library对象。

2、编写一个发送程序来创建要序列化类的实例,并发送。

3、编写一个接收程序从流中读取数据,并重新创建原来的序列化类。

  编写要序列化的类

每个要通过网络传输数据的类必须在原代码文件里使用[Serializable]标签。这表明,类中所有的数据在传输时都将要被序列化。下面展示了如何创建一个可以序列化的类。

using System;
   
   
[Serializable]
   
   
public class SerialEmployee
   
   
{
   
   
  public int EmployeeID
   
   
  public string LastName;
   
   
  public string FirstName;
   
   
  public int YearsService;
   
   
  public double Salary;
   
   
  public SerialEmployee()
   
   
  {
   
   
   EmployeeID = 0;
   
   
   LastName = null;
   
   
   FirstName = null;
   
   
   YearsService = 0;
   
   
   Salary = 0.0;
   
   
  }
   
   
}
   
   

为了使用该类来传输数据,必须现创建一个library文件:

csc /t:library SerialEmployee.cs

  编写一个传输程序

创建数据类以后,可以创建一个程序来传输数据。可以使用BinaryFormatter和SoapFormatter类来序列化数据。

BinaryFormatter将数据序列化为二进制流。通常在实际数据中,增加一些信息,例如类名和版本号信息。

也可以使用SoapFormatter类使用XML格式来传输数据。使用XML的好处就是可以在任何系统和程序间传递数据。

   第一必须创建一个流的实例来传递数据。可以是任何类型的流,包括FileStreamMemoryStreamNetworkStream。然后,可以创建一个序列化类,使用Serialize()方法来通过流对象传递数据:

Stream str = new FileStream(  "testfile.bin", FileMode.Create, FileAccess.ReadWrite);
   
   
IFormatter formatter = new BinaryFormatter();
   
   

formatter.Serialize(str, data);

Iformatter类创建了一个用来序列化的类的实例(BinaryFormatter或者SoapFormatter),使用Serialize()类来将数据序列化

using System;
   
   
using System.IO;
   
   
using System.Runtime.Serialization;
   
   
using System.Runtime.Serialization.Formatters.Soap;
   
   
class SoapTest
   
   
{
   
   
  public static void Main()
   
   
  {
   
   
   SerialEmployee emp1 = new SerialEmployee();
   
   
   SerialEmployee emp2 = new SerialEmployee();
   
   
   emp1.EmployeeID = 1;
   
   
   emp1.LastName = "Blum";
   
   
   emp1.FirstName = "Katie Jane";
   
   
   emp1.YearsService = 12;
   
   
   emp1.Salary = 35000.50;
   
   
   emp2.EmployeeID = 2;
   
   
   emp2.LastName = "Blum";
   
   
   emp2.FirstName = "Jessica";
   
   
   emp2.YearsService = 9;
   
   
   emp2.Salary = 23700.30;
   
   
   Stream str = new FileStream("soaptest.xml", FileMode.Create,
   
   
    FileAccess.ReadWrite);
   
   
   IFormatter formatter = new SoapFormatter();
   
   
   formatter.Serialize(str, emp1);
   
   
   formatter.Serialize(str, emp2);
   
   
   str.Close();
   
   
  }
   
   
}
   
   

      SoapFormatter类包含在System.Runtime.Serialization.Formatters.Soap命名空间,BinaryFormatter类包含在System.Runtime.Serialization.Formatters.Binary命名空间,Iformatter接口包含在System.Runtime.Serialization命名空间。

编译代码:CSC /r:SerialEmployee.dll SoapTest.cs

运行SoapTest.exe程序后,可以查看产生的soaptest.xml文件

<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Â
  
  
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC= Â
  
  
"http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV= Â
  
  
"http://schemas.xmlsoap.org/soap/envelope/" xmlns:clr= Â
  
  
"http://schemas.microsoft.com/soap/encoding/clr/1.0" SOAP-ENV:encodingStyle= Â
  
  
"http://schemas.xmlsoap.org/soap/encoding/">
   
   
<SOAP-ENV:Body>
   
   
<a1:SerialEmployee id="ref-1" xmlns:a1= Â
  
  
"http://schemas.microsoft.com/clr/assem/SerialEmployee%2C%20Version%3D0.Â
  
  
0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
   
   
<EmployeeID>1</EmployeeID>
   
   
<LastName id="ref-3">Blum</LastName>
   
   
<FirstName id="ref-4">Katie Jane</FirstName>
   
   
<YearsService>12</YearsService>
   
   
<Salary>35000.5</Salary>
   
   
</a1:SerialEmployee>
   
   
</SOAP-ENV:Body>
   
   
</SOAP-ENV:Envelope>
   
   
<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Â
  
  
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC= Â
  
  
"http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV= Â
  
  
"http://schemas.xmlsoap.org/soap/envelope/" xmlns:clr= Â
  
  
"http://schemas.microsoft.com/soap/encoding/clr/1.0" SOAP-ENV:encodingStyle= Â
  
  
"http://schemas.xmlsoap.org/soap/encoding/">
   
   
<SOAP-ENV:Body>
   
   
<a1:SerialEmployee id="ref-1" xmlns:a1= Â
  
  
"http://schemas.microsoft.com/clr/assem/SerialEmployee%2C%20Version%3D0.Â
  
  
0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
   
   
<EmployeeID>2</EmployeeID>
   
   
<LastName id="ref-3">Blum</LastName>
   
   
<FirstName id="ref-4">Jessica</FirstName>
   
   
<YearsService>9</YearsService>
   
   
<Salary>23700.3</Salary>
   
   
</a1:SerialEmployee>
   
   
</SOAP-ENV:Body>
   
   
</SOAP-ENV:Envelope>
   
   

       查看soaptest.xml文件,我们可以发现在序列化类中SOAP是如何定义每个数据元素。一个值得注意的重要XML数据特点如下:

<a1:SerialEmployee id="ref-1" xmlns:a1= Â
  
  
"http://schemas.microsoft.com/clr/assem/SerialEmployee%2C%20Version%3D0.Â0.0.0.%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
   
   

      这里,XML中定义的数据使用了序列化数据类的实际类名。如果接收程序使用了另一个不同的类名,会和从流中读取的XML数据不匹配。类不匹配,读取将会失败。

      下面的代码展示了如何序列化数据,将数据传送到远程系统。

using System;
   
   
using System.Net;
   
   
using System.Net.Sockets;
   
   
using System.Runtime.Serialization;
   
   
using System.Runtime.Serialization.Formatters.Binary;
   
   
class BinaryDataSender
   
   
{
   
   
  public static void Main()
   
   
  {
   
   
   SerialEmployee emp1 = new SerialEmployee();
   
   
   SerialEmployee emp2 = new SerialEmployee();
   
   
   emp1.EmployeeID = 1;
   
   
   emp1.LastName = "Blum";
   
   
   emp1.FirstName = "Katie Jane";
   
   
   emp1.YearsService = 12;
   
   
   emp1.Salary = 35000.50;
   
   
   emp2.EmployeeID = 2;
   
   
   emp2.LastName = "Blum";
   
   
   emp2.FirstName = "Jessica";
   
   
   emp2.YearsService = 9;
   
   
   emp2.Salary = 23700.30;
   
   
   TcpClient client = new TcpClient("127.0.0.1", 9050);
   
   
   IFormatter formatter = new BinaryFormatter();
   
   
   NetworkStream strm = client.GetStream();
   
   
   formatter.Serialize(strm, emp1);
   
   
   formatter.Serialize(strm, emp2);
   
   
   strm.Close();
   
   
   client.Close();
   
   
  }
   
   
}

因为BinaryFormatterSoapFormatter类需要一个Stream对象来传递序列化的数据,所以要使用一个TCP Socket对象或者一个TcpClient对象来传递数据,不能直接使用UDP

③编写一个接收程序

using System;
   
   
using System.Net;
   
   
using System.Net.Sockets;
   
   
using System.Runtime.Serialization;
   
   
using System.Runtime.Serialization.Formatters.Binary;
   
   
class BinaryDataRcvr
   
   
{
   
   
  public static void Main()
   
   
  {
   
   
   TcpListener server = new TcpListener(9050);
   
   
   server.Start();
   
   
   TcpClient client = server.AcceptTcpClient();
   
   
   NetworkStream strm = client.GetStream();
   
   
   IFormatter formatter = new BinaryFormatter();
   
   
   SerialEmployee emp1 = (SerialEmployee)formatter.Deserialize(strm);
   
   
   Console.WriteLine("emp1.EmployeeID = {0}", emp1.EmployeeID);
   
   
   Console.WriteLine("emp1.LastName = {0}", emp1.LastName);
   
   
   Console.WriteLine("emp1.FirstName = {0}", emp1.FirstName);
   
   
   Console.WriteLine("emp1.YearsService = {0}", emp1.YearsService);
   
   
   Console.WriteLine("emp1.Salary = {0}/n", emp1.Salary);
   
   
   SerialEmployee emp2 = (SerialEmployee)formatter.Deserialize(strm);
   
   
   Console.WriteLine("emp2.EmployeeID = {0}", emp2.EmployeeID);
   
   
   Console.WriteLine("emp2.LastName = {0}", emp2.LastName);
   
   
   Console.WriteLine("emp2.FirstName = {0}", emp2.FirstName);
   
   
   Console.WriteLine("emp2.YearsService = {0}", emp2.YearsService);
   
   
   Console.WriteLine("emp2.Salary = {0}", emp2.Salary);
   
   
   strm.Close();
   
   
   server.Stop();
   
   
  }
   
   

}

二、     程序改进

在前面的程序中有一个假设:发送者的所有数据都被接收者接收。如果数据丢失,调用Deserialize()方法会发生错误。一个简单的解决方法是将序列化数据放到MemoryStream对象中。MemoryStream对象将所有的序列化数据保存在内存中,可以很容易得到序列化数据的大小。当传递数据时,将数据大小和数据一起传递。

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Soap;
class BetterDataSender
{
  public void SendData (NetworkStream strm, SerialEmployee emp)
  {
   IFormatter formatter = new SoapFormatter();
   MemoryStream memstrm = new MemoryStream();
   formatter.Serialize(memstrm, emp);
   byte[] data = memstrm.GetBuffer();
   int memsize = (int)memstrm.Length;
   byte[] size = BitConverter.GetBytes(memsize);
   strm.Write(size, 0, 4);
   strm.Write(data, 0, memsize);
   strm.Flush();
   memstrm.Close();
  }
  public BetterDataSender()
  {
   SerialEmployee emp1 = new SerialEmployee();
   SerialEmployee emp2 = new SerialEmployee();
   emp1.EmployeeID = 1;
   emp1.LastName = "Blum";
   emp1.FirstName = "Katie Jane";
   emp1.YearsService = 12;
   emp1.Salary = 35000.50;
   emp2.EmployeeID = 2;
   emp2.LastName = "Blum";
   emp2.FirstName = "Jessica";
   emp2.YearsService = 9;
   emp2.Salary = 23700.30;
   TcpClient client = new TcpClient("127.0.0.1", 9050);
   NetworkStream strm = client.GetStream();
   SendData(strm, emp1);
   SendData(strm, emp2);
   strm.Close();
   client.Close();
  }
  public static void Main()
  {
   BetterDataSender bds = new BetterDataSender();
  }
}

接收数据程序如下:

using System;
   
   
using System.IO;
   
   
using System.Net;
   
   
using System.Net.Sockets;
   
   
using System.Runtime.Serialization;
   
   
using System.Runtime.Serialization.Formatters.Soap;
   
   
class BetterDataRcvr
   
   
{
   
   
  private SerialEmployee RecvData (NetworkStream strm)
   
   
  {
   
   
   MemoryStream memstrm = new MemoryStream();
   
   
   byte[] data = new byte[4];
   
   
   int recv = strm.Read(data, 0, 4);
   
   
   int size = BitConverter.ToInt32(data, 0);
   
   
   int offset = 0;
   
   
   while(size > 0)
   
   
   {
   
   
     data = new byte[1024];
   
   
     recv = strm.Read(data, 0, size);
   
   
     memstrm.Write(data, offset, recv);
   
   
     offset += recv;
   
   
     size -= recv;
   
   
   }
   
   
   IFormatter formatter = new SoapFormatter();
   
   
   memstrm.Position = 0;
   
   
   SerialEmployee emp = (SerialEmployee)formatter.Deserialize(memstrm);
   
   
   memstrm.Close();
   
   
   return emp;
   
   
  } 
   
   
  public BetterDataRcvr()
   
   
  {
   
   
   TcpListener server = new TcpListener(9050);
   
   
   server.Start();
   
   
   TcpClient client = server.AcceptTcpClient();
   
   
   NetworkStream strm = client.GetStream();
   
   
   SerialEmployee emp1 = RecvData(strm);
   
   
   Console.WriteLine("emp1.EmployeeID = {0}", emp1.EmployeeID);
   
   
   Console.WriteLine("emp1.LastName = {0}", emp1.LastName);
   
   
   Console.WriteLine("emp1.FirstName = {0}", emp1.FirstName);
   
   
   Console.WriteLine("emp1.YearsService = {0}", emp1.YearsService);
   
   
   Console.WriteLine("emp1.Salary = {0}/n", emp1.Salary);
   
   
   SerialEmployee emp2 = RecvData(strm);
   
   
   Console.WriteLine("emp2.EmployeeID = {0}", emp2.EmployeeID);
   
   
   Console.WriteLine("emp2.LastName = {0}", emp2.LastName);
   
   
   Console.WriteLine("emp2.FirstName = {0}", emp2.FirstName);
   
   
   Console.WriteLine("emp2.YearsService = {0}", emp2.YearsService);
   
   
   Console.WriteLine("emp2.Salary = {0}", emp2.Salary);
   
   
   strm.Close();
   
   
   server.Stop();
   
   
  }
   
   
  public static void Main()
   
   
  {
   
   
   BetterDataRcvr bdr = new BetterDataRcvr();
   
   
  }
   
   
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值