Skip to content

网络复习

HTTP

基本概念

HTTP 是互联网上应用最为广泛的一种网络协议。它是一个客户端和服务器端请求和应答的协议,基于 TCP/IP 进行通信。HTTP 主要用于从 WWW 服务器传输超文本到本地浏览器,故称为“超文本传输协议”。

工作原理

HTTP协议定义了 Web 客户端如何从 Web 服务器请求 Web 页面,以及服务器如何将 Web 页面传送给客户端。HTTP 通信开始时,客户端向服务器发送一个请求报文,服务器则以一个响应报文进行回复。

请求/响应的步骤

  1. 客户端连接到Web服务器
  2. 发送HTTP请求
  3. 服务器接受请求并返回HTTP响应
  4. 释放TCP连接
  5. 客户端(浏览器)解析HTML内容

记忆口诀:连接、发送、响应三步走,释放后解析不放过。

请求方法

  • GET: 请求指定的页面信息,并返回实体主体。
  • HEAD: 与 GET 方法类似,但服务器在响应中只返回头部信息,不返回实体的主体。
  • POST: 向指定资源提交数据,请求服务器进行处理。这可能导致资源的状态改变或服务器上的其他一些操作。
  • PUT: 从客户端向服务器传送数据,用于更新或创建资源。
  • DELETE: 请求服务器删除指定的页面。
  • CONNECT: HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。
  • OPTIONS: 允许客户端查看服务器的性能。
  • TRACE: 回显服务器收到的请求,主要用于测试或诊断。
  • PATCH: 对资源进行部分修改。

HTTP报文的组成成分

HTTP 报文主要由请求报文和响应报文组成。其中,

  • 请求报文 包括:请求行、请求头、空行、请求体,请求行由以下组成:HTTP方法、URL、HTTP版本
  • 响应报文 包括:状态行、响应头、空行、响应体

Request Header 示例:

markdown
1. GET /sample.Jsp HTTP/1.1  // 请求行
2. Host: www.uuid.online  // 请求的目标域名
3. Origin: http://localhost:8081  // 请求的来源域名和端口号
4. Referer: https://localhost:8081/link?query=xxxxx  // 请求资源的完整URI
5. User-Agent: Mozilla/5.0 ... Safari/537.36  // 浏览器信息
6. Cookie: BAIDUID=FA89F036:... sugstore=0  // 当前域名下的Cookie
7. Accept: text/html,image/apng  // 希望接受的数据类型
8. Accept-Encoding: gzip, deflate  // 支持的压缩格式
9. Accept-Language: zh-CN,zh;q=0.9  // 支持的语言
10. Connection: keep-alive  // TCP连接控制
1. GET /sample.Jsp HTTP/1.1  // 请求行
2. Host: www.uuid.online  // 请求的目标域名
3. Origin: http://localhost:8081  // 请求的来源域名和端口号
4. Referer: https://localhost:8081/link?query=xxxxx  // 请求资源的完整URI
5. User-Agent: Mozilla/5.0 ... Safari/537.36  // 浏览器信息
6. Cookie: BAIDUID=FA89F036:... sugstore=0  // 当前域名下的Cookie
7. Accept: text/html,image/apng  // 希望接受的数据类型
8. Accept-Encoding: gzip, deflate  // 支持的压缩格式
9. Accept-Language: zh-CN,zh;q=0.9  // 支持的语言
10. Connection: keep-alive  // TCP连接控制

GET与POST的区别

  1. 浏览器回退表现不同:GET 在浏览器回退时是无害的,而 POST 会再次提交请求。
  2. 浏览器对请求地址的处理不同:GET 请求的地址会被浏览器主动缓存,而 POST 不会,除非手动设置。
  3. 浏览器对响应的处理不同:GET 请求的参数会被完整地保留在浏览器的历史记录里,而 POST 的参数不会被保留。
  4. 参数大小不同:GET 请求在 URL 中传送的参数是有长度限制的,而 POST 没有。
  5. 安全性不同:GET 参数通过 URL 传递,可能会被暴露,相对不安全;而 POST 参数存放在 Request Body 中,相对更安全。
  6. 针对数据操作的类型不同:GET 主要用于查询数据,而 POST 用于添加、修改或删除数据。简言之,GET 是只读的,而 POST 是用于写操作的。

Response Header 示例:

markdown
HTTP/1.1 200 OK  // 状态行,其中200是状态代码,表示请求成功,OK是状态消息
Date: Tue, 25 Oct 2023 15:30:20 GMT  // 响应生成的日期和时间
Server: Apache/2.4.41 (Unix)  // 提供此响应的服务器类型和版本信息。
Last-Modified: Mon, 23 Oct 2023 14:30:58 GMT  // 此资源上次修改的日期和时间。
ETag: "e2a-5d8880c9c2b40"  // 资源的版本标识符。用于缓存管理和确保数据的一致性。
Accept-Ranges: bytes  // 指定服务器接受的范围类型,通常用于断点续传。
Content-Length: 374  // 响应正文的长度(以字节为单位)。
Cache-Control: max-age=3600, public  // 指定缓存指令,告诉客户端如何缓存响应内容。
Content-Type: text/html; charset=UTF-8  // 响应内容的MIME类型和字符集。
Connection: close  // 响应头用来告诉客户端服务器想要如何处理网络连接。
HTTP/1.1 200 OK  // 状态行,其中200是状态代码,表示请求成功,OK是状态消息
Date: Tue, 25 Oct 2023 15:30:20 GMT  // 响应生成的日期和时间
Server: Apache/2.4.41 (Unix)  // 提供此响应的服务器类型和版本信息。
Last-Modified: Mon, 23 Oct 2023 14:30:58 GMT  // 此资源上次修改的日期和时间。
ETag: "e2a-5d8880c9c2b40"  // 资源的版本标识符。用于缓存管理和确保数据的一致性。
Accept-Ranges: bytes  // 指定服务器接受的范围类型,通常用于断点续传。
Content-Length: 374  // 响应正文的长度(以字节为单位)。
Cache-Control: max-age=3600, public  // 指定缓存指令,告诉客户端如何缓存响应内容。
Content-Type: text/html; charset=UTF-8  // 响应内容的MIME类型和字符集。
Connection: close  // 响应头用来告诉客户端服务器想要如何处理网络连接。

HTTP请求跨域问题

  • 跨域的原理

    • 跨域 是指由于浏览器的同源策略限制,一个域的文档或脚本试图去请求另一个域的资源。
    • 同源策略 是一种约定,它是浏览器对 JavaScript 实施的安全限制。只要协议域名、或端口有任何一个不同,那么两个域就被认为是不同的域。
    • 跨域原理 是指通过某种方式绕过这些安全限制,使得不同的域之间可以互相请求资源。
  • 非同源限制

    • 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
    • 无法对非同源网页的 DOM 和JS对象进行操作
    • 无法向非同源地址发送 AJAX请求
  • 解决方案

    • JSONP: 利用<script>标签的src属性可以访问跨域的 js 脚本的特性,服务器返回一个调用某个函数的 js 代码,客户端再通过此函数接收数据。 缺点 只支持 GET 请求;需要后端配合返回指定格式的数据。

      js
      var script = document.createElement('script');
      function getData(data) {
          console.log(data);
      }
      script.src = 'http://localhost:3000/?callback=getData';
      document.body.appendChild(script);
      var script = document.createElement('script');
      function getData(data) {
          console.log(data);
      }
      script.src = 'http://localhost:3000/?callback=getData';
      document.body.appendChild(script);
    • document.domain: 适用于子域名不同,但基础域名相同的情况。

    • window.name: 所有窗口内的域名都共享同一个 window.name。

    • CORS: 服务器设置Access-Control-Allow-Origin HTTP响应头,浏览器会允许跨域请求。

    • proxy代理: 利用服务器端进行转发请求,从而绕过浏览器的同源策略。

    • window.postMessage(): 利用 HTML5 的 postMessage 方法进行跨域通信。

    • Websocket: 允许进行全双工通信的网络协议,可以进行跨域通信。

HTTP状态码及常见状态码

HTTP状态码

  • 1xx:指示信息类 - 表示请求已接受,继续处理。
  • 2xx:指示成功类 - 表示请求已成功被服务器接收、理解并接受。
  • 3xx:指示重定向 - 表示要完成请求必须进行进一步的操作。
  • 4xx:指示客户端错误 - 请求包含错误的语法或不能完成。
  • 5xx:指示服务器错误 - 服务器在处理请求时出错。

常见状态码

  • 200 OK:客户端请求成功。
  • 301 Moved Permanently:所请求的页面已永久重定向至新的URL。
  • 302 Found:所请求的页面已临时重定向至新的URL。
  • 304 Not Modified:资源自上次请求后未发生变化。
  • 403 Forbidden:服务器拒绝了客户端的请求。
  • 404 Not Found:服务器找不到请求的资源。
  • 500 Internal Server Error:服务器发生内部错误。
  • 503 Service Unavailable:服务器暂时无法处理请求。

状态码详细分类

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

    • 100 - 继续
    • 101 - 切换协议
  • 2xx(成功) 表示成功处理了请求的状态码。

    • 200 - OK
    • 201 - 已创建
    • 202 - 已接受
    • 203 - 非授权信息
    • 204 - 无内容
    • 205 - 重置内容
  • 3xx(重定向) 表示要完成请求需要进行进一步的操作。

    • 300 - 多种选择
    • 301 - 永久移动
    • 302 - 临时移动
    • 303 - 查看其它位置
    • 304 - 未修改
    • 305 - 使用代理
    • 307 - 临时重定向
  • 4xx(客户端错误) 这些状态码表示请求可能出错,妨碍了服务器的处理。

    • 400 - 错误请求
    • 401 - 未授权
    • 403 - 禁止
    • 404 - 未找到
    • 405 - 方法禁用
    • 406 - 不接受
    • 407 - 需要代理授权
    • 408 - 请求超时
    • 410 - 已删除
    • 413 - 请求实体过大
    • 414 - 请求的URI过长
  • 5xx(服务器错误) 这些状态码表示服务器在尝试处理请求时发生内部错误。

    • 500 - 服务器内部错误
    • 501 - 尚未实施
    • 502 - 错误网关
    • 503 - 服务器不可用
    • 504 - 网关超时
    • 505 - HTTP版本不受支持

HTTPS

HTTPS 的基本概念

HTTPS 是 HTTP 下加入 SSL/TLS 层进行加密的协议,它旨在提供安全的 HTTP 通道。

HTTPS 协议的主要作用是:

  • 建立一个信息安全的通道,确保数据的加密传输。
  • 通过证书验证服务器的真实性,防止中间人攻击。

HTTP 和 HTTPS 的区别?

  • HTTP 是明文传输的超文本传输协议,而 HTTPS 是具有安全性的 SSL/TLS 加密传输协议。
  • HTTPS 需要使用 CA 证书,而这通常是有成本的。
  • HTTP 和 HTTPS 使用不同的端口,通常 HTTP 使用 80,而 HTTPS 使用 443。
  • HTTP 的连接是无状态的,而 HTTPS 的连接除了是有状态的,还是加密的。

记忆口诀:明文传输与超文本,安全等级各有差异。CA 证书需付费,HTTP 无状态、HTTPS 加密传。

HTTPS 协议的工作原理

HTTPS 在与 Web 服务器通信时的步骤如下:

  1. 客户端通过 HTTPS URL 请求服务器,要求建立 SSL/TLS 链接。
  2. 服务器将其证书(含公钥)发送给客户端。
  3. 客户端与服务器协商确定加密算法和密钥。
  4. 客户端使用公钥加密一个随机的会话密钥,并发送给服务器。
  5. 服务器使用私钥解密得到会话密钥。
  6. 双方使用会话密钥进行加密通信。

记忆口诀:HTTPS 连接步骤六,密钥交换安全必。

HTTPS 协议的优缺点

  • HTTPS 比 HTTP 更加安全,能够保护数据在传输过程中不被窃取或篡改,确保数据完整性。
  • HTTPS 握手阶段相对耗时,可能会导致页面加载时间增加约50%,并可能导致增加10%~20%的电量消耗。
  • HTTPS 的缓存效率低于 HTTP,可能增加数据传输的开销。
  • SSL 证书需要购买,功能更强大的证书通常费用更高。
  • SSL 证书需要与 IP 地址绑定,这意味着不能在同一个 IP 地址上绑定多个域名,这种做法受到IPv4资源的限制。

TCP

TCP/IP 网络模型

TCP/IP模型是互联网的核心,由一系列网络协议组成。这些协议可以分为四层:链路层、网络层、传输层和应用层。

  • 链路层:负责封装和解封装 IP 报文,以及发送和接收 ARP/RARP 报文等。
  • 网络层:负责选择最优的路由路径,将分组报文发送到目标网络或主机。
  • 传输层:对报文进行分段和重组,并根据 TCP 或 UDP 协议格式进行封装。
  • 应用层:提供各种应用程序接口,如 HTTP、FTP、Telnet、DNS、SMTP 等。

TCP 三次握手

  1. 第一次握手:客户端发送 SYN 包 (SYN=j) 到服务器,并转换到 SYN_SENT 状态,等待服务器确认。
  2. 第二次握手:服务器接收到 SYN 包后确认客户端的 SYN(ACK=j+1),同时发送自己的 SYN 包(SYN=k),即发送 SYN+ACK 包,此时服务器进入SYN_RECV状态。
  3. 第三次握手:客户端收到服务器的 SYN+ACK 包后,发送确认包 ACK(ACK=k+1),完成后,双方进入 ESTABLISHED 状态,完成三次握手。

握手过程中传送的包不包含数据内容。只有在三次握手完毕后,客户端和服务器才开始正式传输数据。

TCP 四次挥手

  1. 客户端发出连接释放报文,停止发送数据。此释放报文头部设置 FIN=1,序列号为 seq=u,随后客户端进入 FIN-WAIT-1 状态。
  2. 服务器收到释放报文后,发出确认报文,进入 CLOSE-WAIT 状态。
  3. 客户端收到服务器的确认后,进入 FIN-WAIT-2 状态,等待服务器的释放报文。
  4. 服务器发送最后的数据和连接释放报文,随后进入 LAST-ACK 状态。
  5. 客户端收到服务器的释放报文后,发送确认,然后进入 TIME-WAIT 状态。经过一段时间后,客户端进入 CLOSED 状态。
  6. 服务器收到客户端的确认后,立即进入 CLOSED 状态。

TCP 和 UDP 的区别

  1. TCP 是面向连接的,而 UDP 是无连接的。
  2. TCP 仅支持单播传输,而 UDP 支持单播、多播和广播。
  3. TCP 通过三次握手保证了连接的可靠性。而 UDP 是无连接且不可靠的,没有建立连接的步骤,发送端不确保数据的正确接收。
  4. UDP 的头部开销小于 TCP,数据传输速率更快,更适合于要求实时性的应用。

Cookie、sessionStorage、localStorage 的区别

相同点

  • 都是存储在客户端的技术。

不同点

  • 存储大小

    • cookie 数据大小不能超过4K。
    • sessionStorage 和 localStorage 可以存储约5M的数据。
  • 生命周期

    • cookie 根据设置的过期时间持续有效。
    • localStorage 数据持久保存,即使浏览器关闭也不会丢失,除非用户主动删除数据。
    • sessionStorage 数据在当前浏览器窗口或标签页关闭后自动删除。
  • 与服务器交互

    • cookie 数据在每次向服务器发送请求时都会被自动附带,无论是否需要。
    • sessionStorage 和 localStorage 数据只保存在本地,不会被自动发送到服务器。

介绍下304过程

  1. 强缓存阶段

    • 当浏览器请求资源时,首先检查资源的ExpiresCache-ControlExpires基于特定的日期/时间失效,但如果用户更改了本地时间,可能会影响其有效性。而Cache-Control: max-age则定义了资源的最大生存时间,超过该时间则认为缓存失效。
  2. 协商缓存阶段

    • 当强缓存失效后,浏览器会发送请求到服务器进行协商缓存验证。
    • 服务器可以使用ETag来验证资源是否已更改。ETag确保每个资源是唯一的,资源的任何更改都会导致ETag值的更改。当浏览器再次请求该资源时,它会发送If-None-Match头,该头的值为上次返回的ETag值。如果值匹配,服务器会返回304 Not Modified,表示资源未更改并且可以使用缓存。
    • 另一个验证方法是使用Last-ModifiedIf-Modified-Since头。服务器会在首次提供资源时返回Last-Modified头,指示资源的最后修改日期。在后续请求中,浏览器会发送If-Modified-Since头,该头的值为上次返回的Last-Modified值。如果资源自此时间以来未更改,服务器会返回304 Not Modified

此流程确保在可能的情况下从浏览器缓存中提供资源,而不是重新从服务器获取。这可以减少网络带宽的使用并加快页面加载速度。

粘包问题分析与对策

TCP粘包是指发送方发送的多个包,在接收方被接收时合并为一个包。与之相反的,UDP由于保留消息边界,不会出现粘包现象。

粘包出现原因

主要是因为TCP基于字节流,而UDP基于消息。在流传输中,TCP会尽量确保传输效率,可能会将多个小包合并为一个大包进行传输。

对策

  1. 发送方可以设置TCP的PUSH操作,强制立即发送数据,而不等待发送缓冲区满。
  2. 接收方可以优化程序设计,尽快处理并接收数据,避免数据在接收缓冲区中积压。
  3. 人为分包发送,例如每次发送固定大小的数据包。

以上方法均有缺点,如降低传输效率或增加处理复杂性。最实用的方法可能是接收方预处理数据,将接收到的数据进行解包操作,这种方法经实验表明是高效且可行的。

以下是对您提供的文档的完善和修复:

浏览器的缓存机制: 强制缓存 & 协商缓存

当浏览器与服务器进行通信,它采用的是应答模式。简言之,浏览器发起HTTP请求,然后服务器响应该请求。当浏览器第一次从服务器获取请求结果后,它会根据响应报文的HTTP头部中的缓存标识来决定是否要缓存结果。如果决定缓存,浏览器会将请求结果和缓存标识保存在其缓存中。

为了进一步理解,我们可以分析下图的简化流程:

从上图我们可以得知:

  • 浏览器在每次发起请求前,会先在其缓存中查找该请求的结果以及缓存标识。
  • 浏览器在接收到每个请求的返回结果后,会将该结果及其缓存标识保存在浏览器缓存中。

基于上述两点,我们可以深入研究浏览器缓存机制。这里,我们按照是否需要向服务器重新发起HTTP请求,将缓存过程分为两大部分:强制缓存协商缓存

  • 强制缓存

    强制缓存是指浏览器在检查其缓存中是否有某个请求结果,并基于该结果的缓存规则来决定是否使用该缓存结果的过程。当浏览器向服务器发起请求时,服务器会将缓存规则放入HTTP响应报文头部,并与请求结果一起返回。控制强制缓存的字段有 ExpiresCache-Control,其中Cache-Control的优先级比Expires高。

    强制缓存的主要情况有三种:

    1. 浏览器缓存中不存在该请求的结果和缓存标识,或者该结果已经过期。此时强制缓存失效,浏览器直接向服务器发起新请求。
    2. 浏览器缓存中有该请求的结果和缓存标识,但结果已经过期。此时强制缓存失效,浏览器进入协商缓存过程。
    3. 浏览器缓存中有该请求的结果和缓存标识,且结果尚未过期。此时强制缓存生效,浏览器直接使用缓存中的结果。
  • 协商缓存

    当强制缓存失效后,协商缓存开始起作用。这是一个浏览器携带缓存标识向服务器发起请求的过程,服务器会根据缓存标识来决定是否使用缓存。协商缓存的标识也是在响应报文的HTTP头部中返回的,关键的字段有:Last-Modified / If-Modified-SinceEtag / If-None-Match,其中Etag / If-None-Match的优先级比Last-Modified / If-Modified-Since高。

    协商缓存的主要情况有两种:

    1. 服务器认为缓存仍然有效,返回HTTP 304状态码,告诉浏览器可以使用缓存。
    2. 服务器认为缓存已经过期或无效,返回HTTP 200状态码和新的请求结果。

更多详细信息,请访问:彻底理解浏览器的缓存机制

客户端与服务端长连接的几种方式

  1. Ajax 轮询

    实现原理:Ajax 轮询是指客户端每间隔一段时间向服务端发起请求,从而保持数据的同步。

    优点:可以实现基础的数据更新,特别是当间隔时间较短。

    缺点:尽管模拟了实时传输,但它不是真正的即时通讯。可能存在服务端数据已更新但客户端未发起请求,或者客户端请求时,服务端数据并未更新的情况。导致多次无用请求,效率低下且数据更新可能不及时。

  2. Long Poll 长轮询

    实现原理: 当客户端发送请求后,如果服务器没有数据返回,它不会立即关闭连接,而是将请求挂起。只有当有数据需要返回给客户端时,服务器才会发送响应。接着,客户端会再次发起请求,形成轮询。

    优点:减少了无用的网络传输,确保每次请求都有数据返回,不会持续占用线程。

    缺点:在高并发环境中可能难以应对,因为服务器需要维护多个长时间的连接。此外,服务器不能主动向客户端推送数据。

  3. Iframe 长连接

    实现原理
    网页中嵌入一个 iframe 标签,其 src 属性指向一个长连接请求。服务端可以通过这个连接持续地向客户端传输信息。

    优点:能够实时传输消息。

    缺点:可能会消耗大量的服务器资源。

  4. WebSocket

    实现原理: WebSocket 支持客户端与服务端的双向通信。与 HTTP 不同,WebSocket 只需建立一次连接,即可实现持续的数据交换。

    优点

    • 双向通信:客户端和服务端都可以主动发起通讯。
    • 无同源限制:客户端可以与任意服务器通信,无跨域问题。
    • 数据轻量:与 HTTP 相比,WebSocket 在数据传输时无需带有冗余的请求头。
    • 高效:由于仅需要建立一次连接,数据传输效率更高。

    缺点

    • 需要后端的业务代码非常稳定。
    • 长连接可能受到网络限制,需要处理重连。
    • 兼容性问题,如只支持 IE10 及以上版本。
    • 维护长连接有一定的服务器成本。
    • 与 HTTP 相比,WebSocket 的生态圈较小,问题难以快速解决。

利用Socket建立网络连接的步骤

建立 Socket 连接至少需要一对套接字,其中一个位于客户端,称为 ClientSocket,另一个位于服务器端,称为 ServerSocket

套接字之间的连接可以分为以下三个步骤:

  1. 服务器监听
    服务器端的套接字会持续监听网络状态,等待客户端的连接请求。

  2. 客户端请求
    客户端的套接字需要指明要连接的服务器端套接字的地址和端口号,然后向其发起连接请求。

  3. 连接确认
    当服务器端套接字接收到客户端的连接请求后,它会创建一个新的线程来处理该请求。一旦客户端确认了服务器的描述信息,双方就会正式建立连接。此后,服务器端套接字会继续监听,以等待其他客户端的连接请求。

非对称加密RSA

简介

  1. 对称加密算法和非对称加密算法是两种主要的加密方法。
  2. 非对称加密是计算机通信安全的基石,能够确保数据的安全传输。
  3. 非对称加密算法需要两个密钥:公开密钥(public key)和私有密钥(private key)。
  4. 公开密钥和私有密钥是一对。
  • 使用公开密钥加密的数据,只有对应的私有密钥才能解密。
  • 使用私有密钥加密的数据,只有对应的公开密钥才能解密。

特点

  • 算法复杂度高,安全性主要依赖于算法和密钥。
  • 相对于对称加密,非对称加密的加密和解密速度较慢。

与对称加密算法的对比

  • 对称加密使用同一个密钥进行加密和解密,该密钥需要保持私密。
  • 非对称加密使用两个密钥,其中一个可以公开。

RSA 应用场景

由于RSA算法的加密解密速度相对较慢,因此在实际应用中,通常会结合对称加密方法。即,数据的加密和解密使用对称加密算法(如AES),而对称加密所需的密钥则使用RSA算法加密进行传输。

HTTP 版本

HTTP/0.9

HTTP/0.9 是于 1991 年提出的,主要用于学术交流,需求很简单——用来在网络之间传递 HTML 超文本的内容,所以被称为超文本传输协议

HTTP/0.9 的一个完整的请求流程

  • 因为 HTTP 都是基于 TCP 协议的,所以客户端先要根据 IP 地址、端口和服务器建立 TCP 连接,而建立连接的过程就是 TCP 协议三次握手的过程。
  • 建立好连接之后,会发送一个 GET 请求行的信息,如GET /index.html用来获取 index.html。
  • 服务器接收请求信息之后,读取对应的 HTML 文件,并将数据以 ASCII 字符流返回给客户端。
  • HTML 文档传输完成后,断开连接。

总的来说,当时的需求很简单,就是用来传输体积很小的 HTML 文件,所以 HTTP/0.9 的实现有以下三个特点。

  • 第一个是只有一个请求行,并没有 HTTP 请求头和请求体,因为只需要一个请求行就可以完整表达客户端的需求了。
  • 第二个是服务器也没有返回头信息,这是因为服务器端并不需要告诉客户端太多信息,只需要返回数据就可以了。
  • 第三个是返回的文件内容是以 ASCII 字符流来传输的,因为都是 HTML 格式的文件,所以使用 ASCII 字节码来传输是最合适的。

HTTP 1.0

HTTP 1.0 是在 1996 年引入的,由于在浏览器中展示的不单是 HTML 文件了,还包括了 JavaScript、CSS、图片、音频、视频等不同类型的文件。因此支持多种类型的文件下载是 HTTP/1.0 的一个核心诉求。

  • HTTP 1.0 仅仅提供了最基本的认证,这时候用户名和密码还未经加密,因此很容易收到窥探。
  • HTTP/1.0 引入了请求头响应头,它们都是以为 Key-Value 形式保存的
  • HTTP 1.0 被设计用来使用短链接
    • 即每次发送数据都会经过 TCP 的三次握手和四次挥手,效率比较低。
  • HTTP 1.0 只使用 header 中的 If-Modified-Since 和 Expires 作为缓存失效的标准。
  • HTTP 1.0 不支持断点续传,也就是说,每次都会传送全部的页面和数据。

HTTP 1.1

HTTP 1.1 是 HTTP 1.0 开发三年后出现的,也就是 1999 年,它做出了以下方面的变化

  • HTTP 1.1 使用了摘要算法来进行身份验证
  • HTTP 1.1 默认使用长连接(持久连接)
    • 长连接就是只需一次建立就可以传输多次数据,传输完成后,只需要一次切断连接即可。
    • 长连接的连接时长可以通过请求头中的 keep-alive 来设置
    • 持久连接在 HTTP/1.1 中是默认开启
  • HTTP 1.1 中新增加了 E-tag, If-Match, If-None-Match 等缓存控制标头来控制缓存失效。
  • HTTP 1.1 支持断点续传,通过使用请求头中的 Range 来实现。

HTTP 2.0

HTTP 2.0 是 2015 年开发出来的标准,HTTP/2 的一个核心特性是使用了 多路复用技术,因此它可以 通过一个 TCP 连接来发送多个 URL 请求。多路复用技术能充分利用带宽,最大限度规避了 TCP 的慢启动所带来的问题。

  • 头部压缩
    • 由于 HTTP 1.1 经常会出现 User-Agent、Cookie、Accept、Server、Range 等字段可能会占用几百甚至几千字节,而 Body 却经常只有几十字节,所以导致头部偏重
    • HTTP 2.0 使用 HPACK 算法进行压缩。
  • 二进制格式
    • HTTP 2.0 使用了更加靠近 TCP/IP 的二进制格式
    • 而抛弃了 ASCII 码,提升了解析效率
  • 强化安全
    • HTTP2.0 一般都跑在 HTTPS 上。
  • 多路复用
    • 一个域名只使用一个 TCP 长连接来传输数据
    • 一个请求对应一个id,这样一个连接上可以有多个请求。
    • 通过引入二进制分帧层,就实现了 HTTP 的多路复用技术
  • 可以设置请求的优先级
  • 服务器推送

HTTP 3.0

HTTP/3 选择了一个折衷的方法——UDP 协议,基于 UDP 实现了类似于 TCP 的多路数据流传输可靠性等功能,我们把这套功能称为 QUIC 协议

  • 实现了类似 TCP 的流量控制、传输可靠性的功能。虽然 UDP 不提供可靠性的传输,但 QUIC 在 UDP 的基础之上增加了一层来保证数据可靠性传输。它提供了数据包重传、拥塞控制以及其他一些 TCP 中存在的特性。
  • 集成了 TLS 加密功能。目前 QUIC 使用的是 TLS1.3,相较于早期版本 TLS1.2 有更多的优点,其中最重要的一点是减少了握手所花费的 RTT 个数
  • 实现了 HTTP/2 中的多路复用功能。和 TCP 不同,QUIC 实现了在同一物理连接上可以有多个独立的逻辑数据流。实现了数据流的单独传输,就解决了 TCP 中的问题。
  • 实现了快速握手功能。由于 QUIC 是基于 UDP 的,所以 QUIC 可以实现使用 0-RTT 或者 1-RTT 来建立连接,这意味着 QUIC 可以用最快的速度来发送和接收数据,这样可以大大提升首次打开页面的速度。

WebSocket 协议

HTML5开始提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议。它基于TCP传输协议,并复用HTTP的握手通道。

WebSocket连接是如何创建的

在客户端的使用

javascript
const url = "ws://localhost:8080/websocket/text";
const ws = new WebSocket(url);

ws.onopen = function(event) {
  console.log("websocket connection open.");
  console.log(event);
};

ws.onmessage = function(event) {
  console.log("websocket message received.")
  console.log(event.data);
};

ws.onclose = function (event) {
  console.log("websocket connection close.");
  console.log(event.code);
};

ws.onerror = function(event) {
  console.log("websocket connection error.");
  console.log(event);
};
const url = "ws://localhost:8080/websocket/text";
const ws = new WebSocket(url);

ws.onopen = function(event) {
  console.log("websocket connection open.");
  console.log(event);
};

ws.onmessage = function(event) {
  console.log("websocket message received.")
  console.log(event.data);
};

ws.onclose = function (event) {
  console.log("websocket connection close.");
  console.log(event.code);
};

ws.onerror = function(event) {
  console.log("websocket connection error.");
  console.log(event);
};

链接状态(readyState)

javascript
const readyState = socket.readyState;
const readyState = socket.readyState;

返回值

状态代码描述
0WebSocket.CONNECTING (正在链接中)
1WebSocket.OPEN (已经链接并且可以通讯)
2WebSocket.CLOSING (连接正在关闭)
3WebSocket.CLOSED (连接已关闭或没有链接成功)

首先,WebSocket 连接必须由浏览器发起,因为请求协议是一个标准的HTTP请求

txt
+GET ws://localhost:3000/ws/chat HTTP/1.1
Host: localhost
+Upgrade: websocket
+Connection: Upgrade
Origin: http://localhost:3000
+Sec-WebSocket-Key: client-random-string
+Sec-WebSocket-Version: 13
+GET ws://localhost:3000/ws/chat HTTP/1.1
Host: localhost
+Upgrade: websocket
+Connection: Upgrade
Origin: http://localhost:3000
+Sec-WebSocket-Key: client-random-string
+Sec-WebSocket-Version: 13

该请求和普通的HTTP请求有几点不同:

  • GET请求的地址不是类似/path/,而是以ws://开头的地址;
  • 请求头Upgrade: websocket和Connection: Upgrade表示这个连接将要被转换为WebSocket连接
  • Sec-WebSocket-Key是用于标识这个连接,并非用于加密数据;
  • Sec-WebSocket-Version指定了WebSocket的协议版本。

随后,服务器如果接受该请求,就会返回如下响应:

txt
+HTTP/1.1 101 Switching Protocols
+Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: server-random-string
+HTTP/1.1 101 Switching Protocols
+Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: server-random-string

该响应代码101表示本次连接的HTTP协议即将被更改,更改后的协议就是Upgrade: websocket指定的WebSocket协议。

WebSocket 优点

  • 较少的控制开销
    • 在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小
  • 更强的实时性
    • 由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于 HTTP 请求需要等待客户端发起请求服务端才能响应,延迟明显更少。
  • 保持连接状态
    • 与 HTTP 不同的是,WebSocket 需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息。
  • 更好的二进制支持
    • WebSocket 定义了二进制帧,相对 HTTP,可以更轻松地处理二进制内容。
  • 可以支持扩展。
    • WebSocket 定义了扩展,用户可以扩展协议、实现部分自定义的子协议。

DNS协议

网络世界的地址簿

Socket 库提供查询 IP 地址的功能

向 DNS 服务器发出查询,并接收服务器返回的响应消息。对于 DNS 服务器,我们的计算机上一定有相应的 DNS 客户端,而相当于 DNS 客户端的部分称为 DNS 解析器,或者简称解析器

通过 DNS 查询 IP 地址的操作称为域名解析

解析器实际上是一段程序,它包含在操作系统的 Socket 库中。

解析器向 DNS 服务器发出查询

调用解析器后,解析器会向 DNS 服务器发送查询消息,然后 DNS 服务器会返回响应消息。响应消息中包含查询到的 IP 地址,解析器会取出 IP 地址,并将其写入浏览器指定的内存地址中。接下来,浏览器在向 Web 服务器发送消息时,只要从该内存地址取出 IP 地址,并将它与 HTTP 请求消息一起交给操作系统就可以了。

在 Socket 库中,采用 UDP 协议,进行信息的查询。

DNS 服务器的工作步骤

DNS 服务器的基本工作就是接收来自客户端的查询消息,然后根据消息的内容返回响应

来自客户端的查询消息包含以下 3 种信息

类型描述
域名服务器、邮件服务器(邮件地址中 @ 后面的部分)的名称
ClassClass 的值永远是代表互联网的IN
记录类型表示域名对应何种类型的记录,类型为 A 时:表示域名对应的是 IP 地址,类型为 MX 时:表示域名对应的是邮件服务器

A 是 Address 的缩写 /MX:Mail eXchange,邮件交换的缩写

DNS 服务器上事先保存有前面这 3 种信息对应的记录数据。

例如,如果要查询 www.wl.com 这个域名对应的 IP 地址,客 户端会向 DNS 服务器发送包含以下信息的查询消息。

信息
域名 = <www.wl.com>
Class = IN
记录类型 = A

然后,DNS 服务器会从已有的记录中查找域名、Class 和记录类型全部匹配的记录。

DNS 服务器会从域名与 IP 地址的对照表中查找相应的记录,并返回 IP 地址。

实际上还有很多其他的类型。

  • 根据 IP 地址反查域名的 PTR 类型
  • 查询域名相关别名的 CNAME类型
  • 查询 DNS 服务器 IP 地址的 NS 类型
  • 以及查询域名属性信息的SOA 类型等

信息是如何在 DNS 服务器上注册

首先,DNS 服务器中的所有信息都是按照域名以分层次的结构来保存的。DNS 中的域名都是用句点来分隔的,比如 <www.wl.com,这里的句点代表了不同层次之间的界限。>

在域名中,越靠右的位置表示其层级越高

这种具有层次结构的域名信息会注册到 DNS 服务器中,而每个域都是作为一个整体来处理的。

于是,DNS 服务器也具有了像域名一样的层次结构,每个域的信息都存放在相应层级的 DNS 服务器中。

寻找相应的 DNS 服务器并获取 IP 地址

这里的关键在于如何找到我们要访问的 Web 服务器的信息归哪一台 DNS 服务器管

com、cn 这些域(称为顶级域),它们各自负责保存下级 DNS 服务器的信息。在互联网中,com 和 cn 的上面还有一级域,称为根域。根域不像 com、cn 那样有自己的名字,因此在一般书写域名时经常被省略,如果要明确表示根域,应该像 <www.wl.com>. 这样在域名的最后再加上一个句点,而这个最后的句点就代表根域。

根域的 DNS 服务器中保管着 com、cn 等的 DNS 服务器的信息

除此之外还需要完成另一项工作,那就是将根域的 DNS 服务器信息保存在互联网中所有的 DNS 服务器中。客户端只要能够找到任意一台DNS 服务器,就可以通过它找到根域 DNS 服务器,然后再一路顺藤摸瓜找到位于下层的某台目标 DNS 服务器。

分配给根域 DNS 服务器的 IP 地址在全世界仅有 13 个 ,而且这些地址几乎不发生变化。

DNS 解析流程:

1.电脑客户端会发出一个 DNS 请求,问 <www.wl.com> 的 IP 是啥啊,并发给本地域名服务器 (本地 DNS)。

2.本地 DNS 收到来自客户端的请求。然后,查找对应的记录信息。

  • 如果能找到 <www.wl.com,它直接就返回> IP 地址。
  • 如果没有,本地 DNS 会去问它的根域名服务器。 3.根 DNS 收到来自本地 DNS 的请求,发现后缀是 .com,说:“www.wl.com 啊,这个域名是由.com 区域管理,我给你它的顶级域名服务器的地址,你去问问它吧。”

4.本地 DNS 转向问顶级域名服务器:

  • 顶级域名服务器就是大名鼎鼎的比如 .com、.net、 .org 这些一级域名
  • 负责管理二级域名,比如 wl.com,所以它能提供一条更清晰的方向 5.顶级域名服务器说:“我给你负责 <www.wl.com> 区域的权威 DNS 服务器的地址,你去问它应该能问到。”

6.本地 DNS 转向问权威 DNS 服务器:“www.wl.com 对应的 IP 是啥呀?”

  • wl.com 的权威 DNS 服务器,它是域名解析结果的原出处。
  • 为啥叫权威呢?就是我的域名我做主。 7.权威 DNS 服务器查询后将对应的 IP 地址 X.X.X.X 告诉本地 DNS。

8.本地 DNS 再将 IP 地址返回客户端,客户端和目标建立连接。

通过缓存加快 DNS 服务器的响应:

如果要查询的域名和相关信息已经在缓存中,那么就可以直接返回响应,接下来的查询可以从缓存的位置开始向下进行。相比每次都从根域找起来说,缓存可以减少查询所需的时间。

这个缓存机制中有一点需要注意,那就是信息被缓存后,原本的注册信息可能会发生改变,这时缓存中的信息就有可能是不正确的。因此,DNS 服务器中保存的信息都设置有一个有效期,当缓存中的信息超过有效期后,数据就会从缓存中删除。

负载均衡

内部负载均衡:
例如,某个应用要访问另外一个应用,如果配置另外一个应用的 IP 地址,那么这个访问就是一对一的。但是当被访问的应用撑不住的时候,我们其实可以部署多个。但是,访问它的应用,如何在多个之间进行负载均衡?只要配置成为域名就可以了。在域名解析的时候,我们只要配置策略,这次返回第一个 IP,下次返回第二个 IP,就可以实现负载均衡了。

全局负载均衡:
为了保证我们的应用高可用,往往会部署在多个机房,每个地方都会有自己的 IP 地址。当用户访问某个域名的时候,这个 IP 地址可以轮询访问多个数据中心。如果一个数据中心因为某种原因挂了,只要在 DNS 服务器里面,将这个数据中心对应的 IP 地址删除,就可以实现一定的高可用

TCP 协议

三次握手

字段解析:

  • 源端口号 Source Port / 目标端口号 Destination Port:
    • 用于区别主机中的不同进程
    • IP地址是用来区分不同的主机的
    • 源端口号和目的端口号配合上IP首部中的源IP地址和目的IP地址就能唯一的确定一个TCP连接;
  • 分包序号Sequence Number:
    • 应用程序数据如果 大于 最大分段大小Maximum Segment Size(MSS)就得要进行分段。
    • 这个 Sequence Number 就是记录每个封包的序号,可以让 server 重新将 TCP 的数据组合起来。
    • 主要用来解决网络报乱序的问题
  • 回应序号Acknowledge Number:
    • 为了确认发送端确实有收到接收端所送出的封包数据。
    • 接收端收到这个确认码时,就能够确定之前传递的封包已经被正确的收下了。
    • 回应序号应当是上次已成功收到分包序号加1
    • 用来解决不丢包的问题 -Code(控制标识码Control Flag)
    • 这个字段共有 6 个 bits ,分别代表 6 个句柄,若为 1 则为启动 (只介绍常用的) -SYN:若为 1,表示 client 希望双方建立同步处理 -ACK:若为 1 代表这个封包为响应封包 -FIN:若为 1 ,表示传送结束,所以通知对方数据传毕, 是否同意断线,只是发送者还在等待对方的响应而已

1.SYN

  • 客户端选择一个随机序列号 x,并发送一个 SYN 分组

2.SYN + ACK

  • y
  • x + 1
  • 服务器给 x 加 1
  • 并选择自己的一个随机序列号
  • 然后返回响应

3.ACK

  • x + 1
  • y + 1
  • 客户端给 x 和 y 加 1
  • 并发送握手期间的最后一个 ACK 分组

三次握手完成后,客户端与服务器之间就可以通信了。客户端可以在发送 ACK 分组之后立即发送数据,而服务器必须等接收到 ACK 分组之后才能发送数据。

滑动窗口

TCP 采用滑动窗口来管理数据发送和 ACK 号的操作。

所谓滑动窗口,就是在发送一个包之后,不等待 ACK 号返回,而是直接发送后续的一系列包。

通过这种方式,就可以实现同一时间发送多个包,减少网络延迟。其实,通过窗口,TCP 可以控制双向发送数据的速度

流量控制

流量控制是一种预防发送端过多向接收端发送数据的机制。

为实现流量控制,TCP 连接的每一方都要通告自己的接收窗口(rwnd),其中包含能够保存数据的缓冲区空间大小信息

第一次建立连接时,两端都会使用自身系统的默认设置来发送 rwnd。在后面的数据交换过程中,每个 ACK 分组都会携带相应的最新 rwnd 值,以便两端动态调整数据流速,使之适应发送端和接收端的容量及处理能力。

慢启动 (利用可用宽带)

流量控制确实可以防止发送端向接收端过多发送数据,但却没有机制预防任何一端向潜在网络过多发送数据。换句话说,发送端和接收端在连接建立之初,谁也不知道可用带宽是多少。因此需要一个估算机制,然后还要根据网络中不断变化的条件而动态改变速度

拥塞窗口大小(cwnd):发送端对从客户端接收确认(ACK)之前可以发送数据量的限制。发送端不会通告 cwnd 变量,即发送端和接收端不会交换这个值

服务器和客户端怎么确定拥塞窗口大小的最优值呢:解决方案就是慢启动:即在分组被确认后增大窗口大小,慢慢地启动

无论带宽多大,每个 TCP 连接都必须经过慢启动阶段,换句话说,应用不可能一上来就完全利用连接的最大带宽,把初始拥塞窗口大小增加到一个合理值,可以减少客户端与服务器之间的往返时间。

TCP 队首阻塞

TCP 在不可靠的信道上实现了可靠的网络传输

每个 TCP 分组都会带着一个唯一的序列号被发出,而所有分组必须按顺序传送到接收端。如果中途有一个分组没能到达接收端,那么后续分组必须保存在接收端的 TCP 缓冲区,等待丢失的分组重发并到达接收端。这一切都发生在 TCP 层,应用程序对 TCP 重发和缓冲区中排队的分组一无所知,必须等待分组全部到达才能访问数据。在此之前,应用程序只能在通过套接字读数据时感觉到延迟交付。这种效应称为TCP 队首阻塞Head of Line Blocking(HOL)

队首阻塞造成的延迟可以让我们的应用程序不用关心分组重排和重组,分组到达时间会存在无法预知的延迟变化。这个时间变化通常被称为抖动,也是影响应用程序性能的一个主要因素。

TCP 队首阻塞造成的延迟,也是影响应用程序性能的一个主要因素

四次挥手

1.FIN

  • 客户端选择一个随机序列号 x,并发送一个 FIN 分组

2.ACK

  • x + 1
  • 服务器给 x 加 1

3.等后端数据都传输完毕后。。。。。 4.FIN

  • y
  • 服务器选择自己的一个随机序列号

5.ACK

  • x + 1
  • y + 1
  • 客户端给 x 和 y 加 1
  • 并发送握手期间的最后一个 ACK 分组

注意点:

  • 相比三次握手,四次挥手,在 server 发起的时候,是将控制标志码由 SYN 换成 FIN。
  • 可以看到,在第二次挥手和第三次挥手中间,有很多未发送完成的数据,其实也好理解,在 client 接收到 server 传入的 FIN 包时候,此时可能正处于某些大包数据的发送阶段,如果此时直接回复 发送端的断开操作。并且,如果 server FIN 包早于其他正常数据包到达 client。那这些本应该被 client 收录的数据,就会平白无故的丢失

为什么要四次挥手

TCP协议是一种面向连接的、可靠的、基于字节流的传输层通信协议。

TCP是全双工模式,这就意味着

  • 当主机1发出FIN报文段时,只是表示主机1已经没有数据要发送了,主机1告诉主机2,它的数据已经全部发送完毕了;但是,这个时候主机1还是可以接受来自主机2的数据
  • 当主机2返回ACK报文段时,表示它已经知道主机1没有数据发送了,但是主机2还是可以发送数据到主机1的
  • 当主机2也发送了FIN报文段时,这个时候就表示主机2也没有数据要发送了,就会告诉主机1,我也没有数据要发送了,之后彼此就会愉快的中断这次TCP连接

更新时间: :

Released under the MIT License.