<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>计算机网络 on 蛋蛋忧桑</title><link>https://dandansad.com/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/</link><description>Recent content in 计算机网络 on 蛋蛋忧桑</description><generator>Hugo -- gohugo.io</generator><language>zh</language><lastBuildDate>Fri, 22 Aug 2025 16:41:32 +0800</lastBuildDate><atom:link href="https://dandansad.com/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/index.xml" rel="self" type="application/rss+xml"/><item><title>httpclient与hertzclient在处理Host header时的差别</title><link>https://dandansad.com/2025/08/22/httpclient%E4%B8%8Ehertzclient%E5%9C%A8%E5%A4%84%E7%90%86host-header%E6%97%B6%E7%9A%84%E5%B7%AE%E5%88%AB/</link><pubDate>Fri, 22 Aug 2025 16:41:32 +0800</pubDate><guid>https://dandansad.com/2025/08/22/httpclient%E4%B8%8Ehertzclient%E5%9C%A8%E5%A4%84%E7%90%86host-header%E6%97%B6%E7%9A%84%E5%B7%AE%E5%88%AB/</guid><description>&lt;p&gt;本文中的Host header、request.header.host、req.Header[&amp;ldquo;Host&amp;rdquo;]都指的是请求头中的“Host”字段。&lt;/p&gt;
&lt;h2 id="问题提出"&gt;&lt;a href="#%e9%97%ae%e9%a2%98%e6%8f%90%e5%87%ba" class="header-anchor"&gt;&lt;/a&gt;问题提出
&lt;/h2&gt;&lt;p&gt;在请求转发的过程中，需要从原请求中拿到一些信息来构造新的转发出去的请求，其中就有Host header的问题。一开始我使用的是httpclient，复制请求头的逻辑就是清除一些字段后直接全部复制过去（Host header不在清除范围内），此时程序正常运行。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;c&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Request&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Header&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;VisitAll&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt; []&lt;span style="color:#66d9ef"&gt;byte&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;req&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Header&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Add&lt;/span&gt;(string(&lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;), string(&lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当我将httpclient切换成hertzclient后，发现一模一样的复制请求头的逻辑遇到了404的错误。经过一番排查发现是httpclient和hertzclient两者对于request.header.host的处理方式不同。对于hertzclient来说，正确的方式应该是不复制请求头中的Host字段。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;c&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Request&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Header&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;VisitAll&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;value&lt;/span&gt; []&lt;span style="color:#66d9ef"&gt;byte&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; string(&lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;) &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Host&amp;#34;&lt;/span&gt; {	 &lt;span style="color:#75715e"&gt;// hertz框架中的client不会更改header.Host，所以不能用原来的Host&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			&lt;span style="color:#a6e22e"&gt;req&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Header&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Add&lt;/span&gt;(string(&lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;), string(&lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="httpclient"&gt;&lt;a href="#httpclient" class="header-anchor"&gt;&lt;/a&gt;httpclient
&lt;/h2&gt;&lt;p&gt;httpclient请求最终发送的 Host header 并不是取自 req.Header[&amp;ldquo;Host&amp;rdquo;]，而是由 req.Host 字段决定，如果 req.Host 为空，则默认为 req.URL.Host。&lt;/p&gt;
&lt;p&gt;所以说虽然我一开始直接把原始的、错误的 Host header 复制了过去，但是httpclient对请求的 Host header 进行了替换，没有用我设置的 Host header ，所以并没有出现问题。&lt;/p&gt;
&lt;h2 id="hertzclient"&gt;&lt;a href="#hertzclient" class="header-anchor"&gt;&lt;/a&gt;hertzclient
&lt;/h2&gt;&lt;p&gt;hertzclient最后发送的 Host header 就是取自 req.Header[&amp;ldquo;Host&amp;rdquo;] 的，所以说当我将原来的 request.header.host 写到新请求中后，hertzclient发送请求直接使用的是我设置的 Host header ，所以出现404的问题。&lt;/p&gt;
&lt;p&gt;如果说依然使用完整复制的方法，在最后需要用&lt;code&gt;req.Header.SetHost()&lt;/code&gt;替换原始的、错误的 Host header 。注意不是&lt;code&gt;req.SetHost()&lt;/code&gt;，这个方法是设置建立连接的主机地址。&lt;/p&gt;
&lt;h2 id="host-header作用"&gt;&lt;a href="#host-header%e4%bd%9c%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;Host header作用
&lt;/h2&gt;&lt;p&gt;那话说回来，为什么使用错误的Host header会出现404的问题呢？Host header究竟是起到什么样的作用呢？&lt;/p&gt;
&lt;p&gt;当一个网络请求发出去之后，网址会经过DNS解析为ip地址并建立连接，当连接到服务器后，则会用到 Host header 进行虚拟主机分流。&lt;/p&gt;
&lt;p&gt;而虚拟主机（Virtual Host）是一种服务器技术，它允许在同一台物理服务器（同一个IP地址）上运行多个网站。换句话说，你可以用一台服务器托管多个不同域名的网站，而不需要每个网站都分配一个独立的服务器。区分用户到底访问的是哪个网站的方式就是使用 Host header 进行分流。&lt;/p&gt;
&lt;p&gt;总体来说，一个请求的流程是：域名 → DNS 解析 → IP 地址 → 建立 TCP/SSL 连接 → 发送 HTTP 请求头 (Host) → 虚拟主机分流。&lt;/p&gt;
&lt;p&gt;这里举一个Nginx配置规则进行分流的例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;server&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;listen&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;server_name&lt;/span&gt; &lt;span style="color:#e6db74"&gt;www.example.com&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;root&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/var/www/example&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;server&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;listen&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;server_name&lt;/span&gt; &lt;span style="color:#e6db74"&gt;www.test.com&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;root&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/var/www/test&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;listen 80;&lt;/code&gt; → 表示监听 &lt;strong&gt;HTTP 默认端口 80&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;server_name&lt;/code&gt; → 表示这个虚拟主机匹配的域名。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;root&lt;/code&gt; → 表示网站的根目录，也就是请求 &lt;code&gt;/&lt;/code&gt; 时返回的文件位置。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nginx 的分流步骤可以总结为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;根据端口匹配 server 块&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这里两个 server 都监听 80，所以都在候选列表中。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;根据 &lt;code&gt;Host&lt;/code&gt; 匹配 server_name&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nginx 会查找与请求 &lt;code&gt;Host&lt;/code&gt; 最匹配的 server 块。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;匹配成功 → 使用该 server 块的 root 和配置处理请求&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;没有匹配 → 使用默认 server&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nginx 会选择 &lt;code&gt;listen&lt;/code&gt; 指令中第一个或 &lt;code&gt;default_server&lt;/code&gt; 标记的 server 作为默认返回。&lt;/p&gt;</description></item><item><title>在 Golang 中复用 HTTP 连接</title><link>https://dandansad.com/2025/08/20/%E5%9C%A8-golang-%E4%B8%AD%E5%A4%8D%E7%94%A8-http-%E8%BF%9E%E6%8E%A5/</link><pubDate>Wed, 20 Aug 2025 16:31:52 +0800</pubDate><guid>https://dandansad.com/2025/08/20/%E5%9C%A8-golang-%E4%B8%AD%E5%A4%8D%E7%94%A8-http-%E8%BF%9E%E6%8E%A5/</guid><description>&lt;h2 id="问题提出"&gt;&lt;a href="#%e9%97%ae%e9%a2%98%e6%8f%90%e5%87%ba" class="header-anchor"&gt;&lt;/a&gt;问题提出
&lt;/h2&gt;&lt;p&gt;最近在实现一个转发大模型调用请求的中转功能，涉及到要构造client发送请求的内容，一开始我每次都是新建一个client来发送请求，这样的代码实现存在一些问题——每次都要构造新的client，并且要重新建立连接。后面了解到在Go中使用 HTTP 客户端（&lt;code&gt;http.Client&lt;/code&gt;）可以实现&lt;strong&gt;连接复用&lt;/strong&gt;，做到提高性能并减少 TCP 连接的重复建立。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// 原始做法&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;client&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Client&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;Timeout&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;consts&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;GetRequestTimeout&lt;/span&gt;(), &lt;span style="color:#75715e"&gt;// 设置请求超时时间&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;resp&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;client&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Do&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;req&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;defer&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;resp&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Body&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Close&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// 修改后做法&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;resp&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;sharedHttpClient&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Do&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;req&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;defer&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;resp&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Body&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Close&lt;/span&gt;()&lt;span style="color:#75715e"&gt;// 必须关闭 Body 才能复用连接&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="原理探寻"&gt;&lt;a href="#%e5%8e%9f%e7%90%86%e6%8e%a2%e5%af%bb" class="header-anchor"&gt;&lt;/a&gt;原理探寻
&lt;/h2&gt;&lt;p&gt;在 Go 中，HTTP 客户端默认会复用 TCP 连接，只要使用同一个 &lt;code&gt;http.Client&lt;/code&gt; 实例，并且服务端支持 &lt;strong&gt;Keep-Alive&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;原因是 &lt;code&gt;http.Transport&lt;/code&gt; 内部维护了一个 &lt;strong&gt;连接池&lt;/strong&gt;。只要满足以下条件就可以进行连接复用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;使用同一个 &lt;code&gt;http.Client&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;响应的 &lt;code&gt;Body&lt;/code&gt; 被正确关闭。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;请求的 Host 相同。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;Transport&lt;/code&gt; 会根据空闲连接数和超时管理复用，可根据需求调整&lt;code&gt;Transport&lt;/code&gt;相关的参数。&lt;/p&gt;</description></item><item><title>流式与非流式有什么区别</title><link>https://dandansad.com/2025/08/20/%E6%B5%81%E5%BC%8F%E4%B8%8E%E9%9D%9E%E6%B5%81%E5%BC%8F%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB/</link><pubDate>Wed, 20 Aug 2025 15:59:58 +0800</pubDate><guid>https://dandansad.com/2025/08/20/%E6%B5%81%E5%BC%8F%E4%B8%8E%E9%9D%9E%E6%B5%81%E5%BC%8F%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB/</guid><description>&lt;h2 id="问题提出"&gt;&lt;a href="#%e9%97%ae%e9%a2%98%e6%8f%90%e5%87%ba" class="header-anchor"&gt;&lt;/a&gt;问题提出
&lt;/h2&gt;&lt;p&gt;最近在实现一个转发大模型调用请求的中转功能，因为现在的大模型都支持流式与非流式调用，所以我在实现的时候自然而然的想到了要不要区分两种调用方式，使用不同的代码逻辑。一开始写的时候分开了两个调用方式来写，后来发现这两种方式对于服务端来说实际没有区别，重点在于客户端的使用。&lt;/p&gt;
&lt;h2 id="原理探寻"&gt;&lt;a href="#%e5%8e%9f%e7%90%86%e6%8e%a2%e5%af%bb" class="header-anchor"&gt;&lt;/a&gt;原理探寻
&lt;/h2&gt;&lt;p&gt;首先看一下两种调用方式实际返回的数据。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;====&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;非流式调用返回&lt;/span&gt; &lt;span style="color:#f92672"&gt;====&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;choices&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;finish_reason&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;stop&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;index&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;logprobs&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; null,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Hello! How can I help you today?&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;reasoning_content&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;用户现在打招呼说Hello!，我要友好回应。所以回复“Hello! How can I help you today?” 这样友好又开放，邀请用户说明需求。&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;assistant&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;created&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1755675226&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;021755675225006530d45f3e18d6c5c13bff8621e54f4f6ee920a&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;model&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;doubao-1-5-thinking-vision-pro-250428&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;service_tier&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;default&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;object&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;chat.completion&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;usage&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;completion_tokens&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;45&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;prompt_tokens&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;70&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;total_tokens&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;115&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;prompt_tokens_details&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;cached_tokens&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;completion_tokens_details&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;reasoning_tokens&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;36&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;====&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;流式调用返回&lt;/span&gt; &lt;span style="color:#f92672"&gt;====&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;data: {&lt;span style="color:#e6db74"&gt;&amp;#34;choices&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;[{&lt;span style="color:#e6db74"&gt;&amp;#34;delta&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;reasoning_content&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;用户&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;assistant&amp;#34;&lt;/span&gt;},&lt;span style="color:#e6db74"&gt;&amp;#34;index&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;}],&lt;span style="color:#e6db74"&gt;&amp;#34;created&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1755675226&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;021755675226374530d45f3e18d6c5c13bff8621e54f4f6ec4025&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;model&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;doubao-1-5-thinking-vision-pro-250428&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;service_tier&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;default&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;object&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;chat.completion.chunk&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;usage&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;null}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;data: {&lt;span style="color:#e6db74"&gt;&amp;#34;choices&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;[{&lt;span style="color:#e6db74"&gt;&amp;#34;delta&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;reasoning_content&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;现在&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;assistant&amp;#34;&lt;/span&gt;},&lt;span style="color:#e6db74"&gt;&amp;#34;index&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;}],&lt;span style="color:#e6db74"&gt;&amp;#34;created&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1755675226&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;021755675226374530d45f3e18d6c5c13bff8621e54f4f6ec4025&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;model&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;doubao-1-5-thinking-vision-pro-250428&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;service_tier&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;default&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;object&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;chat.completion.chunk&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;usage&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;null}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;...........
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;data: {&lt;span style="color:#e6db74"&gt;&amp;#34;choices&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;[{&lt;span style="color:#e6db74"&gt;&amp;#34;delta&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;assistant&amp;#34;&lt;/span&gt;},&lt;span style="color:#e6db74"&gt;&amp;#34;finish_reason&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;stop&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;index&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;}],&lt;span style="color:#e6db74"&gt;&amp;#34;created&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1755675226&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;021755675226374530d45f3e18d6c5c13bff8621e54f4f6ec4025&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;model&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;doubao-1-5-thinking-vision-pro-250428&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;service_tier&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;default&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;object&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;chat.completion.chunk&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;usage&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;null}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;data: [DONE]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以看出，两种请求返回的格式是不一样的，非流式请求返回的是完整的json，而流式请求则是返回分块的json。&lt;/p&gt;
&lt;h2 id="服务端"&gt;&lt;a href="#%e6%9c%8d%e5%8a%a1%e7%ab%af" class="header-anchor"&gt;&lt;/a&gt;服务端
&lt;/h2&gt;&lt;p&gt;在服务端的实现中，实际上我们可以不用在意是流式还是非流式，我们可以统一采用流的方法返回请求体，是否真是流式形式实际上是取决于&lt;strong&gt;约定的返回格式&lt;/strong&gt;以及&lt;strong&gt;客户端处理方式&lt;/strong&gt;。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// 设置响应头&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;k&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;v&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;range&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;resp&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Header&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;_&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;vv&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;range&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;v&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			&lt;span style="color:#a6e22e"&gt;c&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Header&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;k&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;vv&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// 设置状态码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;c&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Status&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;resp&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;StatusCode&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#75715e"&gt;// 流式转发响应体&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;_&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; = &lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Copy&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;c&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Response&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;BodyWriter&lt;/span&gt;(), &lt;span style="color:#a6e22e"&gt;resp&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Body&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#75715e"&gt;// 处理错误&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;hlog&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;CtxErrorf&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;ctx&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Error copying response body: %v&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;c&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;String&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;StatusInternalServerError&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Error copying response body: %v&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当然，由于我们此处是实现转发的功能，因此可以采用统一的方式。在原始的返回代码中，实际上会有些不同。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//----------非流式输出：传统请求处理----------//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/api/user-data&amp;#39;&lt;/span&gt;, (&lt;span style="color:#a6e22e"&gt;req&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;res&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 同步获取所有数据（可能造成阻塞）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;userData&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fetchUserDataFromDB&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;analytics&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;generateUserAnalytics&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;userData&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;preferences&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;getUserPreferences&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;userData&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 一次性返回完整JSON
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;res&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;json&lt;/span&gt;({ 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;userData&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;analytics&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;preferences&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//----------流式输出：实时数据分块传输----------//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/api/real-time-updates&amp;#39;&lt;/span&gt;, (&lt;span style="color:#a6e22e"&gt;req&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;res&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 1. 设置流式响应头
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;res&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;setHeader&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;text/event-stream&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;res&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;setHeader&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;Cache-Control&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;no-cache&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;res&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;setHeader&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;Connection&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;keep-alive&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 2. 立即发送初始数据
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;res&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;write&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;event: init\ndata: Connection established\n\n&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 3. 创建数据流
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dataStream&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;createRealTimeDataStream&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 4. 分块发送数据
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;dataStream&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;on&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;data&amp;#39;&lt;/span&gt;, (&lt;span style="color:#a6e22e"&gt;chunk&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;res&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;write&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;`data: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;JSON&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;stringify&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;chunk&lt;/span&gt;)&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;\n\n`&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 5. 处理连接关闭
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;req&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;on&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;close&amp;#39;&lt;/span&gt;, () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;dataStream&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;destroy&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;res&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;end&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="客户端"&gt;&lt;a href="#%e5%ae%a2%e6%88%b7%e7%ab%af" class="header-anchor"&gt;&lt;/a&gt;客户端
&lt;/h2&gt;&lt;p&gt;客户端的处理方式就是有明显分别了。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//----------非流式输出：传统请求处理（传统Fetch API使用）----------//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;loadUserData&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;response&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fetch&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/api/user-data&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fullData&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;json&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 一次性渲染所有数据
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;renderUserProfile&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;fullData&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;userData&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;renderAnalytics&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;fullData&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;analytics&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;updatePreferencesUI&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;fullData&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;preferences&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;showErrorToast&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;Failed to load data&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//----------流式输出：实时数据处理技术（使用EventSource）----------//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;eventSource&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;EventSource&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/api/real-time-updates&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;eventSource&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;addEventListener&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;init&amp;#39;&lt;/span&gt;, (&lt;span style="color:#a6e22e"&gt;event&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;showConnectionStatus&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;Connected to real-time feed&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;eventSource&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;onmessage&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;event&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;data&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;JSON&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;parse&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;event&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;updateLiveDashboard&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;eventSource&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;onerror&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;showConnectionStatus&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;Connection lost - attempting to reconnect...&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;eventSource&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;close&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="参考资料"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://jishuzhan.net/article/1953334029515796482" target="_blank" rel="noopener"
 &gt;流式输出 vs 非流式输出 - 技术栈&lt;/a&gt;&lt;/p&gt;</description></item><item><title>DNS与CDN的区别?</title><link>https://dandansad.com/2025/05/04/dns%E4%B8%8Ecdn%E7%9A%84%E5%8C%BA%E5%88%AB/</link><pubDate>Sun, 04 May 2025 00:21:28 +0800</pubDate><guid>https://dandansad.com/2025/05/04/dns%E4%B8%8Ecdn%E7%9A%84%E5%8C%BA%E5%88%AB/</guid><description>&lt;h2 id="问题起因"&gt;&lt;a href="#%e9%97%ae%e9%a2%98%e8%b5%b7%e5%9b%a0" class="header-anchor"&gt;&lt;/a&gt;问题起因
&lt;/h2&gt;&lt;p&gt;在使用Cloudflare的过程中，接触到了DNS的相关内容，于是查找资料。然后在自己的个人网站上因为有使用jsDelivr的CDN服务，于是便有了疑惑——用户在访问网站时，DNS与CDN是怎样的一个解析过程，他们之间有什么区别？&lt;/p&gt;
&lt;h2 id="dns解析"&gt;&lt;a href="#dns%e8%a7%a3%e6%9e%90" class="header-anchor"&gt;&lt;/a&gt;DNS解析
&lt;/h2&gt;&lt;h3 id="什么是dns"&gt;&lt;a href="#%e4%bb%80%e4%b9%88%e6%98%afdns" class="header-anchor"&gt;&lt;/a&gt;什么是DNS？
&lt;/h3&gt;&lt;p&gt;DNS（Domain Name System）是互联网的域名解析系统，它的作用是将人们易记的域名（如 &lt;a class="link" href="https://www.example.com" target="_blank" rel="noopener"
 &gt;www.example.com&lt;/a&gt;）转换为计算机可识别的 IP 地址，从而让用户通过域名访问网站。DNS 由多个分层服务器组成，包括根服务器、顶级域服务器和权威服务器，整个解析过程类似查电话号码的过程，是互联网通信的基础服务之一。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="../../images/2025/05/image-1024x740.png"&gt;&lt;/p&gt;
&lt;h3 id="为什么需要-dns"&gt;&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e9%9c%80%e8%a6%81-dns" class="header-anchor"&gt;&lt;/a&gt;为什么需要 DNS？
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;你可以记住 &lt;code&gt;www.baidu.com&lt;/code&gt;，但很难记住 &lt;code&gt;220.181.38.148&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;网站的 IP 地址可能会变，但域名不会变&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;DNS 让用户用域名访问网站，而不是记 IP 地址&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="dns-的核心工作机制"&gt;&lt;a href="#dns-%e7%9a%84%e6%a0%b8%e5%bf%83%e5%b7%a5%e4%bd%9c%e6%9c%ba%e5%88%b6" class="header-anchor"&gt;&lt;/a&gt;DNS 的核心工作机制
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;用户输入域名（如 &lt;code&gt;www.example.com&lt;/code&gt;）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;浏览器向本地 DNS 服务器请求 IP 地址&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果本地没有缓存，就逐级向上查询：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;根 DNS 服务器&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;顶级域（.com）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;权威 DNS 服务器（负责 example.com）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;找到 IP 后，返回给浏览器&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;浏览器用这个 IP 去访问目标服务器&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="常见-dns-类型"&gt;&lt;a href="#%e5%b8%b8%e8%a7%81-dns-%e7%b1%bb%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;常见 DNS 类型
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;记录类型&lt;/th&gt;
					&lt;th&gt;作用&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;A&lt;/td&gt;
					&lt;td&gt;将域名解析为 IPv4 地址&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;AAAA&lt;/td&gt;
					&lt;td&gt;将域名解析为 IPv6 地址&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;CNAME&lt;/td&gt;
					&lt;td&gt;将域名指向另一个域名&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;MX&lt;/td&gt;
					&lt;td&gt;邮件服务器地址&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;TXT&lt;/td&gt;
					&lt;td&gt;文本记录（比如 SPF、验证用）&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="cdn加速"&gt;&lt;a href="#cdn%e5%8a%a0%e9%80%9f" class="header-anchor"&gt;&lt;/a&gt;CDN加速
&lt;/h2&gt;&lt;h3 id="什么是cdn"&gt;&lt;a href="#%e4%bb%80%e4%b9%88%e6%98%afcdn" class="header-anchor"&gt;&lt;/a&gt;什么是CDN？
&lt;/h3&gt;&lt;p&gt;CDN（Content Delivery Network，内容分发网络）是一种通过分布在全球各地的边缘节点，将网站内容缓存并分发给用户的技术，目的是加快访问速度、降低服务器压力，并提升用户体验。当用户访问网站时，CDN 会将请求引导到距离用户最近的节点，从而实现更快、更稳定的内容加载。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="../../images/2025/05/image-1-1024x735.png"&gt;&lt;/p&gt;
&lt;h3 id="为什么需要-cdn"&gt;&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e9%9c%80%e8%a6%81-cdn" class="header-anchor"&gt;&lt;/a&gt;&lt;strong&gt;为什么需要 CDN？&lt;/strong&gt;
&lt;/h3&gt;&lt;p&gt;CDN 解决的是“&lt;strong&gt;距离远、访问慢、负载高&lt;/strong&gt;”的问题，主要有以下几个方面的需求：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;需求&lt;/th&gt;
					&lt;th&gt;说明&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;⏱ 提高访问速度&lt;/td&gt;
					&lt;td&gt;缩短用户与服务器之间的物理距离，减少网络延迟&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🌎 提升全球可访问性&lt;/td&gt;
					&lt;td&gt;跨国/跨地区访问网站时更稳定&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;📉 减轻源站服务器压力&lt;/td&gt;
					&lt;td&gt;边缘节点承担大部分访问请求，避免源站过载&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🔐 增强安全性&lt;/td&gt;
					&lt;td&gt;隐藏源站 IP、防御 DDoS 攻击&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;📶 提升可用性和稳定性&lt;/td&gt;
					&lt;td&gt;某个节点故障时，可自动切换其他节点不中断服务&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="cdn-的工作机制"&gt;&lt;a href="#cdn-%e7%9a%84%e5%b7%a5%e4%bd%9c%e6%9c%ba%e5%88%b6" class="header-anchor"&gt;&lt;/a&gt;&lt;strong&gt;CDN 的工作机制&lt;/strong&gt;
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;内容分发&lt;/strong&gt;：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;网站将资源（如图片、JS、CSS、视频等）通过 CDN 平台分发并缓存到全球各地的节点（边缘服务器）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DNS 解析调度&lt;/strong&gt;：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;用户访问网站时，CDN 的 DNS 系统会将用户请求调度到&lt;strong&gt;最优的 CDN 节点&lt;/strong&gt;（基于距离、负载、网络状态等）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;内容访问&lt;/strong&gt;：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果边缘节点已有缓存资源，直接返回&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果没有缓存，则回源站获取后缓存，并返回给用户（下次访问就命中缓存）&lt;/li&gt;
&lt;/ul&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;CDN 是通过“就近访问 + 智能调度 + 分布式缓存”来提升网站访问速度、稳定性和安全性的加速服务。&lt;/strong&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h2 id="完整流程"&gt;&lt;a href="#%e5%ae%8c%e6%95%b4%e6%b5%81%e7%a8%8b" class="header-anchor"&gt;&lt;/a&gt;完整流程
&lt;/h2&gt;&lt;p&gt;&lt;img loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="../../images/2025/05/d43b3462648b12ee721226fcf3e14271.png"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;（1）用户访问域名 &lt;a class="link" href="https://www.huawei.com" target="_blank" rel="noopener"
 &gt;www.huawei.com&lt;/a&gt; 递归交给本地域名服务器去负责查询。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;（2）本地域名服务器经过迭代查询由 huawei.com 的域名服务器返回CNAME解析到的地址 &lt;a class="link" href="https://www.huawei.com.akadns.net" target="_blank" rel="noopener"
 &gt;www.huawei.com.akadns.net&lt;/a&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;（3）本地域名服务器去迭代查询 &lt;a class="link" href="https://www.huawei.com.akadns.net" target="_blank" rel="noopener"
 &gt;www.huawei.com.akadns.net&lt;/a&gt; 经过dig +trace 命令追踪发现又经过一次CNAME到 &lt;a class="link" href="https://www.huawei.com.lxdns.com" target="_blank" rel="noopener"
 &gt;www.huawei.com.lxdns.com&lt;/a&gt; ，具体原因没有深究，暂且认为此 &lt;a class="link" href="https://www.huawei.com.lxdns.com" target="_blank" rel="noopener"
 &gt;www.huawei.com.lxdns.com&lt;/a&gt; 为真正的CDN DNS域名服务器地址；本地域名服务器再次去迭代查询 &lt;a class="link" href="https://www.huawei.com.lxdns.com" target="_blank" rel="noopener"
 &gt;www.huawei.com.lxdns.com&lt;/a&gt; 的IP地址。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;（4）经过DNS全局负载均衡和区域负载均衡等一系列的智能调度，分配CDN节点，确定 &lt;a class="link" href="https://www.huawei.com.lxdns.com" target="_blank" rel="noopener"
 &gt;www.huawei.com.lxdns.com&lt;/a&gt; 的IP地址。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;全局负载均衡设备根据用户IP地址，以及用户请求的内容URL，选择一台用户所属区域的区域负载均衡设备。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;区域负载均衡设备会为用户选择一台合适的缓存服务器提供服务，选择的依据包括：根据用户IP地址，判断哪一台服务器距用户最近；根据用户所请求的URL中携带的内容名称，判断哪一台服务器上有用户所需内容；查询各个服务器当前的负载情况，判断哪一台服务器尚有服务能力。基于以上这些条件的综合分析之后，区域负载均衡设备会向全局负载均衡设备返回一台缓存服务器的IP地址。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;（5）返回CDN server（CDN节点）的IP地址。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;（6）返回CDN域名 &lt;a class="link" href="https://www.huawei.com.lxdns.com" target="_blank" rel="noopener"
 &gt;www.huawei.com.lxdns.com&lt;/a&gt; 的IP地址。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;（7）返回 &lt;a class="link" href="https://www.huawei.com" target="_blank" rel="noopener"
 &gt;www.huawei.com&lt;/a&gt; 对应 CDN域名 &lt;a class="link" href="https://www.huawei.com.lxdns.com" target="_blank" rel="noopener"
 &gt;www.huawei.com.lxdns.com&lt;/a&gt; 的IP地址&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;（8）用户访问 &lt;a class="link" href="https://www.huawei.com.lxdns.com" target="_blank" rel="noopener"
 &gt;www.huawei.com.lxdns.com&lt;/a&gt; 的IP地址对应的CDN server（CDN 节点），如果有缓存则直接到步骤11返回数据。如果没有则进行步骤9回源，再到步骤10，再到步骤11，返回数据。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="一些疑问"&gt;&lt;a href="#%e4%b8%80%e4%ba%9b%e7%96%91%e9%97%ae" class="header-anchor"&gt;&lt;/a&gt;一些疑问
&lt;/h2&gt;&lt;h3 id="如果cdn的记录是cname是不是还要再解析成ip"&gt;&lt;a href="#%e5%a6%82%e6%9e%9ccdn%e7%9a%84%e8%ae%b0%e5%bd%95%e6%98%afcname%e6%98%af%e4%b8%8d%e6%98%af%e8%bf%98%e8%a6%81%e5%86%8d%e8%a7%a3%e6%9e%90%e6%88%90ip" class="header-anchor"&gt;&lt;/a&gt;如果cdn的记录是cname，是不是还要再解析成ip
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;CNAME 记录&lt;/strong&gt;指的是把一个域名指向另一个域名。客户端（比如浏览器）在解析你的域名（如 &lt;code&gt;www.example.com&lt;/code&gt;）时，发现它是 CNAME 到另一个域名（如 &lt;code&gt;cdn.vendor.com&lt;/code&gt;）。然后，客户端会继续解析 &lt;code&gt;cdn.vendor.com&lt;/code&gt;，直到拿到最终的 &lt;strong&gt;A记录（IP 地址）&lt;/strong&gt;。&lt;strong&gt;最终访问仍然是通过 IP 地址完成的&lt;/strong&gt;，只是中间多了一个跳转。&lt;/p&gt;
&lt;p&gt;但要注意，&lt;strong&gt;解析过程中的所有域名查询（包括 CDN 的 CNAME）都是向 DNS 服务器发出的，而不是向 CDN 本身发出的&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;域名解析只与 DNS 服务器打交道，不和 CDN 本身通信。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="我没有配置相关dns解析记录为什么我的个人网站还能使用了jsdelivr的cdn服务"&gt;&lt;a href="#%e6%88%91%e6%b2%a1%e6%9c%89%e9%85%8d%e7%bd%ae%e7%9b%b8%e5%85%b3dns%e8%a7%a3%e6%9e%90%e8%ae%b0%e5%bd%95%e4%b8%ba%e4%bb%80%e4%b9%88%e6%88%91%e7%9a%84%e4%b8%aa%e4%ba%ba%e7%bd%91%e7%ab%99%e8%bf%98%e8%83%bd%e4%bd%bf%e7%94%a8%e4%ba%86jsdelivr%e7%9a%84cdn%e6%9c%8d%e5%8a%a1" class="header-anchor"&gt;&lt;/a&gt;我没有配置相关DNS解析记录，为什么我的个人网站还能使用了jsDelivr的CDN服务
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;你不是通过你的域名调用 jsDelivr，而是直接用 jsDelivr 的域名&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;举个例子，在 WordPress 的后台或插件里，引用的通常是类似这样的链接：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;https:&amp;amp;#47;&amp;amp;#47;cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这条链接的域名是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cdn.jsdelivr.net
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;也就是说：你根本没有把你自己的网站域名（如 &lt;code&gt;example.com&lt;/code&gt;）CNAME 到 jsDelivr，而是直接在代码中使用了 &lt;strong&gt;jsDelivr 的 CDN 域名&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;访问的流程如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;你的 WordPress 网站访问 cdn.jsdelivr.net&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;浏览器向 &lt;strong&gt;公共 DNS 服务器&lt;/strong&gt; 发起解析请求（比如 8.8.8.8）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;DNS 服务器向 jsDelivr 的权威 DNS 查询&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;jsDelivr 返回就近 CDN 节点的 IP（通过它的 DNS 调度）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;浏览器拿到 IP → 访问 CDN 节点 → 获取资源&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;你的网站并没有“配置” DNS，因为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;你只是“调用”了 jsDelivr 提供的链接&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;域名解析这件事交由浏览器和 jsDelivr 自己去处理了&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;情况&lt;/th&gt;
					&lt;th&gt;是否需要你配置 DNS&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;你用自己的域名接入 CDN（比如 &lt;a class="link" href="https://www.example.com" target="_blank" rel="noopener"
 &gt;www.example.com&lt;/a&gt; CNAME 到 yourcdn.example.net）&lt;/td&gt;
					&lt;td&gt;✅ 需要配置 DNS&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;你直接引用 CDN 提供的公共链接（如 cdn.jsdelivr.net）&lt;/td&gt;
					&lt;td&gt;❌ 不需要配置 DNS&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;你如果以后想让你自己的网站资源也通过 CDN 加速（比如图片、CSS、JS），那就要配置你自己域名的 CNAME 指向 CDN 节点了。&lt;/p&gt;
&lt;h3 id="配置cdn一般都是通过dns吗还有什么其他方法吗"&gt;&lt;a href="#%e9%85%8d%e7%bd%aecdn%e4%b8%80%e8%88%ac%e9%83%bd%e6%98%af%e9%80%9a%e8%bf%87dns%e5%90%97%e8%bf%98%e6%9c%89%e4%bb%80%e4%b9%88%e5%85%b6%e4%bb%96%e6%96%b9%e6%b3%95%e5%90%97" class="header-anchor"&gt;&lt;/a&gt;配置cdn一般都是通过dns吗，还有什么其他方法吗
&lt;/h3&gt;&lt;p&gt;是的，&lt;strong&gt;绝大多数情况下，配置 CDN 是通过 DNS 来实现的&lt;/strong&gt;，但它并不是唯一的方式。下面是完整的解释和对比：&lt;/p&gt;
&lt;h4 id="一主流方式通过-dns-配置-cdn最常见"&gt;&lt;a href="#%e4%b8%80%e4%b8%bb%e6%b5%81%e6%96%b9%e5%bc%8f%e9%80%9a%e8%bf%87-dns-%e9%85%8d%e7%bd%ae-cdn%e6%9c%80%e5%b8%b8%e8%a7%81" class="header-anchor"&gt;&lt;/a&gt;一、主流方式：通过 DNS 配置 CDN（最常见）
&lt;/h4&gt;&lt;p&gt;这是目前绝大多数网站接入 CDN 的标准做法。&lt;/p&gt;
&lt;h5 id="原理"&gt;&lt;a href="#%e5%8e%9f%e7%90%86" class="header-anchor"&gt;&lt;/a&gt;原理：
&lt;/h5&gt;&lt;p&gt;你把你自己的网站域名（比如 &lt;code&gt;www.example.com&lt;/code&gt;）的 DNS 记录改为：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;CNAME → cdn.example.cdnprovider.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;或者如果是根域名（不支持 CNAME）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;A 记录 → CDN 厂商提供的负载均衡 IP（如 Cloudflare 的 Anycast IP）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h5 id="特点"&gt;&lt;a href="#%e7%89%b9%e7%82%b9" class="header-anchor"&gt;&lt;/a&gt;特点：
&lt;/h5&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;适用于大部分 CDN 提供商（如 Cloudflare、阿里云 CDN、腾讯云 CDN、Akamai、jsDelivr 等）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;支持智能调度&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;一般用户看不到 CDN 的存在&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="二其他方式不通过-dns也有但相对少见"&gt;&lt;a href="#%e4%ba%8c%e5%85%b6%e4%bb%96%e6%96%b9%e5%bc%8f%e4%b8%8d%e9%80%9a%e8%bf%87-dns%e4%b9%9f%e6%9c%89%e4%bd%86%e7%9b%b8%e5%af%b9%e5%b0%91%e8%a7%81" class="header-anchor"&gt;&lt;/a&gt;二、其他方式（不通过 DNS）也有，但相对少见：
&lt;/h4&gt;&lt;h5 id="1-直接使用-cdn-提供的-url"&gt;&lt;a href="#1-%e7%9b%b4%e6%8e%a5%e4%bd%bf%e7%94%a8-cdn-%e6%8f%90%e4%be%9b%e7%9a%84-url" class="header-anchor"&gt;&lt;/a&gt;1. &lt;strong&gt;直接使用 CDN 提供的 URL&lt;/strong&gt;
&lt;/h5&gt;&lt;h6 id="举例"&gt;&lt;a href="#%e4%b8%be%e4%be%8b" class="header-anchor"&gt;&lt;/a&gt;举例：
&lt;/h6&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h6 id="适用"&gt;&lt;a href="#%e9%80%82%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;适用：
&lt;/h6&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;公共库/资源&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;不想改自己域名的 DNS&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h6 id="缺点"&gt;&lt;a href="#%e7%bc%ba%e7%82%b9" class="header-anchor"&gt;&lt;/a&gt;缺点：
&lt;/h6&gt;&lt;ul&gt;
&lt;li&gt;域名不是你自己的，缓存行为和品牌一致性无法控制&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id="2-反向代理方式接入-cdn"&gt;&lt;a href="#2-%e5%8f%8d%e5%90%91%e4%bb%a3%e7%90%86%e6%96%b9%e5%bc%8f%e6%8e%a5%e5%85%a5-cdn" class="header-anchor"&gt;&lt;/a&gt;2. &lt;strong&gt;反向代理方式接入 CDN&lt;/strong&gt;
&lt;/h5&gt;&lt;p&gt;一些特殊情况，可以让 CDN 厂商作为反向代理直接部署在源站前，但这通常是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;企业内网、私有部署&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;定制的 CDN 网络&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;安全/合规特殊场景&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这通常不通过 DNS，而是由 CDN 提供的边界设备（比如云防护墙）或专线统一接入。&lt;/p&gt;
&lt;h5 id="3-通过-web-应用接入插件或中间件"&gt;&lt;a href="#3-%e9%80%9a%e8%bf%87-web-%e5%ba%94%e7%94%a8%e6%8e%a5%e5%85%a5%e6%8f%92%e4%bb%b6%e6%88%96%e4%b8%ad%e9%97%b4%e4%bb%b6" class="header-anchor"&gt;&lt;/a&gt;3. &lt;strong&gt;通过 Web 应用接入（插件或中间件）&lt;/strong&gt;
&lt;/h5&gt;&lt;p&gt;例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;WordPress 安装某个插件后自动为图片加上 CDN 前缀&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Web 应用自动切换资源路径，如从 &lt;code&gt;/static/&lt;/code&gt; → &lt;code&gt;https://cdn.yourdomain.com/static/&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这种方式不依赖 DNS，而是代码层配置&lt;strong&gt;资源路径&lt;/strong&gt;实现 CDN 加速。&lt;/p&gt;
&lt;h4 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结：
&lt;/h4&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;接入方式&lt;/th&gt;
					&lt;th&gt;是否通过 DNS&lt;/th&gt;
					&lt;th&gt;是否常用&lt;/th&gt;
					&lt;th&gt;说明&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;修改 CNAME/A 记录&lt;/td&gt;
					&lt;td&gt;✅ 是&lt;/td&gt;
					&lt;td&gt;⭐ 非常常用&lt;/td&gt;
					&lt;td&gt;最标准的接入方式&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;直接用 CDN 链接&lt;/td&gt;
					&lt;td&gt;❌ 否&lt;/td&gt;
					&lt;td&gt;✅ 较常用&lt;/td&gt;
					&lt;td&gt;用于公共库，不影响你自己的域名&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;反向代理模式&lt;/td&gt;
					&lt;td&gt;❌ 否&lt;/td&gt;
					&lt;td&gt;❗ 少数企业使用&lt;/td&gt;
					&lt;td&gt;高级部署方式，成本较高&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;应用层插件/CDN 前缀&lt;/td&gt;
					&lt;td&gt;❌ 否&lt;/td&gt;
					&lt;td&gt;✅ 在某些框架中常用&lt;/td&gt;
					&lt;td&gt;控制静态资源路径即可，不依赖 DNS&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="参考资料"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://zhuanlan.zhihu.com/p/26879931" target="_blank" rel="noopener"
 &gt;20分钟理解什么是网络、DNS与CDN - 知乎&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://blog.csdn.net/am45337908/article/details/87891995" target="_blank" rel="noopener"
 &gt;DNS解析与CDN加速-CSDN博客&lt;/a&gt;&lt;/p&gt;</description></item><item><title>正向代理和反向代理</title><link>https://dandansad.com/2025/05/03/%E6%AD%A3%E5%90%91%E4%BB%A3%E7%90%86%E5%92%8C%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86/</link><pubDate>Sat, 03 May 2025 23:57:34 +0800</pubDate><guid>https://dandansad.com/2025/05/03/%E6%AD%A3%E5%90%91%E4%BB%A3%E7%90%86%E5%92%8C%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86/</guid><description>&lt;h2 id="正向代理-vs-反向代理核心区别"&gt;&lt;a href="#%e6%ad%a3%e5%90%91%e4%bb%a3%e7%90%86-vs-%e5%8f%8d%e5%90%91%e4%bb%a3%e7%90%86%e6%a0%b8%e5%bf%83%e5%8c%ba%e5%88%ab" class="header-anchor"&gt;&lt;/a&gt;正向代理 vs 反向代理：核心区别
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;特性&lt;/th&gt;
					&lt;th&gt;正向代理（Forward Proxy）&lt;/th&gt;
					&lt;th&gt;反向代理（Reverse Proxy）&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;📌 代理的对象&lt;/td&gt;
					&lt;td&gt;代理客户端（用户）访问目标服务器&lt;/td&gt;
					&lt;td&gt;代理服务端（源站）响应外部请求&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🎯 主要用途&lt;/td&gt;
					&lt;td&gt;客户端访问内部或受控网络资源，缓存优化&lt;/td&gt;
					&lt;td&gt;实现负载均衡、隐藏后端、统一入口、安全防护&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🛠 配置位置&lt;/td&gt;
					&lt;td&gt;客户端或客户端网关&lt;/td&gt;
					&lt;td&gt;服务器端或网络边缘设备&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🌍 真实服务器&lt;/td&gt;
					&lt;td&gt;用户试图访问的目标服务器（如内部业务接口）&lt;/td&gt;
					&lt;td&gt;后端应用服务器（如 Web 服务、API 服务等）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🔒 隐藏谁的身份&lt;/td&gt;
					&lt;td&gt;隐藏客户端身份&lt;/td&gt;
					&lt;td&gt;隐藏服务端身份&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🗺 常见用途&lt;/td&gt;
					&lt;td&gt;内网访问控制、缓存加速、日志审计、流量统一出口&lt;/td&gt;
					&lt;td&gt;负载均衡、CDN、服务网关、应用防火墙（WAF）等&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;🌐 举例&lt;/td&gt;
					&lt;td&gt;企业出网网关代理、Squid、局域网 Web 缓存服务器&lt;/td&gt;
					&lt;td&gt;Nginx 反向代理、CDN 边缘节点、WAF 安全设备等&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;一句话区别：&lt;strong&gt;正向代理服务于客户端，反向代理服务于服务端。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="正向代理帮用户访问外部内容"&gt;&lt;a href="#%e6%ad%a3%e5%90%91%e4%bb%a3%e7%90%86%e5%b8%ae%e7%94%a8%e6%88%b7%e8%ae%bf%e9%97%ae%e5%a4%96%e9%83%a8%e5%86%85%e5%ae%b9" class="header-anchor"&gt;&lt;/a&gt;正向代理：帮&lt;strong&gt;用户&lt;/strong&gt;访问外部内容
&lt;/h2&gt;&lt;h3 id="-举例场景"&gt;&lt;a href="#-%e4%b8%be%e4%be%8b%e5%9c%ba%e6%99%af" class="header-anchor"&gt;&lt;/a&gt;🎯 举例场景：
&lt;/h3&gt;&lt;p&gt;员工（在公司内网）→ 配置正向代理服务器（如 Squid）→ 访问公司外部业务系统或 API（如 Salesforce）→ 响应数据返回 → 代理服务器转发给员工电脑
✅ &lt;strong&gt;你是主动用代理“出门”访问别人&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="-常见用途"&gt;&lt;a href="#-%e5%b8%b8%e8%a7%81%e7%94%a8%e9%80%94" class="header-anchor"&gt;&lt;/a&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;/ul&gt;
&lt;h2 id="反向代理帮服务器隐藏控制外部访问"&gt;&lt;a href="#%e5%8f%8d%e5%90%91%e4%bb%a3%e7%90%86%e5%b8%ae%e6%9c%8d%e5%8a%a1%e5%99%a8%e9%9a%90%e8%97%8f%e6%8e%a7%e5%88%b6%e5%a4%96%e9%83%a8%e8%ae%bf%e9%97%ae" class="header-anchor"&gt;&lt;/a&gt;反向代理：帮&lt;strong&gt;服务器&lt;/strong&gt;隐藏/控制外部访问
&lt;/h2&gt;&lt;h3 id="-举例场景-1"&gt;&lt;a href="#-%e4%b8%be%e4%be%8b%e5%9c%ba%e6%99%af-1" class="header-anchor"&gt;&lt;/a&gt;🎯 举例场景：
&lt;/h3&gt;&lt;p&gt;用户 → 访问 &lt;code&gt;www.example.com&lt;/code&gt;（其实是访问 CDN 或 Nginx）→ 代理判断请求 → 转发到后端服务器处理 → 返回内容
✅ &lt;strong&gt;你是访问“前台”，它替你决定后面由谁来服务&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="-常见用途-1"&gt;&lt;a href="#-%e5%b8%b8%e8%a7%81%e7%94%a8%e9%80%94-1" class="header-anchor"&gt;&lt;/a&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;CDN（其实就是分布式反向代理）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;隐藏真实源站 IP&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;安全过滤（WAF）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;HTTPS 统一入口&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>端口被占用的问题——Hyper-V</title><link>https://dandansad.com/2025/04/30/%E7%AB%AF%E5%8F%A3%E8%A2%AB%E5%8D%A0%E7%94%A8%E7%9A%84%E9%97%AE%E9%A2%98-hyper-v/</link><pubDate>Wed, 30 Apr 2025 20:30:51 +0800</pubDate><guid>https://dandansad.com/2025/04/30/%E7%AB%AF%E5%8F%A3%E8%A2%AB%E5%8D%A0%E7%94%A8%E7%9A%84%E9%97%AE%E9%A2%98-hyper-v/</guid><description>&lt;h2 id="问题描述"&gt;&lt;a href="#%e9%97%ae%e9%a2%98%e6%8f%8f%e8%bf%b0" class="header-anchor"&gt;&lt;/a&gt;问题描述
&lt;/h2&gt;&lt;p&gt;在端口被占用时，用命令查看占用的程序。但是没有结果输出，代表没有程序在占用。（实际上是“没有程序在此刻占用这个端口，但不排除有程序在随机保留着这个端口”）&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;netstat &lt;span style="color:#f92672"&gt;-&lt;/span&gt;ano &lt;span style="color:#f92672"&gt;|&lt;/span&gt; findstr &lt;span style="color:#e6db74"&gt;&amp;#34;:7890&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这是因为电脑开启了 Hyper-V ，它会保留一部分 TCP/IP 端口供其虚拟交换机使用，这可能导致某些应用程序无法绑定到这些端口。我们可以用以下命令查看其保留的端口。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;#&lt;/span&gt; 查看 Hyper&lt;span style="color:#f92672"&gt;-&lt;/span&gt;v 启动后的保留端口范围
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;netsh &lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ipv4&lt;/span&gt; show excludedportrange protocol&lt;span style="color:#f92672"&gt;=&lt;/span&gt;tcp
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="../../images/2025/04/PixPin_2025-04-30_20-16-32-1024x514.png"&gt;&lt;/p&gt;
&lt;h2 id="临时解决方案排除单个端口重启可能失效"&gt;&lt;a href="#%e4%b8%b4%e6%97%b6%e8%a7%a3%e5%86%b3%e6%96%b9%e6%a1%88%e6%8e%92%e9%99%a4%e5%8d%95%e4%b8%aa%e7%ab%af%e5%8f%a3%e9%87%8d%e5%90%af%e5%8f%af%e8%83%bd%e5%a4%b1%e6%95%88" class="header-anchor"&gt;&lt;/a&gt;临时解决方案——排除单个端口（重启可能失效）
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;以管理员身份运行 PowerShell 或 CMD&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;停止 Hyper-V 相关服务&lt;/strong&gt;：&lt;code&gt;net stop winnat&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;手动添加端口排除&lt;/strong&gt;（例如释放 &lt;code&gt;7890&lt;/code&gt;）：&lt;code&gt;netsh int ipv4 delete excludedportrange protocol=tcp startport=7890 numberofports=1&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;protocol=tcp&lt;/code&gt;（或 &lt;code&gt;udp&lt;/code&gt;）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;startport=7890&lt;/code&gt;（你要释放的端口）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;numberofports=1&lt;/code&gt;（只释放 1 个端口）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;重启 WinNAT 服务&lt;/strong&gt;（恢复网络）：&lt;code&gt;net start winnat&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;验证是否成功&lt;/strong&gt;：&lt;code&gt;netsh int ipv4 show excludedportrange protocol=tcp | findstr &amp;quot;7890&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果无输出，说明 &lt;code&gt;7890&lt;/code&gt; 已不再被保留，可以正常使用。&lt;/p&gt;
&lt;p&gt;⚠️ &lt;strong&gt;注意&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;重启后可能失效&lt;/strong&gt;，因为 Hyper-V 可能会重新占用该端口。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果仍然无法使用，可能是其他服务占用了该端口（用 &lt;code&gt;netstat -ano | findstr &amp;quot;50000&amp;quot;&lt;/code&gt; 检查）。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Socket（套接字）通信原理 </title><link>https://dandansad.com/2025/02/27/socket%E5%A5%97%E6%8E%A5%E5%AD%97%E9%80%9A%E4%BF%A1%E5%8E%9F%E7%90%86/</link><pubDate>Thu, 27 Feb 2025 11:25:14 +0800</pubDate><guid>https://dandansad.com/2025/02/27/socket%E5%A5%97%E6%8E%A5%E5%AD%97%E9%80%9A%E4%BF%A1%E5%8E%9F%E7%90%86/</guid><description>&lt;h3 id="一socket是什么"&gt;&lt;a href="#%e4%b8%80socket%e6%98%af%e4%bb%80%e4%b9%88" class="header-anchor"&gt;&lt;/a&gt;一、Socket是什么
&lt;/h3&gt;&lt;p&gt;Socket 的中文翻译过来就是“套接字”。套接字是什么，我们先来看看它的英文含义：插座。&lt;/p&gt;
&lt;p&gt;Socket 就像一个电话插座，负责连通两端的电话，进行点对点通信，让电话可以进行通信，端口就像插座上的孔，端口不能同时被其他进程占用。而我们建立连接就像把插头插在这个插座上，创建一个 Socket 实例开始监听后，这个电话插座就时刻监听着消息的传入，谁拨通我这个“IP 地址和端口”，我就接通谁。&lt;/p&gt;
&lt;p&gt;实际上，Socket 是在应用层和传输层之间的一个抽象层，它把 TCP/IP 层复杂的操作抽象为几个简单的接口，供应用层调用实现进程在网络中的通信。Socket 起源于 UNIX，在 UNIX 一切皆文件的思想下，进程间通信就被冠名为&lt;code&gt;文件描述符（file descriptor）&lt;/code&gt;，Socket 是一种“打开—读/写—关闭”模式的实现，服务器和客户端各自维护一个“文件”，在建立连接打开后，可以向文件写入内容供对方读取或者读取对方内容，通讯结束时关闭文件。&lt;/p&gt;
&lt;p&gt;另外我们经常说到的Socket 所在位置如下图：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="../../images/2025/02/image-3.png"&gt;&lt;/p&gt;
&lt;h3 id="二socket有哪些类型"&gt;&lt;a href="#%e4%ba%8csocket%e6%9c%89%e5%93%aa%e4%ba%9b%e7%b1%bb%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;二、Socket有哪些类型
&lt;/h3&gt;&lt;p&gt;世界上有很多种套接字（socket），比如 DARPA Internet 地址（Internet 套接字）、本地节点的路径名（Unix套接字）、CCITT X.25地址（X.25 套接字）等。我们只介绍第一种套接字——Internet 套接字，它是最具代表性的，也是最经典最常用的。以后我们提及套接字，指的都是 Internet 套接字。&lt;/p&gt;
&lt;p&gt;根据数据的传输方式，可以将 Internet 套接字分成两种类型。&lt;/p&gt;
&lt;h5 id="流格式套接字sock_stream"&gt;&lt;a href="#%e6%b5%81%e6%a0%bc%e5%bc%8f%e5%a5%97%e6%8e%a5%e5%ad%97sock_stream" class="header-anchor"&gt;&lt;/a&gt;流格式套接字（SOCK_STREAM）
&lt;/h5&gt;&lt;p&gt;流格式套接字（Stream Sockets）也叫“面向连接的套接字”，是一种可靠的、双向的通信数据流，数据可以准确无误地到达另一台计算机，如果损坏或丢失，可以重新发送。&lt;/p&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;p&gt;可以将 SOCK_STREAM 比喻成一条传送带，只要传送带本身没有问题（不会断网），就能保证数据不丢失；同时，较晚传送的数据不会先到达，较早传送的数据不会晚到达，这就保证了数据是按照顺序传递的。&lt;/p&gt;
&lt;p&gt;为什么流格式套接字可以达到高质量的数据传输呢？这是因为它使用了 TCP 协议（The Transmission Control Protocol，传输控制协议），TCP 协议会控制你的数据按照顺序到达并且没有错误。&lt;/p&gt;
&lt;p&gt;你也许见过 TCP，是因为你经常听说“TCP/IP”。TCP 用来确保数据的正确性，IP（Internet Protocol，网络协议）用来控制数据如何从源头到达目的地，也就是常说的“路由”。&lt;/p&gt;
&lt;p&gt;那么，“数据的发送和接收不同步”该如何理解呢？&lt;/p&gt;
&lt;p&gt;假设传送带传送的是水果，接收者需要凑齐 100 个后才能装袋，但是传送带可能把这 100 个水果分批传送，比如第一批传送 20 个，第二批传送 50 个，第三批传送 30 个。接收者不需要和传送带保持同步，只要根据自己的节奏来装袋即可，不用管传送带传送了几批，也不用每到一批就装袋一次，可以等到凑够了 100 个水果再装袋。&lt;/p&gt;
&lt;p&gt;流格式套接字的内部有一个缓冲区（也就是字符数组），通过 socket 传输的数据将保存到这个缓冲区。接收端在收到数据后并不一定立即读取，只要数据不超过缓冲区的容量，接收端有可能在缓冲区被填满以后一次性地读取，也可能分成好几次读取。&lt;/p&gt;
&lt;p&gt;也就是说，不管数据分几次传送过来，接收端只需要根据自己的要求读取，不用非得在数据到达时立即读取。传送端有自己的节奏，接收端也有自己的节奏，它们是不一致的。&lt;/p&gt;
&lt;p&gt;流格式套接字有什么实际的应用场景吗？浏览器所使用的 http 协议就基于面向连接的套接字，因为必须要确保数据准确无误，否则加载的 HTML 将无法解析。&lt;/p&gt;
&lt;h5 id="数据报格式套接字sock_dgram"&gt;&lt;a href="#%e6%95%b0%e6%8d%ae%e6%8a%a5%e6%a0%bc%e5%bc%8f%e5%a5%97%e6%8e%a5%e5%ad%97sock_dgram" class="header-anchor"&gt;&lt;/a&gt;数据报格式套接字（SOCK_DGRAM）
&lt;/h5&gt;&lt;p&gt;数据报格式套接字（Datagram Sockets）也叫“无连接的套接字”。计算机只管传输数据，不作数据校验，如果数据在传输中损坏，或者没有到达另一台计算机，是没有办法补救的。也就是说，数据错了就错了，无法重传。&lt;/p&gt;
&lt;p&gt;因为数据报套接字所做的校验工作少，所以在传输效率方面比流格式套接字要高。&lt;/p&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;li&gt;
&lt;p&gt;数据的发送和接收是同步的&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;众所周知，速度是快递行业的生命。用摩托车发往同一地点的两件包裹无需保证顺序，只要以最快的速度交给客户就行。这种方式存在损坏或丢失的风险，而且包裹大小有一定限制。因此，想要传递大量包裹，就得分配发送。&lt;/p&gt;
&lt;p&gt;另外，用两辆摩托车分别发送两件包裹，那么接收者也需要分两次接收，所以“数据的发送和接收是同步的”；换句话说，接收次数应该和发送次数相同。&lt;/p&gt;
&lt;p&gt;总之，数据报套接字是一种不可靠的、不按顺序传递的、以追求速度为目的的套接字。&lt;/p&gt;
&lt;p&gt;数据报套接字也使用 IP 协议作路由，但是它不使用 TCP 协议，而是使用 UDP 协议（User Datagram Protocol，用户数据报协议）。&lt;/p&gt;
&lt;p&gt;QQ 视频聊天和语音聊天就使用 SOCK_DGRAM 来传输数据，因为首先要保证通信的效率，尽量减小延迟，而数据的正确性是次要的，即使丢失很小的一部分数据，视频和音频也可以正常解析，最多出现噪点或杂音，不会对通信质量有实质的影响。&lt;/p&gt;
&lt;p&gt;注意：SOCK_DGRAM 没有想象中的糟糕，不会频繁的丢失数据，数据错误只是小概率事件。&lt;/p&gt;
&lt;h3 id="三socket通信过程"&gt;&lt;a href="#%e4%b8%89socket%e9%80%9a%e4%bf%a1%e8%bf%87%e7%a8%8b" class="header-anchor"&gt;&lt;/a&gt;三、Socket通信过程
&lt;/h3&gt;&lt;p&gt;Socket 保证了不同计算机之间的通信，也就是网络通信。对于网站，通信模型是服务器与客户端之间的通信。两端都建立了一个 Socket 对象，然后通过 Socket 对象对数据进行传输。通常服务器处于一个无限循环，等待客户端的连接。&lt;/p&gt;
&lt;p&gt;下面是面向连接的 TCP 时序图：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="../../images/2025/02/image-4.png"&gt;&lt;/p&gt;
&lt;h5 id="客户端过程"&gt;&lt;a href="#%e5%ae%a2%e6%88%b7%e7%ab%af%e8%bf%87%e7%a8%8b" class="header-anchor"&gt;&lt;/a&gt;客户端过程
&lt;/h5&gt;&lt;p&gt;客户端的过程比较简单，创建 Socket，连接服务器，将 Socket 与远程主机连接（注意：只有 TCP 才有“连接”的概念，一些 Socket 比如 UDP、ICMP 和 ARP 没有“连接”的概念），发送数据，读取响应数据，直到数据交换完毕，关闭连接，结束 TCP 对话。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; socket
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; sys
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; __name__ &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sock &lt;span style="color:#f92672"&gt;=&lt;/span&gt; socket&lt;span style="color:#f92672"&gt;.&lt;/span&gt;socket(socket&lt;span style="color:#f92672"&gt;.&lt;/span&gt;AF_INET, socket&lt;span style="color:#f92672"&gt;.&lt;/span&gt;SOCK_STREAM) &lt;span style="color:#75715e"&gt;# 创建 Socket 连接&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sock&lt;span style="color:#f92672"&gt;.&lt;/span&gt;connect((&lt;span style="color:#e6db74"&gt;&amp;#39;127.0.0.1&amp;#39;&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;8001&lt;/span&gt;)) &lt;span style="color:#75715e"&gt;# 连接服务器&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; data &lt;span style="color:#f92672"&gt;=&lt;/span&gt; input(&lt;span style="color:#e6db74"&gt;&amp;#39;Please input data:&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; data:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sock&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sendall(data)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; socket&lt;span style="color:#f92672"&gt;.&lt;/span&gt;error &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; e:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#39;Send Failed...&amp;#39;&lt;/span&gt;, e)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sys&lt;span style="color:#f92672"&gt;.&lt;/span&gt;exit(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#39;Send Successfully&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res &lt;span style="color:#f92672"&gt;=&lt;/span&gt; sock&lt;span style="color:#f92672"&gt;.&lt;/span&gt;recv(&lt;span style="color:#ae81ff"&gt;4096&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 获取服务器返回的数据，还可以用 recvfrom()、recv_into() 等&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(res)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sock&lt;span style="color:#f92672"&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;sock.sendall(data)
这里也可用 send() 方法：不同在于 sendall() 在返回前会尝试发送所有数据，并且成功时返回 None，而 send() 则返回发送的字节数量，失败时都抛出异常。&lt;/p&gt;
&lt;h5 id="服务端过程"&gt;&lt;a href="#%e6%9c%8d%e5%8a%a1%e7%ab%af%e8%bf%87%e7%a8%8b" class="header-anchor"&gt;&lt;/a&gt;服务端过程
&lt;/h5&gt;&lt;p&gt;服务端先初始化 Socket，建立流式套接字，与本机地址及端口进行绑定，然后通知 TCP，准备好接收连接，调用 &lt;code&gt;accept()&lt;/code&gt; 阻塞，等待来自客户端的连接。如果这时客户端与服务器建立了连接，客户端发送数据请求，服务器接收请求并处理请求，然后把响应数据发送给客户端，客户端读取数据，直到数据交换完毕。最后关闭连接，交互结束。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; socket
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; sys
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; __name__ &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sock &lt;span style="color:#f92672"&gt;=&lt;/span&gt; socket&lt;span style="color:#f92672"&gt;.&lt;/span&gt;socket(socket&lt;span style="color:#f92672"&gt;.&lt;/span&gt;AF_INET, socket&lt;span style="color:#f92672"&gt;.&lt;/span&gt;SOCK_STREAM) &lt;span style="color:#75715e"&gt;# 创建 Socket 连接（TCP）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#39;Socket Created&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sock&lt;span style="color:#f92672"&gt;.&lt;/span&gt;bind((&lt;span style="color:#e6db74"&gt;&amp;#39;127.0.0.1&amp;#39;&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;8001&lt;/span&gt;)) &lt;span style="color:#75715e"&gt;# 配置 Socket，绑定 IP 地址和端口号&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; socket&lt;span style="color:#f92672"&gt;.&lt;/span&gt;error &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; e:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#39;Bind Failed...&amp;#39;&lt;/span&gt;, e)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sys&lt;span style="color:#f92672"&gt;.&lt;/span&gt;exit(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sock&lt;span style="color:#f92672"&gt;.&lt;/span&gt;listen(&lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 设置最大允许连接数，各连接和 Server 的通信遵循 FIFO 原则&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;: &lt;span style="color:#75715e"&gt;# 循环轮询 Socket 状态，等待访问&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; conn, addr &lt;span style="color:#f92672"&gt;=&lt;/span&gt; sock&lt;span style="color:#f92672"&gt;.&lt;/span&gt;accept()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; conn&lt;span style="color:#f92672"&gt;.&lt;/span&gt;settimeout(&lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 如果请求超过 10 秒没有完成，就终止操作&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 如果要同时处理多个连接，则下面的语句块应该用多线程来处理&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;: &lt;span style="color:#75715e"&gt;# 获得一个连接，然后开始循环处理这个连接发送的信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; data &lt;span style="color:#f92672"&gt;=&lt;/span&gt; conn&lt;span style="color:#f92672"&gt;.&lt;/span&gt;recv(&lt;span style="color:#ae81ff"&gt;1024&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#39;Get value &amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; data, end&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; data:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#39;Exit Server&amp;#39;&lt;/span&gt;, end&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; conn&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sendall(&lt;span style="color:#e6db74"&gt;&amp;#39;OK&amp;#39;&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 返回数据&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; socket&lt;span style="color:#f92672"&gt;.&lt;/span&gt;timeout: &lt;span style="color:#75715e"&gt;# 建立连接后，该连接在设定的时间内没有数据发来，就会引发超时&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#39;Time out&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; conn&lt;span style="color:#f92672"&gt;.&lt;/span&gt;close() &lt;span style="color:#75715e"&gt;# 当一个连接监听循环退出后，连接可以关掉&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sock&lt;span style="color:#f92672"&gt;.&lt;/span&gt;close()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;conn, addr = sock.accept()&lt;/p&gt;
&lt;p&gt;调用 &lt;code&gt;accept()&lt;/code&gt; 时，Socket 会进入waiting状态。客户端请求连接时，方法建立连接并返回服务器。&lt;code&gt;accept()&lt;/code&gt; 返回一个含有两个元素的元组 (conn, addr)。第一个元素 conn 是新的 Socket 对象，服务器必须通过它与客户端通信；第二个元素 addr 是客户端的 IP 地址及端口。&lt;/p&gt;
&lt;p&gt;data = conn.recv(1024)&lt;/p&gt;
&lt;p&gt;接下来是处理阶段，服务器和客户端通过 &lt;code&gt;send()&lt;/code&gt; 和 &lt;code&gt;recv()&lt;/code&gt; 通信（传输数据）。
服务器调用 &lt;code&gt;send()&lt;/code&gt;，并采用字符串形式向客户端发送信息，&lt;code&gt;send()&lt;/code&gt; 返回已发送的字符个数。
服务器调用 &lt;code&gt;recv()&lt;/code&gt; 从客户端接收信息。调用 &lt;code&gt;recv()&lt;/code&gt; 时，服务器必须指定一个整数，它对应于可通过本次方法调用来接收的最大数据量。&lt;code&gt;recv()&lt;/code&gt; 在接收数据时会进入blocked状态，最后返回一个字符串，用它表示收到的数据。如果发送的数据量超过了 &lt;code&gt;recv()&lt;/code&gt; 所允许的，数据会被截短。多余的数据将缓冲于接收端，以后调用 &lt;code&gt;recv()&lt;/code&gt; 时，会继续读剩余的字节，如果有多余的数据会从缓冲区删除（以及自上次调用 &lt;code&gt;recv()&lt;/code&gt; 以来，客户端可能发送的其它任何数据）。传输结束，服务器调用 Socket 的 &lt;code&gt;close()&lt;/code&gt; 关闭连接。&lt;/p&gt;
&lt;h3 id="四从-tcp-连接的视角看-socket-过程"&gt;&lt;a href="#%e5%9b%9b%e4%bb%8e-tcp-%e8%bf%9e%e6%8e%a5%e7%9a%84%e8%a7%86%e8%a7%92%e7%9c%8b-socket-%e8%bf%87%e7%a8%8b" class="header-anchor"&gt;&lt;/a&gt;四、从 TCP 连接的视角看 Socket 过程
&lt;/h3&gt;&lt;h5 id="tcp-三次握手的-socket-过程"&gt;&lt;a href="#tcp-%e4%b8%89%e6%ac%a1%e6%8f%a1%e6%89%8b%e7%9a%84-socket-%e8%bf%87%e7%a8%8b" class="header-anchor"&gt;&lt;/a&gt;TCP 三次握手的 Socket 过程
&lt;/h5&gt;&lt;p&gt;&lt;img loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="../../images/2025/02/image-5.png"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;服务器调用 &lt;code&gt;socket()&lt;/code&gt;、&lt;code&gt;bind()&lt;/code&gt;、&lt;code&gt;listen()&lt;/code&gt; 完成初始化后，调用 &lt;code&gt;accept()&lt;/code&gt; 阻塞等待；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;客户端 Socket 对象调用 &lt;code&gt;connect()&lt;/code&gt; 向服务器发送了一个 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;客户端收到服务端发送的应答之后，从 &lt;code&gt;connect()&lt;/code&gt; 返回，再发送一个 ACK 给服务器；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;服务器 Socket 对象接收客户端第三次握手 ACK 确认，此时服务端从 &lt;code&gt;accept()&lt;/code&gt; 返回，建立连接。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;接下来就是两个端的连接对象互相收发数据。&lt;/p&gt;
&lt;h5 id="tcp-四次挥手的-socket-过程"&gt;&lt;a href="#tcp-%e5%9b%9b%e6%ac%a1%e6%8c%a5%e6%89%8b%e7%9a%84-socket-%e8%bf%87%e7%a8%8b" class="header-anchor"&gt;&lt;/a&gt;TCP 四次挥手的 Socket 过程
&lt;/h5&gt;&lt;p&gt;&lt;img loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="../../images/2025/02/image-6.png"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;某个应用进程调用 &lt;code&gt;close()&lt;/code&gt; 主动关闭，发送一个 FIN；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;另一端接收到 FIN 后被动执行关闭，并发送 ACK 确认；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;之后被动执行关闭的应用进程调用 &lt;code&gt;close()&lt;/code&gt; 关闭 Socket，并也发送一个 FIN；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;接收到这个 FIN 的一端向另一端 ACK 确认。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;说明：上面的服务端代码只有处理完一个客户端请求才会去处理下一个客户端的请求，这样的服务器处理能力很弱，而实际中服务器都需要有并发处理能力，为了达到并发处理，服务器就需要 fork 一个新的进程或者线程去处理请求。&lt;/p&gt;
&lt;h3 id="五socket常用函数接口及其原理"&gt;&lt;a href="#%e4%ba%94socket%e5%b8%b8%e7%94%a8%e5%87%bd%e6%95%b0%e6%8e%a5%e5%8f%a3%e5%8f%8a%e5%85%b6%e5%8e%9f%e7%90%86" class="header-anchor"&gt;&lt;/a&gt;五、Socket常用函数接口及其原理
&lt;/h3&gt;&lt;p&gt;图解socket函数：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="../../images/2025/02/image-7.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="../../images/2025/02/image-8.png"&gt;&lt;/p&gt;
&lt;h3 id="参考文章"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e6%96%87%e7%ab%a0" class="header-anchor"&gt;&lt;/a&gt;参考文章
&lt;/h3&gt;&lt;p&gt;&lt;a class="link" href="https://www.cnblogs.com/myitnews/p/13790067.html" target="_blank" rel="noopener"
 &gt;Socket（套接字）通信原理 - codedot - 博客园&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://blog.csdn.net/yizhiniu_xuyw/article/details/114083796" target="_blank" rel="noopener"
 &gt;Socket技术详解（一篇就够了）-CSDN博客&lt;/a&gt;&lt;/p&gt;</description></item></channel></rss>