您的位置:首页 > 理论基础 > 计算机网络

在网络中传输数据 [转]

2008-10-27 20:59 197 查看
我们都曾经出去旅游,并且会带回一些纪念品。一般情况,这些纪念品可以放在随身带的旅行包中带回家,甚至因为纪念品足够小,可以放在口袋里带回来。如果你到巴黎旅行,看到埃菲尔铁塔,觉得非常壮观,你很想同你的朋友分享,那么拍下照片,寄给朋友。

假设一切允许,法国政府允许你把埃菲尔铁塔带回你的国家,展览数月,那么你怎样带回去呢?

不要告诉我说,找世界上最大的船王给你造一艘可以将埃菲尔铁塔整个装下的超级货船,因为即使可以造出这艘船,你还需要一辆超级卡车,一个超级起重机,一条超级道路,才可以把埃菲尔铁塔整个从市区运送到码头。

很明显,你不需要超级货船,更不需要超级卡车,只需要绘制铁塔结构图,给组成埃菲尔铁塔的每根钢铁骨架编号,记录,再逐个拆开,用集装箱运送到码头,装上货船,运送到你的国家,根据结构图,按照编号,逐个钢铁骨架组装起来就可以了。(说起来简单,做起来复杂,不过确实可行,记得埃及人为了建阿斯旺水坝,就是用这种方法把古代神庙作了搬迁)。

不要搬迁埃菲尔铁塔,如果有兴趣可以到巴黎参观,因为它是法国文化的一部分;不过我们要面对网络传输数据,可能是很大的数据,因为这是网络文化的一部分。

我们经常同网络另一端的朋友使用聊天工具聊天,分享照片,发送文件,在网络中,传输的并不是聊天的信息,照片和文件,而是网络可以传输的基本单位-字节(byte),尽管实质是Bit,但是我们通常指的网络传输基本单位是字节(8Bit)。

说明一个概念,我们常说的百兆网络,指得是百兆Bit,即100/8=12.5M Byte,所以我们常在局域网发送文件,却看到传输速度最高也就7-8M,这是因为你的文件大小的单位是Byte,百兆网络传输的理论值最高值为12.5M Byte,取除干扰,衰减以及网络传输的控制信息,用来传送数据的有效带宽肯定低于理论值。

我们通过网络发送的任何东西,都是使用字节传输的,为了描述方便,使用数组的概念,即传送文件,实际就是传送字节数组,因为文件的基本组成单位就是字节数组。如果要接收方能够正常使用网络传送的文件,那么需要同运送埃菲尔铁塔一样,要绘制结构图,给传输的基本单位编号,因为计算机系统遵循特定的规则,不需要你绘制结构图了,只需要给传输的基本单位编号就可以了。

对于现在的网络世界,Tcp协议是经常用到的协议,Tcp可以确保网络传输数据的有序性,可靠性,那么你的工作就更加简单了,只需要经文件转换为字节数组,送到网络传输,有接收方把收到的字节组装起来就可以了。

下面说一下将各种数据类型转换为字节数组的方法:

BitConverter类可以将数值型数据转换为字节数组,同样可以将字节数组装换位数值。

//把test转化为byte[]

int test = 1990;

byte[] data = BitConverter.GetBytes(test);

//将byte[]转换为Int16

int test=BitConverter.ToInt16(byte[] data, int offset);

说明:BitConverter类存在ToString(),但是使用它的结果,可能同你预想的不同。

//

string data = "this is a test";

string test = BitConverter.ToString(Encoding.ASCII.GetBytes(data));

Console.WriteLine("data = '{0}'", data);

Console.WriteLine("test = '{0}'", test);

结果:

C:\>test

data = 'this is a test'

test = '74-68-69-73-20-69-73-20-61-20-74-65-73-74'

C:\>

BitConvert.ToString()是把字节数组中的值用16进制显示出来,所以如果要显示文本,要用Encoding.ASCII.GetString()方法。

如果在2台基于Intel处理器,Ms Windows操作系统的机器上传送数值型数据,那么是没有问题的;但是如果传送给其他计算机,就不见得没有问题。

因为BitConvert.GetBytes()方法是把数值转换为字节并按照一定次序放入数组,这个次序同处理器和操作系统有关。

这个问题同CPU存储二进制数据不同有关,字节数组的存储有2种形式:

低位优先:先存放不重要的数据

高位优先:先存放重要的数据

所以对于相同系统的计算机,处理数值型字节数组没有问题,而对于不同系统的计算机,就会带来问题。

//

using System;

using System.Net;

using System.Text;

class BinaryDataTest

结果:

C:\>BinaryDataTest

test1 = 45, string = 2D-00-00-00

test2 = 3.14159, string = 6E-86-1B-F0-F9-21-09-40

test3 = -1234567890, string = 2E-FD-69-B6

test4 = False, string = 00

C:\>

可以看到,执行这个程序的计算机是低位优先的,因为字节放入数组的次序是先0,后2D,先存放的是不重要的数据;如果是高位优先的系统,那么应该是00-00-00-2D。

这个问题在Unix世界非常明显,因为运行Unix系统的计算机种类非常多,你不能保证本机处理数值型数据同远程计算机相同。

解决办法:使用相同类型的存储格式传输数值型数据。

网络字节顺序,即高位优先的定义,要求所有网络传输的数值型数据都要按照相同标准。这样,即使不同平台的计算机也可以通过网络互相交换数值型数据而不会有错误。

.Net 提供了HostToNetworkOrder()可以将本机的数值型数据的数组格式转换为网络字节顺序。

NetworkToHost()可以把网络字节顺序的数组转换为本机数值型数据字节数据。

//

using System;

using System.Net;

using System.Text;

class BinaryNetworkByteOrder

//

C:\>BinaryNetworkByteOrder

test1 = 45, string = 2D-00

test2 = 314159, string = 2F-CB-04-00

test3 = -123456789033452, string = 14-CE-F1-79-B7-8F-FF-FF

test1 = 11520, nbo = 00-2D

test2 = 801833984, nbo = 00-04-CB-2F

test3 = 1499401231033958399, nbo = FF-FF-8F-B7-79-F1-CE-14

C:\>

构造函数是特殊的方法,用在建立对象后初始化时。当建立引用类型的对象时,如果你不显式建立构造函数,系统也会建立一个默认的构造函数。

我们建立对象时的步骤:

1.分配内存:使用new 修饰符 在堆上分配内存

2.使用构造函数初始化对象



上面的2个步骤,实际只用1行代码即可以完成,如建立对象when:

Date when=new Data();

详细说明以上2个步骤:

对于步骤1:给对象分配内存。所有的对象都使用修饰符new来建立,这个没有例外的。我们建立对象时可以显式的使用new,如果我们没有这样做,那么编译器会做这部分工作。

请看下面的代码和应用中完整的代码:

string s = "Hello"; string s = new string("Hello");

class Date

class Test

class Date

class DefaultInit

class Test

对于处理构造函数中的异常,只可以使用thow new Exception方法。

重载构造函数:

构造函数也是一种方法,允许重载:

相同的使用范围,相同的名称,不同的参数;

允许使用多种方法初始化对象。

注意: 如果我们给类写了构造函数,那么编译器就不会再定义一个构造函数。

参考:MSDN Training

Introduction to C# Programming

for the Microsoft® .NET Platform

(Prerelease)

Workbook

Course Number: 2124A

现在存在很多企业级的数据库,如Oracle,SqlServer等等,功能强大,使用方便,唯一的缺点是 占用资源多,一般情况下,应该把一台计算机作为单独的数据库服务器,这不是所有人都能做到的。如果把应用程序,数据库装在同一台计算机,效率会低一些的。

那么使用简单的数据库工具可以解决这个问题,当然也可以使用其它解决方案,如xml文件,不过这里只讨论常见的数据库工具-Access。

本人使用Access的原因,基于处理一下问题:

服务器管理用户状态,使用web.config设置,存在3种方式:

<SessionState>

mode="Off":关闭用户Session管理;

mode="InProc":IIS管理用户Session;

mode="StateServer":可以在其它运行AspState Service的Win2000机器上管理;

mode="SqlServer":使用SqlServer数据库管理;

如果要维护用户状态,那么最后3种方式可以使用,不论是“InProc”还是“StateServer”,都要占用系统资源,对于少量信息维护,可以胜任,对于大量数据维护,“InProc”方式力不从心,“StateServer”方式的扩展性不好。

用数据库方式,可以管理海量数据,扩展性好,唯一不足是速度比其他稍慢。

微软提供了使用SqlServer管理Session的方法,前台将web.config的SessionState中的mode设为“SqlSerer”,后台的数据库脚本在/windows/Microsoft.NET\Framework\v1.0.3215\installsqlsate.sql获得,2者配合,可以使用。

不过不是所有人都使用SqlServer的,对于其他数据库,微软没有提供解决方法。微软的数据库脚本文件,我没有仔细看,因为对SqlServer不熟悉。

根据对Session的理解,自己写管理方法,数据库为常见的Access。

使用Access后,总结几点经验:

1.虽然Access小巧,但是使用起来比操作大型数据库,如Oracle, 麻烦的多,因为小巧的缘故吧。

2.Access的表中少用Date型字段,因为使用Asp.net 不好操作,没有找到对应的数据类型,解决办法是改为字符型的,然后使用CDate()作转换。

3.Access中可以使用存储过程(即查询) ,没有触发器;

关于使用查询:

使用的方法,同调用Oracle等大型数据库的存储过程相同,如:

//Con是OleDbConnection实例

if(Con.State==ConnectionState.Closed)

OleDbCommand Cmd=Con.CreateCommand();

Cmd.CommandType=CommandType.StoredProcedure;

Cmd.CommandText="CheckTimeout"; //CheckTimeout是视图名

int count=Cmd.ExecuteNonQuery();

其中视图CheckTimeout的代码:

UPDATE UserSession SET UserSession.IfEndSession = 1

WHERE (((Now()-CDate([timeout]))>Minute(15)));

属于没有参数的,如果调用有参数的,Asp.Net 部分加入OleDbParameter,其他相同,

视图的代码可为:

PARAMETERS [PersonID] Short;

DELETE *

FROM PersonInfo

WHERE [PersonInfo].[PersonID]=[PersonID];

其中PersonId为参数。

还在测试Session管理的代码,如果基本可用,将贴出来供大家讨论。

Microsoft的产品,入手简单,深入复杂,支持外部的简单的是后台严谨的复杂,我们常在开发Asp.Net程序时使用Session,但是知道Ms怎样处理Session的原理,恐怕知道的人不多,自己写一个Session处理,帮助自己理解Session。

如果一个系统中存在多个Web服务器,那么有一台服务器作为Session服务器,应该是分布式系统的一个好的选择。

我想把我的想法作为一个开源项目,对此感兴趣的所有人都可以阅读,测试,为了方便测试,选择了Access,如果此项目可用,那么可以容易的应用到其他数据库平台。

有了大家的帮助,我们可以进步更快。

关于数据库分类的概念,谢谢春鱼。

附Access的介绍(从其他网站摘录):

在办公软件Office套件中,最为广大用户熟悉的是Word和Excel,因为它们功能强大且方便易用,更因为它们不仅可用于办公,还可用于个人写作和家庭记帐理财等。同为Office套件中一部分的Access,虽然有着同样强大的功能,但使用的人却相对少些,不像Word和Excel那样广泛。事实上,真正用过Access的用户,对其强大功能和灵活应用均称赞“不错,很好的……。”

  Access 数据库管理系统是Microsoft Office 套件的重要组成部分,是Access的最新版本,可在Windows 95环境下运行。Access适用于小型商务活动,用以存贮和管理商务活动所需要的数据。Access不仅是一个数据库,而且它具有强大的数据管理功能,它可以方便地利用各种数据源,生成窗体(表单),查询,报表和应用程序等。

什么是Access 数据库

  数据库是有结构的数据集合,它与一般的数据文件不同,(其中的数据是无结构的)是一串文字或数字流。数据库中的数据可以是文字、图象、声音等。

Microsoft Access是一种关系式数据库,关系式数据库由一系列表组成,表又由一系列行和列组成,每一行是一个记录,每一列是一个字段,每个字段有一个字段名,字段名在一个表中不能重复。图1是一个“产品”表的例子。“产品”表由10个记录组成,一个记录占一行,每一个记录由产品ID、产品名称、库存量、订货量、单价和折扣率6个字段组成。“产品ID”是字段名,其下面的1,2等是字段的值。

  

表与表之间可以建立关系(或称关联,连接),以便查询相关联的信息。Access数据库以文件形式保存,文件的扩展名是MDB。

Access 的6种对象

   Access 数据库由六种对象组成,它们是表、查询、窗体、报表、宏和模块。

 表(Table) ——表是数据库的基本对象,是创建其他5种对象的基础。表由记录组成,记录由字段组成,表用来存贮数据库的数据,故又称数据表。

  查询(Query)——查询可以按索引快速查找到需要的记录,按要求筛选记录并能连接若干个表的字段组成新表。

  窗体(Form)——窗体提供了一种方便的浏览、输入及更改数据的窗口。还可以创建子窗体显示相关联的表的内容。窗体也称表单。

  报表(Report)——报表的功能是将数据库中的数据分类汇总,然后打印出来,以便分析。

  宏(Macro)——宏相当于DOS中的批处理,用来自动执行一系列操作。Access列出了一些常用的操作供用户选择,使用起来十分方便。

  模块(Module)——模块的功能与宏类似,但它定义的操作比宏更精细和复杂,用户可以根据自己的需要编写程序。模块使用Visual Basic编程。

  与Access 以前的版本比较,Access 新增了许多功能,字段类型增加了OLE对象和超级链接,特别是与Internet的融合,在数据库中可以直接链接到指定的Web页面或网络文件,也可以把Web页面上的表格导入到数据库。Access 可以方便地利用各种数据源,包括dBASE, FoxBase,FoxPro,Excel,Word 等。Access 增加了数据库访问的安全机制,可对表一级设置访问许可权。Access 还可以方便地利用FoxPro数据库、Excel电子表格的数据,还可以和Word混合使用,打印通用信函或信封。

MS ACCESS使用VBA語法來開發,在mdb的檔案格式中,提供資料與程式同時建置,或是利用資料庫分割,將程式與資料分隔,有

點類似主從架構,使用mdb最大的好處就是可以盡情使用ACCESS的參數,它可以節省寫SQL語法與過多的VBA程式,不過mdb的缺點

是比較適合單機作業,如果想多人使用資料庫,那麼就必須將資料轉換為SQL存取,只要分別在使用端設定ODBC來指向SQL SERVER

,這樣子就符合多人使用的目的與效果了!

在ACCESS 2000/XP中出現新的格式-ADP,ADP是直接連取SQL SERVER來做資料存取,速度當然比透過ODBC來的快,不過您必

須逐行撰寫 VBA與 SQL語法,在ACCESS 2000中必須安裝OFFICE 2000 SP1更新檔才可以直接存取SQL 2000,無論是MDB或ADP也

好,ACCESS最大的優點在於報表的製作,例如:做識別證,來賓證等,它簡單的報表設定,都是最具有親合力的操作,ACCESS的報表當

然也具有試算的能力,只要懂一點ACCESS的使用著都會自己調整報表列印位置,不必依賴程式設計者,給予使用者莫大的幫助。

浏览器访问WebPage时,虽然使用的地址不变,但是端口会发生变化,用刷新页面作测试,2分钟内刷新,用户的端口不变,超过2分钟,那么端口会发生变化。

Http1.1中说,Connection报头中有持久性连接控制,默认情况下,所有Http1.1连接都认为是持久性的,除非某个请求或者响应包括了一个Connection:close报头。这个持久性,指的好像不是维持相同的Tcp端口。

如果用Http协议维持会话,那么需要给客户端发送标记符SessionId,客户端再次访问时,提供这个标记,好被Server识别。这个标记存在形式是cookie或者查询字符串,要手工处理Session,需要一些功能来实现,带来如下问题:

要实现Access存储Session,那么WebApp的配置文件的

<SessionState>

mode="Off":关闭用户Session管理;

因为不了解IIS的Session管理机制。

那就需要知道怎样处理标记用户请求的SessionID,关于SessionId还在查资料,那位对此有经验,请给与支持。

SessionID:msdn中说,是长度为120Bit的ASC字符,可以在Url中使用,但是经过测试,

//生成SessionId

string id=Session.SessionID;

//显示Id的内容及长度

Response.Write(id+":"+id.Length.ToString()+"<br>");

byte[] by=System.Text.Encoding.ASCII.GetBytes(id);

Response.Write(by.Length.ToString()+"<br>");

使用Session.SessionID生成的SessionId长度为24个ASC字符,即192Bit,应该是原始的SessionID被加密了,长度发生了变化。

发送复合型数据:

除了传输数值型数据,我们更多的是传输复合型数据,如字符,数值,bool等等。是否存在简单办法发送复合型数据呢?

可以建立一个类,

class Employee

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

建立如下方法:

public byte[] GetBytes()

GetBytes()由3个作用:

1.将类中的每个字段转换为字节数组;

2.将每个字节数组都放入相同的总的字节数组中;

3.获取了总字节数组的长度。

关于Buffer.BlockCopy(),是将目的数组copy到原数组的特定位置;

BlockCopy(byte[] array1, int start, byte[] array2, int offset, int size);

我们的目的就是把不同的数组放入到统一数组的特定位置。这个做好了,那么可以通过网络传送了。

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

通过网络,我们说到了这个数组,怎样还原呢?

首先,我们要知道Employee 类的结构,然后,可以

public Employee(byte[] data)

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

完整发送、接收代码:

Client:

using System;

using System.Net;

using System.Net.Sockets;

class EmployeeClient

Server:

using System;

using System.Net;

using System.Net.Sockets;

class EmployeeSrvr

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拆分发送。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: