网络基础知识
1、访问一个域名的过程分析
2、DNS解析原理
dns解析地址主要有两种方式
- 递归查询

- 迭代查询
以上两种查询,查到结果均会在本地域名服务器和浏览器中缓存对应域名的映射IP地址,加快后续的域名解析。
3、TCP/IP
3.1、OSI模型与TCP/IP模型
|----------|-------------|-------------|-------------------|----------------------|
| OSI 7层模型 | TCP/IP 四层模型 | TCP/IP 五层模型 | TCP/IP 协议栈 | 对应典型设备 |
| 应用层 | 应用层 | 应用层 | FTP、NFS | 应用程序,如FTP、HTTP、SMTP |
| 表现层 | 应用层 | 应用层 | Telnet、SNMP | 编码方式、图像编解码、URL字段传输编码 |
| 会话层 | 应用层 | 应用层 | SMTP、DNS | 建立会员、session认证、端点续传 |
| 传输层 | 传输层 | 传输层 | TCP、UDP | 进程和端口 |
| 网络层 | 网际层 | 网络层 | IP、ARP | 路由器、防火墙、多层交换机 |
| 数据链路层 | 网络接口层 | 数据链路层 | Ethernet、PPP、FDDI | 网卡、网桥、交换机 |
| 物理层 | 网络接口层 | 物理层 | IEEE 802.11 | 中继器、集线器、网线、HUB |
一次普通http请求之旅 
3.1.1、物理层
网络世界的数据传播本质还是靠的是电流(<3V),物理层的作用在于将电信号转化为数字信号
3.1.2、数字链路层
- 数字链路层的目的:
将物理层处理后的数字信号101011010......,封装成固定格式的报文---以太网协议,每一帧也叫以太网帧。 - 以太网协议:

- 网络通信的本质:
mac地址之间的通信,mac地址 设备的物理唯一标示,一台设备接收到IP数据包,看下目标mac地址是不是自己,如果是就接收处理,不是就丢弃。 - ARP协议:
地址解析协议,根据IP获取物理地址(mac地址)。 实际网络中,都是使用IP寻址,这时并不知道具体目标对象的mac地址,对外丢一个ARP广播包,对应的目标机器收到后,做出应答,告知自己的mac地址。
问:为啥网络通信都是用IP,而非mac地址?
答:我的理解,mac地址是实际物理地址,而IP地址为逻辑地址;例如 mac地址在网卡上,机器更换网卡之后,mac地址发生变化,而可以将同一个IP分配继续分配给这台机器,对于关联访问机器不需要变更。
3.1.3、网络层
- IP
- DHCP(动态主机配置协议):
一台新的主机刚要加入网络,只有自己的mac地址,这是并没有被分配IP,所以先向网络中使用IP地址0.0.0.0 目标IP地址255.255.255.255 发送一个广播包,DHCP 服务器收到只有mac地址而无IP的广播包,会给予应答给新主机分配一个尚未被使用的IP地址,以及子网掩码,网关。 - gateway(网关):网关在跨网段访问时使用,如下图
- DNS(域名解析)
- CDN(缓存)
3.1.4、传输层
- TCP/UDP
| TCP | UDP | |
|---|---|---|
| 是否连接 | 面向连接 | 无连接 |
| 是否可靠 | 可靠传输,使用流量控制、阻塞控制 | 不可靠传输 |
| 连接对象个数 | 只能一对一通信 | 支持一对一,一对多,多对多通信 |
| 传输方式 | 面向字节流 | 面向报文 |
| 首部开销 | 首部最小20字节最大80字节 | 首部8字节 |
| 适用场景 | 适用于要求可靠传输,日常的http请求等 | 适用于实时性要求高应用,IP电话、视频会议、直播 |
3.1.5、应用层
应用层协议:TCP/UDP + 端口
3.2、IP
3.2.1、IP协议

- 4位版本号:指定IP协议的版本号,4->IPV4、6->IPV6;
- 4位首部长度:表示IP首部的长度,单位为4个字节,所以最大长度为 2^4 -1 = 15 * 4字节=60字节,一般默认为20字节,可以用来切割首部;
- 8位服务类型:
- 16位总长度:表示IP首部+数据部分 一共多少字节;
- 16位标识:用来唯一标识主机发送的报文,数据链路层的设备都会约定每一帧报文的上限,所以一份IP报文会在数据链路层被分片,每一分片的16位标识应均为同一值,接收端则依据这一字段进行报文的重新组合;
- 3位标识:第一位保留位必须为0,第二位表示是否报文是否可分片 0-可分片 1-不可分片,第三位,1-表示分片中段报文,还有后续报文,0-表示已是最后一片报文;
- 13位片偏移:表示如果是分片报文,该分片相较于原始IP报文开始处的偏移量;
- 8位生存时间(TTL):数据到达目的地的最大报文跳数,一般为64,每经过一次路由转发,TTL -1,当减到0时,仍未到达目的地,则丢弃报文。主要用来防范出现路由循环,一个报文在一个循环一直转发,浪费网络资源;
- 8位协议:表示IP的上层是什么协议,例如TCP、UDP、IMCP
- 16位首部校验和:使用CRC校验,鉴别IP首部是否被破坏,只校验头部不校验数据部分(数据部分也有CRC校验例如TCP也有CRC校验和,职责单一,各司其职),校验不过丢弃报文。跟将入参做MD5签名的规则一个道理;
- 32位源IP地址:发送端IP
- 32位目的IP地址:接收端IP
- 选项字段:最大40字节
3.2.2、分片与组装
网络间数据传输,从应用层 ->传输层->网络层->数据链路层->物理层,如上图《一次普通http的请求之旅》,会依次增加各自的协议首部报文头,而网络层IP转给数据链路层时,数据链路层的以太网帧,有长度限制,最大传输单元(MTU),所以过长的IP报文,会被分片发送,而对应的接收主机收到报文后会做组装操作,再还原成一个完整的报文。
3.3、TCP
3.3.1、TCP协议

- TCP首部结构:
- 16位端口号:指定数据的出口端口和目的端口;
- 32位序号:seq,一次TCP文段发送的数据组的第一个字节的序号,一个字节一个序号。例如某次传输报文段的第一个字节序号为300(第一次随机一个序号),报文长度为100个字节,则下一次发送的报文段第一个字节序号为400;
- 32位确认号:ack ,指明下一段报文期待收到的字节序号,表明序号之前的数据已被正确接收。例如收到一段报文的序号为300,报文长度为100字节,ack = 400; 4位头部长度:表示TCP首部长度,单位为4个字节,所以最大长度为 2^4 -1 = 15 * 4字节=60字节,一般默认为20字节
- 6位保留位:未启用,置为0;
- 标志位:
- UGR:紧急指针标志,1-紧急指针有效 0-忽略紧急指针;
- ACK:确认序号标志,1-确认序号有效,0-忽略确认序号字段;
- PUSH:push标志,1-表示接收方在接到报文之后应该尽快将报文交给应用层处理,而不是在缓冲区缓冲。一般而言,一个大报文,会被分为多段报文发送,最后一段报文会加上push标志,告诉接收端可以将数据往应用层送了,接收端收到这个push标志,会将清空本地缓冲区,合并内容,交由应用层开始处理报文;
- RST:重置连接标志,用于主机崩溃或者其他原因出现的错误,要求对方重新确认连接;
- SYN:同步序号,用于连接建立过程,请求建立一个连接;
- FIN:断开连接标志,通知对方断开连接;
- 16位窗口大小:TCP用来控制限制流量的手段,通过此字段告知对象自己本地TCP缓存区还能容纳多少字节的数据,依次来控制发送端发送的频率;
- 16位校验和:发送方赋值,接收方对整个TCP报文,包括TCP首部+TCP数据,做CRC算法校验,确认TCP报文段是否被损坏;
- 16位紧急指针:UGR=1时,有效,正偏移量,它与序号字段的值相加表示最后一个紧急数据的下一个字节序号;
3.3.2、TCP三次握手\四次挥手


PS:实际利用Wireshark抓包,三次握手比较容易,四次挥手比较难,基本都是三次,缺少第二次挥手;按网上资料的说法,是因为ACK有延迟确认机制;
Q:什么是延迟确认机制?
A:即 接收方在接到数据后,并不会立即立即回复ACK而是延迟一段时间,一般ACK的延迟发送时间为200ms,此延迟时间并非指接收到数据响应的延迟时间,而是指系统有一个固定定时器,每隔200ms检查一次是否需要发送ACK包。
这么设计的目的在于,
- ACK可以合并,例如连续收到两个TCP包,并不需要ACK两次,合并在一起回复最终的ACK就行,减少网络流量;
- 如果接收方仍有数据发送,在发送的数据包里会带上ACK信息,避免大量的ACK以独立包发送,减少网络流量;
如上图,红色字体1、2、3为三次握手,很清晰;蓝色字体①②③,则为四次挥手过程,很明显只有三次包记录
4、HTTP
4.1、HTTP简介
HTTP,超文本传输协议(HyperText Transfer Protocol)。
- http协议在TCP/IP之上,属于应用层协议;
- 基于请求-响应模式;
- 无状态;
4.2、HTTP请求与响应
4.2.1、HTTP请求方法
| 请求方法 | 描述 | 使用场景 |
|---|---|---|
| GET | 用于从服务器获取数据,并返回实体主体,用于读取资源和查询数据 | 最常见的请求类型 |
| HEAD | 类似GET请求,区别在于只返回报文头,不返回报文内容 | |
| POST | 向服务器提交数据,将参数包含在请求主体中,一般适用于创建、更新资源 | 常用请求类型 |
| PUT | 用于向服务器更新资源,使用时将更新的数据发送给指定URL,用于替换指定资源的全部内容 | |
| DELETE | 用于从服务器中删除资源 | |
| CONNECT | 用于建立于代理服务器的隧道连接 | |
| OPTIONS | 用于获取服务器支持的http方法和资源的相关信息 | 例如跨域ajax时,利用OPTIONS请求发起预请求,嗅探服务器是否允许跨域请求 |
| TRACE | 回显服务器收到的请求,主要用于测试或诊断 | |
| PATCH | 对PUT方法的补充,用来对已知资源进行局部更新 |
4.2.2、HTTP消息头
4.2.2.1、常用的HTTP请求头
| 协议头 | 说明 | 备注 |
|---|---|---|
| Accept | 可接受的响应内容类型 | text/html,application/xhtml+xml,application/xml |
| Authorization | 授权认证信息 | Authorization:Basic xxxsusdsykdf |
| Cache-Control | 是否使用缓存 | no-cahce |
| Connection | 客户端想使用的连接类型 | Connection:keep-alive, http1.1默认 |
| Cookie | cookie | |
| Content-Length | 以8进制表示的请求体长度 | |
| Content-Type | 请求体的MIME类型(POST和PUT使用) | Content-Type:application/json |
| Host | 服务器域名及服务器暴露的端口号 | |
| If-None-Match | 一般与Etag配合使用,与服务端Etag相同则响应304, 不同则响应 200 | 用于浏览器缓存 |
| If-Modified-Since | 与last-modified配合使用,发送请求时,会将上次响应的last-modified时间带过去,服务端据此判断如何返回,如果时间相同则响应304,否则响应200 | 用于浏览器缓存 |
| If-Match | 一般与Etag配合使用,主要用在PUT方法中。与服务端Etag相同则响应200 OK,否则响应412 | 一般用于多人编辑,如果不相同说明已被别人编辑过,不允许提交 |
| If-Unmodified-Since | 与last-modified配合使用,发送请求时,会将上次响应的last-modified时间带过去,服务端据此判断如何返回,如果相同则响应200,否则响应412 | 一般用于多人编辑;或者与Range配合做断点续传 |
| If-Range | 与Etag或者Last-Modified配合使用,如果服务端校验相同则响应206和指定的部分内容,否则响应200和全部内容 | 断点续传场景下使用 |
| Range | 指定偏移量,获取某个实体的一部分 | 断点续传场景下使用 |
| User-Agent | 浏览器的身份标识符 | |
| Origin | 表示请求的来源,一般在跨域CROS时会使用,服务端判断是否允许跨域访问的依据之一 | |
| Referer | 访问页面的前一个页面,一般在302跳转是会有 | |
| X-Forwarded-For | 客户端访问的原始IP |
4.2.2.2、常用的HTTP响应头
| 响应头 | 说明 | 备注 |
|---|---|---|
| Access-Control-Allow-Credentials | cros跨域请求时是否允许使用cookie | 一般都是设置true |
| Access-Control-Allow-Headers | 跨域请求时是否允许一些特殊的头字段信息 | 只在预检请求时校验 |
| Access-Control-Allow-Methods | 跨域请求时允许的请求方法,GET、POST | 只在预检请求时校验 |
| Access-Control-Allow-Origin | 允许跨域请求的源地址 | 预检请求和正式请求时都会校验 |
| Cache-Control | 缓存机制 | Cache-Control:max-age=3600,no-cache |
| Context-Type | 当前内容的MIME类型 | |
| ETag | 一般与请求头中If-Match、If-No-Match配合使用 | 使用的较多的场景是浏览器缓存 |
| Last-Modified | 一般与请求头中If-Modified-Since、If-UnModified-Since配合使用 | 使用的较多的场景是浏览器缓存技术 |
| Location | 重定向时使用,指定跳转的地址 | IOS app就是通过这种方式 |
| Set-Cookie | 设置HTTP cookie | |
| Status-Code | 响应状态码 |
4.3、HTTP2
4.3.1、HTTP1.1的弊端
- 性能瓶颈
http1.1采用的是串行请求和响应模式,不能充分利用TCP性能。
HTTP Pipelining技术:
将多个http请求放到一个TCP连接中来发送,例如将A请求、B请求一起发送出去,但是服务端仍然按照收到请求的顺序,先响应A请求,再响应B请求。而客户端在未拿到所有请求的响应之前,将会阻塞后面的请求,带来"队头堵塞"(Head-of-line blocking)。
- 头部冗余
HTTP1.1的头部信息没有压缩优化,且每次请求和响应都需要带着完整的头信息,导致头部冗余和带宽浪费。 - 安全性不足:http的弊端,明文传输
4.3.2、HTTP2的改进
- 头部压缩:
HTTP2采用HPACK算法对头部信息进行压缩和优化,减少头部冗余和带宽浪费;简单来说就是Client和Server之间搞了一个类似于哈希表的东西来缓存之前两者发送过的头部信息,后面发送只需要发送索引编号取待完整的头信息,来节省字段。 - 二进制分帧:
HTTP2的一大改进,就用二进制取代了HTTP1.1的文本协议,且将请求和响应分成多个帧,可以并行传输、优先级排序和流量控制,提供性能 - 多路复用:
允许在一个TCP连接上同时传输多个请求和响应,结合二进制分帧,解决了HTTP1.1的对头阻塞问题。 - 服务器推送:
允许将服务器主动向客户端推送资源,例如Client请求Server资源1,资源2, Server在资源1请求时,不仅仅响应了资源1,同时把资源2推送给Client,这样就少了一次请求。 - 安全性提升:HTTP2默认使用TLS进行加密通信。
5、HTTPS
5.1、HTTP的弊端
- HTTP使用明文通信,内容容易被窃听;
- HTTP不需要验证通信方的身份,通信方的身份可能遭遇伪造;
- HTTP报文无法确保其报文的完整性,报文可能会被篡改;
5.2、HTTPS介绍
HTTPS(HTTP Secure),主要而言,HTTPS在原本的HTTP和TCP之间增加了SSL/TLS来做加密处理,HTTP先与SSL/TLS通信,SSL/TLS再与TCP通信, HTTP通过SSL/TLS这个隧道与TCP进行通信。
TLS协议主要由两部分组成:
- 握手协议
握手协议主要处理在通信双方之间进行认证的流程,包括密钥协商,参数协商,建立共享密钥。- 记录协议
记录协议则是使用由握手协议建立的参数来保护通信双方的流量。记录协议讲流量分为一系列记录,每个记录独立使用密码来保护机密性。
5.3、SSL/TLS握手
5.3.1、TLS1.2
C
Client Server
ClientHello ------>
ServerHello
Certificate*
ServerKeyExchange*
CertificateRequest*
<------ ServerHelloDone
Certificate*
ClientKeyExchange
CertificateVerify*
[ChangeCipherSpec]
Finished ------>
[ChangeCipherSpec]
<----- Finished
Application Data <------> Application Data
// *代表可选项
5.3.1.1、RSA加密组件

- client向server发送一个随机数、TLS协议版本、支持的加密套件和压缩算法、上次的session ID(TLS 会话恢复时使用);
- server收到后,返回CA证书、session ID、准备使用的加密套件和压缩算法、同时还会生成一个随机数;
- client收到后,校验CA证书,校验通过之后,计算生成pre_master_secret,并且利用CA证书中公钥及加密算法,加密pre_master_secret,发送给server;
- server收到加密pre_master_secret后,利用私钥解密,得到pre_master_secret;
- client和server均利用pre_master_secret + client.random + server.random,以及协商的算法计算出master_secret;
- client和server互相向对方发送本次握手中所有报文生成的摘要(此也是发出的第一条加密细腻)
- client收到server发送的摘要后,就可以开始正式发送应用数据。
5.3.1.2、DH加密组件
上诉过程简化概述就是如下四步:
- Client -> Server: 请求建立连接;
- Client <- Server: 生成b并计算出B,加上CA证书发给对方;
- Client -> Server: 验证CA证书,生成a并计算出A,发给对方;
- Client、Server分别根据(a, B)、(b, A)计算出K,并进一步计算密钥,分别发送摘要信息
- 后续两方分别用密钥加解密报文进行通信
通过上诉过程可以发现TLS1.2的握手过程,经过了两次RTT(Round Trip Time)
5.3.2、TLS1.3
C
Client Server
ClientHello
+ key_share ---->
ServerHello
+ key_share
{EncryptedExtensions}
{CertificateRequest*}
{Certificate*}
{CertificateVerify*}
{Finished}
<---- [Application Data*]
{Certificate*}
{CertificateVerify*}
{Finished} ----->
<----- [NewSessionTicket]
[Application Data] <-----> [Application Data]
// *代表可选项

- Client 生成 key_share向Server发送,key_share可以理解为一个列表,包含多个<密钥组名,a 和对应使用的p,g以及生成的A>(a不会被发送,这是密钥)
- Server从多个密钥组中选择一个自己能接受的密钥组;如果没有合适的,就会直接返回Server能接受的加密方式,让Client重新生成A,此时1RTT会退化为2RTT。
如果Server已经选定了一组密钥组,则发送选定的密钥组名以及自己利用改组的p,g和自己的b生成的公钥B,同时利用(b, A)计算出K,发送Server.random + 公钥B,然后再导出三个密钥:master_secret、客户端握手密钥(加密Client->Server的握手信息)、服务端握手密钥(加密Server->Client的握手信息)。至此后续的通信均开始加密,发出一条加密的握手摘要信息。 - Client再收到Server的random和公钥B后,利用(a, B)计算出K,导出密钥。接下来解密验证CA证书通过后,利用服务端握手密钥解密收到的Server的握手摘要信息,确认握手过程未被篡改,之后再给Server回复一条使用客户端握手密钥加密之后的握手摘要信息。
- 上诉步骤完成之后,Client和Server两边各自利用master_secret导出如下四个密钥: ①、客户端通信密钥:用来加密Client->Server通信信息;
②、服务端通信密钥:用来加密Server->Client通信信息;
③、恢复密钥:用来参与PSK的计算;
④、导出密钥:用来参与默认的密钥导出计算; - 接下来,Client和Server分别用客户端通信密钥和服务端通信密钥来进行通信。
TLS1.3与TLS1.2相比,最大的两点就是提升了速度和安全性,具体体现在:
- 减少了TLS握手时间,从2RTT缩减到1RTT,并且增加了0RTT模式(体现在TLS会话恢复机制的优化)。简单概括就是取消了之前TLS1.2的密钥协商阶段,将客户端密钥发送的时间提前,提供多组密钥组供服务端选择一个,然后服务端选中密钥组之后,依据算法生成一系列的密钥,同时告诉客户端服务端准备好了,在一次握手完成密钥的交换。
- 更安全,体现在TLS1.3更换了更加安全的加密算法。而且相较于TLS1.2,只有在密钥交换之后的消息,才会加密,例如finished等握手报文摘要。TLS1.3只有第一次握手客户端发送给服务端的消息之外,此后所有的要求都会被加密。
5.4、TLS 会话恢复
HTTPS在HTTP与TCP之间又增加了一层SSL/TLS,虽然解决HTTP的弊端,但是同样也必不可少地带来性能地损耗。client与server只要一旦断开,下一次连接又需要重新做SSL/TLS握手,这也是一笔开销,毕竟两者之前都做过通信,互相验证过身份,所以就有了TLS会话恢复的设计,简单来说就是复用上一次的TLS连接。
5.4.1、TLS1.2
C
Client Server
ClientHello ------>
ServerHello
[ChangeCipherSpec]
<----- Finished
[ChangeCipherSpec]
Finished ------>
Application Data <-----> Application Data
在TLS1.2版本中有两种方式Session ID和Session Ticket
5.4.1.1、Session ID
Session ID在第一次连接时,会生成Session ID, Server同时会在本地维护这个Session ID;当client第二连接时,第一次握手时,只要请求时携带这个Session ID, Server在本地找到存储的Session ID,就可以重复使用对应的通信密钥,不需要再次协商密钥,节省一次RTT。
缺点:
- Server存储无法储存太多Session ID, 内存的消耗在那,所以访问终端多的话效果会打折扣;
- 现在多为分布式系统,单机存储的Session ID, 无法做到集群共享;
5.4.1.2、Session Ticket
为了应对Session ID的缺点,又提出了Session Ticket方案。
相比于Session ID方案,Session Ticket是在第一次连接握手成功后,由Server端将通信密钥利用私钥加密之后的值,发送给Client。Client在第二次连接时,携带ticket,Server接受到ticket如果能成功解密,则可以得到上次的通信密钥,从而达到节省一次RTT。
5.4.2、TLS1.3
TLS1.3会话恢复最大的特点就是能够做到0RTT,而做到这点的关键设计就是PSK(pre share key)。
PSK什么时候生成?
在握手流程结束后,server会给client发送任意数量的ticket,且这些ticket是有有效期的,为了减少泄露带来的损害。然后client会用ticket和master_secret导出的恢复密钥计算出PSK,并保存下来,供下次连接时使用。
TLS1.3 0RTT会话恢复的简单过程? Client在做第二次连接时,除了正常请求的TLS协议版本、加密密钥组、Client.random之外还会利用上次的PSK导出三个密钥,binder密钥、早期数据加密密钥、早期导出密钥。请求中会增加上次保存的PSK对应的ticket并用binder加密做加签处理,防止篡改;应用数据则用早期数据加密密钥进行加密;一起发动给服务端。
服务端在收到客户端请求后,发现有ticket信息,则会验证ticket的真伪,验证通过则利用该ticket和之前master_secret导出的恢复密钥,来生成PSK,再利用这个PSK导出早期数据加密密钥,解密应用数据。之后再重新选择一个密钥组,生成新的密钥,此流程就类同与第一次连接时,一通操作后,返回给客户端CA证书以及生成新密钥所需的信息,再加上新密钥加密的响应应用数据,客户端收到后,同样生成新密钥,解密响应应用数据。并且再发送一条finished的握手报文摘要,告知server握手结束。
如果上诉收到client的ticket, server没法验证,则回退1RTT,当第一次连接处理。
java io
IO流为什么一定要手动关闭?
IO操作的JVM之外的内容,不在虚拟机管理职责范围之类,需要显示调用接口手动关闭。