计算机网络基础|第五节|运输层
运输层
物理层,数据链路层 ,网络层,实现了主机到主机的通信
但实际上,通信的真正实体其实是通信进程
P2P(端到端)运输层的协议
两种主要协议
面向链接的TCP
无连接的UDP
AP(Applications Process)
由于因特网上会出现不同的操作系统,不同格式的进程标识符,就需要统一的方法对TCP/IP体系的应用进程进行标识
端口(Port)
实际上是区分应用进程的标识符(PID),并不是物理端口
端口号(PID)
TCP/IP体系的运输层使用端口号来区分应用层不同的应用进程
- 16比特表示,取值为0~65535
- 熟知端口号:0~1023,IANA把这些端口号指派给TCP/IP体系中最重要的一些应用协议,例如:FTP使用21/20,HTTP适用80,DNS使用53.
- 登记端口号:1024~49151,为没有熟知的端口号使用,这类端口号必须在IANA按照规定的手续登记,以防止重复,例如:Microsft RDP微软远程桌面使用的端口3389。
- 短暂端口号:49152~65535,留给客户进程选择暂时使用。当服务器进程收到客户进程的报文是,就知道客户进程所使用的动态端口号。通信结束后,这个端口号可供其他客户进程以后使用。
复用与分用
发送方的复用
应用报文根据所选的端口号判断是UDP协议进行复用封装成用户数据报。
同上,应用报文根据所选的端口号判断是TCP进行复用封装成报文段。
无论是数据报,还是报文段,都封装为IP数据报(协议字段不同),这个操作称为IP复用
最后发送给接收方
接收方的分用
接收方对IP数据报进行解析,根据协议字段,分别解析出用户数据报和报文段,分别进行UDP分用,TCP分用,再通过端口号解析为应用报文传递给上层
常见的熟知端口号
UDP与TCP
用户数据报协议UDP(User Datagram Protocol)
可以随时发送数据
无连接,支持一对一,一对多,一对全的通信
面向应用报文
提供的也是无连接不可靠的服务,仅仅丢弃数据报,不做任何处理,尽最大努力交付,无流量控制和拥塞控制
UDP数据报首部格式
传输控制协议TCP (Transmission Control Protocol)
- 必须使用三报文握手建立连接
- 必须使用四报文挥手释放连接
- 面向连接,仅支持单播:一对一的通信
- 面向字节流(数据流),不保证大小关系对应,实现可靠传输的基础
- 提供的事面向连接的可靠传输服务,使用流量控制和拥塞控制
- TCP数据报格式
TCP的流量控制
为了保证可靠的传输,控制数据的发送速率的操作被称为流量控制
关键点:
seq是数据的序号字段
原理实际上跟SR有点相似
- 也有发送窗口和接受窗口,但是也有GBN的累计确认。
- 流量控制的是接受窗口,接受窗口控制的接受多少数据,而接受窗口的大小呢由接受端的数据缓存大小决定
- 发送窗口随着接受窗口进行变化,接受方告诉发送方接受窗口的大小,发送方保持发送窗口大小跟接受窗口一致
例子如下:
出现死锁的局面:
通俗的语言来解释TCP的流量控制(小作文了属于是)
- A有16个神奇的苹果(报文段),B的袋子可以放4个苹果(这苹果的神奇之处在于,递出去之后,必须要知道是否接受到才会彻底消失,
薛定谔的苹果) - A问B一次袋子能装多少个,B回答4个(接收窗口)
- A就连续的给B递4个苹果,并给每个苹果标上序号,可是当B放第3个时,把袋子撑破了,第3苹果掉在了地上烂了(报文段丢失)
- B就先拿回家处理了一下(流量控制),把接受到的苹果们放入了冰箱,发现袋子只能放3个苹果了,这时,B告诉A,现在我的袋子只能装3个了,并且第三个苹果烂了且只拿到前两个苹果
- B表示知道了,与此同时A这里的确认收到的苹果彻底消失了,从之前没消失的第三个(就是内个摔烂的没有确认收到的苹果)开始连续的给B递3个苹果,此时序号也从第三个开始,递完之后,A等待B告诉是否收到的信息(设定计时器)
- B接过了所有的苹果,但是,突然大脑呆滞阿巴阿巴,忘记给A回复了,也忘记回家放苹果了(累计确认报文丢失)
- A只好再次把之前的苹果们 的第一个苹果传递给B(超时重传),再次进行等待
- B收到了重新传送的苹果,发现重复了序号3的苹果,就把重复的丢弃给A回复了,这个时候发现拿三个回家,原本应该拿四个的才对,却想越气,决定再空出一只手再拿一个苹果。这时,B回复A都收到了这三个苹果,并且表示我还能再拿一个(流量控制)
- A知道后表示ok,之前的三个消失了,从最新的一个开始拿,想起来B说还能再拿一个,就只递给B一个,然后等待B回复是否收到,B表示额外的一个已经把B塞满了,就先别递了(再次流量控制),A说OK,等你回复什么时候再给你递
- B回家放苹果,当把袋子里的都放好,想起来手上还有一个苹果,也放好了,干脆再重新拿个袋子,顺带告诉A我又可以拿3个苹果了(发现之前袋子实际上装不了4个,最多装三个)
- A啊等啊等,迟迟等不到B给他回信息,为啥呢,发现了 B回的信息A没看见(不要在意为啥看不见,问就是ACK丢失),因为A不知道B到底能不能拿,就再也没有递苹果。(但是在心里暗自决定再等10s)(持续计时器)
- B呢以为A知道了可以递苹果了,在傻傻的等(无计时器),实际上却是:两边都在等,出现了死锁局面
- 这样的局面怎么打破呢?A还是等不下去了(计时器超时),向B打了一个问号 “?” (一个字节的0窗口探测报文)
- B说啊,我现在可以拿3个,A表示OK,就开始向B就递苹果了,就打破了这个死锁的情况
- 假设:B这会打LOL,回复还是拿不了,A只能行叭,只好等一段时间就向B 打个问号 “?” 。(每收到一个0,就启动持续计时器)
- 再假设:这个B玩的亚索0-17,队友也一直再给他打问号,B没发现A给打的问号,也就没有给A回复,那么这种情况,A的也是有忍耐时间的(0窗口探测报文的计数器),见到B还没有回复,就再打个问号(超时重传)
注意,以上情况没有考虑出现拥塞控制
小结
TCP的拥塞控制
四个字,供不应需
需求远远超过已有资源就会出现拥塞,若不进行控制,整个网络的吞吐量将随着输入的负荷增大而下降,降为0就进入死锁状态
需要进行需求控制,也被称为拥塞控制,是相当有必要的
四种拥塞算法
发送方维护一个拥塞窗口cwnd,取决于网络的拥塞程度,并且动态变化
拥塞判断的依据是,发生超时重传
发送方将拥塞窗口作为发送窗口swnd,
发送方维护这一个慢开始门限ssthresh状态变量:
- 当cwnd<ss thresh,使用慢开始算法
- 当cwnd>ss thresh,停止使用慢开始算法该用拥塞避免算法
- 当cwnd=ss thresh ,即可以使用慢开始算法,也可以使用拥塞避免算法
(TCP Tahoe)
慢开始(slow-start)
- 拥塞窗口值按照指数规律增长,按传播轮次
拥塞避免(congestion avoidance)
- 拥塞窗口值线性+1,按传播轮次
- 假设出现拥塞,将ss thresh值更新为发生拥塞时cwnd的一半,cwnd回到默认值,重新开始执行按开始算法
当出现超时重传时不一定会出现拥塞,如果直接判断产生拥塞,cwnd就会回到默认值重新开始,那么会极大的降低传输效率,为了提升TCP性能,引入了新的拥塞控制算法(TCP Reno)
- 快重传(fast retransmit)
- 使发送方尽快进行重传,而不是等超时重传计时器超时再重传,目的是为了让发送方尽早知道发生了个别报文端的丢失
- 出现丢失,发送方依旧不管不顾的继续发送,在超时重传计时器还没超时之前,每接收到一个报文,接收方会提醒一次我接受到了一个,但是之前的丢的还没给我补发(重复的确认我只收到了丢失的前一个),发送方连续收到三次重复确认丢失之前一个的报文的确认,立刻重传丢失的报文,这就是快重传
- 快恢复(fast recovery)
- 发送方一但收到三个重复确认,就知道只是丢失了个别的报文段,并不是出现拥塞,转而执行快恢复算法
- 发送方讲慢开始文献ssthresh 和拥塞窗口cwnd调整为当前窗口的一半,开始执行拥塞避免算法
- 也有的块恢复实现是把快恢复开始时的拥塞窗口cwnd再增大一些 至少加3
- 既然发送方收到了三个重复的确认,说明有三个数据报文段已经离开了网络,不需要消耗资源,就可以增加拥塞窗口,尽可能扩大TCP的传输速率
四者结合动态实现拥塞控制
超时重传时间的选择
这是TCP最复杂的问题之一
太小不行,会出现重复
太大,会浪费传输效率
而且,RTT ,数据传输时间是受各层影响的
所以不能直接使用某次测量的RTT样本超时重传时间RTO
利用每次测量的到的RTT样本,计算加权平均往返时间RTT,计算一个平滑的往返时间
在上式中$0\leq a < 1$:
- 若很接近0,则新的$RTT$样本$RTT_S$影响不大;
- 若很接近1,则新的$RTT$样本$RTT_S$影响较大;
建议标准的RFC6298推荐的值为1/8,即0.125.
- 该方法得到的值就比测量的RTT的值更加平滑,因此超时重传的时间就应该略大于$RTT_S$
即使如此,对于RTT样本的测量一样会出现很多问题
出现以上情况会出现计算差错
对此,karn提出一个算法:出现报文段重传,就不采用这段往返时间RTT作为样本
但是这有引起了新的问题:实际传播的时延突然增大,并且不再减小,就会一直出现超时重传的情况,那么RTO就无法再进行更新,就会一直重复发送数据报文段
进而对Karn算法进行修正,加入报文段每重传一次,就把超时重传时间RTO增大一些。典型的做法是:将RTO的值取为旧值得两倍。
举例说明
TCP可靠传输的实现
- 字节为单位的滑动窗口来实现可靠传输
- 发送窗口,出现拥塞采用拥塞窗口cwnd,未出现拥塞采用接受窗口值rwnd
TCP的运输连接管理
TCP的三次握手
- 建立TCP连接
TCP客户端和TCP服务端都处于CLOSED状态
TCP服务端需要建立一个传输控制块
TCP连接表
指向发送和接受缓存的指针
当前发送和接收的序号
……
建立好之后准备接受TCP客户的连接请求进而变更状态为监听LISTEN状态(该状态也称为被动打开连接状态)
TCP客户进程同样首先创建传输控制块
- TCP连接表
- 指向发送和接受缓存的指针
- 当前发送和接收的序号
- …….
- 发送TCP连接请求并更行状态为SYN-SENT(同步已发送)(主动打开连接)发送第一个报文(该报文用来请求TCP服务器的SYN-RCVD同步状态
- 来简单的说明这个报文:发送的是一个带着SYN标志的报文,该值为1,说明这个报文是专门用来请求连接建立的,seq=x,这个X其实可以为任意值,由客户决定,表示的是序号
TCP服务器收到了第一条请求报文,为了表示请求成功,状态变为SYN_RCVD,回应了第二条新的报文 :标志ack=x+1(序号+1,表示上一个序号为x的报文确认收到了,请求序号为x+1的报文段)SYN=1 同时也表示这是用来建立连接的报文,不能携带数据,ACK=1标志表示这是一个确认的报文 seq=y(同理第一条报文段,这个值可以由服务端自由设定)
当TCP客户端收到第二条报文,根据报文表示没问题,变为连接建立状态,并且发送第三条报文端,:该报问段是普通报文段 没有SYN标志,ACK=1表明这个是用来确认服务器的状态,seq=x+1,是该报文段的序号,ack=y+1则表示请求下一条服务器报文段的序号
TCP服务器收到第三条报文,明白了TCP客户端是想建立连接,将自己状态改为连接已建立状态。
其中建立连接用了三个报文,因此被成为三报文握手
假说不进行确认同步状态,直接双方一起建立连接,去掉第三个报文,改为两报文建立连接,就会出现第一报文丢失重发导致已关闭的TCP服务器误开,浪费网络资源。
TCP的四次挥手
释放TCP连接的过程
小可:我要走了
过了一会
小符:你确定你要走吗
小符,思考了一会,再次问:
你真的确定你要走了么?
(这时可以扔一些小石头之类的(数据)给小可)
过了一会
小可:嗯
小符听到后,小声嘟囔着一句
笨蛋
,然后头也不回的走了
小可有点怅然若失。
等了一会,最后也走了
如图所示:
急着下班的小可(如果少了最后一次握手)
小可最后回复时,一阵大风,
小符没有听到小可最后的回复
而且小可直接溜了,
小符一直等啊等,等小可的最后回答
每等一段就问,
你真的要回去了吗?
……
可是小可早就run了。
即使听到了什么也不再理踩。
如图所示
不想活的小可
小可突然不想活了,吊在大本钟下
小符等了一会,发现小可没有再说话
就每等一会问一次
你特么还活着么?
连续问了十次
小符知道了,
小可估计是寄了
小符走了。
才,才不是因为想偷懒呢
TCP报文段的首部格式
面向字节流
发送缓存中一部分或者全部字节给其添加一个首部成为TCP报文段然后发送
一个TCP报文段由首部和数据载荷两部分构成
TCP的全部功能都体现在它首部中个字段的作用
源端口,目的端口
- 源端口:顾名思义,进程端口,协议进程的端口,主机自己设定的暂时端口等
序号
- 取值 32比特,循环的,当最后一个值加一,回到0重新继续加,数据载荷的第一个字节的序号
确认号(ack),标志位(ACK/URG/PSH/RST/SYN/FIN)
- 确认号取值 32比特,循环的,当最后一个值加一,回到0重新继续加,搭配确认标志号ACK取值为1时才生效
- TCP协议其实有规定,在建立连接后,ACK都必须为1
- ack表示请求序号的下一个,并且表明我已经收到了之前的全部数据
- 标志位:(URG/ACK/PSH/RST/SYN/FIN):表明报文段类型
- RST复位,表示TCP连接异常,必须释放连接,再重新建立,也可以拒绝非法报文段,禁止打开一个TCP连接
- PSH推送,尽快上交,不用等待发送缓存
- URG紧急标志,与紧急指针搭配,1生效,0无效
数据偏移,保留,窗口
- 数据偏移:占4个比特,表示数据载荷部分起始处距离报文段起始的值,以四个字节为单位
- 保留:占6比特,默认为0;
- 窗口:占16比特,就是发送本报文段的一方的接收窗口
校验和,紧急指针
- 检验和:一种检错的数据
- 紧急指针与标志位搭配,指明出紧急的数据载荷部分长度,VIP指针,也不用排队上交给应用层
选项(可选)
- 最大报文段长度MSS选项:TCP报文段数据载荷部分的最大长度
- 窗口扩大选项:扩大窗口,提高吞吐
- 时间戳选项
- 计算往返时间RTT
- 处理序号超过范围的情况,防止序号绕回
- 选择确认选项:选择确认功能
填充
- 用来保证首部能被4整除
总结
这部分还挺简单的,接下来就即将到达计网最高层了捏