一、TCP/IP五层协议
TCP/IP 五层协议和 OSI 的七层协议对应关系如下:
- 应⽤层 (application layer):直接为应⽤进程提供服务。应⽤层协议定义的是应⽤进程间通讯和交互的规则,不同的应⽤有着不同的应⽤层协议,如 HTTP协议(万维⽹服务)、FTP协议(⽂件传输)、SMTP协议(电⼦邮件)、DNS(域名查询)等。
- 传输层 (transport layer):有时也译为运输层,它负责为两台主机中的进程提供通信服务。该层主要有以下两种协议:
传输控制协议
(Transmission Control Protocol,TCP
):提供⾯向连接的、可靠的数据传输服务,数据传输的基本单位是报⽂段(segment)⽤户数据报协议
(User Datagram Protocol,UDP
):提供⽆连接的、尽最⼤努⼒的数据传输服务,但不保证数据传输的可靠性,数据传输的基本单位是⽤户数据报。 - ⽹络层 (internet layer):有时也译为⽹际层,它负责为两台主机提供通信服务,并通过选择合适的路由将数据传递到⽬标主机。
- 数据链路层 (data link layer):负责将⽹络层交下来的 IP 数据报封装成帧,并在链路的两个相邻节点间传送帧,每⼀帧都包含数据和必要的控制信息(如同步信息、地址信息、差错控制等)。
- 物理层 (physical Layer):确保数据可以在各种物理媒介上进⾏传输,为数据的传输提供可靠的环境。
从上图中可以看出, TCP/IP
模型⽐ OSI
模型更加简洁,它把 应⽤层/表示层/会话层
全部整合为了 应⽤层
。
在每⼀层都⼯作着不同的设备,⽐如我们常⽤的交换机就⼯作在数据链路层的,⼀般的路由器是⼯作在⽹络层的。
在每⼀层实现的协议也各不同,即每⼀层的服务也不同,下图列出了每层主要的传输协议:
同样, TCP/IP
五层协议的通信⽅式也是对等通信:
二、TCP和UDP
TCP
和 UDP
都是传输层协议,他们都属于TCP/IP
协议族:
🤔 UDP
UDP的全称是⽤户数据报协议,在⽹络中它与TCP协议⼀样⽤于处理数据包,是⼀种⽆连接的协议。在OSI模型中,在传输层,处于IP协议的上⼀层。UDP有 不提供数据包分组、组装和不能对数据包进⾏排序的缺点
,也就是说,当报⽂发送之后,是⽆法得知其是否安全完整到达的。
它的特点如下:
- 面向无连接
- 有单播、多播、广播的功能
- 面向报文
- 不可靠性
- 头部开销⼩,传输数据报⽂⾼效。
🧐 TCP
- 面向连接
- 仅支持单播传输
- 面向字节流
- 可靠传输
- 提供拥塞控制
- 提供全双工通信
😜 TCP和UDP的区别
🫣 TCP和UDP的使用场景
- TCP应用场景 :效率要求相对低,但对准确性要求相对⾼的场景。因为传输中需要对数据确认、重发、排序等操作,相⽐之下效率没有UDP⾼。例如:⽂件传输(准确⾼要求⾼、但是速度可以相对慢)、接受邮件、远程登录。
- UDP应⽤场景:效率要求相对⾼,对准确性要求相对低的场景。例如:QQ聊天、在线视频、⽹络语⾳电话(即时通讯,速度要求⾼,但是出现偶尔断续不是太⼤问题,并且此处完全不可以使⽤重发机制)、⼴播通信(⼴播、多播)。
🤓 UDP协议为什么不可靠?
UDP在传输数据之前不需要先建⽴连接,远地主机的运输层在接收到UDP报⽂后,不需要确认,提供不可靠交付。总结就以下四点:
- 不保证消息交付:不确认,不重传,⽆超时
- 不保证交付顺序:不设置包序号,不重排,不会发⽣队⾸阻塞
- 不跟踪连接状态:不必建⽴连接或重启状态机
- 不进⾏拥塞控制:不内置客户端或⽹络反馈机制
😌 TCP协议为什么可靠?
TCP 的可靠传输机制是基于连续 ARQ 协议
和滑动窗⼝协议
的。
TCP 协议在发送⽅维持了⼀个发送窗⼝,发送窗⼝以前的报⽂段是已经发送并确认了的报⽂段
,发送窗⼝中包含了已经发送但未确认的报⽂段
和允许发送但还未发送的报⽂段
,发送窗⼝以后的报⽂段是缓存中还不允许发送的报⽂段
。当发送⽅向接收⽅发 送报⽂时,会依次发送窗⼝内的所有报⽂段,并且设置⼀个定时器,这个定时器可以理解为是最早发送但未收到确认的报⽂段
。 如果在定时器的时间内收到某⼀个报⽂段的确认回答,则滑动窗⼝,将窗⼝的⾸部向后滑动到确认报⽂段的后⼀个位置,此时如 果还有已发送但 没有确认的报⽂段
,则重新设置定时器,如果没有了则关闭定时器。如果定时器超时,则重新发送所有已经发送 但还未收到确认的报⽂段
,并将超时的间隔设置为以前的两倍
。当发送⽅收到接收⽅的三个冗余的 确认应答
后,这是⼀种指示, 说明该报⽂段以后的报⽂段很有可能发⽣丢失了,那么发送⽅会启⽤快速重传
的机制,就是当前定时器结束前,发送所有的已发 送但确认的报⽂段
。接收⽅使⽤的是累计确认
的机制,对于所有按序到达的报⽂段,接收⽅返回⼀个报⽂段的肯定回答。如果收到了⼀个乱序的报⽂ 段,那么接⽅会直接丢弃,并返回⼀个最近的按序到达的报⽂段的肯定回答。使⽤累计确认保证了返回的确认号之前的报⽂段都 已经按序到达了,所以发送窗⼝可以移动到已确认报⽂段的后⾯。发送窗⼝的⼤⼩是变化的,它是由接收窗⼝剩余⼤⼩
和⽹络中拥塞程度
来决定的,TCP 就是通过控制发送窗⼝的⻓度来控制报⽂段的发送速率
。
但是 TCP 协议并不完全和滑动窗⼝协议相同,因为许多的 TCP 实现会将失序的报⽂段给缓存起来,并且发⽣重传时,只会重传⼀个报⽂段,因此 TCP 协议的可靠传输机制更像是窗⼝滑动协议
和选择重传协议
的⼀个混合体。
😀 TCP的重传机制
由于TCP的下层⽹络(⽹络层)可能出现丢失
、重复
或失序
的情况,TCP协议提供可靠数据传输服务。为保证数据传输的正确性,TCP会重传其认为已丢失(包括报⽂中的⽐特错误)的包。TCP使⽤两套独⽴的机制来完成重传,⼀是基于时间,⼆是基于确认信息。
TCP在发送⼀个数据之后,就开启⼀个定时器,若是在这个时间内没有收到发送数据的ACK确认报⽂,则对该报⽂进⾏重传,在达到⼀定次数还没有成功时放弃并发送⼀个复位信号。
😃 TCP的拥塞机制
TCP的拥塞控制机制主要是以下四种机制:
- 慢启动
- 拥塞避免
- 快速重传
- 快速恢复
(1)慢启动(慢开始)
- 在开始发送的时候设置cwnd = 1(cwnd指的是拥塞窗⼝)
- 思路:开始的时候不要发送⼤量数据,⽽是先测试⼀下⽹络的拥塞程度,由⼩到⼤增加拥塞窗⼝的⼤⼩。
- 为了防⽌cwnd增⻓过⼤引起⽹络拥塞,设置⼀个慢开始⻔限(ssthresh 状态变量)
- 当cnwd < ssthresh,使⽤慢开始算法
- 当cnwd = ssthresh,既可使⽤慢开始算法,也可以使⽤拥塞避免算法
- 当cnwd > ssthresh,使⽤拥塞避免算法
(2)拥塞避免
- 拥塞避免未必能够完全避免拥塞,是说在拥塞避免阶段将拥塞窗⼝控制为按线性增⻓,使⽹络不容易出现阻塞。
- 思路: 让拥塞窗⼝cwnd缓慢的增⼤,即每经过⼀个返回时间RTT就把发送⽅的拥塞控制窗⼝加⼀。
- ⽆论是在慢开始阶段还是在拥塞避免阶段,只要发送⽅判断⽹络出现拥塞,就把慢开始⻔限设置为出现拥塞时的发送窗⼝⼤⼩的⼀半。然后把拥塞窗⼝设置为1,执⾏慢开始算法。如图所示:
其中,判断⽹络出现拥塞的根据就是没有收到确认,虽然没有收到确认可能是其他原因的分组丢失,但是因为⽆法判定,所以都当做拥塞来处理
(3)快速重传
- 快重传要求接收⽅在收到⼀个失序的报⽂段后就⽴即发出重复确认(为的是使发送⽅及早知道有报⽂段没有到达对⽅)。发送⽅只要连续收到三个重复确认就⽴即重传对⽅尚未收到的报⽂段,⽽不必继续等待设置的重传计时器时间到期。
- 由于不需要等待设置的重传计时器到期,能尽早重传未被确认的报⽂段,能提⾼整个⽹络的吞吐量
(4)快速恢复
- 当发送⽅连续收到三个重复确认时,就执⾏“乘法减⼩”算法,把ssthresh⻔限减半。但是接下去并不执⾏慢开始算法。
- 考虑到如果⽹络出现拥塞的话就不会收到好⼏个重复的确认,所以发送⽅现在认为⽹络可能没有出现拥塞。所以此时不执⾏慢开始算法,⽽是将cwnd设置为ssthresh的⼤⼩,然后执⾏拥塞避免算法。
🤩 TCP的流量控制机制
⼀般来说,流量控制就是为了让发送⽅发送数据的速度不要太快,要让接收⽅来得及接收
。TCP采⽤⼤⼩可变的滑动窗⼝进⾏流量控制,窗⼝⼤⼩的单位是字节。这⾥说的窗⼝⼤⼩其实就是每次传输的数据⼤⼩。
- 当⼀个连接建⽴时,连接的每⼀端分配⼀个缓冲区来保存输⼊的数据,并将缓冲区的⼤⼩发送给另⼀端。
- 当数据到达时,接收⽅发送确认,其中包含了⾃⼰剩余的缓冲区⼤⼩。(剩余的缓冲区空间的⼤⼩被称为窗⼝,指出窗⼝⼤⼩的通知称为窗⼝通告 。接收⽅在发送的每⼀确认中都含有⼀个窗⼝通告。)
- 如果接收⽅应⽤程序读数据的速度能够与数据到达的速度⼀样快,接收⽅将在每⼀确认中发送⼀个正的窗⼝通告。
- 如果发送⽅操作的速度快于接收⽅,接收到的数据最终将充满接收⽅的缓冲区,导致接收⽅通告⼀个零窗⼝ 。发送⽅收到⼀个零窗⼝通告时,必须停⽌发送,直到接收⽅重新通告⼀个正的窗⼝。
🤪 TCP的三次握手和四次挥手
(1)三次握手
三次握⼿(Three-way Handshake)其实就是指建⽴⼀个TCP连接时,需要客户端和服务器总共发送3个包。进⾏三次握⼿的主要作⽤就是为了确认双⽅的接收能⼒和发送能⼒是否正常、指定⾃⼰的初始化序列号为后⾯的可靠性传送做准备
。实质上其实就是连接服务器指定端⼝,建⽴TCP连接,并同步连接双⽅的序列号和确认号,交换TCP窗⼝⼤⼩信息。
刚开始客户端处于 Closed 的状态,服务端处于 Listen 状态。
- 第⼀次握⼿:客户端给服务端发⼀个 SYN 报⽂,并指明客户端的初始化序列号 ISN,此时客户端处于 SYN_SEND 状态。 ⾸部的同步位SYN=1,初始序号seq=x,SYN=1的报⽂段不能携带数据,但要消耗掉⼀个序号。
- 第⼆次握⼿:服务器收到客户端的 SYN 报⽂之后,会以⾃⼰的 SYN 报⽂作为应答,并且也是指定了⾃⼰的初始化序列号 ISN。同时会把客户端的ISN + 1 作为ACK 的值,表示⾃⼰已经收到了客户端的 SYN,此时服务器处于 SYN_REVD 的状态。 在确认报⽂段中SYN=1,ACK=1,确认号ack=x+1,初始序号seq=y
- 第三次握⼿:客户端收到 SYN 报⽂之后,会发送⼀个 ACK 报⽂,当然,也是⼀样把服务器的 ISN+1 作为 ACK 的值,表示已经收到了服务端的 SYN 报⽂,此时客户端处于 ESTABLISHED 状态。服务器收到 ACK 报⽂之后,也处于 ESTABLISHED 状态,此时,双⽅已建⽴起了连接。 确认报⽂段ACK=1,确认号ack=y+1,序号seq=x+1(初始为seq=x,第⼆个报⽂段所以要+1),ACK报⽂段可以携带数据,不携带数据则不消耗序号。
TCP 三次握⼿的建⽴连接的过程就是相互确认初始序号的过程,告诉对⽅,什么样序号的报⽂段能够被正确接收。 第三次握⼿的作⽤是客户端对服务器端的初始序号的确认
。如果只使⽤两次握⼿,那么服务器就没有办法知道⾃⼰的序号是否 已被确认。同时这样也是为了防⽌失效的请求报⽂段被服务器接收,⽽出现错误的情况。
(2)四次挥手
刚开始双⽅都处于 ESTABLISHED 状态,假如是客户端先发起关闭请求。四次挥⼿的过程如下:
- 第⼀次挥⼿: 客户端会发送⼀个 FIN 报⽂,报⽂中会指定⼀个序列号。此时客户端处于 FIN_WAIT1状态。 即发出连接释放报⽂段(FIN=1,序号seq=u),并停⽌再发送数据,主动关闭TCP连接,进⼊FIN_WAIT1(终⽌等待1)状态,等待服务端的确认
- 第⼆次挥⼿:服务端收到 FIN 之后,会发送 ACK 报⽂,且把客户端的序列号值 +1 作为 ACK 报⽂的序列号值,表明已经收到客户端的报⽂了,此时服务端处于 CLOSE_WAIT 状态。 即服务端收到连接释放报⽂段后即发出确认报⽂段(ACK=1,确认号ack=u+1,序号seq=v),服务端进⼊CLOSE_WAIT(关闭等待)状态,此时的TCP处于半关闭状态,客户端到服务端的连接释放。客户端收到服务端的确认后,进⼊FIN_WAIT2(终⽌等待2)状态,等待服务端发出的连接释放报⽂段。
- 第三次挥⼿:如果服务端也想断开连接了,和客户端的第⼀次挥⼿⼀样,发给 FIN 报⽂,且指定⼀个序列号。此时服务端处于 LAST_ACK 的状态。 即服务端没有要向客户端发出的数据,服务端发出连接释放报⽂段(FIN=1,ACK=1,序号seq=w,确认号ack=u+1),服务端进⼊LAST_ACK(最后确认)状态,等待客户端的确认。
- 第四次挥⼿:客户端收到 FIN 之后,⼀样发送⼀个 ACK 报⽂作为应答,且把服务端的序列号值 +1作为⾃⼰ ACK 报⽂的序列号值,此时客户端处于 TIME_WAIT 状态。需要过⼀阵⼦以确保服务端收到⾃⼰的 ACK 报⽂之后才会进⼊ CLOSED 状态,服务端收到 ACK 报⽂之后,就处于关闭连接了,处于 CLOSED 状态。 即客户端收到服务端的连接释放报⽂段后,对此发出确认报⽂段(ACK=1,seq=u+1,ack=w+1),客户端进⼊TIME_WAIT(时间等待)状态。此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL后,客户端才进⼊CLOSED状态。
那为什么需要四次挥⼿呢? 因为当服务端收到客户端的SYN连接请求报⽂后,可以直接发送SYN+ACK报⽂。其中ACK报⽂是⽤来应答的,SYN报⽂是⽤来同步的。但是关闭连接时,当服务端收到FIN报⽂时,很可能并不会⽴即关闭SOCKET,所以只能先回复⼀个ACK报⽂,告诉客户端,“你发的FIN报⽂我收到了”。只有等到我服务端所有的报⽂都发送完了,我才能发送FIN报⽂,因此不能⼀起发送,故需要四次挥⼿。
简单来说就是以下四步:
- 第⼀次挥⼿:若客户端认为数据发送完成,则它需要向服务端发送连接释放请求。
- 第⼆次挥⼿:服务端收到连接释放请求后,会告诉应⽤层要释放 TCP 链接。然后会发送 ACK 包,并进⼊ CLOSE_WAIT 状态,此时表明客户端到服务端的连接已经释放,不再接收客户端发的数据了。但是因为 TCP 连接是双向的,所以服务端仍旧可以发送数据给客户端。
- 第三次挥⼿:服务端如果此时还有没发完的数据会继续发送,完毕后会向客户端发送连接释放请求,然后服务端便进⼊ LAST-ACK 状态。
- 第四次挥⼿:客户端收到释放请求后,向服务端发送确认应答,此时客户端进⼊ TIME-WAIT 状态。该状态会持续 2MSL(最⼤段⽣存期,指报⽂段在⽹络中⽣存的时间,超时会被抛弃) 时间,若该时间段内没有服务端的重发请求的话,就进⼊ CLOSED 状态。当服务端收到确认应答后,也便进⼊CLOSED 状态。
TCP 使⽤四次挥⼿的原因是因为 TCP 的连接是全双⼯的,所以需要双⽅分别释放到对⽅的连接,单独⼀⽅的连接释放,只代表不能再向对⽅发送数据,连接处于的是半释放的状态
。
最后⼀次挥⼿中,客户端会等待⼀段时间再关闭的原因,是为了防⽌发送给服务器的确认报⽂段丢失或者出错,从⽽导致服务器 端不能正常关闭
。