计算机网络之 数据链路层

说完了网络层,现在来说说传输层吧。传输层也是网络体系中最为重要的一层,也是最为复杂的一层。说到复杂那是因为tcp的存在,为了保证传输的可靠性,TCP可是费了不少功夫的。
传输层是一般的 用户可以接触到的最底层的一层,也就是通常人们所使用的socket。从通信和信息处理的角度来看,运输层向它上面的网络层提供通信服务。网络层的层面还在主机,而传输层
的层面已经到了主机中的程序,也就是端口。传输层有两个作用叫做复用和分用,复用是指在发送方不同的应用进程都可以使用同一个运输层协议进行传输数据,而分用是指接收方的运输层在剥去
报文的首部后能够把这些数据正确交付目的应用进程。
传输层有两种重要的协议,UDP(用户数据报协议)和TCP(传输控制协议)。
UDP在传输数据之前不需要先建立连接,接收端受到之后也不需要发送确认。因此从理论上来说UDP是不可靠的传输层协议。但是总体来说UDP简单
TCP在传输数据之前要先建立连接,在传输结束之后要断开连接。但是TCP不提供广播或多播服务,由于TCP提供的是可靠,面向连接的运输服务。因此它就比较复杂,并且协议里面增加了很多内容来保证TCP的正确性。

常见的应用层服务使用UDP的有DNS,TFTP,RIP,DHCP,SNMP,NFS,IGMP, ip电话,流式多媒体通信。而使用TCP的应用层服务有:SMTP,TELNET,HTTP,FTP。

1. 先从最简单的UDP(用户数据包协议)开始说起,
首先UDP它是无连接的,第二 UDP也是尽最大努力交付的,第三是UDP是面向报文段的。第四UPD没有拥塞控制 第五UDP支持一对一,一对多,多对一,多对多的交互通信。 第六UDP的首部开销很小,只有8个字节,TCP最少也需要20个字节
前两个特点没啥好说的,第三个特点是比较特殊的,它是说明 发送方的UDP对应用程序交下来的报文,在添加首部后就想下交付给IP层。 也就是说 UDP不会对应用层传下来的报文进行合并和拆分,应用层给它多长的报文,它就传输多长的报文,也就是说
UDP一次交付一个完成的报文。因此,对于应用程序来说必须选择合适大小的报文。如果报文太长 ,UDP把它交付给IP层后,IP在传输时可能要进行分片,这回降低IP的效率。反之,如果报文太短,UDP把它交给IP后,会使IP数据报的首部相对长度太大,这一降低了IP的效率。

对于一些使用UDP的应用,可能对UDP的不可靠传输进行适当的改进,以减少数据的丢失。在这种情况下,应用进程本身可以在不影响应用的实时性的前提下,增加一些提高可靠性的措施,如采育好难过前向纠错或重传丢失的报文。

UDP的头是非常简单的,上面 已经说过了UDP的头只有8个字节,源端口2个字节,目的端口2个字节,长度2个字节(UDP用户数据报的长度,其最小值为8也就是头部长度),检验和2个字节:这个检验和是整个数据报的检验而不是首部的检验和。
如果接收方UDP发现收到的报文中的目的端口号不正确,就丢弃该报文,并由ICMP协议发送端口不可达 差错报文给发送发。

UDP的首部检验和计算方法比较特殊,首先要计算检验和首先要在UDP数据报之前增加12个字节的伪首部,这个伪首部 并不是UDP数据报的真正的首部。知识在计算检验和的时候,临时添加在UDP用户数据报前面,得到一个临时的UDP用户数据报,检验和即使按照这个临时的UDP数据报来计算的。 

2. 参数控制协议TCP协议
    和UDP一样TCP也有一些特点,第一 TCP是面向连接的传输层协议,第二每一条TCP连接只能由两个端点也就是说TCP是点到点的。第三 TCP提供可靠交付服务 第四 TCP提供全双工通信 第五TCP是面向字节流的。
    TCP中的流指的是流入到进程或从进程流出来的字节序列。TCP并不关心应用进程一次把多长的报文发送到TCP的缓存中,而是根据对方给出的窗口值(接受窗口和拥塞窗口的最小值),来决定一个报文段发送多少个字节,如果应用进程传送给TCP缓存的数据块太长,TCP就把它划的短一些在发送,如果 应用进程一次只发送来一个字节,那么TCP也可以积累足够多的字节后再构成报文发送出去。

都是TCP是可靠的,但是是哪些机制使它这么可靠呢。首先就是TCP的确认机制了。
先从最简单的停等协议开始说起吧。假设有两个主机 A和B,A为发送发,B为接收方。
首先A发送分组M给B,如果B收到M之后将就向A发送确认。A在收到了B的确认后在发送下一个分组。这个是最理想的情况。

如果在发送的过程中出现一些意外情况,例如 B在接收到M时经过校验发现除了差错,就丢弃M,其他什么也不做,其实就是说 不通知A收到有差错的分组。这是因为也可能是M在传输过程中丢失了,这个时候B是什么也不知道的。在这两种清下B是什么也不会做的,但是这个时候A就着急了,它想 都过了这么长时间了 B怎么还没给我确认,是不是出什么事情了(可能数据包在传输过程中丢失了),因此它就重传这个数据报。(刚才描述的这种情况 是依赖与A在发送数据之后启动一个超时计时器的,如果超时计时器到时,那么就重传此数据报)。

那问题又来了,如果A给B发送的数据是在网络中的某个地方延迟了很久,但是没有丢失,最后到达了B,同时B在这之前已经收到了A重传的数据报(当然B是不知道这个数据报是重传的,如果这个数据报在传输中丢失的话)。那么B首先会丢弃这个重复的分组,然后向A再次发送确认(如果他不发送确认,A认为这个数据报又丢失了,所以又会重传,如此循环下去)。

停等协议基本就是这个情况,那么可以分析以下这协议的信道利用率。
假设A发送分组需要的时间是Td,B发送确认的时间为Ta,其中A和B处理分组的时间都忽略不计,那么A在经过时间(Td+RTT+Ta)之后就可以发送下一个分组,因此信道的利用率为 U=Td/(Td+RTT+Ta),这样我们就可以知道 当往返时间RTT远大于分组发送的时间Td时候,信道的利用率就会非常低。因此接下来就来了滑动窗口协议了。

发送发维持一个发送窗口,窗口的意义就子啊与在这个窗口内的分组都可以连续发送出去,而不需要等待对方的确认。而在接收方,一般采用累积确认的方式,也就是说,接收方不必对收到的分组逐个发送确认,而是在受到几个分组之后,对按序列到达的最后一个分组发送确认,这就表示:到这个分组为止的所有分组都已经正确收到了。

选择确认 SACK,如果受到的报文无差错,只是未按序号,中间还缺少一些序号的数据,那么能否设法只传送缺少的数据而不重传已经正确到达接收方的数据呢?如果想这样那么就是使用TCP中的选择确认工作原理 SACK。如果要使用选择确认SACK,那么在建立TCP的时候,就要在TCP首部的选项中加上运行SACK的选项,而双方必须都事先商定好。如果使用选择确认,那么原来首部中的确认号字段的用法仍然不变,展示以后在TCP的报文段的首部中增加了SACK选项,以便告知收到的不连续的字节快的边界。以后与首部想选项字段最长也只有40字节,而指明月hi个边界就要用4个字节(需要为32为,4个字节),因此在选项中最多只能指明4个字节块的边界信息。

TCP的流控
tcp的流控实际就是发送双方协调发送窗口的大小,不至于发送方发的太快而接收方 来不及接收数据。如果接收放给发送发发送接收窗口为0,那么就是说你要不再给我发送数据了。那么问题又来了,加入接收方先给发送发发送了窗口0 这个数据报,但是一会接收方又有空间了,所以有给发送发发送了一个不为0的窗口数据报,但是这个数据在网络中丢失了,那么A将一直等下去。因此为了解决这个问题,TCP为每一个连接设有一个持续计时器,只要TCP连接的以防受到对方的零窗口通知,就启动持续计时器。若持续计时器设置的时间到期,就发送一个零窗口探测报文段,而对方就在确认这个探测报文段的时候给出了现在的窗口值。如果窗口仍然是零,那么收到这个报文短的一方就重新设置持续计时器,如果窗口不为零,俺么死锁的僵局就打破了。


前面已经讲过了,应用进程把数据传送给TCp的发送缓存之后,剩下的发送任务就优TCP来控制了,可以用不同的控制机制来控制TCp报文段的发送时机。
在现实中广泛使用的是Nagle算法, 若发送应用进程把要发送的数据逐个字节送到TCP的发送缓存,则发送方就把第一个数据字节发送出去,把后面达到的数据字节都缓存起来。当发送发受到对第一个数据字符的确认之后,再把发送缓存中的所有数据组装成一个报文段发送出去。

还有一个问题叫做糊涂窗口(傻窗口),设想一种情况,TCP接收方的缓存已经满了,而交互式应用进程一次只从接收缓存中读取一个字节,然后想发送发发送确认,ING把窗口设置为1个字节,接着发送发又发送来1个字节的数据。接收方发回确认。仍然将窗口设置为1个字节,这样下去,使网络的效率很低。要解决这个问题,可以让接收方等待一段时间,使得或者接收缓存已经有足够多空间时,或者等待接收缓存已经有一半的空闲时,接收方就发送确认报文,并向发送发通知当前窗口的大小。

TCP的拥塞控制
TCP的拥塞控制啥呢,说白了就是网络现在出现了拥塞发送发应该怎么办。首先一个问题就是发送方怎么知道现在网络就拥塞了呢?一般有很多方法来标明现在网络拥塞了例如 路由器中的平均队列长度,超时重传的分组数,平均分组时延,或者更为直接的就是丢包了。

TCP有几种比较成熟的拥塞控制方法,分别是慢启动,拥塞避免,快重传和快恢复。不要被这些名字吓到,其实往往最有用的算法其本质很简单。但是这几种方法的共同点都是通过调整TCP的拥塞窗口 cwnd,进而调整发送放的发送窗口实现的。
首先我们假设 接收方给发送发发送过来的rwnd,也就是接收窗口是无线大的,也就是说先不要考虑TCP的流控,就考虑拥塞控制。这个时候发送放的发送窗口等于拥塞窗口

首先来说慢启动和拥塞避免
慢启动的原理是这样的首先 发送方的 发送窗口等于拥塞窗口等于1,当发送完一个报文并收到确认之后那么发送窗口的值加倍,一直这样下去,直到拥塞窗口的大小等于一个阈值时,这个过程就叫做慢启动,有些人说这也不慢呀,每次都翻倍呢,但是相比较一次把所有报文都发送过去,这样是慢了很多的。
当拥塞窗口的大小等于那个阈值之后,这下拥塞窗口的大小开始线性增长(也就是每次只增加1),而这个过程就叫做拥塞避免。
可以简单的来说。
当拥塞窗口 < 阈值 时,使用慢启动方法 窗口大小翻倍
当拥塞窗口 >阈值时,使用拥塞避免方法 窗口大小加一
当拥塞窗口 = 阈值时,既可以使用慢启动方法,也可以使用拥塞避免方法。

而无论在慢启动阶段还是在拥塞避免阶段,只要网络中出现了拥塞,那么立即想阈值变为原来的一半,并且将拥塞窗口和发送窗口全部设为1,从慢启动阶段重新开始。 这样做的目的就是迅速减少主机发送到网络中的分组,使得发生拥塞的路由器有足够的时间把队列中积压的分组处理完毕。

拥塞避免并非完全可以避免拥塞,利用以上的措施要完全避免拥塞是不可能的,拥塞避免是说在拥塞避免阶段将拥塞窗口控制为按线性规律增长,是网络比较不容易出现拥塞。

然后再说说快重传和快回复策略

快重传算法首先要求接收方没收到一个失序的报文段后就立即发出重复确认,而不要等待自己发送数据时才进行捎带确认,按照快重传算法规定,发送方只要一连收到三个回复确认 就立即重传对方还没受到的失序报文,而不必等到那个报文的重传计时器到期,
而快恢复是与快重传配合使用的,当发送放连续受到三个重复确认时,就把阈值减半并开始慢启动方法,但是它有不执行慢开始操作,而是将阈值变为原来一半之后,就把拥塞窗口的大小稍微阈值的一半,然后成线性增长。这就是传说中的快恢复。因此采用快恢复的算法时,慢开始算法只是在TCP连接建立时和网络持出现超时时才使用。


上面在讨论TCP的拥塞控制的时候都假设接收方的窗口是无限大的,也就是说不存在流控,因此发送方的窗口上限值应该取接收方窗口和拥塞窗口较小的那个。
换句话说 要想进行流控和拥塞控制那么必须有一个人得妥协一下。

总的来看拥塞控制,流量控制都是来调整发送方的发送窗口而实现的但是拥塞控制的窗口大小有网络的情况而定,流控的窗口大小根据接收方的鞥努力大小而定。两者结合起来就要取最小值。总体来说 拥塞控制是在网络的宏观层面来做的,而流量控制则是在网络的微观层面来做的。

以上的方法看起来很完美,但是问题又来了真的很完美吗?

首先我们来看看路由器吧,路由器一般都会有一个先进先出的队列,来存放到达的分组,如果这个队列满了,那就把新到来的分组丢掉。那么相应的发送方的重传计时器就会超时,那么TCP就会认为现在网络很拥塞,然后开始慢启动,这下网络中的流量一下就变少了很多。但是有这么一个常有的现象就是一个ip包中可能有多个tcp的数据,也就是说 一个ip包中可以包含多个应用程序的tcp数据,那么问题就显而易见了,如果这样的包被路由器丢掉了,那么就有很多的tcp在同一时刻开始慢启动了,不知不觉的让这些tcp连接都同步了起来,这下就坏了,如果这种tcp连接很多,那么网络中的流量就是同步的下降,同步的上升,这样网络的利用率就会很低,而我们想要看到的是网络的利用率一直出于一种比较稳定的状态。因此为了避免这种事情的发生,tcp又提出了一个随机早期检测的方法RED,其意思就是说 在路由器的队列还没有满之前,也就是说路由器还没有迫不得已丢包的时候,惹事监测到网络拥塞的早起征兆,例如路由器的平均队列长度超过一定的阈值时,让路由器随机的丢一些包,然后这些tcp就开始慢启动了,这样就可以避免全局性的tcp同步发生。

接下来我们休息以下,来看看tcp的报头结构吧

1.tcp是端到端的连接 所以报头第一个项目是 源端口(很好奇为啥不是目的端口,因为ip的报头中先是目的地址才是源地址的)这个占16位
2.接下来是目的端口 这个也占16位
3.然后是序号字段 这个字段占32位 它是个啥呢,也就是唯一的标识这个tcp的序号,接收方用来确认编号,其起始编号由发送方随机生成
4.然后是32位的 确认字段 它是对发送方发来的tcp报文段进行确认的
5.然后下来是4位的数据偏移,这个可不是ip报中的那13位的数据偏移,它表示的是tcp报文段的数据起始处距离tcp报文段的起始处有多远。想想这句话 数据的起始处-tcp报文段的其实处 那不就是tcp的头部长度么,同样 4位
  表示数据便宜数字最大也就是15,所以它的1和ip报文中首部长度表示的一样也是表示4个字节,那么就可以知道了 tcp的首部最长也就是60个字节。
6.接下来6位是保留的,但是现在也没用,我想起了ip的8位 区分服务字段 呵呵呵
7. 接下来是6个字段 也可以说是六个控制位 
   依次是
   1. URG 紧急控制位,URG=1时,标明紧急指针有效,它告诉系统此报文中有紧急数据,应尽快传输
   2. ACk控制位,当ACK=1的时候,tcp报文段中的确认号才有用,也就是说在tcp连接开始建立的时候,发起方发送的第一个tcp连接报文段这个字段肯定是为0的,并且其32为的确认字段也是随机填写的。
   3. PSH控制位,有时候两个应用进程进行交互,一端的应用进程希望自己发送一个报文之后,对方立即回应,那么它就把这个PSH字段置为1,对方受到PSH字段为1的报文,那么立即就把这个报文给上层应用程序,而不是等待缓存满了再交给应用程序。        
      乍一看,这个PSH字段和URG字段很像,都是要求发送端立即发送,但是两者的区别在于,URG只说发送端立即发送,并没有规定接收端受到这个报文之后该怎么办,而PSH字段它规定了接收方受到之后要立即给上层应用。
   4.RST复位控制位,当RST=1时,标明TCP连接中出现了严重差错(肯定是发送这个包的那方 出现了崩溃或者其他原因),必须释放连接,然后再重新建立运输连接
   5.SYN同步控制位,这个想必很熟悉了,这个就是在tcp开始连接的时候用到,发起方将SYN=1 ACK=0 发给接收方,接收方给他回 SYN=1 ACK=1,这个字段实际就是表示请求连接的意思。
   6. FIN 终止控制位,这个想必也很熟悉了,当FIN=1的时候,表示此报文段的发送方的数据已经发送完毕,并要求释放运输连接
8.说完了这个6个控制位 接下来16位表示的是窗口大小,这个可不是拥塞控制里面的那个窗口大小,拥塞控制的窗口大小是自己算出来的而不是从报文段里面取出来的,这个窗口大小说的是接收方现在可以有多大的地方来存放数据
9. 检验和 这个占16位,看过ip的都知道,ip有16位的校验和只校验ip的头不校验ip的数据部分,那么tcp的这个校验和是tcp的整个报文也就是 头部加数据
10. 然后是16位的紧急指针,这个字段当URG=1的时候才有用,它指出数据中紧急数据的字节数(TCP的报文中不是所有数据都是紧急的)。这样对方看到这个字段之后,根据这个指针的大小处理完紧急数据之后就可以恢复正常处理正常数据了
11. 选项字段这个占40个字节

休息完了来讲讲关于TCP为了实现可靠传输的最后一个方面的努力,TCP 的运输连接管理 也就是传说中的三次握手,和四次挥手

三次握手

在TCP建立连接的时候有一个三次握手的事情。假设 A向B发送连接请求建立连接。
首先A发送SYN=1 ACK=0 seq=x 给B
B给A回复SYN=1 ACK=1  seq=y ack=x+1,
然后A在给B回复ACK=1,seq=x+1 ack=y+1

一般来说A发给B 然后B在确认发送给A就可以了,为什么A还要再确认一下呢,这主要是为了防止已经失效的请求连接报文突然又传到了B,也就是说原来A被B发了一个请求连接报文,B过了很长时间没有确认,A认为这个报文丢了,然后它就重传了这个报文,然后得到了B的确认,但是A第一次发送的那个报文其实没有丢 只是在某个地方呆的时间太长了,所以过了一段时间之后它就又到了B 这个时候B会认为A又来了一个连接,确认后等A发送数据,所以如果没有第三次握手,那么这个连接肯定就建立了这样就浪费了B的资源,所以为了避免这种事情的发生,才有了三次握手。

TCP的四次挥手
 TCP在断开连接的时候有个四次挥手的事情。
假设A主动断开与B的连接
那么A先发送 FIN=1 Seq=x给B
B受到A的消息后发送 ACK=1 Seq=y ack=x+1
然后如果B还有数据要发送给A那么就把这些数据发送完
接下来
B发送FIN=1 ACK=1 Seq=W ack=x+1
A再给B发送 ACK=1 Seq=x+1 ack=w+1
这样就断开了连接


总的来说TCP的内容是说完了,TCP为了可靠 在建立连接的时候有三次握手,在传输数据的时候宏观上是拥塞控制,微观上是流量控制,在断开连接的时候是四次挥手,以及各种定时器 才使得TCP变的比较可靠。




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值