再不怕被问TCP/IP/UDP


TCP/IP 五层模型

先看图,为网络模型分层,以及一个完整http请求在五层模型中的完整工作流程

aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcyNjIyMzMxNjk3MC1hLnBuZy1pdHpoYWk.png

  • 应用层:最高层,提供特定于应用程序的协议,运行在该层的协议有HTTP、FTP、SSH、WebScoket等
  • 传输层:为两个主机进程通信提供通用的数据传输协议,如TCP、UDP
  • 网络层:负责寻址和路由功能,将数据包发送到特定的计算机,主要协议是IP协议,路由器就是在这一层
  • 链路层:负责将二进制数据包和网络信号相互转换,交换机、网卡就是在这一层
  • 物理层:主要有接收器、发送器、中继器、光纤电缆等

网络协议通过分层来明确每一层的工作职责,通过定义明确的接口来协同工作,第一层都可以使用下面各层的功能,而不用担心各层是怎么实现的。就好像我们开发封装组件一样,每一个组件各自负责各自的事,互不干扰,也提高了复用度,如上图文件基本传输过程,也就是http分层工作流程

  • 主机A发起请求,数据发送前会被分为许多片段,称为数据包,然后使用http协议将数据包封装,并加上请求头,传给下一层

  • 传输层拿到数据,为每个数据包分配一个端口号,用来确定目标计算机的哪一个应用程序,然后使用TCP协议进行处理,加上TCP头UDP头,通过TCP协议传给下一层

  • 网络层拿到数据后为每个数据包添加目标计算机的IP地址,并决定传给什么路由或接收的主机,再封装传给下一层

  • 链路层将数据转译成电子信号,进一步封装成数据帧,传给物理层

  • 物理层通过电缆传送给主机B这边的链路层

  • 主机B的链路层拿到数据后,检查每个包中的目标地址并确定将其发送到哪里,如果不是发给自己的就丢弃,然后根据数据确定协议类型,再传给网络层的IP协议模块

  • 网络层接收到后拆开获取IP头,判断首部接收的IP地址匹配,然后根据头部协议类型,转发TCP或UDP等

  • 传输层TCP收到后会计算校验,判断数据的完整性,然后处理数据包顺序接收的逻辑,最后根据端口确定要转发给应用层的哪个程序

  • 最终应用层接到数据之后,根据http协议解析数据

这里只展开一下网络层的 IP 和传输层的 TCP 、UDP

IP

如果是在局域网内都是用MAC地址通信,局域网之外,就得用IP了。MAC就像是身份证,IP就像是住址。所有TCP、UDP、ICMP等数据都是以IP数据报格式进行传输

IP协议本身不支持发往目的地址失败的IP数据包,也没有提供直接的方式获取诊断信息,比如发送途中经过哪些路由器,以及往返时间,而这就由ICMP协议来专门负责

ICMP并不为IP网络提供可靠性,只用于反馈各种故障和配置信息,丢包不会触发ICMP

我们常用的ping就是用ICMP查询报文。不过ping使用ICMP协议会直接跳过了传输层,所以ping程序是没有端口号的

IP协议的特点是:

  • IP协议是不可靠的传输协议。如果 ICMP协议出现传输异常,IP都会丢弃数据包并可能会响应一个ICMP差错消息给发送端,而任何要求可靠性必须由上层如TCP协议来提供

  • IP协议是无连接的。就是不维护任何关于后续数据的状态信息,每个数据独立。表现在:可以不按发送顺序接收,不用维护连接状态,免去了维护复制的链路状态信息(TCP会讲到)

UDP

UDP的特点

  • 无连接不需要握手和挥手就可以直接发送数据。

  • 不可靠性:就是一个传递数据的搬运工,来一个包就发一个。不会备份,也不关心对方是否正确收到,传输顺序也无法保证。所以就只能由应用层来保证可靠,因为网络层也是不可靠的

    • 在发送端应用层将数据传给传输层的UDP,它只加一个UDP头标识(UDP协议),就直接发给网络层了。
    • 接收端在网络层将数据发给传输层,传输层UDP只去掉IP报文头就传给应用层了。
    • 其他什么都不会管,不过这也减少开销和发送数据之前的延迟
  • 支持广播:有单播,多播,广播的功能,不只支持一对一传输方式,还支持一对多,多对多的方式

  • 首部开销小:8个字节(源端口号(非必填)、目的端口号UDP长度(数据报的整个长度)、UDP检查和(检测UDP数据报是否有错或者目的端口找不到对应的进程,各2字节),因为它要求不高而且实现的功能没有那么多,所以首部字段不多,而TCP有20个字节。它的数据是可以为0的,所以它最少可以是8个字节

  • 是面向报文的:适合一次性传输少量数据,因为应用层给UDP多长的报文都会照样发送,即一次发送一个完整的报文,即不合并也不拆分。如果报文太长的话,UDP完整的装进来交给网络层的话,网络层就要分片了,因为传给链路层的话它有一个MTU的要求,所以网络层就要分片,这会给网络层的效率造成影响

  • 无拥塞控制:适合实时应用,因为它会一直以恒定的速度发送数据,即使网络条件不好,也不会对发送速率进行调整。这就导致在网络不好的情况下就有可能丢包,但优点也明显,在某些实时性要求高的场景比如说聊天、在线视频、网络语音电话等使用UDP而不是TCP,比如打微信电话出现偶尔断续不是太大问题。当然拥塞太严重也有一些补救措施比如向前纠错或者重传

UDP 为什么不可靠

  • 传输数据之前不需要先建立连接
  • 不保证消息交付,远程主机的传输层在接收到UDP报文后,不需要确认
  • 不保证将会顺序,不设置包序号、不重排、不会发生队首阻塞
  • 不进行拥塞控制,没有内置反馈机制,不重传、无超时

TCP

这是我们平时用的最多的协议,特别是前后端
TCP给应用程序提供了一种与UDP完全不同的服务
TCP是面向连接的可靠的服务,面向连接指TCP的两个应用程序必须在它们可交换数据之前,通过相互联系来建立一个TCP连接
TCP提供了一种字节流抽象概念给应用程序:不会自动插入记录标志或者消息边界,如发送端分别发10字节和30字节,接收端可能会以两个20字节的方式读入

TCP的特点

  • 是面向连接的,通信之前双方必须要先建立连接

  • 只支持单播,就是点对点的传输,一条TCP连接只能有两个端点

  • 提供可靠交付的服务,有完整性校验、数据不会丢失,会丢包重传、且会按顺序到达

  • 是面向字节流的。不像UDP那样一个个报文独立传输,而是在不保留报文边界的情况下以字节流方式进行传输

  • 提供拥塞控制,当网络出现拥塞的情况,有流量控制,能够减少传输数据的速度和数量,缓解拥塞,保证稳定

  • 提供全双工通信和可靠通信,指的是发送方和接收方可以同时发送数据也可以同时接收数据。因为两边都会设置有发送缓存接收缓存

    • 发送缓存就是发送缓存的队列里面有准备发送的数据和已经发送但是还没有收到来自接收方确认的数据,如果没有收到确认还要重发所以不能扔掉,将可能会被重传,因为TCP需要保证可靠传输
    • 接收缓存就是按序到达但是还没有被接收应用程序读取的数据和没按序到达的数据,需要顺序排好了,接收方才能逐一接收数据

为什么说 TCP 是可靠的

因为接收方收到数据后会发送一个ACK确认应答消息,这样发送方就知道自己的数据被对方接收了,如果一直没有收到ACK一定时间后就会重发。因此就算数据没有发到接收方,或者接收方的ACK数据包丢失也有重传机制,确保双方最终可以通过重传也能正确收到消息

重传机制

由于TCP的下层网络层可能出现丢失、重复或乱序的情况,TCP协议需要提供可靠数据传输服务。

为保数据传输的正确性,就是在发送一个数据包之后,就会开启一个定时器,若在一定时间内没有收到发送数据的ACK确认报文,就会对该报文进行重传,在达到一定次数还没有成功时放弃并发送一个复位信号

拥塞控制机制

主要体现在四个方面

  • 一是慢启动,开始的时候不要发送大量数据,先测试一下网络,然后慢慢由小到大的增加拥塞窗口大小
  • 二是拥塞避免,一旦判断网络出现拥塞,就将传送设置成出现拥塞时一半的大小,并把拥塞窗口设为1,再重新开始慢启动算法
  • 三是快速重传,就是接收方在收到一个失序的报文后立即发出重复确认,快重传算法规定发送方只要连续收到三个重复确认就立即重传对方尚未收到的报文段,而不用继续等重传计时器到期
  • 四是快速恢复,考虑到如果网络出现拥塞的话,就不至于能连续收到好几个重复的确认,所以发送方会认为网络可能没有出现拥塞,这样就不执行慢开始算法,而是执行拥塞避免算法

流量控制

就是为了让发送方发送数据的速度不要太快,要让接收方来得及接收。

在接收方缓存中已接受的数据处理不过来时,减小发送方的窗口大小,让接收方有足够的时间来接收数据包。或是接收方比较空闲时,发送方调大窗口大小,以加快传输,合理利用网络资源

TCP 三次握手

  1. 第一次握手:客户端向服务器发送(SYN,seq)
    • 一个SYN报文
    • 一个客户端初始化随机序列号(seq)
  2. 第二次握手:服务器收到请求后向客户端发送(SYN,ACK,seq,ack)
    • 自己的SYN报文ACK报文
    • 一个服务端的初始化随机序列号seq
    • 一个确认号ack=客户端发来的序列号+1,表示自己收到了
  3. 第三次握手:客户端收到服务器的确认应答后,向服务端发送(ACK,seq,ack)
    • 确认应答ACK报文
    • 一个seq,值为第二次握手客户端发过来的ack的值
    • 一个确认号ack,值为服务端的序列号+1,告诉服务端我收到了

为什么不能两次?

  1. 确认双方发送接收的能力是不是正常
  2. 相互确认初始化序列号,并告诉对方什么样序号的报文能被接收,只用两次的话,服务器就不知道自己的序列号有没有被对方确认,可能造成失效的报文段被服务器接收,造成错误情况。

为什么不能四次?

因为没必要,该确认的三次都确认完了

TCP 四次挥手

关闭TCP连接的挥手,客户端和服务端都可以发起关闭操作,以客户端发起为例

  1. 浏览器先发送FIN报文、Seq=初始化序列号给服务器,并停止发送数据,但仍可以接受服务端响应的数据
  2. 服务器收到后,发送ACK=浏览器序列号+1给浏览器,表明收到
  3. 服务器数据都发完了,给浏览器发送FIN报文、Seq=序列号给浏览器
  4. 浏览器收到后,发送ACK=服务器序列号+1给服务器,表明收到

第四次挥手结束后后需要过一阵(时间等待计时器设置的时间2MSL后)以确保服务器收到自己的ack报文才会进入关闭状态,服务器收到ack报文之后,也关闭连接

为什么要等一段时间再关闭,不等不行吗?

这是为了防止发送给服务器的确认报文段丢失或者出错,从而导致服务端不能正常关闭。等待时间是2MSL,这也是报文在网络上的最大生存时间,超过这个时间就会被丢弃

RFC793中规定MSL为2分钟,但实际应用中常用的是30秒,1分钟和2分钟都有,如果超过这个时间,那么主动关闭者就会发送一个RST状态位的包,表示重置连接,这时候被关闭者就知道对方已经关闭了连接

如果主动关闭者不进行等待,由于端口复用的原因,主动关闭可能已经开启了另一个连接,这时候被关闭者还在重试发起FIN请求,导致主动关闭者收到很多没用的包。因为包是有序列号的,所以可以判断到不是本次连接该接收的包,就不会管。为此需要让主动关闭者等待,确保被关闭者不会再发送FIN请求了再进行端口复用

为什么要四次挥手呢

TCP使用四次挥手是因为TCP的连接是全双工的,所以需要双方分别释放对方的连接,单独的一方的连接释放,只能代表不能再向对方发送数据,可以接收数据

所以在关闭连接时,服务端收到FIN报文时,很可能并不会立即关闭socket,因为它可能还有报文没有发送完,所以只能回复一个ack报文,告诉客户端“你发的报文我收到了”,等我所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送,所以需要四次挥手

TCP粘包及处理

粘包是指为了防止数据量过小导致大量传输,全将多个TCP段合并成一个发送。就是将若干个包数据粘成一个包,从接收缓冲区看,后一包数据的头紧接送前一包数据的尾。因为TCP层传输是流式传输,流,最大的问题是没有边界,没有边界就会造成数据粘在一起

拆包就是将任务拆分处理了,降低出错率

造成粘包的场景

  • 接收方不及时接收缓冲区的包,造成多个包接收
  • 因为TCP默认使用Nagle算法,这个算法本身也可能会导致粘包问题
  • 由于TCP的复用造成粘包。由于TCP连接的复用性,建立一条连接可以供一台主机上的多个进程使用,那么多种不同结构的数据到TCP的流式传输里,边界分割肯定会出现奇葩的问题
  • 数据包过大造成的粘包问题,比如应用进程缓冲区的一条消息内容大小超过了发送缓存区的大小,就可能产生粘包,因为消息已经被分割了,前一部分已经被接收了,但另一部分可能刚放入缓存冲区准备发送,这样就会导致后一部分粘包
  • 流量控制,拥塞控制也可能导致粘包
1
2
3
4
5
Nagle算法,主要做两件事:
一是只有上一个分组得到确认,才会发送下一个分组;
二是收集多个小分组,数据包大小达到最大段大小(MSS),在一个确认到来时一起发送。

多个分组拼成一个数据段发出去,如果没有处理好边界问题,在解包的时候就会发生粘包

粘包怎么处理

  • 如果是Nagle算法导致的,结合应用场景适当关闭算法就可以了
  • 如果不是
    • 尾部标记序列。通过特殊标识符表示数据包的边界,比如\n\r\t或一些隐藏字符
    • 头部标记分步接收。在TCP报文的头部加上表示数据长度。使用带消息头的协议,消息头存储开始标识及消息长度信息,服务商获取消息头的时候解析出消息长度,然后向后读取该长度的内容
    • 应用层发送数据时定长发送,服务端读取既定长度的内容作为一条完整消息,如果不够长,就在空位上补固定字符

UDP为什么不会粘包

  1. 因为UDP是面向消息的协议,UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据
  2. UDP具有保护消息边界,在每个UDP包中有消息头(消息来源地址,端口信息等),这样对于接收端来说容易进行分区处理。传输协议把数据当作一条独立的消息在网上传输,接收方只能接收独立的消息,如果消息内容过大,超过接收方一次所能接受的大小,就会丢失一部分数据,因为就算是丢失,它也不会分两次去接收

TCP和UDP的区别和适用场景

  • TCP传送速度慢;UDP速度快
  • TCP协议可靠,有拥塞控制和流量控制;UDP协议不可靠,也没有拥塞控制和流量控制等
  • TCP协议是面向连接,而且需要3次握手;UDP协议采用无连接,不需要握手
  • TCP只能一对一连接;UDP支持广播,一对一,一对多,多对多都可以
  • TCP头部大小最小20个字节;UDP最小8字节
  • TCP在传输上是面向字节流的;而UDP是面向报文的
  • TCP协议在传送数据段的时候要给段标号;UDP协议不用

另外,TCP和UDP的端口号是相互独立的,所以是可以相同的

适用场景

TCP适合传输大量数据,以及要求可靠性传输的场景(要对数据确认、重发、排序等),比如登录,传文件等
UDP适合传输少量数据,以及要求效率高的场景,比如实时应用,即时通讯,聊天视频通话等


文章作者: 沐华
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 沐华 !
 上一篇
一文搞定HTTP和HTTPS 一文搞定HTTP和HTTPS
http 是我们几乎天天都要打交道的东西,相关知识点有点多,所以也有不少面试必问的点,这里做了一些整理
下一篇 
输入URL到页面显示的前端体系知识 输入URL到页面显示的前端体系知识
一道不同回答能体现不同薪资水平的题。本文不讲过多概念,只以面试者角度回答,内容涉及浏览器原理,操作系统,计算机网络,Web等一系列知识