这里写目录标题

  • 一、TCP
    • TCP/IP 网络分层(四层)
      • 应用层
      • 传输层
      • 网络互联层
      • 网络访问层
      • 分层的好处
    • 1、TCP协议(需要补充)
      • UDP
    • 2、TCP首部
      • 端口号(源端口、目标端口)
      • 1、序列号
        • 初始序列号
      • 2、确认号
      • 3、TCP 标记
      • 窗口大小
      • 辅助信息(需要补充)
        • MSS TCP 允许接收的最大报文段
        • SACK 选择确认选项
        • Window Scale 窗口缩放选项
        • Timestamps 时间戳
    • 3、TCP 三次握手(需要补充)
      • TFO
    • 4、TCP 四次挥手
    • 5、TCP 超时重传
      • 1、快重传机制
      • 2、选择确认
    • 6、TCP 流量控制【滑动窗口】
    • 7、TCP 拥塞控制
      • 7.1、拥塞窗口
      • 7.2、慢启动
      • 7.3、拥塞避免
      • 7.4、快速重传
        • 选择确认(SACK)
      • 7.5、快速恢复
    • 8、Nagle 算法
    • 9、延迟确认
    • 10、TCP 的 keepalive 机制
  • 二、HTTP
    • 1、HTTP
      • CDN
      • HTTP 与 TCP 的关系
      • DNS(域名系统)
      • URI/URL
      • HTTPS
    • 2、协议分层
      • 2.1、TCP/IP 网络分层模型(四层)
      • 2.2、OSI 网络分层模型(七层)
      • 2.3、TCP/IP 协议栈的工作方式
    • 3、域名(DNS)
      • 域名解析
      • 域名缓存
    • 4、输入一个URL,会发生什么
      • 4.1、使用 IP 地址访问 Web 服务器
      • 4.2、输入一个URL,访问 Web服务器
    • 5、HTTP 报文
      • 5.1、起始行(请求行、状态行)
      • 5.2、头部字段
    • 6、GET 方法和 POST 方法的区别
    • 7、HTTP 响应状态码
      • 7.1、2××
      • 7.2、3××
      • 7.3、4××
      • 7.4、5××
    • 8、HTTPS
      • 8.1、使用对称和非对称加密结合来保证机密性
      • 8.2、使用摘要算法来保证完整性(数字摘要)
      • 8.3、使用数字证书来解决身份认证
      • 8.4、HTTPS 连接过程
    • 9、HTTP2
      • 头部压缩
      • 二进制格式
      • 虚拟流
      • 强化安全
      • HTTP2 的缺点
    • 10、HTTP3
    • 11、Cookie
      • Cookie 的工作过程
      • Cookie 生命周期

一、TCP

TCP/IP 网络分层(四层)

  • 应用层
  • 传输层
  • 网络互连层
  • 网络访问层
  • 物理层

应用层

应用层主要是规定了应用程序之间如何相互传递报文,比如说 HTTP,它就规定

  • 报文的类型,是请求报文还是响应

  • 报文语法,报文分为几段,各段是什么意思、用什么分隔

应用层的协议还有:DNS域名解析协议,收发邮件SMTP

传输层

传输层就是为两台主机之间的应用进程提供端到端的逻辑通信;

但是并不是将数据包从一台主机传送到另一台,而是对「传输行为进行控制」

网络互联层

网络互连层提供了主机到主机的通信,就是将传输层产生的的数据包封装成分组数据包发送到目标主机,提供路由选择的能力

网络层的主要协议是IP协议,主要的作用就是给包加上源地址和目标地址,然后将数据包传送到目标地址

收到 IP 数据包解析以后,它怎么知道这个分组应该投递到上层的哪一个协议(UDP 或 TCP)

ip报文头部有上层协议的标识

网络访问层

网络访问层提供了主机连接到物理网络需要的硬件和相关的协议

分层的好处

  • 各层独立:限制了依赖关系的范围,各层之间使用标准化的接口,不需要知道上下层是如何工作的,增加或者修改一个应用层协议不影响传输层协议
  • 易于测试和维护:提高了可测试性,可以独立测试特定层,某一层有了更好的实现可以整体替换掉,可插拔性
  • 标准化:每一职责清楚,方便进行标准化

1、TCP协议(需要补充)

TCP是一个可靠的,面向连接的,面向字节流、全双工的协议

  • 可靠的: TCP 是在 IP 协议的基础上构建的传输层协议,IP 是一种无连接不可靠的协议,就是说IP协议只会将数据报从发送者传输给接收者,并不保证包到达的顺序和是否重复,甚至说,不会保证包是否到达接收者;所以说 TCP 必须通过自身的一些机制来保障可靠性(大概是从5个方面)

  • 面向连接: 就是在发送数据之前需要握手建立连接,然后再结束通信时通过挥手来断开连接

  • 字节流: TCP在传输层发送消息的时候,一个消息可能会被分割成多个TCP报文进行转发给网络层,我们不能单纯的认为一个TCP报文就是一个消息,它们之间没有一个固定的边界,所以TCP是面向字节流协议

    TCP提供了一种字节流服务,而收发双方都不保持记录的边界,应用程序应该如何提供他们自己的记录标识呢?

    TCP 这一层收发双方其实不好处理,会交给应用层处理,比如通过报文固定长度,不足补0;特殊前缀等

  • 全双工: 在 TCP 中发送端和接收端可以是客户端,也可以是服务器,通信的双方在任意时刻既可以是接收数据的那一方也可以是发送数据的那一方,每个方向的数据流都独立管理序列号、滑动窗口大小、MSS 等信息

可靠性从5个方面来保证:

  1. 保证包一定到达: 每个TCP包首部中都有两个字节用来表示校验和,防止在传输过程中有损坏。如果收到一个校验和有差错的报文,TCP 不会发送任何确认直接丢弃它,等待发送端重传
  2. 解决包乱序和重复: 在TCP的包首部中有32位的确认序号,假设我们TCP发三个数据包,因为网络的原因导致第二个、第三个包先到接收端,第一个包最后到,接收端不会因为到达顺序和发出顺序不一样就把包弄错,TCP 会根据他们的序号进行重新的排列然后把结果传递给上层应用程序。如果 TCP 接收到重复的数据,可能的原因是超时重传了两次但这个包并没有丢失,接收端会收到两次同样的数据,它能够根据包序号丢弃重复的数据
  3. 超时重传: TCP 发送数据后会启动一个定时器,等待对端确认收到这个数据包。如果在指定的时间内没有收到 ACK 确认,就会重传数据包,然后等待更长时间,如果还没有收到就再重传,在多次重传仍然失败以后,TCP 会放弃这个包
  4. 流量控制:
  5. 拥塞控制:

UDP

TCP 是一个有状态的协议,需要先与对方建立连接然后才能发送数据,而且保证数据不丢失不重复。而 UDP 则比较简单,它无状态,不用事先建立连接就可以任意发送数据,但不保证数据一定会发到对方。两个协议的另一个重要区别在于数据的形式。TCP 的数据是连续的“字节流”,有先后顺序,而 UDP 则是分散的小数据包,是顺序发,乱序收。

2、TCP首部

端口号(源端口、目标端口)

TCP 的首部中会包含源端口号和目标端口号

端口号就是用来区分一个主机上不同应用程序的

端口号分为:

  • 熟知端口号:HTTP、HTTPS
  • 已登记端口号:MySQL、Redis
  • 临时端口号

TCP 用两个字节的整数来表示端口,一台主机最大允许 65536 个端口号;

使用 netstat 和 lsof 来做一些端口号相关的操作

1、序列号

TCP 是面向字节流的协议,通过 TCP 传输的字节流的每个字节都分配了序列号,序列号指的是报文段第一个字节的序列号,序列号加上报文长度就可以确定传输的是哪一段数据,就可以保证包的顺序

序列号主要用来解决数据包乱序和重复的问题,数据包以正确的顺序组装传递给上层应用

初始序列号

初始序列号是在 TCP 三次握手之前生成的,三次握手握手的主要目的就是交换客户端和服务器各自的初始序列号

初始序列号是通过源地址、目标地址、源端口、目标端口和一个随机有因子通过MD5进行计算出来的,因为我们的 MD5 其实是一种摘要算法,如果仅有这几个因子的话,其实初始序列是很有可能相同的,所以我们就是把 MD5 算出来的结果加上时间因子,这样初始序列号就不会重复了

2、确认号

TCP 使用确认号ACK来告知对方下一个期望接收的序列号,小于此确认号数值的所有字节都已经收到

  • 不是所有的包都需要确认的
  • 不是收到了数据包就立马需要确认的,可以延迟一会再确认
  • ACK 包本身不需要被确认,否则就会无穷无尽死循环了
  • 确认号永远是表示小于此确认号的字节都已经收到

3、TCP 标记

TCP 有很多类型的数据包,它们各自的含义也不相同;TCP 首部有一个8比特位的标记位,不同类型的数据包只需要将对应的比特位置为1就行,并且这些标记可以组合使用,SYN+ACK,FIN+ACK

  • SYN(Synchronize):用于发起连接数据包同步双方的初始序列号
  • ACK(Acknowledge):确认数据包
  • RST(Reset):这个标记用来强制断开连接,通常是之前建立的连接已经不在了、包不合法、或者实在无能为力处理
  • FIN(Finish):通知对方我发完了所有数据,准备断开连接,后面我不会再发数据包给你了。
  • PSH(Push):告知对方这些数据包收到以后应该马上交给上层应用,不能缓存起来

窗口大小

TCP 首部中有一个窗口大小,窗口大小表示的是接受端还有多少缓存区可以用来接收数据,当窗口变成 0 时,表示接收端暂时不能接收数据了;

窗口大小是在三次握手的时候确定的

这个窗口的大小是在不断变化的,因此也叫做滑动窗口就,流量控制就是基于滑动窗口来做的

滑动窗口的本质就是接收缓冲区空闲的空间,接收端会通过ACK包来告诉发送端窗口的大小,发送端根据窗口的大小来发送数据达到流量控制的目的

辅助信息(需要补充)

MSS TCP 允许接收的最大报文段

TCP 会主动把数据分割成小段再交给网络层,最大的分段大小称之为 MSS,为了避免被发送方分片

这样一个 MSS 的数据恰好能装进一个 MTU 而不用分片

  • 数据链路层传输的帧大小是有限制的,不能把一个太大的包直接塞给链路层,这个限制被称为「最大传输单元(Maximum Transmission Unit, MTU)」

SACK 选择确认选项

Window Scale 窗口缩放选项

Timestamps 时间戳

  • 发送方发送数据的时候,将自己的时间戳也发过去
  • 接收方收到数据后,将收到的发送方的时间戳返回去,并且把自己的时间戳也放进去

这样做的好处是:

  • 测量两端的往返时延(RTTM)
  • 防止序列号回绕

3、TCP 三次握手(需要补充)

三次握手的目的是为了交换客户端和服务端的初始序列号还有交换一些辅助的信息

辅助信息:

  • 最大段大小(MSS)
  • 窗口大小(Win)
  • 窗口缩放因子(WS)
  • 是否支持选择确认

第一次握手:

客户端首先会确定一个初始序列号,然后会向服务端发送一个 SYN 报文,这个报文的 SYN 标记被置位,这个 SYN 报文的序列号就是我们的初始序列号,第一次握手的作用就是让服务端知道客户端的初始序列号

SYN 报文不携带数据,但是需要被确认,所以是会消耗一个序列号,下次发送数据序列号要加一

第二次握手:

服务端也会确认一个自己的初始序列号,然后向客户端发送一个 SYN 报文和一个 ACK 报文,这个 SYN 报文的作用就是告知客户端服务端的初始序列号;ACK 报文的作用是告诉客户端说,我收到你发的 SYN 报文了,你的下一个报文要从 SYN 的序列号+1开始发

ACK 报文不携带数据,并且不需要被确认,所以不会消耗序列号,只会占用一个序列号,他用完之后,下一个报文可以接着用

第三次握手:

客户端收到了服务端发送的 SYN 报文之后,也会发一个 ACK 报文来进行确认;

TFO

要发数据先得有三次包交互建连。三次握手带来的延迟使得创建一个新 TCP 连接代价非常大,我们可以使用连接重用技术来优化这个过程

TCP 有一个扩展协议 TFO,这个协议能够让经历过一次正常的三次握手之后,能够在之后的三次我握手中,在发送第一个 SYN 包的时候就开始传数据了

TFO 是通过 Cookie 来实现的

第一次握手的时候:

  • 客户端发送一个 SYN 包,头部包含 Fast Open 选项,这个选项的Cookie 是空的,表示说客户端请求 Fast Open Cookie
  • 服务端收取 SYN 包以后,生成一个 cookie 值(一串字符串)
  • 服务端发送 SYN + ACK 包,然后顺带把生成的 Cookie 也传过去
  • 客户端缓存服务端的 IP 和收到的 cookie 值

第一次过后,客户端就有了缓存在本地的 cookie 值,后面的握手和数据传输过程如下:

  • 客户端发送 SYN 数据包,里面包含数据和之前缓存在本地的 Cookie。(这个 SYN 包是携带数据的,普通的三次握手 SYN 包是不能携带数据的)
  • 服务端检验收到的 Cookie 和传输的数据是否合法。如果合法就会返回 SYN + ACK 包进行确认并将数据包传递给应用层,如果不合法就会丢弃数据包,走正常三次握手流程
  • 服务端程序收到数据以后可以握手完成之前发送响应数据给客户端了
  • 客户端发送 ACK 包,确认第二步的 SYN 包和数据
  • 后面的过程就跟非 TFO 连接过程一样了

TFO 可以节省一个 RTT(一个连接的往返时间)

4、TCP 四次挥手

第一次挥手:

客户端会发送一个 FIN 报文给服务端,FIN 报文的作用就是告诉对方,我发完了所有数据,准备断开连接,后面我不会再发数据包给你了。FIN 报文其实就是将 FIN 标志位设置为 1。发完 FIN 报文之后,客户端就不能再发送数据给服务端了,但还可以接收服务端的数据

FIN 报文时可以携带数据的,客户端可以在发送最后一个数据块的时候把 FIN 报文给捎上;也可以不带数据,都会消耗一个序列号

第二次挥手:

服务端收到客户端发的 FIN 报文之后,会发一个 ACK 报文给 客户端,就是告诉客户端说我知道你准备关闭了

第三次挥手:

服务端也没有数据要发了,就会给客户端发一个 FIN 报文,然后等待客户端进行确认

第四次挥手:

客户端收到服务端的 FIN 报文以后,回复 ACK 报文用来确认第三步里的 FIN 报文,进入TIME_WAIT状态,等待 2 个 MSL 以后进入 CLOSED状态。服务端收到 ACK 以后进入CLOSED状态。

MSL:

MSL 是 TCP 报文在网络中的最大生存时间

为什么要等待两个 MSL / TIME_WAIT 存在的原因是什么:

  1. 假如服务端给客户端发送了一个报文,因为网络延迟,迟迟没有到达,这个时候客户端直接进入CLOSED状态。然后客户端和原来的服务端又建立的一条 TCP 连接,开始发送数据,这个时候,之前的报文到了,就会造成数据的混乱;等待两个 MSL,保证了在创建新的 TCP 连接以后,老连接来迟的包已经在网络中被丢弃,不会干扰新的连接。
  2. 关闭连接的四次挥手中,最终的 ACK 由主动关闭方发出,如果这个 ACK 丢失,对端(被动关闭方)将重发 FIN,如果主动关闭方不维持 TIME_WAIT 直接进入 CLOSED 状态,则无法重传 ACK,被动关闭方因此不能及时可靠释放。

为什么是两个 MSL:

  • 1 个 MSL 确保四次挥手中主动关闭方最后的 ACK 报文最终能达到对端
  • 1 个 MSL 确保对端没有收到 ACK 重传的 FIN 报文可以到达
  • 2MS = 去向 ACK 消息最大存活时间(MSL) + 来向 FIN 消息的最大存活时间(MSL)

TIME_WAIT 的连接会存活2MSL,也就是60S,这就意味着端口号在一分钟之内是无法复用的;如果有大量连接处于 TIME_WAIT 会造成端口不够用情况

5、TCP 超时重传

TCP 发送数据后会启动一个定时器,等待对端确认收到这个数据包。如果在指定的时间内没有收到 ACK 确认,就会重传数据包,然后等待更长时间,如果还没有收到就再重传

1、快重传机制

当发送端收到 3 个或以上重复 ACK,就意识到之前发的包可能丢了,于是马上进行重传,不用傻傻的等到超时再重传

2、选择确认

选择确认在发重复ACK的包时,会把接收端后续接收到的包告诉发送端,这样发送端就知道后续的包已经被接收端收到,只有重传丢失的包就行了

6、TCP 流量控制【滑动窗口】

流量处理防止发送端向接收端过多的发送数据

TCP 会把要发送的数据放入发送缓冲区,接收到的数据放入接收缓冲区,程序会不停的读取接收缓冲区的内容进行处理。

流量控制做的事情就是,如果接收缓冲区已满,发送端应该停止发送数据;为了控制发送端的速率,接收端通过 ACK 包来告诉客户端自己的接收窗口,就是接收缓冲区的空闲部分,发送端要根据这个值调整自己的发送策略

ACK 包里面会告诉发送端接收端的窗口的大小,比如说win=3000,发送端收到这个 ACK 包之后,会把自己的发送窗口限制在3000之内,如果这个值win=0,发送端就应该停止发送数据

7、TCP 拥塞控制

流量处理只能防止发送端向接收端过多的发送数据,有考虑整个网络的通信状况,所以需要拥塞控制,拥塞控制从整个网络的层面去控制数据的发送

TCP 拥塞控制的本质就是通过在发送端本地内存中维护拥塞窗口值和慢启动阈值来实现的,这两个值不会被交换,当拥塞窗口值小于慢启动阈值时,使用慢启动算法,拥塞窗口值呈指数级增长;当拥塞窗口值大于慢启动阈值时,使用拥塞避免算法,拥塞窗口值呈线性增长;但是,拥塞窗口值只是基于当前网络状况的一个理论值,发送窗口的实际大小还是要参考接收端接收窗口的大小,取拥塞窗口和接收窗口中最小的作为发送窗口

主要涉及到四个算法:

  • 慢启动
  • 拥塞避免
  • 快速重传
  • 快速恢复

为了实现上面的算法,TCP 的每条连接都要维护两个值:

  • 拥塞窗口
  • 慢启动阈值

拥塞控制其实就是通过这四种算法来控制拥塞窗口的变化

7.1、拥塞窗口

拥塞控制窗口的大小取决于网络的拥塞程度,并且动态变化。发送方让自己的发送窗口取为拥塞窗口和接收方的接受窗口中较小的一个

如果接收窗口比拥塞窗口小,表示接收端处理能力不够。如果拥塞窗口小于接收窗口,表示接收端处理能力 ok,但网络拥塞。

拥塞控制的算法的本质是控制拥塞窗口(cwnd)的变化

7.2、慢启动

在 TCP 连接建立之初,发送端是不知道接收端有多快,如果是一个缓慢的网络,发送端一下发送大量的数据,就会导致更大的网络延迟。

所以每个 TCP 连接都有一个拥塞窗口的限制,最初整个值很小,随着时间的推移,每次发送的数据量慢慢递增,这种机制就叫慢启动

慢启动算法的过程如下:

  • 第一步,三次握手以后,双方通过 ACK 告诉了对方自己的接收窗口(rwnd)的大小,之后就可以互相发数据了
  • 第二步,通信双方各自初始化自己的「拥塞窗口」(cwnd)
  • 第三步,cwnd 初始值较小时,每收到一个 ACK,cwnd + 1,每经过一个 RTT,cwnd 变为之前的两倍。

TCP 在发送一个包时,会记录这个包的发送的时间 t1,用收到这个包的确认包时 t2 减去 t1 就可以得到这次的 RTT。

拥塞窗口的大小肯定不能就一直增长,我们通过慢启动阈值来控制,当拥塞窗口大小小于慢启动阈值时,拥塞窗口按指数级增长;当大于慢启动阈值后,按线性增长,这就是拥塞避免

7.3、拥塞避免

当我们的拥塞窗口大于慢启动阈值时,就会进入拥塞避免阶段,每次增加一个 MSS,直到检测到拥塞为止;MSS是TCP允许接收的最大报文段;

与慢启动的区别在于

  • 慢启动的做法是 RTT 时间内每收到一个 ACK,拥塞窗口 cwnd 就加 1,也就是每经过 1 个 RTT,cwnd 翻倍
  • 拥塞避免的做法保守的多,每经过一个RTT 才将拥塞窗口加 1,不管期间收到多少个 ACK

7.4、快速重传

重传的时间间隔,要等几百毫秒才会进行第一次重传,并且重传的时候,TCP 将会使用定时器来要求传输暂停。在暂停的这段时间内,没有新的或复制的数据包被发送,所以有了快速重传

当接收端收到一个不按序到达的数据段时,TCP 立刻发送 1 个重复 ACK,当发送端收到 3 个或以上重复 ACK,就意识到之前发的包可能丢了,于是马上进行重传,不用傻傻的等到重传定时器超时再重传。

选择确认(SACK)

快速重传解决了重传问题,但又引出了一个新的问题,那就是,丢失的那个包之后发出去的包也有可能丢失,到底是重传丢的包还是都重传呢,选择确认解决了这个问题

选择确认在发重复ACK的包时,会把接收端后续接收到的包告诉发送端,这样发送端就知道后续的包已经被接收端收到,只有重传丢失的包就行了

7.5、快速恢复

当收到三次重复 ACK 时,进入快速恢复阶段,这阶段可以理解为网络轻度拥塞。

  • 拥塞阈值 降低为 拥塞窗口 的一半:ssthresh = cwnd / 2
  • 拥塞窗口 cwnd 设置为 拥塞阈值
  • 拥塞窗口线性增加

8、Nagle 算法

Nagle 算法作用是减少发送端频繁的发送小包给对方

Nagle 算法是应用在发送端的,简而言之就是,对发送端而言:

  • 当第一次发送数据时不用等待,就算是 1byte 的小包也立即发送
  • 后面发送数据时需要累积数据包直到满足下面的条件之一才会继续发送数据:
    • 数据包达到最大段大小MSS
    • 接收端收到之前数据包的确认 ACK

Nagle 算法可以使得不再那么频繁的发送小包,而是合并到一起,代价是稍微有一些延迟。

而且,Nagle 算法和延迟确认一起使用会出现很严重的性能问题了。Nagle 攒着包一次发一个,延迟确认收到包不马上回。

9、延迟确认

如果收到一个数据包以后暂时没有数据要分给对端,它可以等一段时间(Linux 上是 40ms)再确认。如果这段时间刚好有数据要传给对端,ACK 就可以随着数据一起发出去了。如果超过时间还没有数据要发送,也发送 ACK,以免对端以为丢包了。这种方式成为「延迟确认」。

这个原因跟 Nagle 算法其实一样,回复一个空的 ACK 太浪费了。

  • 如果接收端这个时候恰好有数据要回复客户端,那么 ACK 搭上顺风车一块发送。
  • 如果期间又有客户端的数据传过来,那可以把多次 ACK 合并成一个立刻发送出去
  • 如果一段时间没有顺风车,那么没办法,不能让接收端等太久,一个空包也得发。

这种机制被称为延迟确认

10、TCP 的 keepalive 机制

网络故障或者系统宕机都将使得对端无法得知这个消息。如果应用程序不发送数据,可能永远无法得知该连接已经失效。假设应用程序是一个 web 服务器,客户端发出三次握手以后故障宕机或被踢掉网线,对于 web 服务器而已,下一个数据包将永远无法到来,但是它一无所知

TCP 协议的设计者考虑到了这种检测长时间死连接的需求,于是乎设计了 keepalive 机制。

二、HTTP

1、HTTP

HTTP 是超文本传输协议其实就是一种约定,约定了计算机之间的交流通信的规范,还有各种控制和错误处理的方式,交流通信其实就是传输数据,它传输的数据不像 TCP 一样传输的是二进制包,HTTP 传输的能够被浏览器服务器处理的完整的有意义的数据,比如我们的HTML

HTTP 通常跑在 TCP/IP 协议栈之上,依靠 IP 协议实现寻址和路由、TCP 协议实现可靠数据传输、DNS 协议实现域名查找、SSL/TLS 协议实现安全通信。

CDN

浏览器不会说直接连到服务器,中间会有 CDN,主要起到缓存加速的作用

CDN 可以缓存源站的数据,让浏览器的请求不用“千里迢迢”地到达源站服务器,直接在“半路”就可以获取响应。

HTTP 与 TCP 的关系

TCP 是传输控制协议,基于 IP 协议来提供可靠的字节流形式的通信,是 HTTP 的基础,HTTP 不关心传输的细节,其实是运行在了 TCP 协议上

TCP 是 HTTP 的下层协议,负责具体的数据传输

DNS(域名系统)

在 TCP/IP 协议中使用 IP 地址来标识计算机,数字形式的地址对于计算机来说是方便了,但是对人来说,数字不好记

DNS 就是用有意义名字来代替IP地址那一串数字,就叫做域名

但是我们用 TCP/IP 协议来通信仍然要使用 IP 地址,所以需要把域名做一个转换,“映射”到它的真实 IP,这就是所谓的“域名解析”

URI/URL

DNS 和 IP 地址只是标记了互联网上的主机,但主机上有很多多文本、图片、页面,我们需要用 URI(统一资源标识符),来唯一的标记互联网上的资源

URL, 统一资源定位符,也就是我们俗称的“网址”,它实际上是 URI 的一个子集,不过因为这两者几乎是相同的,差异不大,所以通常不会做严格的区分。

URI 主要有三个基本的部分构成:

  • 协议名:即访问该资源应当使用的协议,在这里是“http”;
  • 主机名:即互联网上主机的标记,可以是域名或 IP 地址,在这里是“nginx.org”;
  • 路径:即资源在主机上的位置,使用“/”分隔多级目录,在这里是“/en/download.html”。

HTTPS

HTTPS 相当于“HTTP+SSL/TLS+TCP/IP”,为 HTTP 套了一个安全的外壳;

2、协议分层

2.1、TCP/IP 网络分层模型(四层)

  • **链接层:**负责在以太网、WiFi 这样的底层网络上发送原始数据包,工作在网卡这个层次,使用 MAC 地址来标记网络上的设备,所以有时候也叫 MAC 层。
  • **网际层:**IP 协议就处在这一层。因为 IP 协议定义了“IP 地址”的概念,所以就可以在“链接层”的基础上,用 IP 地址取代 MAC 地址,把许许多多的局域网、广域网连接成一个虚拟的巨大网络,在这个网络里找设备时只要把 IP 地址再“翻译”成 MAC 地址就可以了。
  • **传输层:**这个层次协议的职责是保证数据在 IP 地址标记的两点之间“可靠”地传输,是 TCP 协议工作的层次,还有 UDP
  • **应用层:**HTTP

MAC 层的传输单位是帧(frame),IP 层的传输单位是包(packet),TCP 层的传输单位是段(segment),HTTP 的传输单位则是消息或报文(message)。但这些名词并没有什么本质的区分,可以统称为数据包。

2.2、OSI 网络分层模型(七层)

  1. 第一层:物理层,网络的物理形式,例如电缆、光纤、网卡、集线器等等;
  2. 第二层:数据链路层,它基本相当于 TCP/IP 的链接层;
  3. 第三层:网络层,相当于 TCP/IP 里的网际层;第四层:传输层,相当于 TCP/IP 里的传输层;
  4. 第五层:会话层,维护网络中的连接状态,即保持会话和同步;
  5. 第六层:表示层,把数据转换为合适、可理解的语法和语义;
  6. 第七层:应用层,面向具体的应用传输数据。

2.3、TCP/IP 协议栈的工作方式

HTTP 利用 TCP/IP 协议栈传输数据可以想象成一个发快递的过程

假设你要发一个快递给同学,你要先把东西包起来,这个东西就是 HTTP 要传输的内容,比如 HTML,包起来就相当于是 HTTP 协议为它加了一个 HTTP 专用附加数据

然后把快递交给快递员,快递员会给快读套个盒子,贴快递单号,相当于在 TCP 层给数据再次打包,加上了 TCP 头。

接着快递小哥,把快递运到集散点,然后再装进更大的卡车里,相当于在 IP 层、MAC 层对 TCP 数据包加上了 IP 头、MAC 头。

到了另外一个城市,就要卸货,相当于是在 IP 层、MAC 层传输后拆包。

递员到了你朋友的家门口,撕掉标签,去除了 TCP 层的头,你朋友再拆掉塑料袋包装,也就是 HTTP 头,最后就拿到了玩具,也就是真正的 HTML 页面。

3、域名(DNS)

在 TCP/IP 协议中使用 IP 地址来标识计算机,数字形式的地址对于计算机来说是方便了,但是对人来说,数字不好记

DNS 就是用有意义名字来代替IP地址那一串数字,就叫做域名

但是我们用 TCP/IP 协议来通信仍然要使用 IP 地址,所以需要把域名做一个转换,“映射”到它的真实 IP,这就是所谓的“域名解析”

域名解析

DNS 的核心系统是一个三层的树状、分布式服务,基本对应域名的结构:

  1. 根域名服务器(Root DNS Server):管理顶级域名服务器,返回“com”“net”“cn”等顶级域名服务器的 IP 地址;
  2. 顶级域名服务器(Top-level DNS Server):管理各自域名下的权威域名服务器,比如 com 顶级域名服务器可以返回 apple.com 域名服务器的 IP 地址;
  3. 权威域名服务器(Authoritative DNS Server):管理自己域名下主机的 IP 地址,比如 apple.com 权威域名服务器可以返回 www.apple.com 的 IP 地址。

例如,你要访问“www.apple.com”,就要进行下面的三次查询:

  1. 访问根域名服务器,它会告诉你“com”顶级域名服务器的地址;
  2. 访问“com”顶级域名服务器,它再告诉你“apple.com”域名服务器的地址;
  3. 最后访问“apple.com”域名服务器,就得到了“www.apple.com”的地址。

域名缓存

  1. 大公司、网络运行商都会建立自己的 DNS 服务器,作为用户 DNS 查询的代理,代替用户访问核心 DNS 系统

    用户访问某个网址,并不会直接去请求权威域名服务器,而是请求一些“非权威域名服务器”,由它们代理去请求“权威域名服务器”,如果这些“非权威域名服务器”在自己的缓存中找到了要请求的地址,就直接返回,无需再去请求“权威域名服务器”,大大减轻了“权威域名服务器”的压力。

  2. 操作系统里也会对 DNS 解析结果做缓存,如果你之前访问过“www.apple.com”,那么下一次在浏览器里再输入这个网址的时候就不会再跑到 DNS 那里去问了,直接在操作系统里就可以拿到 IP 地址。

  3. 另外,操作系统里还有一个特殊的“主机映射”文件,如果操作系统在缓存里找不到 DNS 记录,就会找这个文件。

4、输入一个URL,会发生什么

4.1、使用 IP 地址访问 Web 服务器

  1. 浏览器从地址栏的输入中获得服务器的 IP 地址和端口号;
  2. 浏览器用 TCP 的三次握手与服务器建立连接;
  3. 浏览器向服务器发送拼好的报文;
  4. 服务器收到报文后处理请求,同样拼好报文再发给浏览器;浏览器解析报文,渲染输出页面。

4.2、输入一个URL,访问 Web服务器

浏览器首先会判断你输入的是不是 IP 地址,不是的话然后会进行域名解析,然后检查一下浏览器自己的缓存里面有没有,如果没有就向操作系统的缓存要,还没有就检查本机hosts文件,也就我们的本机域名解析文件,如果有就建立 TCP 连接发送 HTTP 请求;

还没有的话,就要从 根域名 顶级域名 权威域名 层层查找

例如,你要访问“www.apple.com”,就要进行下面的三次查询:

  1. 访问根域名服务器,它会告诉你“com”顶级域名服务器的地址;
  2. 访问“com”顶级域名服务器,它再告诉你“apple.com”域名服务器的地址;
  3. 最后访问“apple.com”域名服务器,就得到了“www.apple.com”的地址。

解析失败的浏览器尝试换别的DNS服务器,最终失败的进入错误页面

5、HTTP 报文

HTTP 协议的请求报文和响应报文的结构基本相同,由三大部分组成:

  1. 起始行(start line):描述请求或响应的基本信息;
  2. 头部字段集合(header):使用 key-value 形式更详细地说明报文;
  3. 消息正文(entity):实际传输的数据,它不一定是纯文本,可以是图片、视频等二进制数据。

这其中前两部分起始行和头部字段经常又合称为“请求头”或“响应头”,消息正文又称为“实体”,但与“header”对应,很多时候就直接称为“body”。

HTTP 协议规定报文必须有 header,但可以没有 body

5.1、起始行(请求行、状态行)

请求报文中的起始行叫请求行;

请求行由三部分构成:

  1. 请求方法:是一个动词,如 GET/POST,表示对资源的操作;
  2. 请求目标:通常是一个 URI,标记了请求方法要操作的资源;
  3. 版本号:表示报文使用的 HTTP 协议版本。

响应报文中的起始行叫状态行;

状态行由三部分构成:

  1. 版本号:表示报文使用的 HTTP 协议版本;
  2. 状态码:一个三位数,用代码的形式表示处理的结果,比如 200 是成功,500 是服务器错误;
  3. 原因:作为数字状态码补充,是更详细的解释文字,帮助人理解原因。

5.2、头部字段

请求行或状态行再加上头部字段集合就构成了 HTTP 报文里完整的请求头或响应头

头部字段是 key-value 的形式,key 和 value 之间用“:”分隔,最后用 CRLF 换行表示字段结束。比如在“Host: 127.0.0.1”这一行里 key 就是“Host”,value 就是“127.0.0.1”。

6、GET 方法和 POST 方法的区别

  • get用来获取数据,post用来提交数据
  • get参数有长度限制(受限于url长度,具体的数值取决于浏览器和服务器的限制,最长2048字节),而post无限制。
  • get请求的数据会附加在url之 ,以 " ? "分割url和传输数据,多个参数用 "&"连接,而post请求会把请求的数据放在http请求体中。
  • get是明文传输,post是放在请求体中,但其实是可以通过抓包工具看到,也相当于是明文的。

get 和 post 本质上其实都是 TCP 连接,因为 HTTP 协议的约定,可能导致了它们在使用的过程中有一点差别,本质上其实是一样的;

**差别就是:**get在请求时发送一个数据包,会将header和data一起发送过去,而post会产生两个数据包先发送header,服务器返回100,然后在发送data,服务器返回200

7、HTTP 响应状态码

状态码的就是表达 HTTP 数据处理的“状态”,客户端可以依据状态码做出下一步的动作

大体分为五类:

  • 1××:提示信息,表示目前是协议处理的中间状态,还需要后续的操作;
  • 2××:成功,报文已经收到并被正确处理;
  • 3××:重定向,资源位置发生变动,需要客户端重新发送请求;
  • 4××:客户端错误,请求报文有误,服务器无法处理;
  • 5××:服务器错误,服务器在处理请求时内部发生了错误。

7.1、2××

2×× 类状态码表示服务器收到并成功处理了客户端的请求

“200 OK”是最常见的成功状态码,表示一切正常

7.2、3××

3×× 类状态码表示客户端请求的资源发生了变动,客户端必须用新的 URI 重新发送请求获取资源,也就是通常所说的“重定向”

“301 Moved Permanently 永久重定向”,含义是此次请求的资源已经不存在了,需要改用新的 URI 再次访问。

“302 Found 临时重定向”,请求的资源还在,但需要暂时用另一个 URI 来访问

7.3、4××

4××类状态码表示客户端发送的请求报文有误,服务器无法处理,它就是真正的“错误码”含义了

“400 Bad Request”是一个通用的错误码,不会指明具体的错误,非常笼统

“403 Forbidden” 表示服务器禁止访问资源。

“404 Not Found” 资源在本服务器上未找到,所以无法提供给客户端。

7.4、5××

5×× 类状态码表示客户端请求报文正确,但服务器在处理时内部发生了错误,无法返回应有的响应数据,是服务器端的“错误码”

“500 Internal Server Error” ,是一个通用的错误码

8、HTTPS

由于 HTTP 是明文传输,整个传输过程完全透明,任何人都能够在链路中截获、修改或者伪造请求 / 响应报文,数据不具有可信性

HTTPS 把 HTTP 的下层协议换成了 SSL/TLS , 让 HTTP 运行在了安全的 SSL/TLS 协议上,相当于是在应用层和传输层之间加了一个TLS安全层

从三个维度来保证 HTTP 的安全:

  • 机密性,就是让不该看到的人看不到
  • 完整性:保证数据在传输过程中没有被篡改,保持完整性
  • 身份认证:确认对方真的是对方,不是别人冒充的

8.1、使用对称和非对称加密结合来保证机密性

对称加密

对称加密就是加密和解密都使用同一个密钥,需要保证密钥的安全,对称加密的问题就在于如何保证密钥的安全传输

非对称加密

非对称加密有两个密钥,一个公钥一个私钥,公钥可以公开随便用,但是私钥必须严格保密

公钥加密只能用私钥解密,私钥加密只能用公钥解密

非对称加密基于大数运算,比如大素数或者椭圆曲线,是复杂的数学难题,所以消耗计算量,运算速度慢。

我们采用非对称加密的方式来传输对称加密的密钥,通过这个方式保证对称加密密钥的安全

8.2、使用摘要算法来保证完整性(数字摘要)

黑客虽然拿不到会话密钥,无法破解密文,但可以通过窃听收集到足够多的密文,再尝试着修改、重组后发给网站。因为没有完整性保证,服务器只能“照单全收”,然后他就可以通过服务器的响应获取进一步的线索,最终就会破解出明文。

摘要算法其实就是哈希算法,就是把一不定长的输入映射成定长的输出;比如说 MD5

我们把对称加密的密钥哈希一下,然后把摘要附在密钥之后,然后用公钥加密进行传输,这个就叫数字摘要

8.3、使用数字证书来解决身份认证

还涉及到一个公钥的信任问题,黑客可以冒充网站发布公钥

这个时候我们需要使用CA来解决公钥信任的问题

小 CA 让大 CA 签名认证,一直到 根CA

证书撤销的问题

8.4、HTTPS 连接过程

在 HTTP 协议里,建立连接后,浏览器会立即发送请求报文,但如果使用的是 HTTPS 协议,需要再来一个握手过程, 在 TCP 上建立安全连接,之后才会收发 HTTP 报文,HTTPS 需要额外的握手

HTTPS 连接建立的过程:

首先会进行 TCP 的三次握手,建立 TCP 连接

首先是进行客户端和服务端信息的交换

  • 客户端—>服务器: 客户端的版本号、支持的密码套件,还有一个客户端随机数

  • 服务端核对客户端版本号,从客户端支持的密码套件中选择一个,会把选择的密码套件和服务端随机数发送过去;

  • 服务端—>客户端: 服务端会把CA证书发送给客户端端,用来证明自己的身份;还会发送一个服务端公钥用来实现密钥交换算法,为了防止公钥被篡改,会用自己的私钥做签名认证

然后是客户端对证书进行验证以及发送客户端公钥

  • 客户端拿到证书之后,走证书链逐级验证,确认证书的真实性,再用证书公钥验证签名,就确认了服务器的身份

  • 客户端端也会生成一个客户端公钥发送给服务端

生成主密钥

  • 客户端和服务端手里现在都有客户端公钥和服务端公钥,这个时候客户端和服务端会根据这两个参数用算法生成一个随机数
  • 客户端和服务端跟据客户端公钥、服务端公钥和随机数生成主密钥
  • 因为使用一个密钥可能会带来安全隐患,所以用主密钥派生出一些会话密钥用于通信
  • 最后客户端和服务端都会互相发送信息,确认之后进行加密通信

9、HTTP2

HTTP2 新特性:引入了 HPACK 和 Stream 协议

  • 头部压缩
  • 二进制形式
  • 基于流传输
  • 强化安全

头部压缩

HTTP1 的时候呢,我可以在 HTTP Header 中指定 Body 的压缩方式来对 Body 进行压缩,这样来节省带宽

但是 HTTP 协议中,Header 的长度远远大于 Body,并且 Header 中的很多字段都是重复的,大量带宽消耗在了这些冗余度极高的东西上面

所以说 HTTP2 对 Header 进行了压缩

使用 HPACK 算法,这个算法在客户端和服务端都建立一个字段,用索引号来表示重复的字符串;并且采用哈夫曼编码来压缩整数和字符串

二进制格式

HTTP2 它已经逐渐向下层 TCP 协议靠拢了,它的报文形式不再是纯文本了,而是采用二进制

人能看懂的东西,往往计算机是看不懂的,需要进行一个解析,所以说效率会很低,使用二进制的话,就会机器读起来就会快很多

HTTP2 打散了 传统的 HTTP 协议的 “Header+Body”的报文结构,把 TCP 协议的部分特性挪到了应用层,把原来的“Header+Body”的消息变成了多个小片的二进制“帧”,用“HEADERS”帧存放头数据、“DATA”帧存放实体数据。

虚拟流

HTTP2 为了把 二进制帧传过去后,再重新组装起来,引入了 流 的概念,就是 Stream 协议;

这个流会给同一个消息往返的帧会分配一个唯一的流 ID,些数据帧按照次序组装起来就是 HTTP/1 里的请求报文和响应报文。非常像 TCP 协议中的包序列号;

这个流还带来了一个好处,就是可以在一个 TCP 连接上用“流”同时发送多个“碎片化”的消息,这就是常说的“多路复用”( Multiplexing)——多个往返通信都复用一个连接来处理。

并且HTTP/2 还在一定程度上改变了传统的“请求 - 应答”工作模式,服务器不再是完全被动地响应请求,也可以新建“流”主动向客户端发送消息。类似于 TCP 的全双工协议,反正充分利用了 TCP 的全双工通道

HTTP/2 还添加了一些控制帧来管理虚拟的“流”,实现了优先级和流量控制,这些特性也和 TCP 协议非常相似

强化安全

HTTP2 对 HTTPS 进行了一定的强化,弃用了部分安全性较差的密码套件

HTTP2 的缺点

与 HTTP/1“并发多个连接”不同,HTTP/2 的“多路复用”特性要求对一个域名(或者 IP)只用一个 TCP 连接,所有的数据都在这一个连接上传输,这样不仅节约了客户端、服务器和网络的资源

在移动网络中发生 IP 地址切换的时候,下层的 TCP 必须重新建连,要再次“握手”,经历“慢启动”,而且之前连接里积累的 HPACK 字典也都消失了,必须重头开始计算,导致带宽浪费和时延

HTTP/2 对一个域名只开一个连接,所以一旦这个连接出问题,那么整个网站的体验也就变差了。

10、HTTP3

11、Cookie

HTTP 是“无状态”的

Cookie 就是服务器委托浏览器存储在客户端里的一些数据,而这些数据通常都会记录用户的关键识别信息。

Cookie 的工作过程

需要用到两个字段:响应头字段 Set-Cookie 和请求头字段 Cookie。

  • 当用户通过浏览器第一次访问服务器的时候,服务器肯定是不知道他的身份的。所以,就要创建一个独特的身份标识数据,格式是“key=value”,然后放进 Set-Cookie 字段里,随着响应报文一同发给浏览器。
  • 浏览器收到响应报文,看到里面有 Set-Cookie,知道这是服务器给的身份标识,于是就保存起来,下次再请求的时候就自动把这个值放进 Cookie 字段里发给服务器。
  • 因为第二次请求里面有了 Cookie 字段,服务器就知道这个用户不是新人,之前来过,就可以拿出 Cookie 里的值,识别出用户的身份,然后提供个性化的服务。

Cookie 生命周期

Cookie 就是服务器委托浏览器存储在客户端里的一些数据,而这些数据通常都会记录用户的关键识别信息。所以,就需要在“key=value”外再用一些手段来保护,防止外泄或窃取,这些手段就是 Cookie 的属性

通过三种方式来保证Cookie的安全:

  • 设置 Cookie 的生存周期:让它只能在一段时间内可用,一旦超过这个期限浏览器就认为是 Cookie 失效,在存储里删除,也不会发送给服务器。

  • 设置 Cookie 的作用域:让浏览器仅发送给特定的服务器和 URI,避免被其他网站盗用。

    作用域的设置比较简单,“Domain”和“Path”指定了 Cookie 所属的域名和路径,浏览器在发送 Cookie 前会从 URI 中提取出 host 和 path 部分,对比 Cookie 的属性。如果不满足条件,就不会在请求头里发送 Cookie。

  • 三个属性:

    • HttpOnly:会告诉浏览器,此 Cookie 只能通过浏览器 HTTP 协议传输,禁止其他方式访问
    • SameSite:严格限定 Cookie 不能随着跳转链接跨站发送
    • Secure:表示这个 Cookie 仅能用 HTTPS 协议加密传输,明文的 HTTP 协议会禁止发送。但 Cookie 本身不是加密的,浏览器里还是以明文的形式存在

TCP 与 HTTP相关推荐

  1. HTTP 协议入门 — (TCP/IP协议族、通信传输流、URI 与 URL 的区别、Cookie 状态管理、HTTP 支持的方法、状态码类别、HTTP 首部字段)

    TCP/IP协议族 在介绍 HTTP 协议之前,我们先对 TCP/IP 协议族有个大概的了解,TCP/IP 协议从上到下主要分为应用层.传输层.网络层和数据链路层,各层的主要功能如下表所示: 协议层 ...

  2. TCP三次握手和四次挥手的解释

    基础知识 在TCP层,有个FLAGS字段,这个字段有以下几个标识:SYN, FIN, ACK, PSH, RST, URG. 其中,对于我们日常的分析有用的就是前面的五个字段. 它们的含义是: SYN ...

  3. TCP/UDP协议基本概念

    TCP和UDP协议是TCP/IP协议的核心. TCP 传输协议:TCP 协议是一TCP (Transmission Control Protocol)和UDP(User Datagram Protoc ...

  4. TCP/UDP对比总结

    在计算机网络中,有三种体系结构划分方式,第一种是OSI七层协议体系结构,由上到下分别是:应用层,表示层,会话层,运输层,网络层,数据链路层,物理层:第二种是TCP/IP四层协议,由上到下分别是:应用层 ...

  5. TCP/IP协议三次握手与四次握手流程解析

    原文链接地址:http://www.2cto.com/net/201310/251896.html TCP/IP协议三次握手与四次握手流程解析 TCP/IP协议的详细信息参看<TCP/IP协议详 ...

  6. 【TCP/IP详解 卷一:协议】第十九章 TCP的交互数据流

    19.1 引言 前一章我们介绍了TCP连接的建立与释放:三握四挥,以及状态转移图. TCP报文段分为:交互数据,以及成块数据(下一章介绍). 交互数据:例如telnet,ssh,这种类型的协议在大多数 ...

  7. erlang的tcp服务器模板

    改来改去,最后放github了,贴的也累,蛋疼 还有一个tcp批量客户端的,也一起了 大概思路是 混合模式 使用erlang:send_after添加recv的超时处理 send在socket的opt ...

  8. 用TCP/IP进行网际互联一

    地址解析协议ARP 主机知道某个目的主机的IP就可以知道该目的主机的物理地址. 改进ARP 每个ARP广播分组中都包含有发送方自身的IP和物理地址的绑定,接收方在处理ARP分组时,先在自己的缓存中更新 ...

  9. 利用 socket 获取 tcp 包并解析的问题。

    服务器端代码如下:(Java Servlet 实现) protected void doPost(HttpServletRequest request, HttpServletResponse res ...

  10. linux7 kernel.sem,centos7.4内核调优,tcp单服务器万级并发

    在使用linux的centos7.4遇到的各种坑,其中一个项目采用四层架构,配置层,平台层,逻辑服务器管理层和集体逻辑服务器层的,一个整体的游戏项目,其中,作为整个项目负责人和架构打架着,项目运行一年 ...

最新文章

  1. TIME_WAIT状态及存在原因
  2. USACO Training Section 1.1 坏掉的项链Broken Necklace
  3. TensorFlow(二)函数基础
  4. docker 相关操作
  5. leetcode283. 移动零 比官方更好的解法。
  6. linux80端口检查,Linux下基于端口的服务检查脚本
  7. java 缓存 30秒后失效_如何处理缓存失效、缓存穿透、缓存并发等问题
  8. MySQL--My.cnf配置文件模板 MYSQL AND MARIADB CONFIGURATION FILE TEMPLATE (MY.CNF/MY.INI)
  9. java开发flex_FLEX+Java开发
  10. JSP中乱码问题,你真的理解了么?
  11. 康华光电子技术基础第六版习题答案
  12. 引用nbsp;TDA2030功放电路图
  13. 什么是软件测试?简介,基础知识和重要性
  14. 凸集(Convex sets)
  15. RTMP流媒体直播资料
  16. ASP微信支付之扫码支付
  17. C:\Users\86131\.gradle\caches\transforms-2\files-2.1\6753cd877c1ba9dbc8b7c64e227cb479\transition-1.2
  18. JavaScript 时间范围
  19. SNMP Private Enterprise Number 申请流程
  20. 索引提高sql查询效率速成宝典

热门文章

  1. 实验室设计规范与标准(实验室设计原则)
  2. 赚钱之路之初识C语言|第三讲
  3. 没有计算机的一天英语作文带翻译,我的一天英语作文带翻译
  4. CSS动画效果——语音播放小喇叭 (实用,赞)
  5. onethink和phpwind共用
  6. 使用teamviewer搭建内网服务器。
  7. 微信小程序---双向绑定传参
  8. Java关键字之catch简介说明
  9. 一边卖不掉,一边买不到,我看电商的“危”与“机”
  10. 应用维特比算法进行路径规划