General
为什么需要TCP/IP网络模型?
对于同一台设备上的进程间通信,有很多种方式,比如有管道、消息队列、共享内存、信号等方式
而对于不同设备上的进程间通信,就需要网络通信,而设备是多样性的,所以要兼容多种多样的设备,就协商出了一套通用的网络协议TCP/IP网络协议
TCP/IP网络模型有哪几层?
总体来说,TCP/IP网络模型分为4层,从上到下依次是应用层、传输层、网络层、网络接口层
应用层简介
应用层对应于OSI参考模型的高层,为用户提供所需要的各种服务
最上层的,也是我们能直接接触到的就是应用层(Application Layer)
我们电脑或手机使用的应用软件都是在应用层实现
HTTP、FTP、Telnet、DNS、SMTP
通常我们使用浏览器访问各自网站都是基于应用层的HTTP协议,当在地址栏键入网址后,浏览器就会自动发送HTTP请求给服务端
应用层是工作在操作系统中的用户态,传输层及以下则工作在内核态
传输层简介
传输层对应于OSI参考模型的传输层,为应用层实体提供端到端的通信功能,保证了数据包的顺序传送及数据的完整性。
该层定义了两个主要的协议:传输控制协议(TCP)和用户数据报协议(UDP)。
TCP协议提供的是一种可靠的、通过“三次握手”来连接的数据传输服务;而UDP协议提供的则是不保证可靠的(并不是不可靠)、无连接的数据传输服务
TCP 的全称叫传输控制协议(Transmission Control Protocol),大部分应用使用的正是 TCP 传输层协议,比如 HTTP 应用层协议。TCP 相比 UDP 多了很多特性,比如流量控制、超时重传、拥塞控制等,这些都是为了保证数据包能可靠地传输给对方。
UDP 相对来说就很简单,简单到只负责发送数据包,不保证数据包是否能抵达对方,但它实时性相对更好,传输效率也高。当然,UDP 也可以实现可靠传输,把 TCP 的特性在应用层上实现就可以,不过要实现一个商用的可靠 UDP 传输协议,也不是一件简单的事情。
应用需要传输的数据可能会非常大,如果直接传输就不好控制,因此当传输层的数据包大小超过 MSS(TCP 最大报文段长度) ,就要将数据包分块,这样即使中途有一个分块丢失或损坏了,只需要重新发送这一个分块,而不用重新发送整个数据包。在 TCP 协议中,我们把每个分块称为一个 TCP 段(TCP Segment)。
TCP会给HTTP每个TCP段添加上TCP头部,TCP头部包含一些用于保证可靠传输的状态信息,和区分应用程序的编号-端口号
网络层简介
网络层对应于OSI参考模型的网络层,主要解决主机到主机的通信问题。负责网络包的封装、分片、路由、转发,该层的协议有IP、ICMP等
网络层最常使用的是 IP 协议(Internet Protocol),IP 协议会将传输层的报文作为数据部分,再加上 IP 包头组装成 IP 报文,如果 IP 报文大小超过 MTU(以太网中一般为 1500 字节)就会再次进行分片,得到一个即将发送到网络的 IP 报文。
IP地址由两部分组成:网络号,负责标识该 IP 地址是属于哪个「子网」的;主机号,负责标识同一「子网」下的不同主机
子网掩码和IP地址按位与运算,可以算出 IP 地址 的网络号和主机号
在寻址的过程中,先匹配到相同的网络号(表示要找到同一个子网),才会去找对应的主机
路由。实际场景中,两台设备并不是用一条网线连接起来的,而是通过很多网关、路由器、交换机等众多网络设备连接起来的,那么就会形成很多条网络的路径,因此当数据包到达一个网络节点,就需要通过路由算法决定下一步走哪条路径
网络接口层简介
网络接口层与OSI参考模型中的物理层和数据链路层相对应,负责网络包在物理网络中的传输,比如网络包的封帧、 MAC 寻址、差错检测,以及通过网卡传输网络帧等
IP 头部中的接收方 IP 地址表示网络包的目的地,通过这个地址我们就可以判断要将包发到那个网络中,但是具体到网络中的哪台主机,就需要以太网技术了
什么是以太网呢?电脑上的以太网接口,Wi-Fi接口,以太网交换机、路由器上的千兆,万兆以太网口,还有网线,它们都是以太网的组成部分。
以太网就是一种在「局域网」内,把附近的设备连接起来,使它们之间可以进行通讯的技术。
以太网进行通讯要用到 MAC 地址。 MAC地址用于在网络中唯一标示一个网卡,一台设备
网络接口层会给IP数据包加上MAC头部,它包含了接收方和发送方的 MAC 地址等信息,我们可以通过 ARP 协议获取对方的 MAC 地址
所以说,网络接口层主要为网络层提供「链路级别」传输的服务,负责在以太网、WiFi 这样的底层网络上发送原始数据包,工作在网卡这个层次,使用MAC 地址来标识网络上的设备
键入网址到网页显示,期间发生了什么?
HTTP
首先浏览器做的第一步工作就是要对URL进行解析,确定协议类型、Web 服务器和文件名。
根据这些信息来生成HTTP请求消息
DNS解析
通过浏览器解析 URL 并生成 HTTP 消息后,需要委托操作系统将消息发送给 Web 服务器。
但在发送之前,还有一项工作需要完成,那就是查询服务器域名对应的 IP 地址,因为委托操作系统发送消息时,必须提供通信对象的 IP 地址。
所以,有一种服务器就专门保存了 Web 服务器域名与 IP 的对应关系,它就是 DNS 服务器
DNS 中的域名都是用句点来分隔的,比如 www.server.com,这里的句点代表了不同层次之间的界限
在域名中,越靠右的位置表示其层级越高
域名的层级关系类似一个树状结构,从上到下依次是根 DNS 服务器(.)、顶级域 DNS 服务器(.com)、权威 DNS 服务器(server.com)
根域的 DNS 服务器信息保存在互联网中所有的 DNS 服务器中
这样一来,任何 DNS 服务器就都可以找到并访问根域 DNS 服务器了。
因此,客户端只要能够找到任意一台 DNS 服务器,就可以通过它找到根域 DNS 服务器,然后再一路顺藤摸瓜找到位于下层的某台目标 DNS 服务器。
客户端首先会发给本地 DNS 服务器一个 DNS 请求,问 www.server.com 的 IP 是啥
本地域名服务器👉缓存👉根域
本地域名服务器👉顶级域
本地域名服务器👉权威域
权威 DNS 服务器查询后将对应的 IP 地址 X.X.X.X 告诉本地 DNS
本地 DNS 再将 IP 地址返回客户端,客户端和目标建立连接
因为有缓存的存在,不是每次解析域名都要经过那么多的步骤
浏览器会先看自身有没有对这个域名的缓存,如果有,就直接返回,如果没有,就去问操作系统
操作系统也会去看自己的缓存,如果有,就直接返回,如果没有,再去 hosts 文件看
hosts文件中也没有,才会去问「本地 DNS 服务器」
协议栈
通过 DNS 获取到 IP 后,就可以把 HTTP 的传输工作交给操作系统中的协议栈。
协议栈的内部分为几个部分,分别承担不同的工作。上下关系是有一定的规则的,上面的部分会向下面的部分委托工作,下面的部分收到委托的工作并执行。
应用程序(浏览器)通过调用 Socket 库,来委托协议栈工作。协议栈的上半部分有两块,分别是负责收发数据的 TCP 和 UDP 协议,这两个传输协议会接受应用层的委托执行收发数据的操作。
协议栈的下面一半是用 IP 协议控制网络包收发操作,在互联网上传数据时,数据会被切分成一块块的网络包,而将网络包发送给对方的操作就是由 IP 负责的。
IP 下面的网卡驱动程序负责控制网卡硬件,而最下面的网卡则负责完成实际的收发操作,也就是对网线中的信号执行发送和接收操作。
ICMP 用于告知网络包传送过程中产生的错误以及各种控制信息
ARP 用于根据 IP 地址查询相应的以太网 MAC 地址
可靠传输-TCP
HTTP 是基于 TCP 协议传输的
首先,源端口号和目标端口号是不可少的,如果没有这两个端口号,数据就不知道应该发给哪个应用。
接下来有包的序号,这个是为了解决包乱序的问题。
还有应该有的是确认号,目的是确认发出去对方是否有收到。如果没有收到就应该重新发送,直到送达,这个是为了解决丢包的问题。
接下来还有一些状态位。例如 SYN 是发起一个连接,ACK 是回复,RST 是重新连接,FIN 是结束连接等。TCP 是面向连接的,因而双方要维护连接的状态,这些带状态位的包的发送,会引起双方的状态变更。
还有一个重要的就是窗口大小。TCP 要做流量控制,通信双方各声明一个窗口(缓存大小),标识自己当前能够的处理能力,别发送的太快,撑死我,也别发的太慢,饿死我。
除了做流量控制以外,TCP还会做拥塞控制,对于真正的通路堵车不堵车,它无能为力,唯一能做的就是控制自己,也即控制发送的速度。不能改变世界,就改变自己嘛。
在 HTTP 传输数据之前,首先需要 TCP 建立连接,TCP 连接的建立,通常称为三次握手
这个所谓的「连接」,只是双方计算机里维护一个状态机,说一下在连接建立的过程中,双方的状态变化时序图
一开始,客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端口,处于 LISTEN 状态
然后客户端主动发起连接 SYN,之后处于 SYN-SENT 状态
服务端收到发起的连接,返回 SYN,并且 ACK 客户端的 SYN,之后处于 SYN-RCVD 状态
客户端收到服务端发送的 SYN 和 ACK 之后,发送对 SYN 确认的 ACK,之后处于 ESTABLISHED 状态,因为它一发一收成功了
服务端收到 ACK 的 ACK 之后,处于 ESTABLISHED 状态,因为它也一发一收了
所以三次握手目的是保证双方都有发送和接收的能力
如果 HTTP 请求消息比较长,超过了 MSS 的长度,这时 TCP 就需要把 HTTP 的数据拆解成一块块的数据发送,而不是一次性发送所有数据
MSS:除去 IP 和 TCP 头部之后,一个网络包所能容纳的 TCP 数据的最大长度。
MTU:一个网络包的最大长度,以太网中一般为 1500 字节。
数据会被以 MSS 的长度为单位进行拆分,拆分出来的每一块数据都会被放进单独的网络包中。也就是在每个被拆分的数据加上 TCP 头信息,然后交给 IP 模块来发送数据。
TCP报文生成。
远程定位-IP
TCP 模块在执行连接、收发、断开等各阶段操作时,都需要委托 IP 模块将数据封装成网络包发送给通信对象。
在 IP 协议里面需要有源地址 IP 和 目标地址 IP:源地址IP,即是客户端输出的 IP 地址;目标地址,即通过 DNS 域名解析得到的 Web 服务器 IP。
源地址 IP的确定方式需要根据路由表规则,来判断哪一个网卡作为源地址 IP,也就是使用哪一个网卡发送数据;使用目标IP和路由表中逐项的子网掩码进行与运算,就可以最终求得源IP。
路由表中有默认网关,如果其他所有条目都无法匹配,就会自动匹配这一行。并且后续就把包发给路由器
IP报文生成。
两点传输-MAC
生成了 IP 头部之后,接下来网络包还需要在 IP 头部的前面加上 MAC 头部。
MAC 头部是以太网使用的头部,它包含了接收方和发送方的 MAC 地址等信息,用于两点之间的传输。
发送方的 MAC 地址获取就比较简单了,MAC 地址是在网卡生产时写入到 ROM 里的,只要将这个值读取出来写入到 MAC 头部就可以了
通过查询路由表,已经匹配到了下一跳的IP地址,如何根据IP地址查询MAC地址
此时就需要 ARP 协议帮我们找到下一跳(通常是路由器)的 MAC 地址
ARP 协议会在以太网中以广播的形式发送查询请求,如果对方和自己处于同一个子网中,那么通过上面的操作就可以得到对方的 MAC 地址
操作系统会把本次查询结果放到一块叫做 ARP 缓存的内存空间留着以后用,不过缓存的时间就几分钟
MAC报文生成。
出口-网卡
网络包只是存放在内存中的一串二进制数字信息,没有办法直接发送给对方。因此,我们需要将数字信息转换为电信号,才能在网线上传输,也就是说,这才是真正的数据发送过程。
负责执行这一操作的是网卡,要控制网卡还需要靠网卡驱动程序。
网卡驱动获取网络包之后,会将其复制到网卡内的缓存区中,接着会在其开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列。
起始帧分界符是一个用来表示包起始位置的标记
末尾的 FCS(帧校验序列)用来检查包传输过程是否有损坏
最后网卡会将包转为电信号,通过网线发送出去。
网络转发
网络环境是纷繁复杂,互联网通常是由交换机、路由器以及服务器连接起来的
由网卡发送出去的网络包经过交换机、路由器以及其他服务器最终抵达目标服务器
响应
请求抵达服务器后,服务器开始拆包
数据包抵达服务器后,服务器会先扒开数据包的 MAC 头部,查看是否和服务器自己的 MAC 地址符合,符合就将包收起来。
接着继续扒开数据包的 IP 头,发现 IP 地址符合,根据 IP 头中协议项,知道自己上层是 TCP 协议。
于是,扒开 TCP 的头,里面有序列号,需要看一看这个序列包是不是我想要的,如果是就放入缓存中然后返回一个 ACK,如果不是就丢弃。TCP头部里面还有端口号, HTTP 的服务器正在监听这个端口号。
于是,服务器自然就知道是 HTTP 进程想要这个包,于是就将包发给 HTTP 进程。
服务器的 HTTP 进程看到,原来这个请求是要访问一个页面,于是就把这个网页封装在 HTTP 响应报文里。
HTTP 响应报文也需要穿上 TCP、IP、MAC 头部,不过这次是源地址是服务器 IP 地址,目的地址是客户端 IP 地址。
响应报文经历和请求报文类似的流程到达客户端
客户端开始拆包,把收到的数据包拆到只剩 HTTP 响应报文后,交给浏览器去渲染页面,这样这次请求的响应就显示出来了
最后,客户端要离开了,向服务器发起了 TCP 四次挥手,至此双方的连接就断开了