运输层
-
运输层和网络层的关系
概述:在协议栈中,运输层刚好位于网络层之上。网络层提供了主机之间的逻辑通信,而运输层为运行在不同主机上的进程之间提供了逻辑通信。
好比两个家庭的孩子互相写信,然后由某一个大孩子负责将所有的信交到邮局和从邮局收集到所有人的信然后分发给各个孩子。中间的传输则交给邮局来处理。此处,
应用层报文=信封上的字符
进程=堂兄弟姐妹
主机(又称为端系统)=家庭
运输层协议=负责收发的两个大孩子
网络层协议=邮政服务(包括邮车)
*注:而对于不同的运输层协议,就好比不同的大孩子,可能这些大孩子比较粗心,会有遗漏,或者有丢失。
运输协议能够提供的服务常常受制于底层网络层协议的服务模型。如果网络层协议无法为主机之间发送的运输层报文段提供时延或带宽保证的话,运输层协议也就无法为进程之间发送的应用程序报文提供时延或带宽保证。
服务扩展:将两个端系统间IP的交付服务扩展为运行在端系统上的两个进程之间的交付服务
-
多路复用与多路分解
进程,套接字,端口号:一个进程对应一个端口号,但是一个端口号可以对应于多个套接字。
多路复用和多路分解是在套接字层面进行讨论的,多路分解:从网络层传输上来的信息可能来自不同主机,或同一主机的不同进程,要把这些报文段发向正确的进程,这就是多路分解,将信息分拣出来。多路复用:同理,在源主机从不同套接字中收集数据块,并为每个数据块封装上首部信息(这将在以后用于分解)从而生成报文段,然后将报文段传递到网络层。
端口号分配:如果应用程序开发者所编写的代码实现的是一个“周知协议”的服务器端,那么开发者就必须为其分配一个相应的周知端口号。通常,应用程序的客户端让运输层自动地(并 且是透明地)分配端口号,而服务器端则分配一个特定的端口号。
一个UDP套接字是由一个二元组(目的IP地址,目的端口号)识别的,即当两个具有相同的目的IP地址和目的端口号发至这个主机的网络层时,运输层将把它们发往相同的目的套接字和相同的目的进程。 原端口号和原IP地址的作用主要是当之前的接收方需要回发一个报文给之前的发送方式,可以从之前的报文中获取相关信息。
一个TCP套接字是由一个四元组(源IP地址,源端口号,目的IP地址,目的端口号)识别的。所以即使两个TCP报文段具有相同的目的IP地址,目的端口号,如果他们来自于不同的IP地址或者不同的端口号,那么他们就会被定向到不同的套接字。问题:进程、端口号、套接字的对应关系?以服务器作为主角,当有两个不同用户访问同一个服务器上的同一个进程时,这时由TCP套接字的标识可知,服务器会新建一个套接字以供服务,这就是线程。
-
无连接运输:UDP
运输层最低限度必须提供一种复用/分解服务,以便在网络层与正确的应用级进程之间传递数据。 而UDP只是做了运输协议能够做的最少工作:复用/分解功能及少量的差错检测外。
无连接:使用UDP时,在发送报文段之前,发送方和接收方的运输层实体之间没有握手。如使用了UDP协议的DNS。
主机端的UDP为查询报文添加首部字段,然后将形成的报文段交给网络层。网络层将此UDP报文段封装进一个IP数据报中,然后将其发送给一个名字服务器。在查询主机中的DNS应用程序则等待对该查询的响应。如果它没有收到响应(可能是由于底层网络丢失了查询或响应),则要么试图向另一个名字服务器发送该査询,要么通知调用的应用程序它不能获得响应。
### UDP报文段结构:todo
### UDP检验和
发送方的UDP对报文段中的所有16比特字的和,在计算和的时候,对溢出的进位进行回卷(也就是把溢出的数字再加到数字的尾部,反复循环直至没有溢出),计算完和之后再对这个和取反得到的结构就是检验和。在接收方UDP将传输过来的所有16比特字再进行求和并将检验和也一并求和,最后得出的结果若为16个1则表明没有出错,若有0则表明出错了。
端到端原则的体现:在既无法确保逐链路的可靠性,又无法确保内存中的差错检测的情况下,如果端到端数据传输服务要提供差错检测,UDP就必须在端到端基础上在运输层提供差错检测。与在较高级别提供这些 功能的代价相比,在较低级别上设置的功能可能是冗余的或几乎没有价值的。但是UDP的对差错的恢复无能为力,它只能做到丢弃受损的报文段,或者将受损的报文段交给应用程序并给出警告。
-
可靠数据传输原理
rdt1.0:假设信道是完全可靠的,即在传输过程中不存在出错,那么发送方发完之后就万事大吉了,直接等到上一层的新数据。
对于如何判断数据包是否产生错误,采用的是之前讲过的校验码checksum
rdt2.0:因为信道不可能是完全可靠的,所以在传输的过程中,通过NAK和ACK作为反馈信号,当接收了并且是未损坏的数据时,则向发送发返回一个ACK,过程结束。若接受了但是是损坏的数据时,则向发送方返回一个NAK,接收方等待,发送方进行重传操作。问题:停等,NAK/ACK出错的话会产生混乱。
利用状态进行判断,以前的话只是通过验证码NAK/ACK进行判断,现在加强,不仅仅看验证码还要看当前所处的状态与传过来的验证码是否匹配。
rdt2.1:主要解决的是2.0中出现的反馈信息出错的问题,添加了01编号,也就是说01分别代表两个数据包,然后这样循环往复。①那么发送的人发送0号数据包,发送的的人开始等待反馈信息,此时接受者若接收到0号数据包未损坏,则返回ACK,但是ACK在途中发生错误跳转成了NAK或损失成乱七八糟的东西,此时发送方进行重传0号数据包,到了接收方,但是因为接收方之前正确接受了0号数据包处于等待1号数据包的状态,所以会出现拒收,避免冗余。②发送的人发送0号数据包,但是因为有损伤所以接收方发送NAK,但是若NAK变成了ACK,此时发送方就继续发送1号数据包了,由于接受者还处于接收0号数据包的状态,所以会拒绝1号数据包,避免错序。
rdt2.1中
ss发送0号数据包,rr收到之后发现损坏返回NAK并进入等待0号数据包,但是NAK在途中跳转成了ACK,ss收到ACK之后发送1号数据块,rr接收到1号数据块之后返回ACK,ss接收到ACK就继续循环了,那么0号数据包不就一直没传过去嘛?也就是说,我感觉ss无法判断rr传过来的是对0号数据的ACK还是对1号数据的ACK啊解答:用checksum对反馈数据进行检验,所以即使是NAK转变成了ACK,ss还是会判断他是错的,所以还是会进行重发。
rdt2.2:其实仔细分析以上的状态转换会发现,接受者发送NAK/ACK表明的意思不就是我还想要当前数据包再给我重发一份和我已经接收了当前数据包你可以发下一份了。那么在2.2中,ss会在发送的数据包进行01编号,对于rr而言,只要你的数据包有损坏或者你发的不是rr当前所处状态的数据包(比如说rr处在接收0号数据包的状态结果传来了1号数据包或者0号有损数据包,那么我就返回ACK,1就好了,这个1既可以代表有损,也可以代表传错),因为这样的一个返回对ss来说都是进行同样的操作也就是重传。也相当于将2.1rr端的几种情况进行合并,因为这对ss而言所做的操作是一样的。问题:既然每次返回都是ACK,ACK还有什么存在的意义呢?
rdt3.0:这就比较好理解了,在2.2的基础上(能够处理冗余数据包)若数据包卡在了传输路上或者反馈信号卡住了,那么一直等下去毕竟不是个办法,所以增加了一个定时功能,若反馈信号在时间内都没有到达的话就直接重传。
### 流水线(pipelining)操作:
解决了2.0中就出现的停等问题,停等的话ss必须得等到返回当前数据包的一个正向反馈为止。也就是在一个RTT的时间内都需要在等待,而流水线则是在这个RTT中反复发送多个分组,然后之后再等反馈信息。
-
回退N步-GBN
N的含义:因为在RTT中可以反复发送多个分组,但是我需要发完分组后收到分组的正反馈然后继续发送。那么GBN就是在流水线中未确认的分组数不能超过某个最大允许数N。
选择重传-SR
-
-
流量控制
一条TCP连接的时候都会为接收方和发送方配备一个接收缓存,当对方发送的数据过快时,可将收到的数据先暂时放在缓存中,然后上层一点一点读取。这里不与之前说的乱序包裹产生冲突,乱序包裹应该也是放在这个缓存中,等待失序的包裹到来时,一起交付给上层。流量控制就是为了控制这个缓存空间不会被溢出,因为溢出了接下来的包裹就会被丢弃。
假设主机A向主机B发送一个大文件。主机B会对自己的接收缓存设置两个变量:
并用RcvBuffer表示缓存空间的大小。显然可知:
LastByteRcvd - LastByteRead<=RcvBuffer
由此可计算出B的缓存可用可见,用接收窗口rwnd表示:
rwnd = RcvBuffer - [ LastByteRcvd - LastByteRead ],
主机B会将rwnd的值放入它发给主机A的报文段接收窗口字段中,也就是告知主机A我还有多少的空间供你使用。
同理,主机A的缓存空间也有两个变量:LastByteSent和LastByteAcked,也就是最后发送和最后被正确接收的,易得LastByteSent-LastByteAcked也就是发送还为接收的数据,控制LastByteSent-LastByteAcked这个数据的大小要小于rwnd即可。
但实际上还有一个小bug,就是rwnd是需要依附在B向A发送的报文段上的,若B的缓存空间已满,在告知A自己rwnd已经为0之后,不再向A发送任何反馈信息。这时的A就相当于被阻塞了而不能再发送数据。(B将数据收到缓存中就会向A发送ACK了)
解决:TCP规范中要求:当主机B的接收窗口为0时,主机A继续发送只有一个字节数据的报文段。这些报文段将会被接收方确认。最终缓存将开始清空,并且确认报文里将包含一个非0的 rwnd 值。
- 拥塞带来的问题
- 拥塞控制
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。