System.Net :在网络中传输数据(II) (转载)

除了传输数值型数据,我们更多的是传输复合型数据,如字符,数值,bool等等。是否存在简单办法发送复合型数据呢?
   可以建立一个类,
None.gif class  Employee
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
public int EmployeeID;
InBlock.gif
public int LastNameSize;
InBlock.gif
public string LastName;
InBlock.gif
public int FirstNameSize;
InBlock.gif
public string FirstName;
InBlock.gif
public int YearsService;
InBlock.gif
public double Salary;
InBlock.gif
public int size;
ExpandedBlockEnd.gif}

None.gif


   可以把Employee看作是一个数据类型,它包括很多子类型。可以将Empolyee中的字段赋值后,转换为字节数组,因为数值型数据长度固定,如Int32 需要占用4个字节,int16占用2个字节,而字符型数据,长度是不固定的,所以要加入说明字段,标示字符长度。
建立如下方法:

None.gif public   byte [] GetBytes()
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
//字节数组,将Empolyee转换为字节数组的容器
InBlock.gif
byte[] data = new byte[1024];
InBlock.gif
//位置标示
InBlock.gif
int place = 0;
InBlock.gif
//转换Int32 类型的数据为Byte[],加入到data中
InBlock.gif
Buffer.BlockCopy(BitConverter.GetBytes(EmployeeID), 0, data, place, 4);
InBlock.gif
//位移+4
InBlock.gif
place += 4;
InBlock.gif
//将字符LastName的长度转换为字节数组,加入到data
InBlock.gif
Buffer.BlockCopy(BitConverter.GetBytes(LastName.Length), 0, data, place, 4);
InBlock.gif
//增加位移
InBlock.gif
place += 4;
InBlock.gif
//把字符转换为字节数组,加入到Data
InBlock.gif
Buffer.BlockCopy(Encoding.ASCII.GetBytes(LastName), 0,data, place, LastName.Length);
InBlock.gif
//增加相应位移
InBlock.gif
place += LastName.Length;
InBlock.gif
//将字符FirstName的长度转换为字节数组,加入到Data
InBlock.gif
Buffer.BlockCopy(BitConverter.GetBytes(FirstName.Length),0, data, place, 4);
InBlock.gif
//增加位移
InBlock.gif
place += 4;
InBlock.gif
//把字符转换为字节数组,加入到data
InBlock.gif
Buffer.BlockCopy(Encoding.ASCII.GetBytes(FirstName), 0,data, place, FirstName.Length);
InBlock.gif
//增加位移
InBlock.gif
place += FirstName.Length;
InBlock.gif
//转换YearService为byte[],加入到data
InBlock.gif
Buffer.BlockCopy(BitConverter.GetBytes(YearsService), 0, data, place, 4);
InBlock.gif
//增加位移
InBlock.gif
place += 4;
InBlock.gif
//转换类型为double的Salary为Byte[],加入到data
InBlock.gif
Buffer.BlockCopy(BitConverter.GetBytes(Salary), 0, data, place, 8);
InBlock.gif
//增加位移
InBlock.gif
place += 8;
InBlock.gif
//获取Employee类转换为字节数组的实际长度
InBlock.gif
size = place;
InBlock.gif
return data;
ExpandedBlockEnd.gif}

None.gif


GetBytes()由3个作用:
1.将类中的每个字段转换为字节数组;
2.将每个字节数组都放入相同的总的字节数组中;
3.获取了总字节数组的长度。
    关于Buffer.BlockCopy(),是将目的数组copy到原数组的特定位置;
BlockCopy(byte[] array1, int start, byte[] array2, int offset, int size);
    我们的目的就是把不同的数组放入到统一数组的特定位置。这个做好了,那么可以通过网络传送了。

     可以这样理解网络中传输的字节数组,网络就像是有2条铁轨的道路,允许2列火车同时运行,而火车由车头,车厢组成,车厢又分为硬座车型,软座车箱,硬卧车厢,软卧车厢,餐车,邮件车厢等,每个数据类型的字节数组,对应上面的车箱,因为特定乘客要到特定车厢,所以特定的数组也要在发送的总的数组的特定位置。

   通过网络,我们说到了这个数组,怎样还原呢?
   首先,我们要知道Employee 类的结构,然后,可以

None.gif public  Employee( byte [] data)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
int place = 0;
InBlock.gifEmployeeID 
= BitConverter.ToInt32(data, place);
InBlock.gifplace 
+= 4;
InBlock.gifLastNameSize 
= BitConverter.ToInt32(data, place);
InBlock.gifplace 
+= 4;
InBlock.gifLastName 
= Encoding.ASCII.GetString(data, place, LastNameSize);
InBlock.gifplace 
= place + LastNameSize;
InBlock.gifFirstNameSize 
= BitConverter.ToInt32(data, place);
InBlock.gifplace 
+= 4;
InBlock.gifFirstName 
= Encoding.ASCII.GetString(data, place, FirstNameSize);
InBlock.gifplace 
+= FirstNameSize;
InBlock.gifYearsService 
= BitConverter.ToInt32(data, place);
InBlock.gifplace 
+= 4;
InBlock.gifSalary 
= BitConverter.ToDouble(data, place);
ExpandedBlockEnd.gif}

None.gif


    是不是想起了什么,觉得很熟悉,对了,序列化,同使用序列化类,通过网络传输,然后再反序列化,获取类中的字段的值,非常相似,是不是可以这样说,序列化的原理就是这样?

完整发送、接收代码:
Client:

None.gif using  System;
None.gif
using  System.Net;
None.gif
using  System.Net.Sockets;
None.gif
class  EmployeeClient
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
public static void Main()
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gifEmployee emp1 
= new Employee();
InBlock.gifEmployee emp2 
= new Employee();
InBlock.gifTcpClient client;
InBlock.gifemp1.EmployeeID 
= 1;
InBlock.gifemp1.LastName 
= "Blum";
InBlock.gifemp1.FirstName 
= "Katie Jane";
InBlock.gifemp1.YearsService 
= 12;
InBlock.gifemp1.Salary 
= 35000.50;
InBlock.gifemp2.EmployeeID 
= 2;
InBlock.gifemp2.LastName 
= "Blum";
InBlock.gifemp2.FirstName 
= "Jessica";
InBlock.gifemp2.YearsService 
= 9;
InBlock.gifemp2.Salary 
= 23700.30;
InBlock.gif
try
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gifclient 
= new TcpClient("127.0.0.1"9050);
ExpandedSubBlockEnd.gif}
 catch (SocketException)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gifConsole.WriteLine(
"Unable to connect to server");
InBlock.gif
return;
ExpandedSubBlockEnd.gif}

InBlock.gifNetworkStream ns 
= client.GetStream();
InBlock.gif
byte[] data = emp1.GetBytes();
InBlock.gif
int size = emp1.size;
InBlock.gif
byte[] packsize = new byte[2];
InBlock.gifConsole.WriteLine(
"packet size = {0}", size);
InBlock.gifpacksize 
= BitConverter.GetBytes(size);
InBlock.gifns.Write(packsize, 
02);
InBlock.gifns.Write(data, 
0, size);
InBlock.gifns.Flush();
InBlock.gifdata 
= emp2.GetBytes();
InBlock.gifsize 
= emp2.size;
InBlock.gifpacksize 
= new byte[2];
InBlock.gifConsole.WriteLine(
"packet size = {0}", size);
InBlock.gifpacksize 
= BitConverter.GetBytes(size);
InBlock.gifns.Write(packsize, 
02);
InBlock.gifns.Write(data, 
0, size);
InBlock.gifns.Flush();
InBlock.gifns.Close();
InBlock.gifclient.Close();
ExpandedSubBlockEnd.gif}

ExpandedBlockEnd.gif}

None.gif


Server:

None.gif using  System;
None.gif
using  System.Net;
None.gif
using  System.Net.Sockets;
None.gif
class  EmployeeSrvr
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
public static void Main()
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
byte[] data = new byte[1024];
InBlock.gifTcpListener server 
= new TcpListener(9050);
InBlock.gifserver.Start();
InBlock.gifTcpClient client 
= server.AcceptTcpClient();
InBlock.gifNetworkStream ns 
= client.GetStream();
InBlock.gif
byte[] size = new byte[2];
InBlock.gif
int recv = ns.Read(size, 02);
InBlock.gif
int packsize = BitConverter.ToInt16(size, 0);
InBlock.gifConsole.WriteLine(
"packet size = {0}", packsize);
InBlock.gifrecv 
= ns.Read(data, 0, packsize);
InBlock.gifEmployee emp1 
= new Employee(data);
InBlock.gifConsole.WriteLine(
"emp1.EmployeeID = {0}", emp1.EmployeeID);
InBlock.gifConsole.WriteLine(
"emp1.LastName = {0}", emp1.LastName);
InBlock.gifConsole.WriteLine(
"emp1.FirstName = {0}", emp1.FirstName);
InBlock.gifConsole.WriteLine(
"emp1.YearsService = {0}", emp1.YearsService);
InBlock.gifConsole.WriteLine(
"emp1.Salary = {0}\n", emp1.Salary);
InBlock.gifsize 
= new byte[2];
InBlock.gifrecv 
= ns.Read(size, 02);
InBlock.gifpacksize 
= BitConverter.ToInt16(size, 0);
InBlock.gifdata 
= new byte[packsize];
InBlock.gifConsole.WriteLine(
"packet size = {0}", packsize);
InBlock.gifrecv 
= ns.Read(data, 0, packsize);
InBlock.gifEmployee emp2 
= new Employee(data);
InBlock.gifConsole.WriteLine(
"emp2.EmployeeID = {0}", emp2.EmployeeID);
InBlock.gifConsole.WriteLine(
"emp2.LastName = {0}", emp2.LastName);
InBlock.gifConsole.WriteLine(
"emp2.FirstName = {0}", emp2.FirstName);
InBlock.gifConsole.WriteLine(
"emp2.YearsService = {0}", emp2.YearsService);
InBlock.gifConsole.WriteLine(
"emp2.Salary = {0}", emp2.Salary);
InBlock.gifns.Close();
InBlock.gifclient.Close();
InBlock.gifserver.Stop();
ExpandedSubBlockEnd.gif}

ExpandedBlockEnd.gif}

None.gif


        Server 先读取2个字节,转换为数值,获取Client要发送的字节数组总长度,然后根据长度,读取数据。

       现在我们把复合类型转换为单一类型,通过网络发送到目的地,然后又重新组合起来,同前面说的搬运埃菲尔铁塔,用的是相同的道理。

        我们经常遇到的另一种情况是传送文件,文件可以方便的转换为字节数组,通过网络传输,要注意:
1 .因为MTU的原因,每次传输的最大字节数组的长度为1024,不要大于这个值,如果文件较大,可以每次传输1024个字节,接收方按顺序收到后,在还原成原始文件
2.可以使用每次发送固定长度字节的办法,在获取文件长度后,每次发送固定长度,直到发完。

  关于在网络中传输数据,就写到这里,如果有问题,请给我留言。

  谢谢浮云。
        MTU(Maximum Transmission Unit)  最大传输单元:
经网络发送的单一包可容纳的最大数据量。每种网络技术都定义一个MTU(以太网的MTU是1500个字节)。
       以太网和802.3对数据帧的长度都有一个限制,其最大值分别是1500和1492字节。链路层的这个特性称作MTU,最大传输单元。不同类型的网络大多数都有一个上限。
        如果IP层有一个数据报要传,而且数据的长度比链路层的MTU还大,那么IP层就需要进行分片(fragmentation),把数据报分成若干片,这样每一片都小于MTU。
       网络传输文件,因为在IP层作了处理,所以我们不必再做处理。可以发送大于1024字节的数据。
       关于“是不是我们就不需要关心每次发送的包大小了呢? ”,发送文件的大小取决于内部消息缓冲区或者网络的限制,因为网络可以将数据分割,所以只需要考虑有没有传送时间的限制,如Http上传的时间限制,另外,系统的内部缓冲区的大小默认是8192字节,即8K,所以发送文件时,按照8K拆分发送。

posted on 2005-06-22 09:22 Pierce 阅读(1087) 评论(3)  编辑 收藏

 

转载于:https://www.cnblogs.com/silva/archive/2005/07/13/192139.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值