Remoting中数据序列化

** 数据移动,序列化 ** ** **

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

一、 使用序列化类

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

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的好处就是可以在任何系统和程序间传递数据。

第一必须创建一个 流的实例来传递数据。可以是任何类型的流,包括 FileStream , MemoryStream , NetworkStream 。然后,可以创建一个序列化类,使用 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 文件

 1<soap-env:envelope "="" "http:="" 1.0"="" clr="" encoding="" envelope="" schemas.microsoft.com="" schemas.xmlsoap.org="" soap="" soap-env:encodingstyle="Â" xmlns:clr="Â" xmlns:soap-enc="Â" xmlns:soap-env="Â" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" â="">
 2<soap-env:body>
 3<a1:serialemployee "http:="" 0.0.0%2c%20culture%3dneutral%2c%20publickeytoken%3dnull"="" assem="" clr="" id="ref-1" schemas.microsoft.com="" serialemployee%2c%20version%3d0.â="" xmlns:a1="Â">
 4<employeeid>1</employeeid>
 5<lastname id="ref-3">Blum</lastname>
 6<firstname id="ref-4">Katie Jane</firstname>
 7<yearsservice>12</yearsservice>
 8<salary>35000.5</salary>
 9</a1:serialemployee>
10</soap-env:body>
11</soap-env:envelope>
 1<soap-env:envelope "="" "http:="" 1.0"="" clr="" encoding="" envelope="" schemas.microsoft.com="" schemas.xmlsoap.org="" soap="" soap-env:encodingstyle="Â" xmlns:clr="Â" xmlns:soap-enc="Â" xmlns:soap-env="Â" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" â="">
 2<soap-env:body>
 3<a1:serialemployee "http:="" 0.0.0%2c%20culture%3dneutral%2c%20publickeytoken%3dnull"="" assem="" clr="" id="ref-1" schemas.microsoft.com="" serialemployee%2c%20version%3d0.â="" xmlns:a1="Â">
 4<employeeid>2</employeeid>
 5<lastname id="ref-3">Blum</lastname>
 6<firstname id="ref-4">Jessica</firstname>
 7<yearsservice>9</yearsservice>
 8<salary>23700.3</salary>
 9</a1:serialemployee>
10</soap-env:body>
11</soap-env:envelope>

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

  1<a1:serialemployee "http:="" assem="" clr="" id="ref-1" schemas.microsoft.com="" serialemployee%2c%20version%3d0.â0.0.0.%2c%20culture%3dneutral%2c%20publickeytoken%3dnull"="" xmlns:a1="Â">
  2
  3这里,  XML  中定义的数据使用了序列化数据类的实际类名。如果接收程序使用了另一个不同的类名,会和从流中读取的  XML  数据不匹配。类不匹配,读取将会失败。 
  4
  5下面的代码展示了如何序列化数据,将数据传送到远程系统。 
  6    
  7    
  8    using System;
  9    
 10    
 11    using System.Net;
 12    
 13    
 14    using System.Net.Sockets;
 15    
 16    
 17    using System.Runtime.Serialization;
 18    
 19    
 20    using System.Runtime.Serialization.Formatters.Binary;
 21    
 22    
 23    class BinaryDataSender
 24    
 25    
 26    {
 27    
 28    
 29      public static void Main()
 30    
 31    
 32      {
 33    
 34    
 35       SerialEmployee emp1 = new SerialEmployee();
 36    
 37    
 38       SerialEmployee emp2 = new SerialEmployee();
 39    
 40    
 41       emp1.EmployeeID = 1;
 42    
 43    
 44       emp1.LastName = "Blum";
 45    
 46    
 47       emp1.FirstName = "Katie Jane";
 48    
 49    
 50       emp1.YearsService = 12;
 51    
 52    
 53       emp1.Salary = 35000.50;
 54    
 55    
 56       emp2.EmployeeID = 2;
 57    
 58    
 59       emp2.LastName = "Blum";
 60    
 61    
 62       emp2.FirstName = "Jessica";
 63    
 64    
 65       emp2.YearsService = 9;
 66    
 67    
 68       emp2.Salary = 23700.30;
 69    
 70    
 71       TcpClient client = new TcpClient("127.0.0.1", 9050);
 72    
 73    
 74       IFormatter formatter = new BinaryFormatter();
 75    
 76    
 77       NetworkStream strm = client.GetStream();
 78    
 79    
 80       formatter.Serialize(strm, emp1);
 81    
 82    
 83       formatter.Serialize(strm, emp2);
 84    
 85    
 86       strm.Close();
 87    
 88    
 89       client.Close();
 90    
 91    
 92      }
 93    
 94    
 95    }
 96
 97因为  BinaryFormatter  和  SoapFormatter  类需要一个  Stream  对象来传递序列化的数据,所以要使用一个  TCP Socket  对象或者一个  TcpClient  对象来传递数据,不能直接使用  UDP  。 
 98
 99** ③编写一个接收程序  ** ** **
100    
101    
102    using System;
103    
104    
105    using System.Net;
106    
107    
108    using System.Net.Sockets;
109    
110    
111    using System.Runtime.Serialization;
112    
113    
114    using System.Runtime.Serialization.Formatters.Binary;
115    
116    
117    class BinaryDataRcvr
118    
119    
120    {
121    
122    
123      public static void Main()
124    
125    
126      {
127    
128    
129       TcpListener server = new TcpListener(9050);
130    
131    
132       server.Start();
133    
134    
135       TcpClient client = server.AcceptTcpClient();
136    
137    
138       NetworkStream strm = client.GetStream();
139    
140    
141       IFormatter formatter = new BinaryFormatter();
142    
143    
144       SerialEmployee emp1 = (SerialEmployee)formatter.Deserialize(strm);
145    
146    
147       Console.WriteLine("emp1.EmployeeID = {0}", emp1.EmployeeID);
148    
149    
150       Console.WriteLine("emp1.LastName = {0}", emp1.LastName);
151    
152    
153       Console.WriteLine("emp1.FirstName = {0}", emp1.FirstName);
154    
155    
156       Console.WriteLine("emp1.YearsService = {0}", emp1.YearsService);
157    
158    
159       Console.WriteLine("emp1.Salary = {0}\n", emp1.Salary);
160    
161    
162       SerialEmployee emp2 = (SerialEmployee)formatter.Deserialize(strm);
163    
164    
165       Console.WriteLine("emp2.EmployeeID = {0}", emp2.EmployeeID);
166    
167    
168       Console.WriteLine("emp2.LastName = {0}", emp2.LastName);
169    
170    
171       Console.WriteLine("emp2.FirstName = {0}", emp2.FirstName);
172    
173    
174       Console.WriteLine("emp2.YearsService = {0}", emp2.YearsService);
175    
176    
177       Console.WriteLine("emp2.Salary = {0}", emp2.Salary);
178    
179    
180       strm.Close();
181    
182    
183       server.Stop();
184    
185    
186      }
187
188} 
189
190二、  程序改进 
191
192在前面的程序中有一个假设:发送者的所有数据都被接收者接收。如果数据丢失,调用  Deserialize()  方法会发生错误。一个简单的解决方法是将序列化数据放到  MemoryStream  对象中。  MemoryStream  对象将所有的序列化数据保存在内存中,可以很容易得到序列化数据的大小。当传递数据时,将数据大小和数据一起传递。 
193    
194    
195    using System;
196    
197    
198    using System.IO;
199    
200    
201    using System.Net;
202    
203    
204    using System.Net.Sockets;
205    
206    
207    using System.Runtime.Serialization;
208    
209    
210    using System.Runtime.Serialization.Formatters.Soap;
211    
212    
213    class BetterDataSender
214    
215    
216    {
217    
218    
219      public void SendData (NetworkStream strm, SerialEmployee emp)
220    
221    
222      {
223    
224    
225       IFormatter formatter = new SoapFormatter();
226    
227    
228       MemoryStream memstrm = new MemoryStream();
229    
230    
231       formatter.Serialize(memstrm, emp);
232    
233    
234       byte[] data = memstrm.GetBuffer();
235    
236    
237       int memsize = (int)memstrm.Length;
238    
239    
240       byte[] size = BitConverter.GetBytes(memsize);
241    
242    
243       strm.Write(size, 0, 4);
244    
245    
246       strm.Write(data, 0, memsize);
247    
248    
249       strm.Flush();
250    
251    
252       memstrm.Close();
253    
254    
255      }
256    
257    
258      public BetterDataSender()
259    
260    
261      {
262    
263    
264       SerialEmployee emp1 = new SerialEmployee();
265    
266    
267       SerialEmployee emp2 = new SerialEmployee();
268    
269    
270       emp1.EmployeeID = 1;
271    
272    
273       emp1.LastName = "Blum";
274    
275    
276       emp1.FirstName = "Katie Jane";
277    
278    
279       emp1.YearsService = 12;
280    
281    
282       emp1.Salary = 35000.50;
283    
284    
285       emp2.EmployeeID = 2;
286    
287    
288       emp2.LastName = "Blum";
289    
290    
291       emp2.FirstName = "Jessica";
292    
293    
294       emp2.YearsService = 9;
295    
296    
297       emp2.Salary = 23700.30;
298    
299    
300       TcpClient client = new TcpClient("127.0.0.1", 9050);
301    
302    
303       NetworkStream strm = client.GetStream();
304    
305    
306       SendData(strm, emp1);
307    
308    
309       SendData(strm, emp2);
310    
311    
312       strm.Close();
313    
314    
315       client.Close();
316    
317    
318      }
319    
320    
321      public static void Main()
322    
323    
324      {
325    
326    
327       BetterDataSender bds = new BetterDataSender();
328    
329    
330      }
331    
332    
333    }
334
335接收数据程序如下: 
336    
337    
338    using System;
339    
340    
341    using System.IO;
342    
343    
344    using System.Net;
345    
346    
347    using System.Net.Sockets;
348    
349    
350    using System.Runtime.Serialization;
351    
352    
353    using System.Runtime.Serialization.Formatters.Soap;
354    
355    
356    class BetterDataRcvr
357    
358    
359    {
360    
361    
362      private SerialEmployee RecvData (NetworkStream strm)
363    
364    
365      {
366    
367    
368       MemoryStream memstrm = new MemoryStream();
369    
370    
371       byte[] data = new byte[4];
372    
373    
374       int recv = strm.Read(data, 0, 4);
375    
376    
377       int size = BitConverter.ToInt32(data, 0);
378    
379    
380       int offset = 0;
381    
382    
383       while(size &gt; 0)
384    
385    
386       {
387    
388    
389         data = new byte[1024];
390    
391    
392         recv = strm.Read(data, 0, size);
393    
394    
395         memstrm.Write(data, offset, recv);
396    
397    
398         offset += recv;
399    
400    
401         size -= recv;
402    
403    
404       }
405    
406    
407       IFormatter formatter = new SoapFormatter();
408    
409    
410       memstrm.Position = 0;
411    
412    
413       SerialEmployee emp = (SerialEmployee)formatter.Deserialize(memstrm);
414    
415    
416       memstrm.Close();
417    
418    
419       return emp;
420    
421    
422      } 
423    
424    
425      public BetterDataRcvr()
426    
427    
428      {
429    
430    
431       TcpListener server = new TcpListener(9050);
432    
433    
434       server.Start();
435    
436    
437       TcpClient client = server.AcceptTcpClient();
438    
439    
440       NetworkStream strm = client.GetStream();
441    
442    
443       SerialEmployee emp1 = RecvData(strm);
444    
445    
446       Console.WriteLine("emp1.EmployeeID = {0}", emp1.EmployeeID);
447    
448    
449       Console.WriteLine("emp1.LastName = {0}", emp1.LastName);
450    
451    
452       Console.WriteLine("emp1.First</a1:serialemployee>
Published At
Categories with Web编程
Tagged with
comments powered by Disqus