应用层
当应用程序间需要进行网络通讯,在 TCP/IP 五层协议的应用层需要做的工作是,程序员 自定义应用层协议 。
举个例子:
当前要开发一个关于“点外卖软件”的一个项目,其中有一个功能为:获取用户的订单历史(后端服务器从数据库中取出数据返回给前端页面),类似像这样的功能,就需要涉及到前后端彼此之间的交互。
前后端交互是基于网络进行交互的,那么在这个交互的过程中就需要约定好:前端发啥样的数据,后端返回对应的数据:
形如上述的工作,就是在设计一个应用层协议。
自定义应用层协议的两个步骤:
- 根据需求,明确要传递的信息 ( 如简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问协议(Telnet)等)
- 明确要传递的数据格式 (数据格式:我们要把这些数据根据什么样的方式进行组织,常见数据格式:
xml
json
protobutter
)
-
xml
格式:
xml 通过这些标签,很好的体现了对应数据的可读性,很明显就可以读懂每个部分表达什么意思。但是也是由于这些标签,需要引入太多的辅助信息。
我们要知道,对于一个服务器程序,最贵的是硬件资源,就是网络带宽。对于 xml 来说,因为要表示这些辅助信息,就导致传输相同数目的请求的时候,占用的带宽是更高的。
-
json
,当下最流行的一种,设计应用层协议的数据格式
json 中通过构建了一个个键值对的方式来进行数据传输。
常见的应用层协议
-
DNS
:进行域名解析 -
FTP
:文件传输协议 -
telnet
:远程终端协议 -
HTTP
:超文本传输协议,网络可靠交换数据的重要基础
http使用面向连接的TCP作为运输层协议,保证了数据的可靠传输。 -
SMTP
:电子邮件传送协议
传输层
在传输层中最著名的两个协议分别是
UDP
和TCP
,传输层负责的是 “端到端” 之间的通信,在发送方发出的数据,接收方有没有准确无误的接收到,是传输层主要关心的工作。
在学习一个协议,最主要的就是研究这个协议是以什么样的报文格式进行数据传送的,以及它有什么样的功能特点
UDP
在传输层封装协议就是给一个完整的应用层数据封装上8个字节的UDP协议格式,这8个字节中的信息用来保证进行客户端和服务器通讯的过程中数据传输的正确和可靠的。
一般在教科书上 UDP 报文格式是如下这样的:
UDP 特点:
面向数据报
数据传输分为面向数据流和面向数据报,UDP协议是面向数据报的,应用层交给UDP多长的报文, UDP原样发送, 既不会拆分, 也不会合并。
无连接
就比如打电话和寄信两种通话方式,UDP相当于寄信,知道目的程序的IP和端口号就直接进行传输, 不需要建立连接。
不可靠
没有任何安全机制,发送端发送数据报以后,如果因为网络故障该段无法发到对方,UDP协议层也不会给应用层返回任何错误信息(发送方也不知道信有没有寄到)
全双工
既可以读,也可以写。也就是发送方和接收方身份可以互相转化。
TCP
TCP全称为 “传输控制协议(Transmission Control Protocol”),人如其名, 要对数据的传输进行一个详细的控制
TCP是一个非常重要的传输层协议,在实际开发中被广泛使用,我们主要需要了解关于TCP可靠传输的机制。
- 源/目的端口号:表示数据是从哪个进程来,到哪个进程去;
- 4位TCP报头长度:表示该TCP头部有多少个32位bit(有多少个4字节);所以TCP头部最大长度是15 * 4 = 60
- 6位标志位:
- URG:紧急指针是否有效
- ACK:确认号是否有效
- PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走
- RST:对方要求重新建立连接;我们把携带RST标识的称为复位报文段
- SYN:请求建立连接;我们把携带SYN标识的称为同步报文段
- FIN:通知对方,本端要关闭了,我们称携带FIN标识的为结束报文段
- 16位校验和:发送端填充,CRC校验。接收端校验不通过,则认为数据有问题。此处的检验和不光包含TCP首部,也包含TCP数据部分。
- 16位紧急指针:标识哪部分数据是紧急数据;
TCP协议的主要机制
TCP对数据传输提供的管控机制,主要体现在两个方面:安全和效率。
这些机制和多线程的设计原则类似:保证数据传输安全的前提下,尽可能的提高传输效率。
1. 确认应答
确认应答机制是保证可靠传输的核心机制,关键就是,接收方收到消息之后,给发送方返回一个应答报文(ACK,acknowledge),表示自己已经收到了。
在网路上,数据接收的顺序不一定和发送的顺序完全一致,会存在后发现至的情况。
为了解决这个问题,就需要对消息进行编号
TCP 将每个字节的数据都进行了编号。即为序列号:
每一个ACK都带有对应的确认序列号,意思是告诉发送者,我已经收到了哪些数据;下一次你从哪里开始发。
2. 超时重传
超时重传相当于对确认应答机制进行的补充,确认应答是网络一切正常的时候,通过 ACK 通知发送方确认收到,但是如果出现了丢包的情况,发送方就收不到这个 ACK 了。而对于发送方来说,是无法区分由于哪种原因导致没有收到 ACK,稳妥起见,发送方在等待一段时间之后就会进行超时重传,将数据再发一次。
但是有一个问题,如果接收方已经收到了这个数据,只是在返回 ACK 的时候这个应答消息丢包了,此时发送方再发送一次数据,这样是不合理也是不安全的,例如:转账操作。
TCP内部的去重机制
上述问题 TCP 它在内部用一个去重操作来避免的,接收方收到的数据库会先放到操作系统内核的 “接收缓冲区” 中。
收到新的数据,TCP 就会根据序号来检查这个数据是不是在接收缓冲区中已经存在了,如果不存在,就放进去,如果存在,直接丢弃。当顺利收到 ACK 之后,就可以把这个数据从缓冲区拿出进行下一步的封装传送了~
注意:
超时重传是不会无休止的进行重传的,一次重传失败会再尝试,但如果连续两次传输都失败,说明能够顺利发送数据的可能性是比较小了,所以是不会无休止的重传的。连续几次重传都不行,就认为这个网络可能是遇到了严重的问题,发送方就只能放弃了。
基于上述确认应答和超时重传两个机制,TCP的可靠性,得到了有效的保障。
3. 连接管理(重中之重的面试考点)
TCP 进行可靠传输最重要的保障机制,也是是网络部分最高频出现的讨论问题,也就是著名的:三次握手,四次挥手。
如何建立连接:三次握手
三次握手是客户端和服务器之间,通过三次交互,完成了建立连接的过程,"握手"是一个形象的比喻。
在TCP报文头中,有以下六个标志位:
在上述 TCP 报文头中已经做了详细介绍。
下图是三次握手的交互过程:
解释:
三次握手有什么用?和可靠性有什么关系?
- TCP 的 三次握手 和 四次挥手 都是为了保证数据传输的可靠性。
- 三次握手相当于投石问路,检查当前这个网络的情况是否满足可靠传输的基本条件。 也就是检测通讯双方的
发送能力
和接受能力
是否正常。 - 通过以上的 “三次握手” 过程,客户端和服务器之间已经确定建立好了连接,接下来就可以进行真正的数据传输了。
经典面试题
-
描述TCP三次握手的过程
画上面的最简易的图,理解好那些五颜六色的文字,用自己的话描述出来。 -
为什么握手是三次?而不是两次?四次?
两次:服务器验证不了自己的发送能力和对方的接受能力,此刻,服务器对于当下能否满足可靠传输心里是没底的,所以需要第三次握手接收以下客户端的 ACK,才能保证通讯是可以进行的。
四次:可以,但没必要。中间一次服务器发送的 SYN 和 ACK 可以合为一次发送。 -
当浏览器里面输入了 URL 之后,程序是怎么执行的:
① 对 URL 进行校验,如果校验通过(符合http协议),先进性 DNS 解析,找到对应的目的 IP,目的端口号
② TCP 连接(3次握手)
③ 数据封装(发送)
④ 服务器端程序接收到消息,对消息进行解析,通过 Request 对象来获取参数,对参数的正确性和非空性进行校验,才会调用数据库执行业务处理和查询;查询之后得到结果,将结果封装起来发送给客户端
⑤ 客户端会得到服务器端的响应结果,浏览器会使用自己的引擎进行解析和渲染,并最终展示给用户
⑥ 断开 TCP 连接(4次挥手)
断开连接:四次挥手
在建立好连接之后,接下来要进行数据通信,所以在客户端和服务器的操作系统内核中,都会使用一定的数据结构来保存连接相关的信息。(源IP,源端口,目的IP,目的端口)
那么在通信完成时,就需要断开连接,也就是释放掉关于对方的一些不必要的资源。所以就需要:四次挥手
。
在四次挥手过程中,先由请求断开连接一方首先发送 FIN
(结束报文段) 请求,接收方收到这个断开连接请求时会先有操作系统内核回复一个 ACK
,表示已确认收到请求信息;
接下来服务器也同样会发送一个 FIN
表示同意断开连接并且付出行动(触发代码来close),客户端收到后再次返回一个 ACK
这样就可以有效的断开双方的 TCP 连接了。
四次挥手和三次握手的区别
三次握手,一定是客户端主动发起的(主动发起的一方才叫客户端)
四次挥手,可能是客户端发起的,也可能是服务器发起的
三次握手,中间两次能合并
四次挥手,中间两次有时不能合并
四次挥手过程中,FIN 和 ACK 可以合并发送吗?
有时可以合并,有时不可以。主要原因在于,很多时候中间两次的 FIN 和 ACK 发送的时机可能是不同的。
因为:双方通讯时所发送的 ACK 和 SYN 都是操作系统内核负责的;而所发送的 FIN 是用户代码负责的(代码中执行到了 socket.close()
,才会触发 FIN)
状态转换
服务端状态转化:
- [CLOSED -> LISTEN] 服务器端调用listen后进入LISTEN状态,等待客户端连接;
- [LISTEN -> SYN_RCVD] 一旦监听到连接请求(同步报文段),就将该连接放入内核等待队列中,并向客户端发送SYN确认报文。
- [SYN_RCVD -> ESTABLISHED] 服务端一旦收到客户端的确认报文,就进入ESTABLISHED状态,可以进行读写数据了。
- [ESTABLISHED -> CLOSE_WAIT] 当客户端主动关闭连接(调用close),服务器会收到结束报文段,服务器返回确认报文段并进入CLOSE_WAIT;
- [CLOSE_WAIT -> LAST_ACK] 进入CLOSE_WAIT后说明服务器准备关闭连接(需要处理完之前的数据);当服务器真正调用close关闭连接时,会向客户端发送FIN,此时服务器进入LAST_ACK状态,等待最后一个ACK到来(这个ACK是客户端确认收到了FIN)
- [LAST_ACK -> CLOSED] 服务器收到了对FIN的ACK,彻底关闭连接。
客户端状态转化:
- [CLOSED -> SYN_SENT] 客户端调用connect,发送同步报文段;
- [SYN_SENT -> ESTABLISHED] connect调用成功,则进入ESTABLISHED状态,开始读写数据;
- [ESTABLISHED -> FIN_WAIT_1] 客户端主动调用close时,向服务器发送结束报文段,同时进入FIN_WAIT_1;
- [FIN_WAIT_1 -> FIN_WAIT_2] 客户端收到服务器对结束报文段的确认,则进入FIN_WAIT_2,开始等待服务器的结束报文段;
- [FIN_WAIT_2 -> TIME_WAIT] 客户端收到服务器发来的结束报文段,进入TIME_WAIT,并发出LAST_ACK;
- [TIME_WAIT -> CLOSED] 客户端要等待一个2MSL(Max Segment Life,报文最大生存时间)的时间,才会进入CLOSED状态。
两个重要状态
4. 滑动窗口
滑动窗口存在的意义就是在保证可靠性的前提下,尽量提高传输效率
如果不采取滑动窗口机制,根据以上的确认应答机制,数据传输是如下图进行的
由于确认应答机制的存在,导致每执行一次发送操作,都需要等待一个 ACK 的到达,那么会耗费大量的时间在传输 ACK 了。
那么就引出了:滑动窗口机制,本质就是在 “批量发送数据”,一次发一组数据,然后一起等这一组数据的 ACK。
具体窗口是怎么“滑动”的?
不知道大家有没有发现,上述过程有一个核心的问题:丢包!
那么,在 “滑动窗口” 的背景下,如果 丢包 ,改如何重传呢?
情况一:数据包已经抵达,ACK被丢了。
这种情况下,部分ACK丢了并不要紧,因为可以通过后续的ACK进行确认;
情况二:数据包就直接丢了。
必须处理
还是图例解释:
注意:
这里的重传只需要把丢了的那一块数据给重传即可,其他的已经到了的数据就不必再重传了。所以,整体的传输效率还是比较高的。
5. 流量控制
流量控制是滑动窗口的延伸,目的是为了保证可靠传输,因为在 滑动窗口 中,窗口越大,传输速率就越高。而接收端处理数据的速度是有限的。如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发送端继续发送,就会造成丢包,继而引起丢包重传等等一系列连锁反应。
因此 TCP 支持根据接收端的处理能力,来决定发送端的发送速度。这个机制就叫做流量控制(Flow Control)。流量控制的关键,就是得能够衡量接收方的处理速度。
那么,接收方是如何告知发送方,剩余空间还有多大的?
通过 ACK 报文来告知
当接收缓冲区满了,是不是 A 就不再发送数据了?
虽然 B 反馈的窗口大小是 0,但是 A 也不能完全不发数据;A 需要定期的发送一个
探测报文
,探测报文不传输实际的数据,只是为了触发 ACK,用于知道当前 缓冲区的大小是多少。
6. 拥塞控制
滑动窗口的延伸,同时也是限制滑动窗口发送的速率。
拥塞控制衡量的是,发送方到接收方这整个链路之间的拥堵情况(处理能力)
拥塞窗口变化图解(这个牛逼!)
7. 延迟应答
相当于流量控制的延伸
流量控制是控制了发送方的传输速率
而延时应答,是想在这个基础上,能够尽量的再让窗口大一些
在 A 发送收据到缓冲区时,B 也同时在从“缓冲区”中拿出数据,这个延时应答,就是为了能够让 B 在这个延时的时间内,多拿走一些数据,这样缓冲区的空间就更大了,A 也就通过返回的 ACK 感知到了一个认为比较大空间,就可以发送更到的数据了。
8. 捎带应答
捎带应答时延迟应答的延伸
客户端和服务器之间的通信有以下几种类型:
- 一问一答,客户端发送一个请求,服务器就返回一个对应的响应(浏览器请求页面)
- 多问一答(上传文件)
- 一问多答(下载文件)
- 多问多答(直播)
因为延迟应答的存在,导致 ACK 不一定是立即返回的。
如果当前的延迟应答导致 ACK 的返回时机和应用程序代码中返回的响应时机重合了,就可以把这个 ACK
和 响应数据
合二为一。
9. 面向字节流
TCP 发送接收数据是面向字节流的,而面向字节流的机制会存在一个问题:粘包问题
TCP 粘包粘的是 应用层数据报,在 TCP 接收缓冲区中,若干个“应用层数据包”混在一起了。
解决方案:关键就是在应用层协议加入包之间的边界。
10. TCP 的异常处理
- 进程终止
在进程毫无防备的情况下,突然结束进程,这个时候该进程的 TCP 连接是怎么样的?
TCP 连接,是通过 socket 来建立的,socket 本质上是进程打开的一个文件,文件其实就存储在于:进程的 PCB 里面有个 “文件描述符表”
每次打开一个文件(包括 socket),都在文件描述符表里增加一项;
每次关闭一个文件,都在文件描述符表里进行删除一项。
如果直接杀死进程,PCB 也就没了,里面的文件描述符表也就没了,此处的文件相当于“自动关闭” 了。这个过程其实和手动调用 socket.close() 一样,都会触发四次挥手。
- 机器关闭
按照操作系统约定的正常流程关闭
正常流程的关机。会让操作系统杀死所有进程,然后再关机。
- 机器掉电 / 网线断开
直接拔电源
此时操作系统没有任何反应时间,更不会有任何的处理措施。
面试题
如何基于 UDP 实现可靠传输
引入 确认应答 机制
实现序号,确认序号以及去重机制
实现超时重传
…
实际这个问题就是在考 TCP 实现可靠传输都引入了哪些机制~
TCP / UDP 分别在怎么样的场景适合使用
- 什么时候使用TCP?
对可靠性有一定要求,(日常开发中的大多数情况,都基于TCP)- 如果传输的单个数据报比较长(超过 64k)
首选 TCP- 啥时候使用UDP?
对可靠性要求不高,对于效率的要求更高(机房内部的主机之间通信,分布式系统中,广播就是首选UDP)
网络层
在复杂的网络环境中确定一个合适的路径。
进行 1. 地址管理,2. 路由选择
IP 协议
- 4位版本号(version):指定IP协议的版本,对于IPv4来说,就是4。
- 4位头部长度(header length):IP头部的长度是多少个32bit,也就是 length * 4 的字节数。4bit表示最大的数字是15,因此IP头部最大长度是60字节。
- 8位服务类型(Type Of Service):3位优先权字段(已经弃用),4位TOS字段,和1位保留字段(必须置为0)。4位TOS分别表示:最小延时,最大吞吐量,最高可靠性,最小成本。这四者相互冲突,只能选择一个。对于ssh/telnet这样的应用程序,最小延时比较重要;对于ftp这样的程序,最大吞吐量比较重要。
- 16位总长度(total length):IP数据报整体占多少个字节。
- 16位标识(id):唯一的标识主机发送的报文。如果IP报文在数据链路层被分片了,那么每一个片里面的这个id都是相同的。
- 3位标志字段:第一位保留(保留的意思是现在不用,但是还没想好说不定以后要用到)。第二位置为1表示禁止分片,这时候如果报文长度超过MTU,IP模块就会丢弃报文。第三位表示"更多分片",如果分片了的话,最后一个分片置为1,其他是0。类似于一个结束标记。
- 13位分片偏移(framegament offset):是分片相对于原始IP报文开始处的偏移。其实就是在表示当前分片在原报文中处在哪个位置。实际偏移的字节数是这个值 * 8 得到的。因此,除了最后一个报文之外,其他报文的长度必须是8的整数倍(否则报文就不连续了)。
- 8位生存时间(Time To Live,TTL):数据报到达目的地的最大报文跳数。一般是64。每次经过一个路由,TTL -= 1,一直减到0还没到达,那么就丢弃了。这个字段主要是用来防止出现路由循环。
8位协议:表示上层协议的类型。 - 16位头部校验和:使用CRC进行校验,来鉴别头部是否损坏。
- 32位源地址和32位目标地址:表示发送端和接收端。
地址管理
以上所说的 IP 地址 是一个
点分十进制
构成的数据,把 IP 地址分成了两个部分,网络号 + 主机号。网络号:描述当前的网段信息
主机号:区分了局域网内部的主机
一般要求:同一个局域网里,主机之间的网络号是相同的,主机号不能相同;而两个相邻的局域网(由同一个路由器连接的),网络号也不能相同。
一些特殊的 IP 地址
- 如果 IP 的主机号为
全0
,该 IP 就表示网络号
,(局域网中的一个正常设备,主机号是不能为0的) - 如果 IP 的主机号为
全1(255)
,该 IP 就表示广播地址
,(往这个广播地址上发的消息,整个局域网都能收到) - IP 地址是 127 开头的,该 IP 都表示
环回IP
,表示主机自己,(127.0.0.1
:环回 IP 中的典型代表) - IP 地址是
10
开头,192.168
开头,172.16 - 172.31
开头,表示该 IP 地址是一个局域网内部的 IP,(内网IP) - 除此之外,剩下的 IP 称为
外网IP
(直接在广域网上使用的IP)
要求外网IP 一定是唯一的,每个 外网IP 都会对应到唯一的一个设备
内网IP 只是在当前局域网中是唯一的,不同的局域网里,可以有相同的 内网IP 的设备。
当前的 IPv 协议,使用的 IP 地址是 32 位整数,32 位整数表示的数据范围:42亿9千万,如果给每个设备都分配一个唯一的 IP,意味着世界上的设备就不能超过 42亿9千万?
NAT 机制
路由选择
路由选择也就是规划路径
,两个设备之间,要找出一条通道,能够完成传输的过程
数据链路层
数据链路层考虑的是相邻节点之间的数据传输。
认识以太网
以太
:本来是物理学上的概念,“以太网”
,不是一种具体的网络,而是一种技术标准;
既包含了数据链路层的内容,也包含了一些物理层的内容。例如:规定了网络拓扑结构,访问控制方式,传输速率等;
以太网帧格式
以太网数据帧格式比 TCP 和 IP 都简单很多
- 源地址和目的地址是指网卡的硬件地址(也叫 MAC地址),长度是48位,是在网卡出厂时固化的;
- 帧协议类型字段有三种值,分别对应IP、ARP、RARP;
- 帧末尾是CRC校验码。
MAC 地址
以太网数据帧的格式中长度 48 位的 MAC 地址(物理地址),它真正做到了
每个设备都是唯一的 MAC 地址
,是在网卡出厂的时候就写死了。
MTU
一个以太网数据帧能够承载的数据范围(这个范围取决于硬件设备)
MTU对IP协议的影响
由于数据链路层MTU的限制,对于较大的IP数据包要进行分包。
- 将较大的IP包分成多个小包,并给每个小包打上标签;
- 每个小包IP协议头的 16位标识(id) 都是相同的;
- 每个小包的IP协议头的3位标志字段中,第2位置为0,表示允许分片,第3位来表示结束标记(当前是否是最后一个小包,是的话置为1,否则置为0);
- 到达对端时再将这些小包,会按顺序重组,拼装到一起返回给传输层;
- 一旦这些小包中任意一个小包丢失,接收端的重组就会失败。但是IP层不会负责重新传输数据;
MSS 和 MTU的关系
MSS :TCP 中 在 IP 补分包的前提下,最多搭载多少载荷
ARP协议
ARP不是一个单纯的数据链路层的协议,而是一个介于数据链路层和网络层之间的协议,
ARP 报文并不是来传输数据的,只是起到了一个辅助的效果
文章来源:https://www.toymoban.com/news/detail-464776.html
文章来源地址https://www.toymoban.com/news/detail-464776.html
到了这里,关于计算机网络—网络原理之<TCP/IP 五层协议 >图解+详述的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!