【面试必备】网络基础知识

62 阅读21分钟

最近复习过程中积累的笔记,本文内容主要是关于 TCP/IP 协议各层知识点。内容来源五花八门,如有不妥或错误欢迎指出。

TCP/IP 网络模型

  1. 应用层,提供特定于应用服务的协议,如 DNS,HTTP,FTP(文件传输协议),IMAP(电子邮件客户端检索协议)。浏览器通过 DNS 获取到 IP 地址后通过调用 Socket 库来委托 协议栈 工作。协议栈的上半部分包括 TCP 协议和 UDP 协议,下半部分包括 IP 协议。
  2. 传输层,负责端到端的通信,如TCP,UDP。
  3. 网络层,使用 IP 地址将数据包发送到特定的计算机,如 IP协议。
  4. 网络接口层,负责二进制数据包在物理网络中的传输。

OSI 七层模型

  • 应用层,负责给应用程序提供统一的接口;
  • 表示层,负责把数据转换成兼容另一个系统能识别的格式;
  • 会话层,负责建立、管理和终止表示层实体之间的通信会话;
  • 传输层,负责端到端的数据传输;
  • 网络层,负责数据的路由、转发、分片;
  • 数据链路层,负责数据的封帧和差错检测,以及 MAC 寻址;
  • 物理层,负责在物理网络中传输数据帧;

从输入URL到页面加载

  1. 首先进行 URL 解析。
  2. DNS 将域名解析为 IP 地址。
  3. 通过 TCP 三次握手建立可靠连接。
  4. 如果是 HTTPS 连接,进行 TLS 握手。
  5. 客户端发送 HTTP 请求。
  6. 服务端处理 HTTP 请求、返回响应。
  7. 浏览器接收响应,解析渲染页面。

HTTP 与 HTTPS 的区别?

HTTP 连接是无状态的。

HTTP 协议是明文传输,客户端与服务端之间通信的内容可以被直接截获,默认端口为 80。HTTPS 协议相比于前者更加安全,加入了 SSL 加密传输协议,默认端口为 443。

HTTPS 连接流程:如果使用 HTTPS 协议,那么在 TCP 三次握手之后,会开始 TLS 握手,传统的 TLS 握手基本都是使用 RSA 算法来实现密钥交换的,会经过四次 TLS 握手。

  1. 客户端向服务器发起 ClientHello 加密通信请求,包含客户端使用的 TLS 版本号、支持的密码套件列表和生成的随机数(Client Server)。
  2. 服务端器接收到请求之后,会确认 TLS 版本号是否支持,并选择一个密码套件,生成随机数(Server Random),接着发送 ServerHello 响应,服务端还会给客户端发送数字证书,并发送 Servcer Certificate 消息,随后发送 ServerHelloDone 消息。
  3. 客户端验证数字证书合法性,并生成新的随机数(pre-master),使用数字证书中的 RSA 公钥加密该随机数,发送 Clien Key Exchange。至此,双方共享了三个随机数并生成会话密钥(Master Secret)用于对后续的 HTTP 请求数据加解密,客户端发送 Encrypted Handshake Message(Finishd) 消息。
  4. 服务端进行同样的操作,发送 Change Cipher SpecEncrypted Handshake Message(Finishd) ,之后客户端和服务端之间利用生成的密钥进行对称加密。

公钥和私钥是非对称加密中的一组密钥,通常用于数据加密、数字签名、身份认证等。核心思想是:公钥加密,私钥解密(用于加密数据);私钥签名,公钥验证(用于数字签名)。

数字证书用于认证公钥持有者(服务器)的身份,以防止第三方进行冒充。通常包含了公钥、持有者信息、证书认证机构(CA)的信息、CA 对这份文件的数字签名及使用的算法、证书有效期和一些其他额外信息。数字签名的签发和验证流程是:首先,CA 将信息打包并用 Hash 算法生成 Hash 值,接着用私钥签名生成 Certificate Signature。客户端验证时是将这些信息用同样的 Hash 算法生成 Hash 值 H1,服务端用公钥验证 Certificate Signature 得到 Hash 值 H2,比较 H1 和 H2 如果相同则为可靠证书。

通常我们在 TLS 加密中得到的数字证书不是根证书(为了确保根证书的安全性),而是由中间证书。于是存在证书信任链的问题:浏览器和操作系统内置了一个受信任的根证书列表,当我们收到一个中间证书,会沿着证书链逐级验证,最终必须追溯到一个信任的根证书才会确认该证书可信。

中间人攻击(Man-in-the-Middle Attack, MITM)就是在 HTTPS 加密通信中,攻击者伪造证书冒充服务器,进而篡改或窃取通信数据的⾏为。

HTTP/1.0 1.1 2 3 的演变

  1. HTTP/1.0 是短连接,在一个 TCP 连接中进行一次 HTTP 请求并收到响应之后,需要再次建立 TCP 连接,导致高延迟。并且⼀个 IP 只能绑⼀个域名,不支持虚拟主机。

  2. HTTP/1.1 于1997年提出,允许一个 TCP 连接中可以多次发起 HTTP 请求,用长连接的方式改善了短连接造成的性能开销。

    但是,目前的 HTTP/1.1 依然有很大的性能问题

    主要的性能问题是高延迟。虽然 HTTP/1.1 支持了长连接,但是要求一次 HTTP 请求收到响应之后,才能发起第二次 HTTP 请求,造成了 HTTP 队头阻塞。而且浏览器对 TCP 连接有最大并发数限制(为了防止 ddos 攻击),例如谷歌浏览器最大并发连接数是 6 个,再加上 TCP 握手慢启动的耗时,都会造成更高的延迟。虽然 HTTP/1.1 支持管道(pipeline)传输,也就是可以同时发送多个请求,但是管道运输要求按照发送的顺序接收响应,这造成实际应用困难,因此很少使用。

    另一个性能问题是 HTTP 头部开销巨大。由于 HTTP是无状态的(Stateless),这意味着服务器不会主动记录每次 HTTP 请求的上下文状态,因此浏览器的每次请求都需要携带 HTTP 头部。

    HTTP/1.1 也不支持服务器推送。虽然 TCP 提供了全双工双向通信,但是 HTTP/1.1 并没有充分利用这个能力,只允许单向的浏览器请求、服务器响应,当客户端需要获取通知时,只能通过定时器不断地拉取消息(Websocket 也是为了应对这个问题)。

    我们可以用一些方法来优化 HTTP/1.1 的性能

    第一是使用缓存来避免不必要的请求,例如强制缓存与协商缓存将资源缓存在本地磁盘。

    第二也是减少不必要的请求,例如按需加载资源、将多个小资源合并成大资源来一次请求,如精灵图(雪碧图)。

    第三是压缩响应数据的大小,从而减少传输时间。可以使用请求头 Content-Encoding 表示浏览器支持的压缩方法,服务端据此返回压缩后的数据。

  3. HTTP/2 于2015年提出,支持多路复用、头部压缩、服务器推送、二进制格式来应对 HTTP/1.1 的各种问题。

    多路复用:在同一个 TCP 连接中,可以同时传输多个请求和响应,并且不需要等待其他请求完成 ,避免了 HTTP 队头阻塞。这种并发主要是通过 Stream、Message、Frame 这三个概念实现的,具体是指一个 TCP 连接中包含一个或者多个 Stream,不同 Stream 的帧可以乱序发送,因为每个帧的头部会携带 Stream ID 信息,所以接收端可以通过 Stream ID 有序组装成 HTTP 消息。Stream 里可以包含 1 个或多个 Message,Message 指的是一个请求或响应,Message 里包含一条或者多个 Frame,是以二进制压缩格式存放的头部或消息体。但是 HTTP/2 存在 TCP 队头阻塞问题,即 TCP 需要保证顺序,如果发生丢包就会触发超时重传机制,其他的所有响应要等找到这个丢失的包才能传输。

    头部压缩(静态表+动态表+Haffman编码) :如果同时发出多个请求,他们的头是一样的或是相似的,协议会消除重复的部分。并且 HTTP/2 为高频出现在头部的字符串和字段建立了一张包含 61 组头部信息的固定静态表,静态表用长度较小的索引号(index)表示重复的头部字段名(Header Name)和值(Header Value),再用 Huffman 编码压缩数据,Huffman 编码的原理是将高频出现的信息用较短的编码表示,从而缩减字符串长度。不在静态表范围内的头部字符串就要自行构建动态表,动态表生效的前提是在同一个连接上,重复传输完全相同的 HTTP 头部,如果头部只发送一次或每次略有不同,动态表就无法很好地利用。

    二进制格式:HTTP/1.1 使用纯文本形式的报文,HTTP / 2 使用二进制形式的帧(frame),分为头信息帧(Headers frame)和数据帧(Data frame)。

    服务器推送:服务端不再是被动地响应,可以主动向客户端发送消息。

    但是因为技术迁移成本以及 TCP 队头阻塞等问题,HTTP2 至今并未得到广泛使用。

  4. HTTP/3 于2019年提出,使用 UDP 代替 TCP 来避免可能存在的 TCP 队头阻塞,并基于 UDP 协议在应用层实现了 QUIC 协议,它具有类似 TCP 的连接管理、拥塞窗口、流量控制的网络特性,相当于将不可靠传输的 UDP 协议变成可靠的了。

DNS 与 CDN 是什么?

DNS(域名系统)保存了域名与IP地址的对应关系,DNS 解析是应用层的部分,任务是将人类可读的域名(如 www.example.com )转换为计算机可读的IP地址(如 8.8.8.8)。DNS 会首先检查浏览器中是否存在缓存,如有则直接返回 IP 地址,如没有则继续查询操作系统缓存,如有则直接返回,如没有则继续查询 host 文件缓存,如有则直接返回,如没有则查询 本地 DNS 解析器(通常由 ISP 提供)。如果本地没有缓存,则递归查询根域名服务器、顶级域名服务器、权威域名服务器

DNS 中的域名都是用句点来分隔的,越靠右等级越高。实际上域名最后还有一个点,比如 www.server.com.,这个最后的一个点代表根域名。也就是,. 根域是在最顶层,它的下一层就是 .com 顶级域,再下面是 server.com。所以域名的层级关系类似一个树状结构:根 DNS 服务器(.)、顶级域 DNS 服务器(.com)、权威 DNS 服务器(server.com)。

CDN(内容分发网络) 提供了一系列边缘服务器,使浏览器可以从最近的边缘服务器获取数据,而不是直接访问源服务器,从而提高加载速度、减轻源服务器压力。

TCP 与 UDP 的区别?

UDP 是无连接、不可靠,面向报文的协议,但开销更小,适用于实时传输,面向报文意味着操作系统不会对消息进行拆分,也就是每个 UDP 报文就是一个用户消息的边界,但可能丢失、重复、乱序。

TCP 是面向连接的可靠的字节流协议。

在一个 TCP 连接中,通信双方会明确 Socket(由 IP 地址和端口号组成)、序列号(解决乱序问题)、窗口大小(用于流量控制)。

TCP 必须经过三次握手才能建立连接,一个 TCP 连接通过四元组源地址、源端口、目的地址、目的端口(也就是两个 Socket,源地址与源端口是一个,目的地址与目的端口是另一个)唯一标识。

我们说 TCP 面向字节流,这意味着消息可能会被操作系统分组成多个 TCP 报文,而 IP 协议传输时也会将较大的数据包分包。因此,TCP 要通过序列号来保证数据准确无误地按序传输。

TCP 还通过 重传机制、流量控制、拥塞控制等特性来保证可靠传输。

重传机制

  • 超时重传:TCP 协议下,收到每个数据包时,接收方都会向发送方发送 ACK 确认。发送方在发送数据时会设定一个定时器,当超过指定的时间后,没有收到 ACK 确认应答报文,就会重发该数据。
  • 快速重传:接收方如果收到一个乱序的数据包(比如收到了Seq=5的包,但Seq=4还没到),会立即重复发送上一个已经确认的包的ACK。发送方如果连续收到3个重复的ACK,就推断出这个ACK后面紧跟的那个数据包很可能丢失了,于是立刻重传那个被认为丢失的数据包。
  • 选择确认(Sack):配合快速重传,能精确重传多个丢失包。

流量控制:防止接收方缓冲区溢出。接收方在每次发送ACK确认报文时,都会在TCP首部带上接收窗口 rwnd。发送方则维护发送窗口不能超过接收方的接收窗口大小。

拥塞控制:防止过多的数据导致网络压力过载。发送方有慢启动,通过感知网络状态,主动自我限制发送速率,维护 拥塞窗口 cwnd 。真正的发送窗口大小 = min(rwnd, cwnd)

IP 协议有什么用?

IP 协议的任务在于寻址和路由

IP地址:IP 地址分为网络号主机号,网络号标志该 IP 地址属于哪一个子网,主机号标志同一子网下的不同主机。例如 10.100.122.0/24 中,10.100.122.0 是 IP 地址,/24 是子网掩码 255.255.255.0 。子网掩码与 IP 地址按位与运算,得到网络号;子网掩码取反后与 IP 地址进行按位与运算,得到主机号。寻址过程中,先匹配到相同的网络号,才会去找对应的主机。

IPv4与IPv6:IPv4有地址长度为32位,有2^32个地址数量,地址表示为点分十进制(192.168.1.1) 。IPv6有地址长度为128位,有2^128个地址数量,地址表示为冒分十六进制(2001:0db8::1)

ICMP和ARPIP 中还有ICMP 协议和 ARP 协议,ICMP 用于告知网络包传送过程中产生的错误以及各种控制信息,ARP 用于根据 IP地址 查询相应的 MAC地址

常见HTTP状态码

1xx:信息状态码,表示请求已被接收,需要继续处理

  • 100 Continue 客户端应继续发送请求的剩余部分
  • 101 Switching Protocols 服务器同意切换协议
  • 102 Processing 服务器已收到请求,正在处理但尚未完成
  • 103 Early Hints 在完整响应之前返回一些响应头,用于预加载关键资源

2xx:成功

  • 200 OK 请求成功,服务器返回正常数据
  • 201 Created 请求成功,并创建了新的资源(常用于 POST / PUT 请求)
  • 202 Accepted 服务器已接受请求,但尚未处理完成(异步任务)
  • 204 No Content 请求成功,但服务器没有返回内容(常用于 DELETE 请求)
  • 206 Partial Content 服务器返回的 body 数据是资源的部分内容而非全部(常用于断点续传)

3xx:重定向状态码

  • 301 Moved Permanently 永久重定向,说明请求的资源已经不存在,需要用新的 URL 再次访问并缓存新的 URL
  • 302 Found / Moved Temporarily 临时重定向,说明请求的资源存在,但是需要暂时用其他 URL 访问
  • 303 See Other 重定向到其他资源,常用于 POST / PUT 方法的响应中
  • 304 Not Modified 资源未修改,用于缓存控制
  • 307 Temporary Redirect 临时重定向,类似于302,但必须使用原请求方法
  • 308 Permanent Redirect 永久重定向,类似于301,但必须使用原请求方法

4xx:客户端错误

  • 400 Bad Request 客户端请求的语法错误(如查询参数错误)
  • 401 Unauthorized 合法请求,但未授权(用户未登录),需要提供身份认证信息
  • 403 Forbidden 合法请求,但服务器拒绝访问(用户无权限)
  • 404 Not Found 页面找不到,一般是请求路径错误

5xx:服务端错误

  • 500 Internal Server Error 服务器内部错误,一般是代码异常
  • 502 Bad Gateway 网关错误,服务器充当网关或者代理的角色时,从上游服务器收到一个无效的响应
  • 503 Service Unavailable 服务器不可用(如服务器过载、维护)
  • 504 Gateway Timeout 网关超时,服务器充当网关或者代理的角色时,未能从上游服务器及时收到响应
  • 505 HTTP Version Not Supported 服务器不支持请求的 HTTP 版本

常见HTTP头

客户端请求头

GET ws://api.other-domain.com/chat HTTP/1.1
Host: example.com                                     // 目标域名
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) // 客户端信息
Accept: text/html, application/json                   // 客户端支持的响应类型
Accept-Encoding: gzip, deflate, br                    // 客户端支持的压缩算法
Content-Type: application/json                        // 请求体的类型
Cache-Control: public, max-age=3600                   // 强制缓存头
Expires: Wed, 21 Oct 2023 07:28:00 GMT                // 强制缓存头
If-Modified-Since: Wed, 21 Oct 2023 07:28:00 GMT      // 协商缓存头
If-None-Match: "33a64df551425fcc55e4d42a148795d9"     // 协商缓存头
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Cookie: sessionId=abc123; theme=dark
Origin: https://example.com                           // 域
Access-Control-Request-Method: POST                   // 预检请求方法
Access-Control-Request-Headers: Content-Type          // 预检请求头
Connection: keep-alive                                // 连接控制
Upgrade: websocket                                    // 协议升级

服务端响应头

GET ws://api.other-domain.com/chat HTTP/1.1
Content-Encoding: gzip                                // 响应体的压缩算法
Content-Type: text/html; charset=utf-8                // 响应体的类型
Cache-Control: public, max-age=3600                   // 强制缓存头
Expires: Wed, 21 Oct 2023 07:28:00 GMT                // 强制缓存头
Last-Modified: Wed, 21 Oct 2023 07:28:00 GMT          // 协商缓存头
ETag: "33a64df551425fcc55e4d42a148795d9"              // 协商缓存头
Strict-Transport-Security: max-age=31536000           // 强制HTTPS
Content-Security-Policy: default-src 'self'           // 内容安全策略(应对xss)
Access-Control-Allow-Origin: *                        // 允许的域
Access-Control-Request-Method: POST                   // 允许的方法
Access-Control-Request-Headers: Content-Type          // 允许的头
Access-Control-Allow-Credentials: true                // 允许凭据 
Set-Cookie: authToken=xyz789; Secure; HttpOnly; SameSite=Strict       // HttpOnly防止xss盗取cookie,SameSite防止登录CSRF,Secure要求以HTTPS传输cookie
Location: https://new.example.com                     // 重定向地址

XSS、CSRF 是什么?

跨站脚本攻击(XSS) 主要指攻击者在网页中注入恶意脚本诱导用户执行,从而窃取数据或执行恶意操作。应对方式主要包括:

  1. 服务端过滤与转义用户输入。

  2. 服务端配置使用内容安全策略,使用 Content-Security-Policy 响应头,限制浏览器能执行的资源。

    Content-Security-Policy: default-src 'self'
    
  3. 服务端在 Set-Cookie 设置 HttpOnly,使其不能被脚本读取。

跨站请求伪造(CSRF) 是指攻击者诱导用户在已登录的情况下,向目标网站发送恶意请求,从而伪造用户请求(如转账、修改密码等)。应对方式主要包括:

  1. 服务端在 Set-CookieSameSite=Strict 选项,限制 cookie 跨站点传递。 SameSite 有 Strict、Lax 和 None 三个值,Strict 最严格,完全禁止第三方 Cookie,Lax 相对宽松,使用 None 的话在任何情况下都会发送 Cookie。
  2. 在关键操作如转账、改密码时,要求用户输入密码、验证码进行双重验证。
  3. 服务端在用户登录或访问页面时,生成随机的 csrf token 放在浏览器网页中,后续请求时浏览器会将这个 token 发送到服务端验证请求站点,恶意网站因为同源策略而不能读取 token 从而无法校验。

TCP三次握手的过程

第一次握手:建立连接时,客户端发送SYN包(syn=j)到服务端,并进入SYN_SENT状态,等待服务器确认。

第二次握手:服务端收到SYN包,发送ACK包(ack=j+1),同时也发送一个自己的SYN包(syn=k),即SYN+ACK包,此时服务端进入SYN_RECV状态;

第三次握手:客户端收到服务端的SYN+ACK包,向服务端发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务端进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

三次握手保证双方都有发送和接收信息的能力

TCP四次挥手的过程

第一次挥手:客户端停止发送数据,发送连接释放报文(FIN=1,seq=u),进入FIN-WAIT-1(终止等待1)状态。

第二次挥手:服务端收到连接释放报文,发出确认报文(ACK=1,ack=u+1),服务端进入CLOSE-WAIT(关闭等待)状态。此时,服务端还可以向客户端发送数据。客户端收到服务端的确认报文后,进入FIN-WAIT-2(终止等待2)状态,并接收服务端发送的数据。

第三次挥手:服务端将最后的数据发送完毕后,向客户端发送连接释放报文(FIN=1,seq=v),服务端进入了LAST-ACK(最后确认)状态。

第四次挥手:客户端收到服务端的连接释放报文后,发出确认报文(ACK=1,ack=v+1),客户端进入了TIME-WAIT(时间等待)状态,经过2MSL(2*最大报文段生命周期)时间后进入CLOSED(关闭)状态。服务器只要收到了客户端发出的确认,立即进入CLOSED(关闭)状态。

WebSocket

TCP 连接是全双工的,即双方在同一时间都可以主动向对方发送数据。但是通用的 HTTP / 1.1 是半双工,适用于网页文章的场景,只需要客户端请求、服务端响应。对于扫码登录的简单场景,可以用定时轮询、长轮询来伪造服务器推送的效果。但游戏网页是客户端和服务器之间都要互相主动发大量数据的场景,这时就需要基于 TCP 的支持全双工的 WebSocket

如果要建立 WebSocket 连接,浏览器将在 TCP 三次握手建立连接之后发起一个 HTTP Upgrade Request(HTTP 升级请求)。

GET ws://api.other-domain.com/chat HTTP/1.1
Host: api.other-domain.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== // 浏览器随机生成的密钥
Sec-WebSocket-Version: 13
Origin: https://your-site.com

如果服务器支持,会返回状态码 101 Switching Protocols 和其他信息。如果浏览器解码返回的字符串一致,则 WebSocket 握手验证通过。

HTTP/1.1 101 Switching Protocols
Sec-WebSocket-Accept: iBJKv/ALIW2DobfoA4dmr3JHBCY=
Upgrade: WebSocket
Connection: Upgrade

为什么要有 RPC?

纯裸 TCP 是能收发数据,但它是个无边界的数据流,易发生粘包等问题,因此上层需要定义消息格式用于定义消息边界。于是,HTTP 和各类 RPC 协议就是在 TCP 之上定义的应用层协议。

RPC 出现更早,早期 HTTP 主要用于 B/S 架构,而 RPC 更多用于 C/S 架构。RPC 的定制化程度更高,gRPC 底层直接用 HTTP / 2 ,性能也更好,因此,企业内部集群的微服务之间可能会采用 RPC 协议进行通讯。

Cookie、Session、Token

Cookie 存储在浏览器,每次请求时会自动附带相关 Cookie。明文存储,容易被 CSRF 攻击窃取。

Session 存储在服务端,每个用户通常有唯一的 Session ID 标识,Session ID 通常存储在 Cookie 中,或通过 URL 参数传递。

Token 储存在客户端(Cookie 或 LocalStorage 中),本质是一个令牌,需要开发者手动通过请求头(如Authorization)携带,因此安全性更高。例如,恶意⽹站⽆法通过诱导浏览器发送请求得到 Token(CSRF 攻击),而是必须先通过 XSS 窃取 Token 再⼿动构造请求,难度⼤幅提升。我们在项⽬中将 setCookie 设置为 HttpOnly: true, SameSite=Strict, secure: true 保护 Cookie,⽽ Token 则结合短期有效期和内存存储,双重保障⽤户安全。