<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Web | w3xse7en</title>
    <link>https://w3xse7en.github.io/docs/web/</link>
      <atom:link href="https://w3xse7en.github.io/docs/web/index.xml" rel="self" type="application/rss+xml" />
    <description>Web</description>
    <generator>Wowchemy (https://wowchemy.com)</generator><language>zh</language>
    <image>
      <url>https://w3xse7en.github.io/images/icon_hua2ec155b4296a9c9791d015323e16eb5_11927_512x512_fill_lanczos_center_2.png</url>
      <title>Web</title>
      <link>https://w3xse7en.github.io/docs/web/</link>
    </image>
    
    <item>
      <title>Socket</title>
      <link>https://w3xse7en.github.io/docs/web/socket/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://w3xse7en.github.io/docs/web/socket/</guid>
      <description>&lt;hr&gt;
&lt;h2&gt;目录&lt;/h2&gt;
&lt;nav id=&#34;TableOfContents&#34;&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;a href=&#34;#io模型&#34;&gt;I/O模型&lt;/a&gt;
      &lt;ul&gt;
        &lt;li&gt;&lt;a href=&#34;#blocking-io---阻塞io&#34;&gt;Blocking I/O - 阻塞I/O&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#nonblocking-io---非阻塞io&#34;&gt;Nonblocking I/O - 非阻塞I/O&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#io-multiplexing---io多路复用&#34;&gt;I/O Multiplexing - I/O多路复用&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#signal-driven-io----信号驱动io&#34;&gt;Signal-Driven I/O  - 信号驱动I/O&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#asynchronous-io---异步io&#34;&gt;Asynchronous I/O - 异步I/O&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#五种-io-模型比较&#34;&gt;五种 I/O 模型比较&lt;/a&gt;&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#select&#34;&gt;select&lt;/a&gt;
      &lt;ul&gt;
        &lt;li&gt;&lt;a href=&#34;#select遇到的问题&#34;&gt;select遇到的问题&lt;/a&gt;&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#epoll&#34;&gt;epoll&lt;/a&gt;
      &lt;ul&gt;
        &lt;li&gt;&lt;a href=&#34;#epoll是如何解决select的三个问题&#34;&gt;epoll是如何解决select的三个问题&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#epoll的伪码描述&#34;&gt;epoll的伪码描述&lt;/a&gt;&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#参考&#34;&gt;参考&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/nav&gt;
&lt;hr&gt;
&lt;br&gt;
&lt;h2 id=&#34;io模型&#34;&gt;I/O模型&lt;/h2&gt;
&lt;p&gt;[UNIX: registered: Network Programming] 提供了5种IO模型&lt;/p&gt;
&lt;h3 id=&#34;blocking-io---阻塞io&#34;&gt;Blocking I/O - 阻塞I/O&lt;/h3&gt;








  











&lt;figure &gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/socket/block-io.png&#34; &gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/socket/block-io.png&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;



&lt;/figure&gt;

&lt;h3 id=&#34;nonblocking-io---非阻塞io&#34;&gt;Nonblocking I/O - 非阻塞I/O&lt;/h3&gt;








  











&lt;figure &gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/socket/nonblock-io.png&#34; &gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/socket/nonblock-io.png&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;



&lt;/figure&gt;

&lt;h3 id=&#34;io-multiplexing---io多路复用&#34;&gt;I/O Multiplexing - I/O多路复用&lt;/h3&gt;








  











&lt;figure &gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/socket/io-multiplexing.png&#34; &gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/socket/io-multiplexing.png&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;



&lt;/figure&gt;

&lt;h3 id=&#34;signal-driven-io----信号驱动io&#34;&gt;Signal-Driven I/O  - 信号驱动I/O&lt;/h3&gt;








  











&lt;figure &gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/socket/signal-driven-io.png&#34; &gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/socket/signal-driven-io.png&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;



&lt;/figure&gt;

&lt;h3 id=&#34;asynchronous-io---异步io&#34;&gt;Asynchronous I/O - 异步I/O&lt;/h3&gt;








  











&lt;figure &gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/socket/asynchronous-io.png&#34; &gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/socket/asynchronous-io.png&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;



&lt;/figure&gt;

&lt;h3 id=&#34;五种-io-模型比较&#34;&gt;五种 I/O 模型比较&lt;/h3&gt;








  











&lt;figure &gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/socket/compare-five-io.png&#34; &gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/socket/compare-five-io.png&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;



&lt;/figure&gt;

&lt;hr&gt;
&lt;h2 id=&#34;select&#34;&gt;select&lt;/h2&gt;
&lt;p&gt;io多路复用是为了解决一个进程同时处理多个socket问题&lt;/p&gt;
&lt;p&gt;一个简单的思路是&lt;/p&gt;
&lt;p&gt;假设有N个socket链接，检测有socket接收到数据，遍历所有socket进行处理&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// fds = file decriptors
for {
    select (fds) // wait while fds poll callback POLL_IN
    for fd range fds{
        if fd has data{
            read fd
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;被监控的fds需要从用户空间拷贝到内核空间
为了减少数据拷贝带来的性能损坏，内核对被监控的fds集合大小做了限制，并且这个是通过宏控制的，大小不可改变(限制为1024)。&lt;/li&gt;
&lt;li&gt;被监控的fds集合中，只要有一个有数据可读，整个socket集合就会被遍历一次调用sk的poll函数收集可读事件
由于当初的需求是朴素，仅仅关心是否有数据可读这样一个事件，当事件通知来的时候，由于数据的到来是异步的，我们不知道事件来的时候，有多少个被监控的socket有数据可读了，于是，只能挨个遍历每个socket来收集可读事件。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;select遇到的问题&#34;&gt;select遇到的问题&lt;/h3&gt;
&lt;p&gt;总共有三个问题需要解决&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;被监控的fds集合大小被限制了1024，不够用&lt;/li&gt;
&lt;li&gt;fds集合需要从用户空间拷贝到内核空间的问题，耗费性能&lt;/li&gt;
&lt;li&gt;需要遍历fds集合才能知道有数据接收的fds列表&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;epoll&#34;&gt;epoll&lt;/h2&gt;
&lt;p&gt;epoll 是对 select 和 poll 的改进，避免了“性能开销大”和“文件描述符数量少”两个缺点。&lt;/p&gt;
&lt;p&gt;epoll 有以下几个特点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;使用&lt;strong&gt;红黑树&lt;/strong&gt;存储文件描述符集合&lt;/li&gt;
&lt;li&gt;使用&lt;strong&gt;队列&lt;/strong&gt;存储就绪的文件描述符&lt;/li&gt;
&lt;li&gt;每个文件描述符只需在添加时传入一次；通过事件更改文件描述符状态&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;epoll一共有3个接口&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;epoll_create创建epoll实例，其实例内部存储：&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;监听列表：所有要监听的文件描述符，使用&lt;strong&gt;红黑树&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;就绪列表：所有就绪的文件描述符，使用&lt;strong&gt;队列&lt;/strong&gt;&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;epoll_ctl用来维护监视列表，可以添加或删除所要监听的 socket&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;epoll_ctl 会将文件描述符 fd 添加到 epoll 实例的监听列表里，同时为 fd 设置一个回调函数，并监听事件event。当fd上发生相应事件时，会调用回调函数，将 fd 添加到 epoll 实例的就绪队列上。&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;当调用epoll_wait时，如果就绪列表中存在socket，则直接返回，如果没有，则阻塞进程&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;epoll是如何解决select的三个问题&#34;&gt;epoll是如何解决select的三个问题&lt;/h3&gt;
&lt;details&gt;
  &lt;summary&gt;被监控的fds集合大小被限制了1024，不够用&lt;/summary&gt;
&lt;p&gt;select 使用整型数组存储文件描述符集合，而 epoll 使用红黑树存储，数量较大。&lt;/p&gt;
&lt;/details&gt;
&lt;details&gt;
  &lt;summary&gt;fds集合需要从用户空间拷贝到内核空间的问题，耗费性能&lt;/summary&gt;
&lt;p&gt;epoll通过内核与用户空间使用mmap(内存映射)，将用户空间的一块地址和内核空间的一块地址同时映射到相同的一块物理内存地址，减少用户态和内核态之间的数据交换。&lt;/p&gt;
&lt;p&gt;epoll 对于每个描述符，只需要在 epoll_ctl 传递一次，之后 epoll_wait 不需要再次传递这也大大提高了效率。&lt;/p&gt;
&lt;/details&gt;
&lt;details&gt;
  &lt;summary&gt;需要遍历fds集合才能知道有数据接收的fds列表&lt;/summary&gt;
&lt;p&gt;epoll_ctl 中为每个文件描述符指定了回调函数，并在就绪时将其加入到就绪列表，因此 epoll 不需要像 select 那样遍历检测每个文件描述符，只需要判断就绪列表是否为空即可。这样，在没有描述符就绪时，epoll 能更早地让出系统资源。&lt;/p&gt;
&lt;p&gt;相当于时间复杂度从 O(n) 降为 O(1)&lt;/p&gt;
&lt;/details&gt;
&lt;h3 id=&#34;epoll的伪码描述&#34;&gt;epoll的伪码描述&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;for{
    active_fd = epoll_wait(fds)
    read fd
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&#34;参考&#34;&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://notes.shichao.io/unp/ch6/#signal-driven-io-model&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Chapter 6. I/O Multiplexing: The select and poll Functions&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/CyC2018/CS-Notes/blob/master/notes/Socket.md&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Socket&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://imageslr.github.io/2020/02/27/select-poll-epoll.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;【操作系统】I/O 多路复用，select / poll / epoll 详解&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.zhihu.com/question/19732473/answer/241673170&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;怎样理解阻塞非阻塞与同步异步的区别&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cloud.tencent.com/developer/article/1005481&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;大话 Select、Poll、Epoll&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://gityuan.com/2019/01/06/linux-epoll/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;源码解读epoll内核机制&lt;/a&gt;&lt;/p&gt;
&lt;br/&gt;
&lt;hr&gt;
</description>
    </item>
    
    <item>
      <title>TCP/IP</title>
      <link>https://w3xse7en.github.io/docs/web/tcp-ip/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://w3xse7en.github.io/docs/web/tcp-ip/</guid>
      <description>&lt;hr&gt;
&lt;h2&gt;目录&lt;/h2&gt;
&lt;nav id=&#34;TableOfContents&#34;&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;a href=&#34;#tcp-头格式&#34;&gt;TCP 头格式&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#tcp-工作在哪一层&#34;&gt;TCP 工作在哪一层&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#什么是-tcp-连接&#34;&gt;什么是 TCP 连接&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#如何唯一确定一个-tcp-连接&#34;&gt;如何唯一确定一个 TCP 连接&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#udp-头格式&#34;&gt;UDP 头格式&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#tcp-udp-区别&#34;&gt;TCP UDP 区别&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#tcp-和-udp-应用场景&#34;&gt;TCP 和 UDP 应用场景：&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#tcp-三次握手过程和状态变迁&#34;&gt;TCP 三次握手过程和状态变迁&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#为什么三次握手才可以建立连接&#34;&gt;为什么三次握手才可以建立连接&lt;/a&gt;
      &lt;ul&gt;
        &lt;li&gt;&lt;a href=&#34;#原因一避免历史连接&#34;&gt;原因一：避免历史连接&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#原因二同步双方初始序列号&#34;&gt;原因二：同步双方初始序列号&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#原因三避免资源浪费&#34;&gt;原因三：避免资源浪费&lt;/a&gt;&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#小结&#34;&gt;小结&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#tcp-四次挥手过程和状态变迁&#34;&gt;TCP 四次挥手过程和状态变迁&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#为什么挥手需要四次&#34;&gt;为什么挥手需要四次&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#为什么-time_wait-等待的时间是-2msl&#34;&gt;为什么 TIME_WAIT 等待的时间是 2MSL&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#为什么需要-time_wait-状态&#34;&gt;为什么需要 TIME_WAIT 状态&lt;/a&gt;
      &lt;ul&gt;
        &lt;li&gt;&lt;a href=&#34;#原因一防止旧连接的数据包&#34;&gt;原因一：防止旧连接的数据包&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#原因二保证连接正确关闭&#34;&gt;原因二：保证连接正确关闭&lt;/a&gt;&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#time_wait-过多有什么危害&#34;&gt;TIME_WAIT 过多有什么危害&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#既然-ip-层会分片为什么-tcp-层还需要-mss-&#34;&gt;既然 IP 层会分片，为什么 TCP 层还需要 MSS ？&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#参考&#34;&gt;参考&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/nav&gt;
&lt;hr&gt;
&lt;br&gt;
&lt;h2 id=&#34;tcp-头格式&#34;&gt;TCP 头格式&lt;/h2&gt;








  











&lt;figure &gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/tcp/tcp_head.webp&#34; &gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/tcp/tcp_head.webp&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;



&lt;/figure&gt;

&lt;p&gt;序列号：在建立连接时由计算机生成的随机数作为其初始值，通过 SYN 包传给接收端主机，每发送一次数据，就「累加」一次该「数据字节数」的大小。用来解决网络包乱序问题。&lt;/p&gt;
&lt;p&gt;确认应答号：指下一次「期望」收到的数据的序列号，发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。用来解决不丢包的问题。&lt;/p&gt;
&lt;p&gt;控制位：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;ACK：该位为 1 时，「确认应答」的字段变为有效，TCP 规定除了最初建立连接时的 SYN 包之外该位必须设置为 1 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;RST：该位为 1 时，表示 TCP 连接中出现异常必须强制断开连接。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SYC：该位为 1 时，表示希望建立连，并在其「序列号」的字段进行序列号初始值的设定。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;FIN：该位为 1 时，表示今后不会再有数据发送，希望断开连接。当通信结束希望断开连接时，通信双方的主机之间就可以相互交换 FIN 位置为 1 的 TCP 段。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;
&lt;h2 id=&#34;tcp-工作在哪一层&#34;&gt;TCP 工作在哪一层&lt;/h2&gt;








  











&lt;figure &gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/tcp/tcp_osi.webp&#34; &gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/tcp/tcp_osi.webp&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;



&lt;/figure&gt;

&lt;p&gt;IP 层（网络层）是「不可靠」的，它不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中的数据的完整性。&lt;/p&gt;
&lt;p&gt;如果需要保障网络数据包的可靠性，那么就需要由上层（传输层）的 TCP 协议来负责。&lt;/p&gt;
&lt;p&gt;因为 TCP 是一个工作在传输层的可靠数据传输的服务，它能确保接收端接收的网络包是无损坏、无间隔、非冗余和按序的。&lt;/p&gt;
&lt;br&gt;
&lt;h2 id=&#34;什么是-tcp-连接&#34;&gt;什么是 TCP 连接&lt;/h2&gt;
&lt;p&gt;我们来看看 RFC 793 是如何定义「连接」的：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Connections:&lt;/p&gt;
&lt;p&gt;The reliability and flow control mechanisms described above require that TCPs initialize and maintain certain status information for each data stream.&lt;/p&gt;
&lt;p&gt;The combination of this information, including sockets, sequence numbers, and window sizes, is called a connection.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;简单来说就是，用于保证可靠性和流量控制维护的某些状态信息，这些信息的组合，包括Socket、序列号和窗口大小称为连接。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Socket：由 IP 地址和端口号组成&lt;/li&gt;
&lt;li&gt;序列号：用来解决乱序问题等&lt;/li&gt;
&lt;li&gt;窗口大小：用来做流量控制&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;
&lt;h2 id=&#34;如何唯一确定一个-tcp-连接&#34;&gt;如何唯一确定一个 TCP 连接&lt;/h2&gt;
&lt;p&gt;TCP 四元组可以唯一的确定一个连接，四元组包括如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;源地址&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;源端口&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;目的地址&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;目的端口&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;源地址和目的地址的字段（32位）是在 IP 头部中，作用是通过 IP 协议发送报文给对方主机。&lt;/p&gt;
&lt;p&gt;源端口和目的端口的字段（16位）是在 TCP 头部中，作用是告诉 TCP 协议应该把报文发给哪个进程。&lt;/p&gt;
&lt;br&gt;
&lt;h2 id=&#34;udp-头格式&#34;&gt;UDP 头格式&lt;/h2&gt;
&lt;p&gt;UDP 不提供复杂的控制机制，利用 IP 提供面向「无连接」的通信服务。&lt;/p&gt;
&lt;p&gt;UDP 协议真的非常简，头部只有 8 个字节（ 64 位），UDP 的头部格式如下&lt;/p&gt;








  











&lt;figure &gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/tcp/udp_head.webp&#34; &gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/tcp/udp_head.webp&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;



&lt;/figure&gt;

&lt;br&gt;
&lt;h2 id=&#34;tcp-udp-区别&#34;&gt;TCP UDP 区别&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;连接&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;TCP 是面向连接的传输层协议，传输数据前先要建立连接。&lt;/p&gt;
&lt;p&gt;UDP 是不需要连接，即刻传输数据。&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;服务对象&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;TCP 是一对一的两点服务，即一条连接只有两个端点。&lt;/p&gt;
&lt;p&gt;UDP 支持一对一、一对多、多对多的交互通信&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;可靠性&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;TCP 是可靠交付数据的，数据可以无差错、不丢失、不重复、按需到达。&lt;/p&gt;
&lt;p&gt;UDP 是尽最大努力交付，不保证可靠交付数据。&lt;/p&gt;
&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;拥塞控制、流量控制&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;TCP 有拥塞控制和流量控制机制，保证数据传输的安全性。&lt;/p&gt;
&lt;p&gt;UDP 则没有，即使网络非常拥堵了，也不会影响 UDP 的发送速率。&lt;/p&gt;
&lt;ol start=&#34;5&#34;&gt;
&lt;li&gt;首部开销&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;TCP 首部长度较长，会有一定的开销，首部在没有使用「选项」字段时是 20 个字节，如果使用了「选项」字段则会变长的。&lt;/p&gt;
&lt;p&gt;UDP 首部只有 8 个字节，并且是固定不变的，开销较小。&lt;/p&gt;
&lt;br&gt;
&lt;h2 id=&#34;tcp-和-udp-应用场景&#34;&gt;TCP 和 UDP 应用场景：&lt;/h2&gt;
&lt;p&gt;由于 TCP 是面向连接，能保证数据的可靠性交付，因此经常用于：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;FTP 文件传输&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;HTTP / HTTPS&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;由于 UDP 面向无连接，它可以随时发送数据，再加上UDP本身的处理既简单又高效，因此经常用于：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;包总量较少的通信，如 DNS 、SNMP 等&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;视频、音频等多媒体通信&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;广播通信&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;
&lt;h2 id=&#34;tcp-三次握手过程和状态变迁&#34;&gt;TCP 三次握手过程和状态变迁&lt;/h2&gt;
&lt;p&gt;TCP 是面向连接的协议，所以使用 TCP 前必须先建立连接，而建立连接是通过三次握手而进行的。&lt;/p&gt;








  











&lt;figure &gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/tcp/tcp_connect.webp&#34; &gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/tcp/tcp_connect.webp&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;



&lt;/figure&gt;

&lt;p&gt;一开始，客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端口，处于 LISTEN 状态&lt;/p&gt;
&lt;p&gt;客户端会随机初始化序号（client_isn），将此序号置于 TCP 首部的「序号」字段中，同时把 SYN 标志位置为 1 ，表示 SYN 报文。接着把第一个 SYN 报文发送给服务端，表示向服务端发起连接，该报文不包含应用层数据，之后客户端处于 SYN-SENT 状态。&lt;/p&gt;
&lt;p&gt;服务端收到客户端的 SYN 报文后，首先服务端也随机初始化自己的序号（server_isn），将此序号填入 TCP 首部的「序号」字段中，其次把 TCP 首部的「确认应答号」字段填入 client_isn + 1, 接着把 SYN 和 ACK 标志位置为 1。最后把该报文发给客户端，该报文也不包含应用层数据，之后服务端处于 SYN-RCVD 状态。&lt;/p&gt;
&lt;p&gt;客户端收到服务端报文后，还要向服务端回应最后一个应答报文，首先该应答报文 TCP 首部 ACK 标志位置为 1 ，其次「确认应答号」字段填入 server_isn + 1 ，最后把报文发送给服务端，这次报文可以携带客户到服务器的数据，之后客户端处于 ESTABLISHED 状态。&lt;/p&gt;
&lt;p&gt;服务器收到客户端的应答报文后，也进入 ESTABLISHED 状态。&lt;/p&gt;
&lt;h2 id=&#34;为什么三次握手才可以建立连接&#34;&gt;为什么三次握手才可以建立连接&lt;/h2&gt;
&lt;p&gt;以三个方面分析三次握手的原因：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;三次握手才可以阻止历史重复连接的初始化（主要原因）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;三次握手才可以同步双方的初始序列号&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;三次握手才可以避免资源浪费&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;原因一避免历史连接&#34;&gt;原因一：避免历史连接&lt;/h3&gt;
&lt;p&gt;我们来看看 RFC 793 指出的 TCP 连接使用三次握手的首要原因：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The principle reason for the three-way handshake is to prevent old duplicate connection initiations from causing confusion.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;简单来说，三次握手的首要原因是为了防止旧的重复连接初始化造成混乱。&lt;/p&gt;
&lt;p&gt;网络环境是错综复杂的，往往并不是如我们期望的一样，先发送的数据包，就先到达目标主机，反而它很骚，可能会由于网络拥堵等乱七八糟的原因，会使得旧的数据包，先到达目标主机，那么这种情况下 TCP 三次握手是如何避免的呢？&lt;/p&gt;








  











&lt;figure id=&#34;figure-三次握手避免历史连接&#34;&gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/tcp/tcp_connect_reason1.webp&#34; data-caption=&#34;三次握手避免历史连接&#34;&gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/tcp/tcp_connect_reason1.webp&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;


  
  
  &lt;figcaption&gt;
    三次握手避免历史连接
  &lt;/figcaption&gt;


&lt;/figure&gt;

&lt;p&gt;客户端连续发送多次 SYN 建立连接的报文，在网络拥堵等情况下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;一个「旧 SYN 报文」比「最新的 SYN 」 报文早到达了服务端；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;那么此时服务端就会回一个 SYN + ACK 报文给客户端；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;客户端收到后可以根据自身的上下文，判断这是一个历史连接（序列号过期或超时），那么客户端就会发送 RST 报文给服务端，表示中止这一次连接。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果是两次握手连接，就不能判断当前连接是否是历史连接，三次握手则可以在客户端（发送方）准备发送第三次报文时，客户端因有足够的上下文来判断当前连接是否是历史连接：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;如果是历史连接（序列号过期或超时），则第三次握手发送的报文是 RST 报文，以此中止历史连接；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果不是历史连接，则第三次发送的报文是 ACK 报文，通信双方就会成功建立连接；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以， TCP 使用三次握手建立连接的最主要原因是 &lt;strong&gt;防止历史连接初始化了连接。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&#34;原因二同步双方初始序列号&#34;&gt;原因二：同步双方初始序列号&lt;/h3&gt;
&lt;p&gt;TCP 协议的通信双方， 都必须维护一个「序列号」， 序列号是可靠传输的一个关键因素，它的作用：&lt;/p&gt;
&lt;p&gt;接收方可以去除重复的数据；&lt;/p&gt;
&lt;p&gt;接收方可以根据数据包的序列号按序接收；&lt;/p&gt;
&lt;p&gt;可以标识发送出去的数据包中， 哪些是已经被对方收到的；&lt;/p&gt;
&lt;p&gt;可见，序列号在 TCP 连接中占据着非常重要的作用，所以当客户端发送携带「初始序列号」的 SYN 报文的时候，需要服务端回一个 ACK 应答报文，
表示客户端的 SYN 报文已被服务端成功接收，那当服务端发送「初始序列号」给客户端的时候，依然也要得到客户端的应答回应，
这样一来一回，才能 &lt;strong&gt;确保双方的初始序列号能被可靠的同步。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;而两次握手只保证了一方的初始序列号能被对方成功接收，没办法保证双方的初始序列号都能被确认接收。&lt;/p&gt;
&lt;h3 id=&#34;原因三避免资源浪费&#34;&gt;原因三：避免资源浪费&lt;/h3&gt;
&lt;p&gt;如果只有「两次握手」，当客户端的 SYN 请求连接在网络中阻塞，客户端没有接收到 ACK 报文，就会重新发送 SYN ，由于没有第三次握手，服务器不清楚客户端是否收到了自己发送的建立连接的 ACK 确认信号，所以每收到一个 SYN 就只能先主动建立一个连接，这会造成什么情况呢？&lt;/p&gt;
&lt;p&gt;如果客户端的 SYN 阻塞了，重复发送多次 SYN 报文，那么服务器在收到请求后就会 &lt;strong&gt;建立多个冗余的无效链接，造成不必要的资源浪费。&lt;/strong&gt;&lt;/p&gt;








  











&lt;figure id=&#34;figure-两次握手会造成资源浪费&#34;&gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/tcp/tcp_connect_reason3.webp&#34; data-caption=&#34;两次握手会造成资源浪费&#34;&gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/tcp/tcp_connect_reason3.webp&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;


  
  
  &lt;figcaption&gt;
    两次握手会造成资源浪费
  &lt;/figcaption&gt;


&lt;/figure&gt;

&lt;p&gt;即两次握手会造成消息滞留情况下，服务器重复接受无用的连接请求 SYN 报文，而造成重复分配资源。&lt;/p&gt;
&lt;p&gt;四次握手其实也能够可靠的同步双方的初始化序号，但由于第二步和第三步可以优化成一步，所以就成了「三次握手」。&lt;/p&gt;








  











&lt;figure id=&#34;figure-四次握手与三次握手&#34;&gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/tcp/tcp_connect_reason2.webp&#34; data-caption=&#34;四次握手与三次握手&#34;&gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/tcp/tcp_connect_reason2.webp&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;


  
  
  &lt;figcaption&gt;
    四次握手与三次握手
  &lt;/figcaption&gt;


&lt;/figure&gt;

&lt;br&gt;
&lt;h2 id=&#34;小结&#34;&gt;小结&lt;/h2&gt;
&lt;p&gt;TCP 建立连接时，通过三次握手 &lt;strong&gt;能防止历史连接的建立，能减少双方不必要的资源开销，能帮助双方同步初始化序列号。&lt;/strong&gt; 序列号能够保证数据包不重复、不丢弃和按序传输。&lt;/p&gt;
&lt;p&gt;不使用「两次握手」和「四次握手」的原因：&lt;/p&gt;
&lt;p&gt;「两次握手」：无法防止历史连接的建立，会造成双方资源的浪费，也无法可靠的同步双方序列号；&lt;/p&gt;
&lt;p&gt;「四次握手」：三次握手就已经理论上最少可靠连接建立，所以不需要使用更多的通信次数。&lt;/p&gt;
&lt;br&gt;
&lt;h2 id=&#34;tcp-四次挥手过程和状态变迁&#34;&gt;TCP 四次挥手过程和状态变迁&lt;/h2&gt;








  











&lt;figure id=&#34;figure-客户端主动关闭连接--tcp-四次挥手&#34;&gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/tcp/tcp_close.webp&#34; data-caption=&#34;客户端主动关闭连接 —— TCP 四次挥手&#34;&gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/tcp/tcp_close.webp&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;


  
  
  &lt;figcaption&gt;
    客户端主动关闭连接 —— TCP 四次挥手
  &lt;/figcaption&gt;


&lt;/figure&gt;

&lt;p&gt;客户端打算关闭连接，此时会发送一个 TCP 首部 FIN 标志位被置为 1 的报文，也即 FIN 报文，之后客户端进入 FIN_WAIT_1 状态。&lt;/p&gt;
&lt;p&gt;服务端收到该报文后，就向客户端发送 ACK 应答报文，接着服务端进入 CLOSED_WAIT 状态。&lt;/p&gt;
&lt;p&gt;客户端收到服务端的 ACK 应答报文后，之后进入 FIN_WAIT_2 状态。&lt;/p&gt;
&lt;p&gt;等待服务端处理完数据后，也向客户端发送 FIN 报文，之后服务端进入 LAST_ACK 状态。&lt;/p&gt;
&lt;p&gt;客户端收到服务端的 FIN 报文后，回一个 ACK 应答报文，之后进入 TIME_WAIT 状态&lt;/p&gt;
&lt;p&gt;服务器收到了 ACK 应答报文后，就进入了 CLOSE 状态，至此服务端已经完成连接的关闭。&lt;/p&gt;
&lt;p&gt;客户端在经过 2MSL 一段时间后，自动进入 CLOSE 状态，至此客户端也完成连接的关闭。&lt;/p&gt;
&lt;p&gt;你可以看到，&lt;strong&gt;每个方向都需要一个 FIN 和一个 ACK，因此通常被称为四次挥手。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这里一点需要注意是：&lt;strong&gt;主动关闭连接的，才有 TIME_WAIT 状态。&lt;/strong&gt;&lt;/p&gt;
&lt;br&gt;
&lt;h2 id=&#34;为什么挥手需要四次&#34;&gt;为什么挥手需要四次&lt;/h2&gt;
&lt;p&gt;再来回顾下四次挥手双方发 FIN 包的过程，就能理解为什么需要四次了。&lt;/p&gt;
&lt;p&gt;关闭连接时，客户端向服务端发送 FIN 时，仅仅表示客户端不再发送数据了但是还能接收数据。&lt;/p&gt;
&lt;p&gt;服务器收到客户端的 FIN 报文时，先回一个 ACK 应答报文，而服务端可能还有数据需要处理和发送，等服务端不再发送数据时，才发送 FIN 报文给客户端来表示同意现在关闭连接。&lt;/p&gt;
&lt;p&gt;从上面过程可知，&lt;strong&gt;服务端通常需要等待完成数据的发送和处理&lt;/strong&gt;，所以服务端的 ACK 和 FIN 一般都会分开发送，从而比三次握手导致多了一次。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;服务端已经完成了数据的发送和处理&lt;/strong&gt;，那么服务端的 ACK 和 FIN 会合并发送，变成三次挥手&lt;/p&gt;








  











&lt;figure id=&#34;figure-curl-http1921683100&#34;&gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/tcp/tcp_connect_wireshark.webp&#34; data-caption=&#34;curl http://192.168.3.100&#34;&gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/tcp/tcp_connect_wireshark.webp&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;


  
  
  &lt;figcaption&gt;
    curl http://192.168.3.100
  &lt;/figcaption&gt;


&lt;/figure&gt;

&lt;br&gt;
&lt;h2 id=&#34;为什么-time_wait-等待的时间是-2msl&#34;&gt;为什么 TIME_WAIT 等待的时间是 2MSL&lt;/h2&gt;
&lt;p&gt;MSL 是 Maximum Segment Lifetime，&lt;strong&gt;报文最大生存时间&lt;/strong&gt;，它是任何报文在网络上存在的最长时间，超过这个时间报文将被丢弃。因为 TCP 报文基于是 IP 协议的，而 IP 头中有一个 TTL 字段，是 IP 数据报可以经过的最大路由数，每经过一个处理他的路由器此值就减 1，当此值为 0 则数据报将被丢弃，同时发送 ICMP 报文通知源主机。&lt;/p&gt;
&lt;p&gt;MSL 与 TTL 的区别：MSL 的单位是时间，而 TTL 是经过路由跳数。所以 MSL 应该要大于等于 TTL 消耗为 0 的时间，以确保报文已被自然消亡。&lt;/p&gt;
&lt;p&gt;TIME_WAIT 等待 2 倍的 MSL，比较合理的解释是：网络中可能存在来自发送方的数据包，当这些发送方的数据包被接收方处理后又会向对方发送响应，&lt;strong&gt;所以一来一回需要等待 2 倍的时间。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;比如，如果被动关闭方没有收到断开连接的最后的 ACK 报文，就会触发超时重发 Fin 报文，另一方接收到 FIN 后，会重发 ACK 给被动关闭方， 一来一去正好 2 个 MSL。&lt;/p&gt;
&lt;p&gt;2MSL 的时间是从客户端接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内，因为客户端的 ACK 没有传输到服务端，客户端又接收到了服务端重发的 FIN 报文，那么 2MSL 时间将重新计时。&lt;/p&gt;
&lt;p&gt;在 Linux 系统里 2MSL 默认是 60 秒，那么一个 MSL 也就是 30 秒。&lt;strong&gt;Linux 系统停留在 TIME_WAIT 的时间为固定的 60 秒。&lt;/strong&gt;&lt;/p&gt;
&lt;br&gt;
&lt;h2 id=&#34;为什么需要-time_wait-状态&#34;&gt;为什么需要 TIME_WAIT 状态&lt;/h2&gt;
&lt;p&gt;主动发起关闭连接的一方，才会有 TIME-WAIT 状态。&lt;/p&gt;
&lt;p&gt;需要 TIME-WAIT 状态，主要是两个原因：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;防止具有相同「四元组」的「旧」数据包被收到；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;保证「被动关闭连接」的一方能被正确的关闭，即保证最后的 ACK 能让被动关闭方接收，从而帮助其正常关闭；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;原因一防止旧连接的数据包&#34;&gt;原因一：防止旧连接的数据包&lt;/h3&gt;
&lt;p&gt;假设 TIME-WAIT 没有等待时间或时间过短，被延迟的数据包抵达后会发生什么呢？&lt;/p&gt;








  











&lt;figure id=&#34;figure-接收到历史数据的异常&#34;&gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/tcp/tcp_close_reason1.webp&#34; data-caption=&#34;接收到历史数据的异常&#34;&gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/tcp/tcp_close_reason1.webp&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;


  
  
  &lt;figcaption&gt;
    接收到历史数据的异常
  &lt;/figcaption&gt;


&lt;/figure&gt;

&lt;p&gt;如上图黄色框框服务端在关闭连接之前发送的 SEQ = 301 报文，被网络延迟了。&lt;/p&gt;
&lt;p&gt;这时有相同端口的 TCP 连接被复用后，被延迟的 SEQ = 301 抵达了客户端，那么客户端是有可能正常接收这个过期的报文，这就会产生数据错乱等严重的问题。&lt;/p&gt;
&lt;p&gt;所以，TCP 就设计出了这么一个机制，经过 2MSL 这个时间，&lt;strong&gt;足以让两个方向上的数据包都被丢弃，使得原来连接的数据包在网络中都自然消失，再出现的数据包一定都是新建立连接所产生的。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&#34;原因二保证连接正确关闭&#34;&gt;原因二：保证连接正确关闭&lt;/h3&gt;
&lt;p&gt;在 RFC 793 指出 TIME-WAIT 另一个重要的作用是：&lt;/p&gt;
&lt;p&gt;TIME-WAIT - represents waiting for enough time to pass to be sure the remote TCP received the acknowledgment of its connection termination request.&lt;/p&gt;
&lt;p&gt;也就是说，TIME-WAIT 作用是等待足够的时间以确保最后的 ACK 能让被动关闭方接收，从而帮助其正常关闭。&lt;/p&gt;
&lt;p&gt;假设 TIME-WAIT 没有等待时间或时间过短，断开连接会造成什么问题呢？&lt;/p&gt;








  











&lt;figure id=&#34;figure-没有确保正常断开的异常&#34;&gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/tcp/tcp_close_reason2.webp&#34; data-caption=&#34;没有确保正常断开的异常&#34;&gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/tcp/tcp_close_reason2.webp&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;


  
  
  &lt;figcaption&gt;
    没有确保正常断开的异常
  &lt;/figcaption&gt;


&lt;/figure&gt;

&lt;p&gt;如上图红色框框客户端四次挥手的最后一个 ACK 报文如果在网络中被丢失了，此时如果客户端 TIME-WAIT 过短或没有，则就直接进入了 CLOSE 状态了，那么服务端则会一直处在 LASE-ACK 状态。&lt;/p&gt;
&lt;p&gt;当客户端发起建立连接的 SYN 请求报文后，服务端会发送 RST 报文给客户端，连接建立的过程就会被终止。&lt;/p&gt;
&lt;p&gt;如果 TIME-WAIT 等待足够长的情况就会遇到两种情况：&lt;/p&gt;
&lt;p&gt;服务端正常收到四次挥手的最后一个 ACK 报文，则服务端正常关闭连接。&lt;/p&gt;
&lt;p&gt;服务端没有收到四次挥手的最后一个 ACK 报文时，则会重发 FIN 关闭连接报文并等待新的 ACK 报文。&lt;/p&gt;
&lt;p&gt;所以客户端在 TIME-WAIT 状态等待 2MSL 时间后，&lt;strong&gt;就可以保证双方的连接都可以正常的关闭。&lt;/strong&gt;&lt;/p&gt;
&lt;br&gt;
&lt;h2 id=&#34;time_wait-过多有什么危害&#34;&gt;TIME_WAIT 过多有什么危害&lt;/h2&gt;
&lt;p&gt;如果服务器有处于 TIME-WAIT 状态的 TCP，则说明是由服务器方主动发起的断开请求。&lt;/p&gt;
&lt;p&gt;过多的 TIME-WAIT 状态主要的危害有两种：&lt;/p&gt;
&lt;p&gt;第一是内存资源占用；&lt;/p&gt;
&lt;p&gt;第二是对端口资源的占用，一个 TCP 连接至少消耗一个本地端口；&lt;/p&gt;
&lt;p&gt;第二个危害是会造成严重的后果的，要知道，端口资源也是有限的，一般可以开启的端口为 32768～61000，也可以通过如下参数设置指定&lt;/p&gt;
&lt;p&gt;&lt;code&gt;net.ipv4.ip_local_port_range&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;服务端 TIME_WAIT 状态过多，占满了所有端口资源，则会导致无法创建新连接。&lt;/strong&gt;&lt;/p&gt;
&lt;br&gt;
&lt;h2 id=&#34;既然-ip-层会分片为什么-tcp-层还需要-mss-&#34;&gt;既然 IP 层会分片，为什么 TCP 层还需要 MSS ？&lt;/h2&gt;








  











&lt;figure id=&#34;figure-mtu-与-mss&#34;&gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/tcp/tcp_mtu_mss.webp&#34; data-caption=&#34;MTU 与 MSS&#34;&gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/tcp/tcp_mtu_mss.webp&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;


  
  
  &lt;figcaption&gt;
    MTU 与 MSS
  &lt;/figcaption&gt;


&lt;/figure&gt;

&lt;p&gt;MTU：一个网络包的最大长度，以太网中一般为 1500 字节；&lt;/p&gt;
&lt;p&gt;MSS：除去 IP 和 TCP 头部之后，一个网络包所能容纳的 TCP 数据的最大长度；&lt;/p&gt;
&lt;p&gt;如果TCP 的整个报文（头部 + 数据）交给 IP 层进行分片，会有什么异常呢？&lt;/p&gt;
&lt;p&gt;当 IP 层有一个超过 MTU 大小的数据（TCP 头部 + TCP 数据）要发送，那么 IP 层就要进行分片，把数据分片成若干片，保证每一个分片都小于 MTU。把一份 IP 数据报进行分片以后，由目标主机的 IP 层来进行重新组装后，在交给上一层 TCP 传输层。&lt;/p&gt;
&lt;p&gt;这看起来井然有序，但这存在隐患的，&lt;strong&gt;那么当如果一个 IP 分片丢失，整个 IP 报文的所有分片都得重传。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;因为 IP 层本身没有超时重传机制，它由传输层的 TCP 来负责超时和重传。&lt;/p&gt;
&lt;p&gt;当接收方发现 TCP 报文（头部 + 数据）的某一片丢失后，则不会响应 ACK 给对方，那么发送方的 TCP 在超时后，就会重发「整个 TCP 报文（头部 + 数据）」。&lt;/p&gt;
&lt;p&gt;因此，可以得知由 IP 层进行分片传输，是非常没有效率的。&lt;/p&gt;
&lt;p&gt;所以，为了达到最佳的传输效能 TCP 协议在 &lt;strong&gt;建立连接的时候通常要协商双方的 MSS 值&lt;/strong&gt; ，当 TCP 层发现数据超过 MSS 时，则就先会进行分片，当然由它形成的 IP 包的长度也就不会大于 MTU ，自然也就不用 IP 分片了。&lt;/p&gt;
&lt;p&gt;经过 TCP 层分片后，如果一个 TCP 分片丢失后，进行重发时也是以 MSS 为单位，而不用重传所有的分片，大大增加了重传的效率。&lt;/p&gt;
&lt;br&gt;
&lt;hr&gt;
&lt;h2 id=&#34;参考&#34;&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://datatracker.ietf.org/doc/rfc1644/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;TCP 协议 RFC 文档&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://mp.weixin.qq.com/s?__biz=MzUxODAzNDg4NQ==&amp;amp;mid=2247484005&amp;amp;idx=1&amp;amp;sn=cb07ee1c891a7bdd0af3859543190202&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;硬不硬你说了算！近 40 张图解被问千百遍的 TCP 三次握手和四次挥手面试题&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://mp.weixin.qq.com/s?__biz=MzUxODAzNDg4NQ==&amp;amp;mid=2247484017&amp;amp;idx=1&amp;amp;sn=dc54d43bfd5dc088e48adcfa2e2bc13f&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;你还在为 TCP 重传、滑动窗口、流量控制、拥塞控制发愁吗？看完图解就不愁了&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.bilibili.com/video/BV1c4411d7jb?p=1&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;湖南科技大学 - 计算机网络微课堂&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://zhuanlan.zhihu.com/p/58668946&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;HTTP/3 都来了，你却还在用 HTTP/1.1？&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
</description>
    </item>
    
    <item>
      <title>Http</title>
      <link>https://w3xse7en.github.io/docs/web/http/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://w3xse7en.github.io/docs/web/http/</guid>
      <description>&lt;hr&gt;
&lt;h2&gt;目录&lt;/h2&gt;
&lt;nav id=&#34;TableOfContents&#34;&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;a href=&#34;#http&#34;&gt;Http&lt;/a&gt;
      &lt;ul&gt;
        &lt;li&gt;&lt;a href=&#34;#http-常见的状态码有哪些&#34;&gt;Http 常见的状态码，有哪些？&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#长连接&#34;&gt;长连接&lt;/a&gt;&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#https&#34;&gt;Https&lt;/a&gt;
      &lt;ul&gt;
        &lt;li&gt;&lt;a href=&#34;#https-解决了-http-的哪些问题&#34;&gt;HTTPS 解决了 HTTP 的哪些问题？&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#混合加密&#34;&gt;混合加密&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#摘要算法&#34;&gt;摘要算法&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#数字证书&#34;&gt;数字证书&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#https-建立连接&#34;&gt;HTTPS 建立连接&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#clienthello&#34;&gt;ClientHello&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#severhello&#34;&gt;SeverHello&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#客户端回应&#34;&gt;客户端回应&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#服务器的最后回应&#34;&gt;服务器的最后回应&lt;/a&gt;&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#http2&#34;&gt;Http/2&lt;/a&gt;
      &lt;ul&gt;
        &lt;li&gt;&lt;a href=&#34;#多路复用-multiplexing&#34;&gt;多路复用 (Multiplexing)&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#二进制分帧&#34;&gt;二进制分帧&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#首部压缩header-compression&#34;&gt;首部压缩（Header Compression）&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#server-push&#34;&gt;Server Push&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#总结&#34;&gt;总结：&lt;/a&gt;&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#http3&#34;&gt;Http/3&lt;/a&gt;
      &lt;ul&gt;
        &lt;li&gt;&lt;a href=&#34;#0-rtt&#34;&gt;0-RTT&lt;/a&gt;&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#qa&#34;&gt;Q&amp;amp;A&lt;/a&gt;
      &lt;ul&gt;
        &lt;li&gt;&lt;a href=&#34;#与服务器建立了一个tcp连接后是否会在一个-http-请求完成后断开什么情况下会断开&#34;&gt;与服务器建立了一个TCP连接后是否会在一个 HTTP 请求完成后断开？什么情况下会断开？&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#一个tcp连接中http请求发送可以一起发送么比如一起发三个请求再三个响应一起接收&#34;&gt;一个TCP连接中HTTP请求发送可以一起发送么（比如一起发三个请求，再三个响应一起接收）&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#为什么有的时候刷新页面不需要重新建立-ssl-连接&#34;&gt;为什么有的时候刷新页面不需要重新建立 SSL 连接？&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&#34;#浏览器对同一-host-建立-tcp-连接到数量有没有限制&#34;&gt;浏览器对同一 Host 建立 TCP 连接到数量有没有限制？&lt;/a&gt;&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&#34;#参考&#34;&gt;参考&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/nav&gt;
&lt;hr&gt;
&lt;br&gt;
&lt;h2 id=&#34;http&#34;&gt;Http&lt;/h2&gt;
&lt;h3 id=&#34;http-常见的状态码有哪些&#34;&gt;Http 常见的状态码，有哪些？&lt;/h3&gt;








  











&lt;figure id=&#34;figure-http状态码&#34;&gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/http/http_status.webp&#34; data-caption=&#34;Http状态码&#34;&gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/http/http_status.webp&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;


  
  
  &lt;figcaption&gt;
    Http状态码
  &lt;/figcaption&gt;


&lt;/figure&gt;

&lt;p&gt;&lt;code&gt;1xx&lt;/code&gt; 类状态码属于提示信息，是协议处理中的一种中间状态，实际用到的比较少。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;2xx&lt;/code&gt; 类状态码表示服务器成功处理了客户端的请求，也是我们最愿意看到的状态。&lt;/p&gt;
&lt;p&gt;「200 OK」是最常见的成功状态码，表示一切正常。如果是非 HEAD 请求，服务器返回的响应头都会有 body 数据。&lt;/p&gt;
&lt;p&gt;「204 No Content」也是常见的成功状态码，与 200 OK 基本相同，但响应头没有 body 数据。&lt;/p&gt;
&lt;p&gt;「206 Partial Content」是应用于 HTTP 分块下载或断电续传，表示响应返回的 body 数据并不是资源的全部，而是其中的一部分，也是服务器处理成功的状态。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;3xx&lt;/code&gt; 类状态码表示客户端请求的资源发送了变动，需要客户端用新的 URL 重新发送请求获取资源，也就是重定向。&lt;/p&gt;
&lt;p&gt;「301 Moved Permanently」表示永久重定向，说明请求的资源已经不存在了，需改用新的 URL 再次访问。&lt;/p&gt;
&lt;p&gt;「302 Moved Permanently」表示临时重定向，说明请求的资源还在，但暂时需要用另一个 URL 来访问。&lt;/p&gt;
&lt;p&gt;301 和 302 都会在响应头里使用字段 Location，指明后续要跳转的 URL，浏览器会自动重定向新的 URL。&lt;/p&gt;
&lt;p&gt;「304 Not Modified」不具有跳转的含义，表示资源未修改，重定向已存在的缓冲文件，也称缓存重定向，用于缓存控制。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;4xx&lt;/code&gt; 类状态码表示客户端发送的报文有误，服务器无法处理，也就是错误码的含义。&lt;/p&gt;
&lt;p&gt;「400 Bad Request」表示客户端请求的报文有错误，但只是个笼统的错误。&lt;/p&gt;
&lt;p&gt;「403 Forbidden」表示服务器禁止访问资源，并不是客户端的请求出错。&lt;/p&gt;
&lt;p&gt;「404 Not Found」表示请求的资源在服务器上不存在或未找到，所以无法提供给客户端。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;5xx&lt;/code&gt; 类状态码表示客户端请求报文正确，但是服务器处理时内部发生了错误，属于服务器端的错误码。&lt;/p&gt;
&lt;p&gt;「500 Internal Server Error」与 400 类型，是个笼统通用的错误码，服务器发生了什么错误，我们并不知道。&lt;/p&gt;
&lt;p&gt;「501 Not Implemented」表示客户端请求的功能还不支持，类似“即将开业，敬请期待”的意思。&lt;/p&gt;
&lt;p&gt;「502 Bad Gateway」通常是服务器作为网关或代理时返回的错误码，表示服务器自身工作正常，访问后端服务器发生了错误。&lt;/p&gt;
&lt;p&gt;「503 Service Unavailable」表示服务器当前很忙，暂时无法响应服务器，类似“网络服务正忙，请稍后重试”的意思。&lt;/p&gt;
&lt;h3 id=&#34;长连接&#34;&gt;长连接&lt;/h3&gt;
&lt;p&gt;早期 HTTP/1.0 性能上的一个很大的问题，那就是每发起一个请求，都要新建一次 TCP 连接（三次握手），而且是串行请求，做了无畏的 TCP 连接建立和断开，增加了通信开销。&lt;/p&gt;
&lt;p&gt;为了解决上述 TCP 连接问题，HTTP/1.1 提出了长连接的通信方式，也叫持久连接。这种方式的好处在于减少了 TCP 连接的重复建立和断开所造成的额外开销，减轻了服务器端的负载。&lt;/p&gt;
&lt;p&gt;持久连接的特点是，只要任意一端没有明确提出断开连接，则保持 TCP 连接状态。&lt;/p&gt;








  











&lt;figure id=&#34;figure-短链接与长链接&#34;&gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/http/http_short_long_connect.webp&#34; data-caption=&#34;短链接与长链接&#34;&gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/http/http_short_long_connect.webp&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;


  
  
  &lt;figcaption&gt;
    短链接与长链接
  &lt;/figcaption&gt;


&lt;/figure&gt;

&lt;br&gt;
&lt;h2 id=&#34;https&#34;&gt;Https&lt;/h2&gt;
&lt;h3 id=&#34;https-解决了-http-的哪些问题&#34;&gt;HTTPS 解决了 HTTP 的哪些问题？&lt;/h3&gt;
&lt;p&gt;HTTP 由于是明文传输，所以安全上存在以下三个风险：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;窃听风险，比如通信链路上可以获取通信内容，用户账号容易没。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;篡改风险，比如强制入垃圾广告，视觉污染，用户眼容易瞎。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;冒充风险，比如冒充淘宝网站，用户钱容易没。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;HTTPS 在 HTTP 与 TCP 层之间加入了 SSL/TLS 协议，来解决上述风险。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;混合加密&lt;/code&gt;的方式实现信息的机密性，解决了窃听的风险。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;摘要算法&lt;/code&gt;的方式来实现完整性，它能够为数据生成独一无二的「指纹」，指纹用于校验数据的完整性，解决了篡改的风险。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;将服务器公钥放入到&lt;code&gt;数字证书&lt;/code&gt;中，解决了冒充的风险。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;混合加密&#34;&gt;混合加密&lt;/h3&gt;
&lt;p&gt;HTTPS 采用的是对称加密和非对称加密结合的「混合加密」方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;通信建立前采用非对称加密的方式交换「会话秘钥」，后续就不再使用非对称加密。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;通信过程中全部使用对称加密的「会话秘钥」的方式加密明文数据。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;采用「混合加密」的方式的原因：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;对称加密只使用一个密钥，运算速度快，密钥必须保密，无法做到安全的密钥交换。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;非对称加密使用两个密钥：公钥和私钥，公钥可以任意分发而私钥保密，解决了密钥交换问题但速度慢。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;








  











&lt;figure id=&#34;figure-混合加密&#34;&gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/http/https_mix_crypt.webp&#34; data-caption=&#34;混合加密&#34;&gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/http/https_mix_crypt.webp&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;


  
  
  &lt;figcaption&gt;
    混合加密
  &lt;/figcaption&gt;


&lt;/figure&gt;

&lt;h3 id=&#34;摘要算法&#34;&gt;摘要算法&lt;/h3&gt;
&lt;p&gt;客户端在发送明文之前会通过摘要算法算出明文的「指纹」&lt;/p&gt;
&lt;p&gt;发送的时候把「指纹 + 明文」一同加密成密文后，发送给服务器&lt;/p&gt;
&lt;p&gt;服务器解密后，用相同的摘要算法算出发送过来的明文&lt;/p&gt;
&lt;p&gt;通过比较客户端携带的「指纹」和当前算出的「指纹」做比较，若「指纹」相同，说明数据是完整的。&lt;/p&gt;








  











&lt;figure id=&#34;figure-校验完整性&#34;&gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/http/https_verify.webp&#34; data-caption=&#34;校验完整性&#34;&gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/http/https_verify.webp&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;


  
  
  &lt;figcaption&gt;
    校验完整性
  &lt;/figcaption&gt;


&lt;/figure&gt;

&lt;h3 id=&#34;数字证书&#34;&gt;数字证书&lt;/h3&gt;
&lt;p&gt;客户端先向服务器端索要公钥，然后用公钥加密信息，服务器收到密文后，用自己的私钥解密。&lt;/p&gt;
&lt;p&gt;这就存在些问题，如何保证公钥不被篡改和信任度？&lt;/p&gt;
&lt;p&gt;所以这里就需要借助第三方权威机构 &lt;code&gt;CA&lt;/code&gt; （数字证书认证机构），将服务器公钥放在数字证书（由数字证书认证机构颁发）中，只要证书是可信的，公钥就是可信的。&lt;/p&gt;








  











&lt;figure id=&#34;figure-数字证书工作流程&#34;&gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/http/https_ca.webp&#34; data-caption=&#34;数字证书工作流程&#34;&gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/http/https_ca.webp&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;


  
  
  &lt;figcaption&gt;
    数字证书工作流程
  &lt;/figcaption&gt;


&lt;/figure&gt;

&lt;h3 id=&#34;https-建立连接&#34;&gt;HTTPS 建立连接&lt;/h3&gt;
&lt;p&gt;SSL/TLS 协议基本流程：&lt;/p&gt;
&lt;p&gt;客户端向服务器索要并验证服务器的公钥。&lt;/p&gt;
&lt;p&gt;双方协商生产「会话秘钥」。&lt;/p&gt;
&lt;p&gt;双方采用「会话秘钥」进行加密通信。&lt;/p&gt;
&lt;p&gt;前两步也就是 SSL/TLS 的建立过程，也就是握手阶段。&lt;/p&gt;
&lt;p&gt;SSL/TLS 的「握手阶段」涉及四次通信，可见下图：&lt;/p&gt;








  











&lt;figure id=&#34;figure-https-连接建立过程&#34;&gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/http/https_connect.webp&#34; data-caption=&#34;HTTPS 连接建立过程&#34;&gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/http/https_connect.webp&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;


  
  
  &lt;figcaption&gt;
    HTTPS 连接建立过程
  &lt;/figcaption&gt;


&lt;/figure&gt;

&lt;p&gt;SSL/TLS 协议建立的详细流程：&lt;/p&gt;
&lt;h3 id=&#34;clienthello&#34;&gt;ClientHello&lt;/h3&gt;
&lt;p&gt;首先，由客户端向服务器发起加密通信请求，也就是 ClientHello 请求。&lt;/p&gt;
&lt;p&gt;在这一步，客户端主要向服务器发送以下信息：&lt;/p&gt;
&lt;p&gt;（1）客户端支持的 SSL/TLS 协议版本，如 TLS 1.2 版本。&lt;/p&gt;
&lt;p&gt;（2）客户端生产的随机数（Client Random），后面用于生产「会话秘钥」。&lt;/p&gt;
&lt;p&gt;（3）客户端支持的密码套件列表，如 RSA 加密算法。&lt;/p&gt;
&lt;h3 id=&#34;severhello&#34;&gt;SeverHello&lt;/h3&gt;
&lt;p&gt;服务器收到客户端请求后，向客户端发出响应，也就是 SeverHello。服务器回应的内容有如下内容：&lt;/p&gt;
&lt;p&gt;（1）确认 SSL/ TLS 协议版本，如果浏览器不支持，则关闭加密通信。&lt;/p&gt;
&lt;p&gt;（2）服务器生产的随机数（Server Random），后面用于生产「会话秘钥」。&lt;/p&gt;
&lt;p&gt;（3）确认的密码套件列表，如 RSA 加密算法。&lt;/p&gt;
&lt;p&gt;（4）服务器的数字证书。&lt;/p&gt;
&lt;h3 id=&#34;客户端回应&#34;&gt;客户端回应&lt;/h3&gt;
&lt;p&gt;客户端收到服务器的回应之后，首先通过浏览器或者操作系统中的 CA 公钥，确认服务器的数字证书的真实性。&lt;/p&gt;
&lt;p&gt;如果证书没有问题，客户端会从数字证书中取出服务器的公钥，然后使用它加密报文，向服务器发送如下信息：&lt;/p&gt;
&lt;p&gt;（1）一个随机数（pre-master key）。该随机数会被服务器公钥加密。&lt;/p&gt;
&lt;p&gt;（2）加密通信算法改变通知，表示随后的信息都将用「会话秘钥」加密通信。&lt;/p&gt;
&lt;p&gt;（3）客户端握手结束通知，表示客户端的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要，用来供服务端校验。&lt;/p&gt;
&lt;p&gt;上面第一项的随机数是整个握手阶段的第三个随机数，这样服务器和客户端就同时有三个随机数，接着就用双方协商的加密算法，各自生成本次通信的「会话秘钥」。&lt;/p&gt;
&lt;h3 id=&#34;服务器的最后回应&#34;&gt;服务器的最后回应&lt;/h3&gt;
&lt;p&gt;服务器收到客户端的第三个随机数（pre-master key）之后，通过协商的加密算法，计算出本次通信的「会话秘钥」。然后，向客户端发生最后的信息：&lt;/p&gt;
&lt;p&gt;（1）加密通信算法改变通知，表示随后的信息都将用「会话秘钥」加密通信。&lt;/p&gt;
&lt;p&gt;（2）服务器握手结束通知，表示服务器的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要，用来供客户端校验。&lt;/p&gt;
&lt;p&gt;至此，整个 SSL/TLS 的握手阶段全部结束。接下来，客户端与服务器进入加密通信，就完全是使用普通的 HTTP 协议，只不过用「会话秘钥」加密内容。&lt;/p&gt;
&lt;br&gt;
&lt;h2 id=&#34;http2&#34;&gt;Http/2&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://http2.akamai.com/demo&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Http/1.1 vs Http/2 demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;多路复用-multiplexing&#34;&gt;多路复用 (Multiplexing)&lt;/h3&gt;
&lt;p&gt;在 HTTP/1.1 协议中 「浏览器客户端在同一时间，针对同一域名下的请求有一定数量限制。超过限制数目的请求会被阻塞」&lt;/p&gt;
&lt;p&gt;RFC-2616-8.1.4 Practical Considerations:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Clients that use persistent connections SHOULD limit the number of simultaneous connections that they maintain to a given server.
A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy.
A proxy SHOULD use up to 2*N connections to another server or proxy, where N is the number of simultaneously active users.
These guidelines are intended to improve HTTP response times and avoid congestion.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;翻译：Chrome中，对Http/1.1协议，最多允许6个tcp链接，超过的则会被阻塞&lt;/p&gt;
&lt;p&gt;HTTP/2 的多路复用(Multiplexing) 则允许同时通过单一的 HTTP/2 连接发起多重的请求-响应消息。&lt;/p&gt;
&lt;p&gt;移除了 HTTP/1.1 中的串行请求，不需要排队等待，也就不会再出现「队头阻塞」问题，降低了延迟，大幅度提高了连接的利用率。&lt;/p&gt;
&lt;p&gt;HTTP/2 的数据包不是按顺序发送的，同一个连接里面连续的数据包，可能属于不同的回应。因此，必须要对数据包做标记，指出它属于哪个回应。&lt;/p&gt;
&lt;p&gt;每个请求或回应的所有数据包，称为一个数据流（Stream）。&lt;/p&gt;
&lt;p&gt;每个数据流都标记着一个独一无二的编号，其中规定客户端发出的数据流编号为奇数， 服务器发出的数据流编号为偶数&lt;/p&gt;
&lt;p&gt;客户端还可以指定数据流的优先级。优先级高的请求，服务器就先响应该请求。&lt;/p&gt;








  











&lt;figure id=&#34;figure-http112-链接发起对比&#34;&gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/http/http1.1_http2.0_connect.jpg&#34; data-caption=&#34;Http/1.1/2 链接发起对比&#34;&gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/http/http1.1_http2.0_connect.jpg&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;


  
  
  &lt;figcaption&gt;
    Http/1.1/2 链接发起对比
  &lt;/figcaption&gt;


&lt;/figure&gt;

&lt;br&gt;
&lt;h3 id=&#34;二进制分帧&#34;&gt;二进制分帧&lt;/h3&gt;
&lt;p&gt;在不改动 HTTP/1.x 的语义、方法、状态码、URI 以及首部字段&amp;hellip;的情况下, HTTP/2 是如何做到「突破 HTTP/1.1 的性能限制，改进传输性能，实现低延迟和高吞吐量」的 ?&lt;/p&gt;
&lt;p&gt;关键之一就是在 应用层(HTTP/2)和传输层(TCP or UDP)之间增加一个二进制分帧层。&lt;/p&gt;








  











&lt;figure id=&#34;figure-http2二进制分帧层&#34;&gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/http/http2.0_binary.jpg&#34; data-caption=&#34;Http/2二进制分帧层&#34;&gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/http/http2.0_binary.jpg&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;


  
  
  &lt;figcaption&gt;
    Http/2二进制分帧层
  &lt;/figcaption&gt;


&lt;/figure&gt;

&lt;p&gt;在二进制分帧层中，HTTP/2 会将所有传输的信息分割为更小的消息和帧（frame），并对它们采用二进制格式的编码，
其中 HTTP1.x 的首部信息会被封装到 HEADER frame，而相应的 Request Body 则封装到 DATA frame 里面。&lt;/p&gt;








  











&lt;figure &gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/http/http2.0_stream.jpg&#34; &gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/http/http2.0_stream.jpg&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;



&lt;/figure&gt;

&lt;h3 id=&#34;首部压缩header-compression&#34;&gt;首部压缩（Header Compression）&lt;/h3&gt;
&lt;p&gt;HTTP/1.1并不支持 HTTP 首部压缩，为此 SPDY 和 HTTP/2 应运而生， SPDY 使用的是通用的DEFLATE 算法，而 HTTP/2 则使用了专门为首部压缩而设计的 HPACK 算法。&lt;/p&gt;
&lt;p&gt;客户端和服务器两端建立“字典”，用索引号表示重复的字符串，还采用哈夫曼编码来压缩整数和字符串，可以达到 50%~90% 的高压缩率。&lt;/p&gt;
&lt;p&gt;具体来说：&lt;/p&gt;
&lt;p&gt;在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键 - 值对，对于相同的数据，不再通过每次请求和响应发送。&lt;/p&gt;
&lt;p&gt;首部表在 HTTP/2 的连接存续期内始终存在，由客户端和服务器共同渐进地更新。&lt;/p&gt;
&lt;p&gt;每个新的首部键 - 值对要么被追加到当前表的末尾，要么替换表中之前的值。&lt;/p&gt;
&lt;p&gt;例如下图中的两个请求， 请求一发送了所有的头部字段，第二个请求则只需要发送差异数据，这样可以减少冗余数据，降低开销 。&lt;/p&gt;








  











&lt;figure &gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/http/http2.0_header_compression.png&#34; &gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/http/http2.0_header_compression.png&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;



&lt;/figure&gt;

&lt;h3 id=&#34;server-push&#34;&gt;Server Push&lt;/h3&gt;
&lt;p&gt;Server Push 即服务端能通过 push 的方式将客户端需要的内容预先推送过去&lt;/p&gt;








  











&lt;figure &gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/http/http2.0_service_push.png&#34; &gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/http/http2.0_service_push.png&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;



&lt;/figure&gt;

&lt;p&gt;服务端可以主动推送，客户端也有权利选择是否接收。&lt;/p&gt;
&lt;p&gt;如果服务端推送的资源已经被浏览器缓存过，浏览器可以通过发送 RST_STREAM 帧来拒收。&lt;/p&gt;
&lt;p&gt;主动推送也遵守同源策略，换句话说，服务器不能随便将第三方资源推送给客户端，而必须是经过双方确认才行。&lt;/p&gt;
&lt;h3 id=&#34;总结&#34;&gt;总结：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;同域名下所有通信都在单个连接上完成。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;单个连接可以承载任意数量的双向数据流。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;数据流以消息的形式发送，而消息又由一个或多个帧组成，多个帧之间可以乱序发送，因为根据帧首部的流标识可以重新组装。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;同个域名只需要占用一个 TCP 连接，使用一个连接并行发送多个请求和响应, 这样整个页面资源的下载过程只需要一次慢启动，同时也避免了多个 TCP 连接竞争带宽所带来的问题。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;并行交错地发送多个请求 / 响应，请求 / 响应之间互不影响。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在 HTTP/2 中，每个请求都可以带一个 31bit 的优先值，0 表示最高优先级， 数值越大优先级越低。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;
&lt;h2 id=&#34;http3&#34;&gt;Http/3&lt;/h2&gt;
&lt;p&gt;HTTP/2 使用了多路复用，一般来说同一域名下只需要使用一个 TCP 连接。但当这个连接中出现了丢包的情况，那就会导致 HTTP/2 的表现情况反倒不如 HTTP/1 了。&lt;/p&gt;
&lt;p&gt;因为在出现丢包的情况下，整个 TCP 都要开始等待重传，也就导致了后面的所有数据都被阻塞了。但是对于 HTTP/1.1 来说，可以开启多个 TCP 连接，出现这种情况反到只会影响其中一个连接，剩余的 TCP 连接还可以正常传输数据。&lt;/p&gt;








  











&lt;figure &gt;


  &lt;a data-fancybox=&#34;&#34; href=&#34;https://w3xse7en.github.io/media/web/http/http2.0_block.png&#34; &gt;


  &lt;img src=&#34;https://w3xse7en.github.io/media/web/http/http2.0_block.png&#34; alt=&#34;&#34;  &gt;
&lt;/a&gt;



&lt;/figure&gt;

&lt;p&gt;那么可能就会有人考虑到去修改 TCP 协议，其实这已经是一件不可能完成的任务了。因为 TCP 存在的时间实在太长，已经充斥在各种设备中，并且这个协议是由操作系统实现的，更新起来不大现实。&lt;/p&gt;
&lt;p&gt;基于这个原因，Google 就更起炉灶搞了一个基于 UDP 协议的 QUIC 协议，并且使用在了 HTTP/3 上，HTTP/3 之前名为 HTTP-over-QUIC，从这个名字中我们也可以发现，HTTP/3 最大的改造就是使用了 QUIC。&lt;/p&gt;
&lt;h3 id=&#34;0-rtt&#34;&gt;0-RTT&lt;/h3&gt;
&lt;p&gt;通过使用类似 TCP 快速打开的技术，缓存当前会话的上下文，在下次恢复会话的时候，只需要将之前的缓存传递给服务端验证通过就可以进行传输了。&lt;strong&gt;0RTT 连接可以说是 QUIC 相比 HTTP2 最大的性能优势。&lt;/strong&gt;&lt;/p&gt;
&lt;br&gt;
&lt;h2 id=&#34;qa&#34;&gt;Q&amp;amp;A&lt;/h2&gt;
&lt;h3 id=&#34;与服务器建立了一个tcp连接后是否会在一个-http-请求完成后断开什么情况下会断开&#34;&gt;与服务器建立了一个TCP连接后是否会在一个 HTTP 请求完成后断开？什么情况下会断开？&lt;/h3&gt;
&lt;p&gt;HTTP/1.1 把 Connection 头写进标准，并且默认开启持久连接，除非请求中写明 Connection: close，那么浏览器和服务器之间是会维持一段时间的 TCP 连接，不会一个请求结束就断掉。&lt;/p&gt;
&lt;h3 id=&#34;一个tcp连接中http请求发送可以一起发送么比如一起发三个请求再三个响应一起接收&#34;&gt;一个TCP连接中HTTP请求发送可以一起发送么（比如一起发三个请求，再三个响应一起接收）&lt;/h3&gt;
&lt;p&gt;HTTP/1.1 存在一个问题，单个 TCP 连接在同一时刻只能处理一个请求，意思是说：两个请求的生命周期不能重叠，任意两个 HTTP 请求从开始到结束的时间在同一个 TCP 连接里不能重叠。&lt;/p&gt;
&lt;p&gt;虽然 HTTP/1.1 规范中规定了 Pipelining 来试图解决这个问题，但是这个功能在浏览器中默认是关闭的。&lt;/p&gt;
&lt;p&gt;RFC 2616 Pipelining&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A client that supports persistent connections MAY &amp;ldquo;pipeline&amp;rdquo; its requests (i.e., send multiple requests without waiting for each response).
A server MUST send its responses to those requests in the same order that the requests were received.&lt;/p&gt;
&lt;p&gt;一个支持持久连接的客户端可以在一个连接中发送多个请求（不需要等待任意请求的响应）。收到请求的服务器必须按照请求收到的顺序发送响应。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;为什么有的时候刷新页面不需要重新建立-ssl-连接&#34;&gt;为什么有的时候刷新页面不需要重新建立 SSL 连接？&lt;/h3&gt;
&lt;p&gt;TCP 连接有的时候会被浏览器和服务端维持一段时间。TCP 不需要重新建立，SSL 自然也会用之前的。&lt;/p&gt;
&lt;h3 id=&#34;浏览器对同一-host-建立-tcp-连接到数量有没有限制&#34;&gt;浏览器对同一 Host 建立 TCP 连接到数量有没有限制？&lt;/h3&gt;
&lt;p&gt;Chrome 最多允许对同一个 Host 建立六个 TCP 连接。不同的浏览器有一些区别。&lt;/p&gt;
&lt;br&gt;
&lt;hr&gt;
&lt;h2 id=&#34;参考&#34;&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://mp.weixin.qq.com/s?__biz=MzUxODAzNDg4NQ==&amp;amp;mid=2247483971&amp;amp;idx=1&amp;amp;sn=8f2d5dae3d95efc446061b352c8e9961&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;硬核！30 张图解 HTTP 常见的面试题&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.zhihu.com/question/34074946/answer/75364178&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;HTTP/2 相比 1.0 有哪些重大改进？&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://blog.fundebug.com/2019/03/07/understand-http2-and-http3/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;一文读懂 HTTP/2 及 HTTP/3 特性&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://zhuanlan.zhihu.com/p/61423830&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;你猜一个 TCP 连接上面能发多少个 HTTP 请求&lt;/a&gt;&lt;/p&gt;
&lt;br/&gt;
&lt;hr&gt;
</description>
    </item>
    
  </channel>
</rss>
