本文是在阅读JavaGuide关于计算机网络相关内容时的个人整理;

请移步:https://javaguide.cn

OSI七层模型

很清晰、理论完整,但是太复杂且不适用,所以更多使用tcp/ip协议

  1. 应用层:为用户提供了与网络进行交互的接口;
  2. 表示层:进行一些数据处理,例如加密解密、编码解码、压缩和解压缩
  3. 会话层:管理应用程序之间的会话
  4. 传输层:为会话之间的通信提供可靠的数据传输
  5. 网络层:IP数据包在传输时的路由和寻址
  6. 链路层:对数据帧进行编码、对传输的误差进行纠正
  7. 物理层:传输比特流数据

image-20260131113557915

image-20260131113621463

TCP/IP四层模型

应用层

应用层:以报文数据单元,为用户应用程序提供网络服务接口

常见协议:

  1. 基于TCP:
  • HTTP协议(HTTPs):基于 TCP 协议,是一种用于传输超文本和多媒体内容的协议,为 Web 浏览器与 Web 服务器之间的通信而设计的
  • Websocket协议:客户端和服务端可以同时发送或接收,基于 TCP 连接的全双工通信,通过心跳机制来保持 WebSocket 连接的稳定性和活跃性
  • SMTP协议(简单邮件传输(发送)协议)、IMAP/POP3协议(负责邮件接收的协议)
  • FTP协议:文件传输,使用两条tcp连接,一条用于传输控制信息,一条用于数据传送;不安全,SFTP安全
  • SSH协议:通过加密认证机制实现安全的访问和文件传输等业务;Telnet协议:用于终端登录服务器,明文传输不安全
  1. 基于UDP
  • RTP协议⚠️、WebRTC⚠️

传输层

传输层:提供端到端的通用的数据传输

TCP协议的三次握手和四次挥手:面向连接的、可靠的

UDP协议:无连接的、尽力的

网络层

网络层:为不同网络之间的主机提供通信服务

封装成IP数据包,解析IP地址,选择合适的路由

常见协议:IP协议(定义数据包的格式、对数据包进行路由和寻址)、ARP协议(网络层地址和链路层地址之间的转换问题)、NAT协议(网络地址转换,内部网到外部网)、ICMP协议⚠️、OSPF协议⚠️、RIP协议⚠️

网络接口层

网络接口层:把IP数据包组装成帧,并在相邻计算机节点之间传输比特流,并提供差错检测和修复等等

HTTP相关

> 以下摁背

HTTP1.0和1.1

  1. 缓存:见缓存
  2. 长短连接:HTTP1.0使用短连接,每次HTTP操作都需要建立tcp连接,会导致握手挥手报文大量占据带宽;长连接可以设置超时时间,减少保持tcp连接的资源浪费。
  3. Host头:域名系统(DNS)允许多个主机名绑定到同一个 IP 地址(服务器)上,所以请求中需要包含域名信息,这样服务端才能理解客户端的需求
1
2
GET /home.html HTTP/1.1
Host: example1.org
  1. 带宽优化:加入Range头部,可以请求**数据(只能是字节型)**的一部分,支持断点续传,例如下载文件到一半
1
2
3
4
5
6
7
8
9
10
11
# 请求:获取一个文件的前 1024 个字节
GET /z4d4kWk.jpg HTTP/1.1
Host: i.imgur.com
Range: bytes=0-1023

# 相应:206状态码
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-1023/146515
Content-Length: 1024

(二进制内容)

HTTP1.1和2.0

  1. 多路复用:HTTP/2.0 在同一连接上可以同时传输多个请求和响应
  2. 二进制帧:HTTP/1.1 使用文本格式的报文,HTTP/2.0 使用二进制帧进行数据传输,减少了传输的数据量和带宽消耗
  3. 头部压缩:HTTP/1.1 支持Body压缩,但Header不支持。HTTP/2.0 支持对Header压缩,使用的是HPACK算法
  4. 服务推送:在客户端请求一个资源时,服务器可以将其他相关资源一并推送给客户端,从而减少了客户端的请求次数和延迟。

HTTP2.0和HTTP3.0

  1. 基于UDP:新增QUIC(Quick UDP Internet Connections)协议来实现可靠的传输
  2. 握手合并:QUIC 将 TCP 握手、TLS 握手 (内置 TLS 1.3 加密) 以及 HTTP/3 的连接握手合并,并使用更高效的 QPACK 进行头部压缩。
  3. 无缝迁移:不再依赖 IP 地址和端口,而是通过 Connection ID 来标识;客户端在使用不同网络时(如 Wi-Fi 和 4G 之间切换)时,连接可以无缝迁移

HTTP和HTTPS

  1. HTTP(明文传输, 没有状态):默认端口80,规范浏览器和服务器端的行为。传输的都是明文,不安全。

    无状态(stateless)协议,服务器不维护任何有关客户端过去所发请求的消息,有状态协议会更加复杂,需要维护状态(历史信息),而且如果客户或服务器失效,会产生状态的不一致,解决这种不一致的代价更高。

  2. HTTPS(加密传输, 证书认证):默认端口号是 443。额外使用 SSL/TLS 协议用作加密和安全认证。

先使用非对称加密传输对称加密的密钥,来保证密钥的安全性,即服务器先把它的公钥传输给客户端,客户端生成对称加密的密钥后,使用服务器的公钥传输过去,服务器用私钥解密;之后再进行对称加密的通信。

可能会存在以下使用诈包的攻击手段,所以服务器得进行第三方实名认证,这个就是证书(CA, Certificate Authority)的作用

诈包攻击

图源:JavaGuide

HTTP缓存

强制缓存

强制缓存优先于协商缓存进行

  1. HTTP1.0使用Expires,它存储一个过期时间,如果在这个时间内就说明缓存有效,但是依赖于浏览器时间

  2. HTTP1.1使用cache-control字段,有6个字段,常用的是max-age表示缓存的有效时间,例如Cache-Control: max-age=10

cache-control字段:

  • max-age决定客户端资源被缓存多久。
  • s-maxage决定代理服务器缓存的时长。
  • no-cache表示是强制进行协商缓存。
  • no-store是表示禁止任何缓存策略。
  • public表示资源即可以被浏览器缓存也可以被代理服务器缓存。
  • private表示资源只能被浏览器缓存。

协商缓存

需要向服务器发起请求进行协商,由服务器决定是否使用缓存

  1. HTTP1.0使用Last-Modified / If-Modified-Since

If-Modified-Since是客户端再次发起该请求时,携带上次请求返回的Last-Modified值,通过此字段值告诉服务器该资源上次请求返回的最后被修改时间。服务器收到该请求,发现请求头含有If-Modified-Since字段,则会根据If-Modified-Since的字段值与该资源在服务器的最后被修改时间做对比,若服务器的资源最后被修改时间大于If-Modified-Since的字段值,则重新返回资源,状态码为200;否则则返回304,代表资源无更新,可继续使用缓存文件;

  1. HTTP1.1使用Etag / If-None-Match

If-None-Match是客户端再次发起该请求时,携带上次请求返回的唯一标识Etag值,通过此字段值告诉服务器该资源上次请求返回的唯一标识值。服务器收到该请求后,发现该请求头中含有If-None-Match,则会根据If-None-Match的字段值与该资源在服务器的Etag值做对比,一致则返回304,代表资源无更新,继续使用缓存文件;不一致则重新返回资源文件,状态码为200;

常见状态码

  1. 1xx(临时响应):表示临时响应并需要请求者继续执行操作的状态码。

101:切换协议,切换到更高级的协议,例如HTTP新版

  1. 2xx(成功):表示成功处理了请求的状态码。

201:请求成功创建一个或多个新的资源

204:请求成功,但没有结果返回(不看重请求结果,只关心是否完成)

206:请求一部分资源,成功响应一部分资源(HTTP1.1断点续传)

  1. 3xx(重定向):要完成请求,需要进一步操作。通常,这些状态码用来重定向。

301:Moved Permanently,资源被永久重定向。比如网站的网址被更换。

  1. 4xx(客户端请求错误)

400 Bad Request:发送的 HTTP 请求存在问题。比如请求参数不合法、请求方法错误。

401 Unauthorized:未认证却请求需要认证之后才能访问的资源。

403 Forbidden:直接拒绝 HTTP 请求,不处理。一般用来针对非法请求。

404 Not Found:你请求的资源未在服务端找到。比如你请求某个用户的信息,服务端并没有找到指定的用户。

409 Conflict:表示请求的资源与服务端当前的状态存在冲突,请求无法被处理。

  1. 5xx(服务器错误)

500 Internal Server Error:服务端出问题了(通常是服务端出 Bug 了)。比如你服务端处理请求的时候突然抛出异常,但是异常并未在服务端被正确处理。

502 Bad Gateway:我们的网关将请求转发到服务端,但是服务端返回的却是一个错误的响应,即网关正常运行,但是网关得到的响应是错误的。

请求头和请求体重要字段

  1. Content-Type的取值
  1. Cookie 是一种在客户端存储数据的技术,它是由服务器发送给客户端的小型文本文件,存储在客户端的浏览器中,大小限制大致在 4KB 左右。在客户端发送请求时,浏览器会自动将相应的 Cookie 信息发送给服务器,服务器通过读取 Cookie 信息,就可以判断该请求来自哪个客户端。Cookie 可以用于存储用户的登录状态、购物车信息等。

  2. session:每一个客户端与服务端连接,服务端都会为该客户端创建一个 session,并将 session 的唯一标识 sessionId 通过设置 Set-Cookie 头的方式响应给客户端,客户端将 sessionId 存到 cookie 中。

sessionId 是 cookie 和 session 之间的桥梁。

  1. sessionId是无意义的,得服务端存储session,然后比对一下才知道sessionId对应哪个用户;但是Token自带用户的信息(虽然也因此长度比较长),不需要服务端存储什么东西,只要解析一下token就知道这是谁了;

    token也是服务端生成传给客户端的,一般放在local storage里面而不是cookie,一方面这是约定成俗的事情,另一方面也是为了安全防护,例如CSRF攻击。

    CSRF攻击是因为浏览器发送请求会自动携带cookie,而token不放cookie里,所以天然防御,但是放local storage中也有风险

TCP相关

可靠性保证

  1. 基于数据块传输:分割成数据块(报文段)再给网络层,网络层也可能进一步分割再形成ip数据包;
  2. 报文段序列号可以用来重新排序和去重;
  3. 报文段包含首部数据,接收后会进行校验和,确保数据没有发生差错;
  4. 流量控制和拥塞控制

访问网页的全过程

  1. DNS解析,得到IP地址
  2. 根据IP地址建立TCP连接
  3. 在TCP连接上发送HTTP请求报文
  4. 服务器处理请求,返回HTTP响应
  5. 解析响应体中HTML代码并渲染
  6. 对HTML中其他需要的资源再次发起请求
  7. 可以主动关闭TCP连接,或等待服务器关闭

ARQ协议

  1. 停止等待ARQ协议(这个没有用到滑动窗口协议)

每发完一个数据包就停止发送,等待对方确认(回复 ACK);如果过了一段时间(超时时间后),还是没有收到 ACK 确认,说明没有发送成功,需要重新发送,直到收到确认后再发下一个数据包。

1.1. 发送的数据包丢了:设置一个定时器,自动重传;

1.2. 确认信息丢了or迟到:发送方没收到确认信息,重传,重复的信息直接丢弃。

  1. 回退 N 步协议 (Go-Back-N ARQ, GBN)

引入了滑动窗口的概念,位于发送窗口内的分组可以连续发送出去,而不需要等待对方确认。

2.1. 接收方累计确认(对最后一个数据包发送确认),信道利用率高;接收方会设置一个计时器(例如每隔500ms发送一次累计确认)

2.2. 滑动窗口中间某个丢了(发送方收到确认时间超时),需要让发送方把丢失的后续都传一遍,导致宽带浪费。

  1. 选择重传协议 (Selective Repeat ARQ, SR)

发送方和接收方都有滑动窗口,接收方会缓存乱序到达的包,发送方只重传那个出错的包

需要接收方有较大的缓冲区来存储乱序数据

重传机制

⚠️参考:知乎专栏

流量控制

  • 为了控制发送方发送速率,接收方来得及接收和处理;客户端和服务端各自维护发送窗口和接收窗口

  • 滑动窗口:针对报文段的划分方式,大小是根据接收端处理数据的速度动态调整的;tcp三次握手的时候会协调出窗口的大小

为什么这种机制能做到流量控制?为发送和确认的异步性提供了缓冲(又快又慢)

  1. 快:不仅得发送,还得确认接受,这样就不用阻塞在等待确认上了,可以先发送多个然后一起等待确认;
  2. 慢:如果一直不确认累积一定数目,就会不发送;只有确认才能让滑动窗口移动;

滑动窗口

图源:JavaGuide

拥塞控制

防止过多的数据注入到网络中,维护一个拥塞窗口cwnd,在实际使用中会选取滑动窗口和拥塞窗口的最小值

慢开始和拥塞避免

tcp协议在一开始发送数据时,会让拥塞窗口从小到大倍数增长,即每经过一个传播轮次RTT,cwnd加倍;

但是后续不可能增大这么快,所以等到达阈值后,会使用每经过一个传播轮次RTT,cwnd+1的方式;

快重传和快恢复

FRR-fast retransmit and recovery

  1. 如果没有 FRR,当数据包丢失了,TCP 将会使用定时器来要求传输暂停。在暂停的这段时间内,没有新的或复制的数据包被发送。
  2. 有了 FRR,当接收机接收到一个不按顺序的数据段(例如接收了1 3),它会立即给发送机发送一个重复确认(此时依然正常接收其他数据包,例如接收1 3 4)。如果发送机接收到三个重复确认(如果是1个,可能把网络延迟给误判成丢包了),它会假定确认件指出的数据段丢失了,并立即重传这些丢失的数据段。
  3. 快重传:指接收方接收到三个重复确认,此时立即重传对应序列的报文段;
  4. 快恢复:发生快重传时,把阈值ssthresh、拥塞窗口cwnd都缩小到当前cwnd的一半,并切换到拥塞避免算法(每经过一个传播轮次RTT,cwnd+1)

image-20250111181016426

图源:王道考研

三次握手

先总说:1. 客户端发送数据包等待服务端确认,2. 服务端确认的同时,也需要客户端确认能否收到自己的数据;3. 客户端确认能收到服务端的数据

  • 第一次握手,发送端首先发送一个带SYN(synchronize)标志的数据包给接收方,第一次的seq序列号是随机产生的x,这样是为了网络安全,如果不是随机产生初始序列号,黑客将会以很容易的方式获取到你与其他主机之间的初始化序列号,并且伪造序列号进行攻击。即SYN x
  • 第二次握手,接收端收到后,回传一个带有SYN/ACK(acknowledgement)标志的数据包以示传达确认信息;ACK是为了告诉发送端,发送方到接收方的通道没问题,会携带确认号x+1,用来告诉发送方应该发x+1给我;SYN用来验证接收方到发送方的通道没问题,会随机初始化序列号y,即SYN y, ACK x+1
  • 第三次握手,发送端再回传一个带ACK标志的数据包,代表握手结束,会携带序列号x+1和确认号y+1。若在握手某个过程中某个阶段莫名中断,TCP协议会再次以相同的顺序发送相同的数据包,即SYN X+1, ACK y+1

四次挥手

  • 客户端发送一个FIN,用来关闭服务端到客户端的数据传送,记序列号seq为x,这并不是初始化的

  • 服务端收到这个FIN,它发回一个ACK,确认序号为x+1,此时seq没有额外区别,但也要占用一个序号,记作seq=y

  • 服务端关闭与客户端的连接,发送一个FIN给客户端,此时seq为y+n,因为第二次挥手后可能又发送一些数据

  • 客户端发回ACK报文确认,并将确认序号设置为y+n+1

问题:为什么不能把服务端发送的 ACK 和 FIN 合并起来,变成三次挥手?

因为服务端收到客户端断开连接的请求时,可能还有一些数据没有发完,这时先回复 ACK,表示接收到了断开连接的请求。等到数据发完之后再发 FIN,断开服务端到客户端的数据传送。