计算机网络
从键入网址到网页显示,期间发生了什么?
url 解析
生成HTTP消息
真实地址查询
查询服务器域名对应的 IP 地址
专门保存了 Web
服务器域名与 IP
的对应关系,它就是 DNS
服务器
域名解析工作流程:
- 客户端首先会发出一个 DNS 请求,问 www.server.com 的 IP 是啥,并发给本地 DNS 服务器(也就是客户端的 TCP/IP 设置中填写的 DNS 服务器地址)。
- 本地域名服务器收到客户端的请求后,如果缓存里的表格能找到 www.server.com,则它直接返回 IP 地址。如果没有,本地 DNS 会去问它的根域名服务器:“老大, 能告诉我 www.server.com 的 IP 地址吗?” 根域名服务器是最高层次的,它不直接用于域名解析,但能指明一条道路。
- 根 DNS 收到来自本地 DNS 的请求后,发现后置是 .com,说:“www.server.com 这个域名归 .com 区域管理”,我给你 .com 顶级域名服务器地址给你,你去问问它吧。”
- 本地 DNS 收到顶级域名服务器的地址后,发起请求问“老二, 你能告诉我 www.server.com 的 IP 地址吗?”
- 顶级域名服务器说:“我给你负责 www.server.com 区域的权威 DNS 服务器的地址,你去问它应该能问到”。
- 本地 DNS 于是转向问权威 DNS 服务器:“老三,www.server.com对应的IP是啥呀?” server.com 的权威 DNS 服务器,它是域名解析结果的原出处。为啥叫权威呢?就是我的域名我做主。
- 权威 DNS 服务器查询后将对应的 IP 地址 X.X.X.X 告诉本地 DNS。
- 本地 DNS 再将 IP 地址返回客户端,客户端和目标建立连接。
至此,我们完成了 DNS 的解析过程。
指南好帮手 —— 协议栈
DNS获取IP后,就可以把HTTP的传输工作交给操作系统中的协议栈
应用程序调研socket库委托协议栈工作。协议栈的下面一半是用 IP 协议控制网络包收发操作,在互联网上传数据时,数据会被切分成一块块的网络包,而将网络包发送给对方的操作就是由 IP 负责的。
此外 IP 中还包括 ICMP
协议和 ARP
协议。
ICMP
用于告知网络包传送过程中产生的错误以及各种控制信息。ARP
用于根据 IP 地址查询相应的以太网 MAC 地址。
IP 下面的网卡驱动程序负责控制网卡硬件,而最下面的网卡则负责完成实际的收发操作,也就是对网线中的信号执行发送和接收操作。
TCP可靠传输
TCP报文格式
序号:解决乱序问题
确认序列:解决丢包问题
状态位:例如
SYN
是发起一个连接,ACK
是回复,RST
是重新连接,FIN
是结束连接等。TCP 是面向连接的,因而双方要维护连接的状态,这些带状态位的包的发送,会引起双方的状态变更。窗口大小:为了进行流量控制,声明一个窗口,标识自己当前的处理能力
TCP建立连接
双方计算机里维护一个状态机,在连接建立的过程中,双方的状态变化如图:
- 一开始双方状态均为close
- 服务器先监听某个端口,处于listen状态
- 客户端主动发起连接,发送SYN包,处于syn_sent态
- 服务器收到SYN包后,发送SYN+ACK包,表明收到了数据包,处于syn_rcvd态
- 客户端收到服务器的ACK包后,发送ACK包到服务器,处于established态(因为它一发一收成功了)
- 服务器收到ACK的ACK包后,处于established态(因为它一发一收成功了)
所以三次握手目的是保证双方都有发送和接收的能力。
TCP 的连接状态查看,在 Linux 可以通过 netstat -napt
命令查看。
TCP分割数据
如果 HTTP 请求消息比较长,超过了 MSS
的长度,这时 TCP 就需要把 HTTP 的数据以MSS为单元,拆解成一块块的数据发送,而不是一次性发送所有数据。
MTU
:一个网络包的最大长度,以太网中一般为1500
字节。MSS
:除去 IP 和 TCP 头部之后,一个网络包所能容纳的 TCP 数据的最大长度。
IP远程定位
IP报文格式
假设客户端有多个网卡,就会有多个 IP 地址,那 IP 头部的源地址应该选择哪个 IP 呢?
需要根据路由表规则,来判断哪一个网卡作为源地址 IP。
在 Linux 操作系统,我们可以使用 route -n
命令查看当前系统的路由表。
第三条目比较特殊,它目标地址和子网掩码都是 0.0.0.0
,这表示默认网关,如果其他所有条目都无法匹配,就会自动匹配这一行。并且后续就把包发给路由器,Gateway
即是路由器的 IP 地址。
MAC两点传输
MAC包头格式
需要发送方和接收方的MAC地址,协议类型在TCP/IP通信里一般为:
- 0800:IP
- 0806:ARP
发送方的 MAC 地址获取就比较简单了,MAC 地址是在网卡生产时写入到 ROM 里的,只要将这个值读取出来写入到 MAC 头部就可以了。
ARP协议获取接收方MAC地址
操作系统会把本次查询结果放到一块叫做 ARP 缓存的内存空间留着以后用。在发包时:
- 先查询 ARP 缓存,如果其中已经保存了对方的 MAC 地址,就不需要发送 ARP 查询,直接使用 ARP 缓存中的地址。
- 而当 ARP 缓存中不存在对方 MAC 地址时,则发送 ARP 广播查询。
在 Linux 系统中,我们可以使用 arp -a
命令来查看 ARP 缓存的内容。
出口—网卡
我们需要将数字信息转化为电信号,才能在网线上传输。负责这一操作的正是网卡,控制网卡还需要网卡驱动程序。网卡驱动获取网络包之后,会将其复制到网卡内的缓存区中,接着会在其开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列(FCS)。
送别者—交换机
交换机的设计是将网络包原样转发到目的地。交换机工作在 MAC 层,也称为二层网络设备。交换机的端口不核对接收方 MAC 地址,而是直接接收所有的包并存放到缓冲区中。和网卡不同,交换机的端口不具有 MAC 地址
将包存入缓冲区后,接下来需要查询一下这个包的接收方 MAC 地址是否已经在 MAC 地址表中有记录了。
交换机的 MAC 地址表主要包含两个信息:
- 一个是设备的 MAC 地址,
- 另一个是该设备连接在交换机的哪个端口上。
地址表中找不到指定的 MAC 地址。这可能是因为具有该地址的设备还没有向交换机发送过包,或者这个设备一段时间没有工作导致地址被从地址表中删除了。这种情况下,交换机无法判断应该把包转发到哪个端口,只能将包转发到除了源端口之外的所有端口上,无论该设备连接在哪个端口上都能收到这个包。这样做不会产生什么问题,因为以太网的设计本来就是将包发送到整个网络的,然后只有相应的接收者才接收包,而其他设备则会忽略这个包。其实完全不用过于担心,因为发送了包之后目标设备会作出响应,只要返回了响应包,交换机就可以将它的地址写入 MAC 地址表,下次也就不需要把包发到所有端口了。
此外,如果接收方 MAC 地址是一个广播地址,那么交换机会将包发送到除源端口之外的所有端口。
以下两个属于广播地址:
- MAC 地址中的
FF:FF:FF:FF:FF:FF
- IP 地址中的
255.255.255.255
处境大门—路由器
这一步转发的工作原理和交换机类似,也是通过查表判断包转发的目标。
不过在具体的操作过程上,路由器和交换机是有区别的。
- 因为路由器是基于 IP 设计的,俗称三层网络设备,路由器的各个端口都具有 MAC 地址和 IP 地址;
- 而交换机是基于以太网设计的,俗称二层网络设备,交换机的端口不具有 MAC 地址。
路由器的端口具有 MAC 地址,因此它就能够成为以太网的发送方和接收方;同时还具有 IP 地址,从这个意义上来说,它和计算机的网卡是一样的。
当转发包时,首先路由器端口会接收发给自己的以太网包,然后路由表查询转发目标,再由相应的端口作为发送方将以太网包发送出去。
路由器包接收
- 进行错误校验
- 如果没问题,检查MAC头部的接收方MAC地址,如果是发给自己的包就放到接受缓冲区,否则丢弃
查询路由表确定输出端口
MAC 头部的作用就是将包送达路由器,其中的接收方 MAC 地址就是路由器端口的 MAC 地址。因此,当包到达路由器之后,MAC 头部的任务就完成了,于是 MAC 头部就会被丢弃。
查询路由表判断转发目标:
路由器包发送
接下来就会进入包的发送操作。
我们需要根据路由表的网关列判断对方的地址。
- 如果网关是一个 IP 地址,则这个IP 地址就是我们要转发到的目标地址,还未抵达终点,还需继续需要路由器转发。
- 如果网关为空,则 IP 头部中的接收方 IP 地址就是要转发到的目标地址,也是就终于找到 IP 包头里的目标地址了,说明已抵达终点。
知道对方的 IP 地址之后,接下来需要通过 ARP
协议根据 IP 地址查询 MAC 地址,并将查询的结果作为接收方 MAC 地址。路由器也有 ARP 缓存,因此首先会在 ARP 缓存中查询,如果找不到则发送 ARP 查询请求。接下来,下一个路由器会将包转发给再下一个路由器,经过层层转发之后,网络包就到达了最终的目的地。
抵达服务器
扒开 TCP 的头,里面有序列号,需要看一看这个序列包是不是我想要的,如果是就放入缓存中然后返回一个 ACK,如果不是就丢弃。TCP头部里面还有端口号, HTTP 的服务器正在监听这个端口号。于是,服务器自然就知道是 HTTP 进程想要这个包,于是就将包发给 HTTP 进程。服务器的 HTTP 进程看到,原来这个请求是要访问一个页面,于是就把这个网页封装在 HTTP 响应报文里。
HTTP 响应报文也需要穿上 TCP、IP、MAC 头部,不过这次是源地址是服务器 IP 地址,目的地址是客户端 IP 地址。
最后,客户端要离开了,向服务器发起了 TCP 四次挥手,至此双方的连接就断开了。
Linux系统是如何收发包的?
Linux接收网络包的过程
当网卡接收到一个网络包后,会通过 DMA 技术,将网络包写入到指定的内存地址,也就是写入到 Ring Buffer ,这个是一个环形缓冲区,接着就会告诉操作系统这个网络包已经到达。
如何告诉操作系统这个网络包已到达?
- 触发中断,每当网卡收到一个网络包后,就触发一个中断告诉操作系统
- Linux 内核在 2.6 版本中引入了 NAPI 机制。它的核心概念就是不采用中断的方式读取数据,而是首先采用中断唤醒数据接收的服务程序,然后
poll
的方法来轮询数据。
因此,当有网络包到达时,会通过 DMA 技术,将网络包写入到指定的内存地址,接着网卡向 CPU 发起硬件中断,当 CPU 收到硬件中断请求后,根据中断表,调用已经注册的中断处理函数。
硬件中断处理函数会做如下的事情:
- 需要先「暂时屏蔽中断」,表示已经知道内存中有数据了,告诉网卡下次再收到数据包直接写内存就可以了,不要再通知 CPU 了,这样可以提高效率,避免 CPU 不停的被中断。
- 接着,发起「软中断」,然后恢复刚才屏蔽的中断。
至此,硬件中断处理函数的工作就已经完成。
硬件中断处理函数做的事情很少,主要耗时的工作都交给软中断处理函数了。
软中断如何处理?
内核中的 ksoftirqd 线程专门负责软中断的处理,当 ksoftirqd 内核线程收到软中断后,就会来轮询处理数据。
ksoftirqd 线程会从 Ring Buffer 中获取一个数据帧,用 sk_buff 表示,从而可以作为一个网络包交给网络协议栈进行逐层处理。
网络协议栈做了哪些工作?
首先,会先进入到网络接口层,进行包校验,若有误则丢弃
到网络层后,取出IP包,根据目标IP地址判断是发到上层还是进行转发。确定发到本机后,从IP头中看上层协议类型是UDP还是TCP,然后去掉IP头,交给上层处理。
到传输层后,取出TCP头或UDP头,根据四元组「源 IP、源端口、目的 IP、目的端口」 作为标识,找出对应的 Socket,并把数据放到 Socket 的接收缓冲区。
最后,应用层程序调用 Socket 接口,将内核的 Socket 接收缓冲区的数据「拷贝」到应用层的缓冲区,然后唤醒用户进程。
Linux 发送网络包的流程
首先,应用程序会调用 Socket 发送数据包的接口,由于这个是系统调用,所以会从用户态陷入到内核态中的 Socket 层,内核会申请一个内核态的 sk_buff 内存,将用户待发送的数据拷贝到 sk_buff 内存,并将其加入到发送缓冲区。
接下来,网络协议栈从 Socket 发送缓冲区中取出 sk_buff,并按照 TCP/IP 协议栈从上到下逐层处理。
如果使用的是 TCP 传输协议发送数据,那么先拷贝一个新的 sk_buff 副本 ,这是因为 sk_buff 后续在调用网络层,最后到达网卡发送完成的时候,这个 sk_buff 会被释放掉。
而 TCP 协议是支持丢失重传的,在收到对方的 ACK 之前,这个 sk_buff 不能被删除。所以内核的做法就是每次调用网卡发送的时候,实际上传递出去的是 sk_buff 的一个拷贝,等收到 ACK 再真正删除。
接着,对 sk_buff 填充 TCP 头。这里提一下,sk_buff 可以表示各个层的数据包,在应用层数据包叫 data,在 TCP 层我们称为 segment,在 IP 层我们叫 packet,在数据链路层称为 frame。
你可能会好奇,为什么全部数据包只用一个结构体来描述呢?
协议栈采用的是分层结构,上层向下层传递数据时需要增加包头,下层向上层数据时又需要去掉包头,如果每一层都用一个结构体,那在层之间传递数据的时候,就要发生多次拷贝,这将大大降低 CPU 效率。
于是,为了在层级之间传递数据时,不发生拷贝,只用 sk_buff 一个结构体来描述所有的网络包,那它是如何做到的呢?是通过调整 sk_buff 中 data
的指针,比如:
然后交给网络层,在网络层里会做这些工作:选取路由(确认下一跳的 IP)、填充 IP 头、对超过 MTU 大小的数据包进行分片。处理完这些工作后会交给网络接口层处理。
网络接口层会通过 ARP 协议获得下一跳的 MAC 地址,然后对 sk_buff 填充帧头和帧尾,接着将 sk_buff 放到网卡的发送队列中。
这一些工作准备好后,会触发「软中断」告诉网卡驱动程序,这里有新的网络包需要发送,驱动程序会从发送队列中读取 sk_buff,将这个 sk_buff 挂到 RingBuffer 中,接着将 sk_buff 数据映射到网卡可访问的内存 DMA 区域,最后触发真实的发送。
当数据发送完成以后,其实工作并没有结束,因为内存还没有清理。当发送完成的时候,网卡设备会触发一个硬中断来释放内存,主要是释放 sk_buff 内存和清理 RingBuffer 内存。
最后,当收到这个 TCP 报文的 ACK 应答时,传输层就会释放原始的 sk_buff 。
HTTP 常见面试题
HTTP基本概念
是什么
是超文本传输协议。使用计算机能够理解的语言确立了一种两台计算机之间交流通信(传输文字,图片,音频等)的规范,以及各种控制和错误处理方式。
状态码
3xx
- 301:永久重定向
- 302:暂时重定向
301 和 302 都会在响应头里使用字段
Location
,指明后续要跳转的 URL,浏览器会自动重定向新的 URL。4xx
- 400:请求报文错误
- 403:服务器禁止访问资源
- 404:请求的资源在服务器未找到
5xx
- 500:服务器通用错误
- 501:客户端请求的功能还不支持
- 502:通常是服务器作为网关或代理时返回的错误码
- 503:服务器当前很忙,暂时无法访问
常见字段
Host:指定服务器域名
Content-Length:表明本次返回的数据长度
- 通过设置回车符、换行符作为HTTP header的边界
- 通过Content-Length作为HTTP body的边界
Connection:
Keep-Alive
用于客户端要求服务器使用【HTTP长连接】机制,以便其他请求复用Content-Type:本次数据的格式,
Content-Type: text/html; Charset=utf-8
Content-Encoding:说明数据的压缩方法(压缩格式),
Content-Encoding: gzip
GET与POST
GET与POST的区别
- GET:从服务器获取指定的资源
- POST:根据请求报文对指定的资源做出处理
安全和幂等
安全:请求方法不会破坏服务器上的资源
幂等:多次执行相同的操作,结果都是相同的
GET方法是安全且幂等的,因此可以对GET请求的数据做缓存
POST方法是不安全且不幂等的,‘多次提交创建多个资源,且有可能会修改服务器上的资源’
HTTP传输的内容都是明文的,要避免被窃取,可以使用HTTPS协议
HTTP缓存计术
强制缓存
只要浏览器判断缓存没有过期,则直接使用浏览器的本地缓存,如下图就是使用了强制缓存:
如何实现?
根据HTTP响应头部字段实现的,它们都用来表示资源在客户端缓存的有效期:
- Cache-Control:相对时间
- Expires:绝对时间
浏览器再次访问服务器中该资源时,会通过请求资源的时间与Cache-Control中设置的过期时间大小,来计算出该资源是否过期。服务器再次收到请求后,会再次更新Response头部的Cache-Control。
协商缓存
通过服务器告知客户端是否可以使用缓存的方式被称为协商缓存。返回的响应码是 304
如何实现?
第一种,基于请求头部中的If-Modified-Since
与响应头部中的Last-Modified
字段实现:
- Last-Modified:响应资源的最后修改时间
- If-Modified-Since:当资源过期,发现响应头部有Last-Modified声明,则再次发起请求的时候带上Last-Modified时间,服务器收到请求后,发现有If-Modified-Since,则与被请求资源的最后修改时间进行比较,如果改过,则返回最新资源,HTTP 200 OK,否则返回HTTP 304。
注意,协商缓存这两个字段都需要配合强制缓存中 Cache-Control 字段来使用,只有在未能命中强制缓存的时候,才能发起带有协商缓存字段的请求。
当使用 ETag 字段实现的协商缓存的过程:
当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在 Response 头部加上 ETag 唯一标识,这个唯一标识的值是根据当前请求的资源生成的;
当浏览器再次请求访问服务器中的该资源时,首先会先检查强制缓存是否过期:
- 如果没有过期,则直接使用本地缓存;
- 如果缓存过期了,会在 Request 头部加上 If-None-Match 字段,该字段的值就是 ETag 唯一标识;
服务器再次收到请求后,
会根据请求中的 If-None-Match 值与当前请求的资源生成的唯一标识进行比较:
- 如果值相等,则返回 304 Not Modified,不会返回资源;
- 如果不相等,则返回 200 状态码和返回资源,并在 Response 头部加上新的 ETag 唯一标识;
如果浏览器收到 304 的请求响应状态码,则会从本地缓存中加载资源,否则更新资源。
HTTP特性
- 简单
报文格式就是简单的header+body
的格式,简单且易于理解
- 灵活和易于扩展
HTTP协议里的各类请求方法、URI、状态码、头字段等各个组成要求都没有固定死,允许开发者自定义和扩充
其下层也可以随意变化:
如https就是在HTTP与TCP之间增加了SSL安全传输层
HTTP3.0改用了UDP协议
- 应用广泛和跨平台
HTTP/1.1的缺点?
- 无状态双刃剑
好处:
服务器不会去记忆HTTP的状态,所以不需要额外的资源来记录状态信息,可以有效减轻服务器的负担
坏处:
在完成有关联性的操作时会非常麻烦
如何解决无状态的问题?
使用cookie技术:在请求和响应报文中写入cookie信息来控制客户端的状态。
2. 明文传输双刃剑
通信使用明文,可能会被窃听
HTTP的性能如何?
- 长连接
早期HTTP每发起一个请求都要新建一次TCP连接(三次握手),而且是串行请求,做了无谓的TCP连接和断开。因此,HTTP1.1提出了长连接的通信方式,也叫持久连接
- 管道网络传输
即在同一个TCP连接里,客户端可以发起多个请求,减少整体响应时间。
HTTP1.1管道解决了请求的队头堵塞,但没有解决响应的队头堵塞。
- 队头堵塞
HTTP与HTTPS
两者的区别
- HTTPS加入了SSL安全协议,使得报文可以加密传输
- HTTPS建立连接时,还需要SSL握手过程
- HTTP默认端口是80,HTTPS的默认端口是443
- 需要向CA申请数字证书,来保证服务器的身份是可信的
HTTPS解决了HTTP的哪些问题?
HTTP存在:
- 窃听风险
- 篡改风险
- 冒充风险
HTTPS可以:
- 信息加密:使用混和加密的方式来保密
- 检验机制:使用摘要算法来实现完整性
- 身份证书:将服务器公钥放入到数字证书中,解决了冒充的风险
- 混合加密
HTTPS采用对称加密和非对称加密结合的“混合加密”。
- 在通信建立前采用非对称加密的方式交换「会话秘钥」,后续就不再使用非对称加密。
- 在通信过程中全部使用对称加密的「会话秘钥」的方式加密明文数据。
采用「混合加密」的方式的原因:
- 对称加密只使用一个密钥,运算速度快,密钥必须保密,无法做到安全的密钥交换。
- 非对称加密使用两个密钥:公钥和私钥,公钥可以任意分发而私钥保密,解决了密钥交换问题但速度慢。
- 摘要算法+数字签名
使用摘要算法计算出内容的哈希值(唯一)
缺点:只能保证内容不会被篡改,但不能保证内容+哈希不会被中间人替换
非对称加密算法:
- 公钥加密,私钥解密:为了保证内容传输的安全,只有持有私钥的人才能解密出实际的内容
- 私钥加密,公钥解密:为了保证消息不会被冒充,如果公钥可以解密,说明这个消息是持有私钥的人发的
非对称加密的主要用途:通过「私钥加密,公钥解密」的方式,来确认消息的身份。数字签名算法用的就是这种方式,不过私钥加密的不是内容,而是内容的哈希值。
私钥由服务器保管,然后服务器会向客户端颁发对应的公钥,如果客户端收到的信息,能被公钥解密,就说明该消息是由服务器发送的。
- 数字证书
- 可以通过哈希算法来保证消息的完整性;
- 可以通过数字签名来保证消息的来源可靠性(能确认消息是由持有私钥的一方发送的);
还缺少身份验证的环节,万一公钥是被伪造的呢(公钥可能被替换成坏人的公钥)?
把公钥注册到数字证书认证机构CA,他们会用自己的私钥对公钥做一个数字签名,最终将【个人信息,公钥,数字签名】打包一个数字证书。服务端用自己的私钥签名后,还会把数字证书给客户端。客户端会通过CA判断是否可信。
HTTPS是如何建立连接的?期间交互了什么?
SSL/TLS协议基本流程:
- 客户端向服务器索要并验证服务器的公钥
- 双方协商产生会话密钥
- 双方采用会话密钥进行加密通信
TLS握手阶段涉及4次通信,若采用RSA密钥交换算法:
客户端校验数字证书的流程是怎样的?
为什么要使用证书链?
这是为了确保根证书的绝对安全性,将根证书隔离地越严格越好,不然根证书如果失守了,那么整个信任链都会有问题。
HTTPS中的应用数据是如何保证完整性的?
TLS 在实现上分为握手协议和记录协议两层:
- 握手协议:负责协商加密算法和生成对称密钥,后续用此密钥来保护应用程序数据(即 HTTP 数据)
- 记录协议:TLS 记录协议负责保护应用程序数据并验证其完整性和来源,所以对 HTTP 数据加密是使用记录协议;
- 分片
- 压缩
- 计算 MAC 值(消息验证码)
- 加密
- Post title:计网复习笔记
- Post author:sixwalter
- Create time:2023-08-05 11:14:26
- Post link:https://coelien.github.io/2023/08/05/jobs/计算机网络/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.