我们在前面讨论 OSI 和 TCP/IP 分层协定概念的时候﹐已经指出﹕不管协定设计者如何定义层级﹐各层级协定大致分成两类﹕网路群组﹑和使用者群组前面介绍的 ARP ﹑IP﹑RIP 等协定﹐可以算是网路群组的范围﹐假如您对前述概念都有一定认识﹐已经知道了一个封包如何从一个节点传递到另一个节点然而﹐这仅是 TCP/IP 协定的一半而已﹐要xx了解 TCP/IP 的精髓﹐在 IP 协定的更上一个层级﹐属于使用者群组协定之一﹕TCP 协定﹐是不可不知道的只有当我们同时把 IP 协定和 TCP 协定理解进来﹐才能完整的描述电脑与电脑之间的资料传送过程﹔也只有如此﹐我们才有把握进行日常的 TCP/IP 网路管理TCP 与 IP 这对孪生兄弟﹐是每一个网路管理人员必须混熟的朋友下面﹐我们将一起探讨在 TCP/IP 协定中举足轻重的传送层﹐是如何影响我们日常的网路资料传输的
传送层的功能
在前面讨论网际网路层的时候﹐我们知道﹕网际网路层协定只提供路由资讯的判断﹐以确定封包的传送路径但事实上 IP 协定只确保封包交换设备之间的传输﹐并没有提供一套机制来确保数据的传输在低层的通讯里﹐封包可能在传送过程中发生错误﹐诸如网路硬体的损坏﹑网路负荷过重等等﹐导致封包被丢弃或损坏由于封包路由的多样性和复杂性﹐以及影响路由因素众多及其不可预测性﹐封包之抵达常是不依序的﹐或是会发生重复传送的情形因此﹐我们必须提供一套网路技术﹐以达成更可靠和有效的传送
再者﹐ IP 封包的体积是有限的﹐然而﹐网路程式之间交换的数据往往会超过这个体积限制﹔那么﹐我们必须有另一套机制将程式送来的资料进行规划﹐以符合 IP 封包的传送要求在高层的程式里﹐除非利用非可靠和非连线型(connectionless)的资料传送方式﹐否则,程式设计者必须对每一个一个应用程式处理侦错和修复的动作﹐这无疑增加了程式设计和修改的难度﹐而且也做成许多重复的处理动作因此﹐我们也有必要找出一个可靠的资料流传送方法﹐以建立单独且适用于所有应用程式的资料传送协定这样就可以将应用程式与网路内部协定隔离﹐同时提供一致的资料流传送界面
传送层的设计可以说是应上述要求而生的﹐它的主要功能有﹕
· 接管由上层协定传来的资料﹐并以 IP 封包可以接受的格式进行“封装”工作
· 进行资料传送和回应的确认﹐以及处理资料流的检测和控制
· 对不同的连线进行追踪及转换
在 TCP/IP 协定组中,关于传送层的协定就是 TCP 和 UDP 了﹐我们将在下面详细讨论简而言之﹐TCP 提供的是一个可靠的资料流传送服务﹔相对而言﹐UDP 提供的是一个非可靠的非连线型(connectionless)的资料流传送服务
可靠性传送服务的特性
在应用程式对 TCP 的可靠性传送服务之主要要求有五个﹕
· 资料流导向处理程式之间的大量资料传送﹐确保双方的位元资料流之统一性
· 虚拟电路连接建立和回应资料流传送的连线请求﹐并验证传送期间的资料﹐同时对通讯进行侦错
· 缓冲处理如果程式送出的资料太小﹐协定将等到收集到一定大小的资料包之后才进行传送﹐然而协定允许“push”机制强行送出
· 非结构化资料流应用程式在建立连线之前﹐要先了解资料流动内容与格式﹐方能使用资料流服务
· 全双工连线允许双向性的资料传送﹐各自被视为互不相关的独立资料流然而﹐它提供了返回资料流中携带传送控制资讯的机制
TCP 协定在进行传输的时侯,必须依靠 IP 协定传送封包相对于 TCP , IP 协定属于不可靠协定﹐因为两个协定必需同时困绑工作,因此只要其一能做到可靠传输就可以了要详细的描述 TCP 如何提供可靠性传送是非常复杂的﹐但大部分可靠性协定都采用一定的确认机制来保证传送之可靠性这种技术需要接收端以确认信息(Acknowledgement) 回应发送端﹐肯定资料无误的到达﹐同时双方保留传送的封包记录﹐以作下一笔资料的确认依据此外﹐还利用定时器的机制﹐以在传送逾时后重新发送封包,以确保资料的完整性我们可以从下图中看到确认机制的简单模式﹕
发送端在送出封包之后﹐会起始一个专门针对该封包的计时器﹐当下层网路延迟过久导致封包不能按预估时间获得接收端的确认信息﹐那么发送端会认为该封包可能在传送过程中丢失﹐然后会重新发送该封包、并同时重设计时器﹔如果封包的确认信息在逾时前被接收到﹐则取消该封包的计时器﹐以进行下一封包的传送
计时器虽然解决了封包丢失的问题﹐但如果封包的抵达只是因为网路延迟的关系没有在预定时间完成﹐但却在发送端重发后抵达﹐那么﹐接收端就有可能接收到重复的封包为解决这个困绕﹐传送协定会为每一个封包分配一个序号﹐并要求接收端按封包序号传回确认信息这样﹐当接收端收到封包的时候﹐则可以依据序号判断封包是否被重复传送,同时也能正确的重组资料顺序﹔而发送端也能根据确认封包的序号来判断封包是否被正确接收
滑动视窗(Sliding Window)
从刚才介绍的可靠性传送知识作一个推断:假如每一个单一封包都需要需要等待前面的封包确认之后才进行传送的话﹐将会导致整个连线过程时间的增加﹐同时也会造成频宽的浪费假如在低速的网路上面﹐或设备延迟﹐甚至还会造成网路处于空闲状态有鉴于此﹐聪明的传送层协定设计者们引入了一个滑动视窗的概念
我们可以将滑动视窗理解为多重发送和多重确认的技术它允许发送端在接收到确认信息之前同时传送多个封包﹐因而能够更充份的利用网路频宽和加速资料传送速度滑动视窗的操作可以想象为下图﹕
我们利用 Sliding Window 在收发两端各划分出一个缓冲范围(buffer)﹐定义了多大的资料量可被打包传送在连线建立起来之初﹐两端都会将 window 的设定值还原到初始值﹐比方说﹕ 3 个封包发送端一次过发送三个封包出去﹐如果接收端够顺利﹐也能一次处理接收下来的三个封包的话﹐就会向发送端确认全部三个封包,并告知接收端之 window 值为 3 然后,发送端视窗则会往后移动三个封包﹐填补发送出去之封包的空缺但如果接收端太忙﹐或是其它因素影响﹐暂时只能处理两个封包﹐那么﹐在视窗里面就剩下一个封包﹐然后就会告诉发送端 window 值为 2这个时候﹐发送端就只送出两个封包﹐而视窗就会往后移动两个封包﹐填补发送出去的空缺因此,视窗的大小是不固定的,这就是为什么我们会在视窗前面加上“滑动”字眼的原因了(注意:这里使用封包数目作 window size 是不正确的,仅作例子参考而已实际上的单位应是位元组,视不同的作业系统而各有不同,一般为 4096 bytes,但也有扩展至 16384 bytes 的而且视窗的 size 是每个确认封包都不同的,端视当前的缓冲区状况,其机制比前述复杂许多)
在启动滑动视窗之后﹐封包的传送看起来如下图﹕
滑动视窗会记住哪些封包已经被确认﹐并且为每一个未被确认的封包保留各自的计时器如果在逾时后还没得到该封包的确认﹐则重发该封包发送端在移动视窗的时候﹐它会移过所有已确认的封包在视窗中﹐编号{zd1}的封包﹐往往是序列中的{dy}个未被确认的封包
通讯埠口(port)
大多数的作业系统都提供多工环境﹐允许多个应用程式同时执行﹐在系统术语里面﹐我们管每一个程式的起止为一个行程每一个行程都是动态产生的﹐发送端无法预知接收端的某一个行程的实际状况如何那么﹐当一个封包抵达目的地之后﹐接收如何将封包交给正确的行程处理呢﹖
在传送层协定里面﹐我们为程式产生的行程分配一个通讯埠口﹐其值为一个正正数当一个应用程式需要建立网路连线的时候﹐传送层协定就为该应用程式产生的行程建立一个埠口而事实上,所谓的网路连线,就是两个通讯埠口之间的连线关于网路通讯模式的建立有两种﹕
· 主动连线
· 被动连线
主动连线是当埠口建立之后﹐行程透过该埠口主动发出连线的要求﹔被动模式则是﹐当埠口建立之后﹐行程在该埠口等待连线的请求在 client/server 的架构之下,连线的建立顺序通常是伺服器端先建立好被动连线﹐然后等待客户端的主动连线
在技术上﹐行程使用哪一个埠口并不重要﹐关键是能让对方知道埠口是哪一个就行我们可以把 IP 位址看成主机的门牌号码﹐而埠口则是服务柜台在多工的环境下﹐行程会在一个门牌上面开启多个柜台您或许会问:由起始端主动发起之连线封包抵达之后﹐它究竟凭什么来判断究竟哪个柜台才是正确的行程呢?在日常的生活中﹐大不了逐个柜台去问… 然而在网路系统上面﹐这个似乎有点不切实际因为﹐每一个埠口的建立和关闭都是随机的﹐在不同的时段里﹐所开启的埠口数目和号码都不尽相同既然如此﹐等待连线那端何不先将接收行程所使用埠口号码告知起始端呢?但问题是:既然连线要由起始端主动建立才能连上等待端﹐在没有真正连上之前如何得知呢?不是鸡生蛋、蛋生鸡的问题吗?
有见及此﹐在网际网路的实作应用中﹐人们将一些常用的服务程式所使用埠口号码固定起来例如﹕21 给 FTP 服务使用﹑23 给 TELNET 服务使用﹑25 给 SMTP 服务使用… 我们称这样的埠口为 Well-Known Port在伺服器端﹐这些常用服务会先行建立好被动连线﹐打开所分配的埠口﹐以等待起始端的连线请求那么﹐起始端只要在封包填上目的端的埠口值﹐接收端就能将封包传给正确的服务了, 这也就是透过约定俗成的分配来建立连线但事实上,您大可架设一个地下网站,故意使用其它非 Well-Known Port 来建立被动连线埠口,这样,只有那些事先被告知埠口值、且能修改主动连线设定的客户端才知门而入了
除