<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>慕雪的寒舍</title>
  
  <subtitle>雪下了一夜</subtitle>
  <link href="https://blog.musnow.top/atom.xml" rel="self"/>
  
  <link href="https://blog.musnow.top/"/>
  <updated>2023-04-03T01:23:39.649Z</updated>
  <id>https://blog.musnow.top/</id>
  
  <author>
    <name>muxue</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>【日志】又拍云CDN被恶意请求</title>
    <link href="https://blog.musnow.top/posts/1758576550/"/>
    <id>https://blog.musnow.top/posts/1758576550/</id>
    <published>2023-03-30T01:35:28.000Z</published>
    <updated>2023-04-03T01:23:39.649Z</updated>
    
    <content type="html"><![CDATA[<p>自一周之前起，图床CDN遭到了恶意请求</p><span id="more"></span><h1 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h1><p>请求来自同一个ip，请求的是同一张图片，每秒请求15次，高峰期都是在凌晨。最终导致日请求量上了100w，日均扣费4元😒</p><p>在我禁掉他的移动ip后，他换了另外一个电信的ip继续请求同一张图片。</p><p>因为两个ip的属地相同，都是<strong>家宽</strong>，无法被ping，所以我认为是同一个人！</p><p>我发现的已经有点晚了，对方已经恶意请求了将近一周。虽然又拍云OSS配置了IP访问限制，但咨询了客服后，得知即便被403挡掉的请求（在日志中，一个小时就有4w个请求被403阻止），依旧会计入https费用。</p><p>在此提醒大家，又拍云的日扣费提醒最低只能设置为10元，<strong>建议隔1-2天上控制台查看一下统计信息</strong>，及时发现并阻止对方的恶意请求</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">禁止他的ip，删除源站图片，配置404，严重的时候直接临时关闭域名解析</span><br></pre></td></tr></table></figure><h1 id="截图"><a href="#截图" class="headerlink" title="截图"></a>截图</h1><p>下图中请求迅速降低是因为我关闭了该cdn的域名解析</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6424f0c047909.png"                      alt="image-20230330101520677"                ></p><p>昨天上午就请求了44w次，3.28一整天请求了101w次</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6424f2a191d25.png"                      alt="image-20230330102329284"                ></p><p>扣费，心疼啊！</p><p><img                       lazyload                     alt="image"                     data-src="https://img2.musnow.top/i/2023/03/642291535bfa1.png"                      alt="1679987022014.png"                ></p><p>个人觉得https请求如果被403了还进行相同价格的扣费是不太合理的（</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6424f0dfc9b88.png"                      alt="image-20230330101559503"                ></p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6424f271debb8.png"                      alt="image-20230330102241589"                ></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;自一周之前起，图床CDN遭到了恶意请求&lt;/p&gt;</summary>
    
    
    
    <category term="差生文具多" scheme="https://blog.musnow.top/categories/%E5%B7%AE%E7%94%9F%E6%96%87%E5%85%B7%E5%A4%9A/"/>
    
    
    <category term="图床" scheme="https://blog.musnow.top/tags/%E5%9B%BE%E5%BA%8A/"/>
    
  </entry>
  
  <entry>
    <title>【Python】CentOS安装Python3.10</title>
    <link href="https://blog.musnow.top/posts/847195816/"/>
    <id>https://blog.musnow.top/posts/847195816/</id>
    <published>2023-03-25T14:21:00.000Z</published>
    <updated>2023-04-03T01:23:39.561Z</updated>
    
    <content type="html"><![CDATA[<p>CentOS安装Python3.10</p><span id="more"></span><p>将所有命令里面python对应的版本号替换，就可以安装其他版本的python</p><h1 id="1-安装编译所需的依赖"><a href="#1-安装编译所需的依赖" class="headerlink" title="1.安装编译所需的依赖"></a>1.安装编译所需的依赖</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">sudo yum -y update</span><br><span class="line">sudo yum -y install openssl-devel libffi-devel bzip2-devel</span><br><span class="line">sudo yum -y groupinstall <span class="string">&quot;Development Tools&quot;</span></span><br></pre></td></tr></table></figure><h1 id="2-安装-Python-3-10-必需的-openssl-gt-x3D-1-1-1"><a href="#2-安装-Python-3-10-必需的-openssl-gt-x3D-1-1-1" class="headerlink" title="2. 安装 Python 3.10 必需的 openssl &gt;&#x3D; 1.1.1"></a>2. 安装 Python 3.10 必需的 openssl &gt;&#x3D; 1.1.1</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 下载</span></span><br><span class="line">wget https://www.openssl.org/source/openssl-1.1.1q.tar.gz --no-check-certificate</span><br><span class="line"><span class="comment"># 解压</span></span><br><span class="line">tar zxf openssl-1.1.1q.tar.gz</span><br><span class="line"><span class="comment"># 安装</span></span><br><span class="line"><span class="built_in">cd</span> openssl-1.1.1q</span><br><span class="line">./config --prefix=/usr/local/openssl-1.1.1</span><br><span class="line">sudo make &amp;&amp; sudo make install</span><br></pre></td></tr></table></figure><p>安装完毕这个之后，请检查环境变量中的CA证书是否正常，否则python运行时会报错。（主要是调用api时会报错）</p><p><a href="https://blog.musnow.top/posts/3348986841/">https://blog.musnow.top/posts/3348986841/</a></p><h1 id="3-安装-Python-3-10"><a href="#3-安装-Python-3-10" class="headerlink" title="3. 安装 Python 3.10"></a>3. 安装 Python 3.10</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 下载</span></span><br><span class="line">wget https://www.python.org/ftp/python/3.10.5/Python-3.10.5.tgz</span><br><span class="line"><span class="comment"># 解压</span></span><br><span class="line">tar zxf Python-3.10.5.tgz </span><br><span class="line"><span class="comment"># 安装</span></span><br><span class="line"><span class="built_in">cd</span> Python-3.10.5</span><br><span class="line">./configure --enable-optimizations --with-openssl=/usr/local/openssl-1.1.1 --with-openssl-rpath=auto</span><br><span class="line">sudo make altinstall</span><br></pre></td></tr></table></figure><blockquote><p><font color=Red>慕雪的警告</font>: 为了避免自己安装的python影响系统yum安装的使用，请不要将软连接直接连接成python</p><p>比如我们安装的是<code>python3.10.5</code>，那软链接的时候，请链接<code>python3.10</code>命令，而不是链接python命令！</p><p>当前官方提供的python3.10安装包，会自动链接<code>python3.10</code>和<code>pip3.10</code>，理论上无需手动操作!</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641f09026e6a7.png"                      alt="image-20230325224521144"                ></p></blockquote><p>原文后半部分都是建立软连接的教程，为了避免引发误解，故此删除</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">版权声明：本文为CSDN博主「一_二」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。</span><br><span class="line">原文链接：https://blog.csdn.net/zltliqi/article/details/126449120</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;CentOS安装Python3.10&lt;/p&gt;</summary>
    
    
    
    <category term="速通Python" scheme="https://blog.musnow.top/categories/%E9%80%9F%E9%80%9APython/"/>
    
    
    <category term="Python" scheme="https://blog.musnow.top/tags/Python/"/>
    
    <category term="CentOS" scheme="https://blog.musnow.top/tags/CentOS/"/>
    
  </entry>
  
  <entry>
    <title>【Python】设置logging日志时间的时区</title>
    <link href="https://blog.musnow.top/posts/1349268248/"/>
    <id>https://blog.musnow.top/posts/1349268248/</id>
    <published>2023-03-24T12:21:00.000Z</published>
    <updated>2023-04-03T01:23:39.561Z</updated>
    
    <content type="html"><![CDATA[<p>设置logging日志时间的时区</p><span id="more"></span><h1 id="1-问题"><a href="#1-问题" class="headerlink" title="1.问题"></a>1.问题</h1><p>最近我的几个python-bot项目都放在了replit上面白嫖运行，于是遇到了一个很让人头疼的问题。replit的终端采用的是UTC标准时间，比北京时间晚了8h，而我之前的项目，全都是用print来打印信息的（当时还不会用logging）</p><p>为了知道日志输出的时间，我在每一个print里面都调用了一个获取当前时间的函数</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">getTime</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;获取当前时间，格式为 `23-01-01 00:00:00`&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">return</span> time.strftime(<span class="string">&quot;%y-%m-%d %H:%M:%S&quot;</span>, time.localtime())</span><br></pre></td></tr></table></figure><p>这个函数会返回系统的当前时间。对于linux、windows这些时区正确的机器而言，输出的都是北京时间，没有什么问题。</p><blockquote><p>如果你的linux机器（特别是用docker安装的）时区有问题，可以参考这篇文章来解决: <a href="https://blog.musnow.top/posts/809108065/">点我</a></p></blockquote><p>但是到replit上面就不一样了，即便你在<code>.replit</code>文件中添加了时区的环境变量，其依旧无法让<code>localtime</code>返回北京时间</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">TZ = &quot;Asia/Shanghai&quot;</span><br></pre></td></tr></table></figure><p>shell中输入date查看当前时间，依旧晚8h</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641d967c52c54.png"                      alt="image-20230324202422995"                ></p><p>我已经尝试过了，replit这个并不能像 <a href="https://blog.musnow.top/posts/809108065/">linux修改时区</a> 一样修改系统时间。</p><p>我们就只能从python下手来解决这个问题了</p><h1 id="2-解决"><a href="#2-解决" class="headerlink" title="2.解决"></a>2.解决</h1><p>首先是<code>getTime</code>函数如和强制返回北京时间，我们需要将time改成使用datetime</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime</span><br><span class="line"><span class="keyword">from</span> zoneinfo <span class="keyword">import</span> ZoneInfo <span class="comment"># 在Py3.9以后是标准库</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getTime</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;获取当前时间，格式为 `23-01-01 00:00:00`&quot;&quot;&quot;</span></span><br><span class="line">    a = datetime.now(ZoneInfo(<span class="string">&#x27;Asia/Shanghai&#x27;</span>)) <span class="comment"># 返回北京时间</span></span><br><span class="line">    <span class="keyword">return</span> a.strftime(<span class="string">&#x27;%y-%m-%d %H:%M:%S&#x27;</span>)</span><br></pre></td></tr></table></figure><p>针对logging模块，也需要进行特殊处理</p><blockquote><p>参考 <a class="link"   href="https://zhuanlan.zhihu.com/p/304672864" >https://zhuanlan.zhihu.com/p/304672864<i class="fas fa-external-link-alt"></i></a></p></blockquote><p><code>logging</code> 模块中 <code>asctime</code> 的时间使用的是<code>time.localtime()</code>返回的时间 </p><p>文档：<a href="%5Bhttps://docs.python.org/3/library/logging.html#logging.Formatter.formatTime%5D(https://link.zhihu.com/?target=https://docs.python.org/3/library/logging.html#logging.Formatter.formatTime)">library&#x2F;logging</a>，<a class="link"   href="https://docs.python.org/3/library/time.html#time.localtime" >library&#x2F;time<i class="fas fa-external-link-alt"></i></a></p><p>而 <code>time.localtime()</code> 又是 ，使用的 <code>time.time()</code> 返回的时间，而 <code>time.time()</code> 返回的是 UTC 时间，即从 1970-1-1 00:00:00 到现在的秒数，由于时区不对，所以没有转成中国所在的 UTC+8 时区。</p><p>经过 stackoverflow 上<a class="link"   href="https://stackoverflow.com/a/6321221/6751201" >这篇回答<i class="fas fa-external-link-alt"></i></a>的提醒，可以设定 <code>logging.Formatter.converter</code> 来转换时间，但是回答里是转换成 GMT 时间，我们需要自己重写一个函数来加上 UTC 偏移，返回正确的时间，传给 <code>logging.Formatter.converter</code></p><h2 id="修改logging-Formatter-converter"><a href="#修改logging-Formatter-converter" class="headerlink" title="修改logging.Formatter.converter"></a>修改logging.Formatter.converter</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> logging</span><br><span class="line"><span class="keyword">import</span> datetime</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">beijing</span>(<span class="params">sec, what</span>):</span><br><span class="line">    beijing_time = datetime.datetime.now() + datetime.timedelta(hours=<span class="number">8</span>)</span><br><span class="line">    <span class="keyword">return</span> beijing_time.timetuple()</span><br><span class="line"></span><br><span class="line">logging.Formatter.converter = beijing</span><br><span class="line"></span><br><span class="line"><span class="comment"># 只打印info以上的日志（debug低于info）</span></span><br><span class="line">logging.basicConfig(level=logging.INFO,</span><br><span class="line">                    <span class="built_in">format</span>=<span class="string">&quot;[%(asctime)s] %(levelname)s:%(filename)s:%(funcName)s:%(lineno)d | %(message)s&quot;</span>,</span><br><span class="line">                    datefmt=<span class="string">&quot;%y-%m-%d %H:%M:%S&quot;</span>)</span><br></pre></td></tr></table></figure><p>还可以使用如下办法</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> logging</span><br><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime</span><br><span class="line"><span class="keyword">from</span> zoneinfo <span class="keyword">import</span> ZoneInfo</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">beijing</span>(<span class="params">sec, what</span>):</span><br><span class="line">    beijing_time = datetime.now(ZoneInfo(<span class="string">&#x27;Asia/Shanghai&#x27;</span>)) <span class="comment"># 返回北京时间</span></span><br><span class="line">    <span class="keyword">return</span> beijing_time.timetuple()</span><br><span class="line"><span class="comment"># 日志时间改为北京时间</span></span><br><span class="line">logging.Formatter.converter = beijing</span><br></pre></td></tr></table></figure><p>两种办法都可以的！</p><h1 id="3-验证"><a href="#3-验证" class="headerlink" title="3.验证"></a>3.验证</h1><p>可以看到，修改之前，log输出的时间离正确的时间差8h</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641d9882be464.png"                      alt="image-20230324203308707"                ></p><p>修改了之后，输出的时间就对了！</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641d98b72917a.png"                      alt="image-20230324203401212"                ></p><p>不过，修改这个也需要看你的项目<strong>面向的对象</strong>是谁。因为我写的都是kook平台的机器人，kook作为国内平台，压根没有几个歪果仁用，所以直接修改，将程序里面所有需要涉及到<strong>可读时间</strong>的都指定为北京时间。</p><p>如果你的项目是discord平台的机器人，那最好还是保留原状吧！</p><p>需要注意的是，<strong>时区的设置</strong>影响的是可读时间，即<code>23-03-24 20:14:48</code>这样的时间；和时间戳并没有关系。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;设置logging日志时间的时区&lt;/p&gt;</summary>
    
    
    
    <category term="速通Python" scheme="https://blog.musnow.top/categories/%E9%80%9F%E9%80%9APython/"/>
    
    
    <category term="Python" scheme="https://blog.musnow.top/tags/Python/"/>
    
  </entry>
  
  <entry>
    <title>【教程】netlify或vercel完全自动部署hexo</title>
    <link href="https://blog.musnow.top/posts/3433631517/"/>
    <id>https://blog.musnow.top/posts/3433631517/</id>
    <published>2023-03-22T15:12:14.000Z</published>
    <updated>2023-04-03T01:23:39.566Z</updated>
    
    <content type="html"><![CDATA[<p>教你用netlify或vercel完全自动部署hexo</p><span id="more"></span><h1 id="1-完全自动？"><a href="#1-完全自动？" class="headerlink" title="1.完全自动？"></a>1.完全自动？</h1><p>在 <a href="https://blog.musnow.top/posts/138502038/">【教程】hexo搭建博客+keep主题美化</a> 里面，就提到过使用netlify或者vercel来部署hexo。但是那个是部署的静态页面</p><ul><li>部署静态页面仓库，类似<code>github pages</code></li><li>直接帮你从配置文件部署，只需要<code>git push</code>更新仓库</li></ul><p>之前的博客中，提到的是第一种方式。本文中是第二种方式的教程。直接从配置文件部署，有以下几大好处</p><ul><li>任何地方，只要有网，你都可以通过修改github仓库中的<code>source/_post</code>里面的md文件，实现更新博客</li><li>如果你更换了电脑，无须再为了hexo配置<code>node.js</code>等环境。这对于不写前端的我来说很棒</li></ul><p>话不多说，直接开始！</p><h1 id="2-github仓库"><a href="#2-github仓库" class="headerlink" title="2.github仓库"></a>2.github仓库</h1><p>要做到这一点，先需要准备一个存放hexo配置文件的仓库。本地的如下文件夹，就是hexo的配置文件</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641c6eaabf3b5.png"                      alt="image-20230323232211389"                ></p><p>准备配置仓库，需要保证<code>node_modules</code>和<code>public</code>文件夹不要上传到仓库中，否则后续部署会失败。这是我的gitignore文件，仅供参考</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">*.deploy_git/</span><br><span class="line">db.json</span><br><span class="line">*public/</span><br><span class="line">.git/</span><br><span class="line">node_modules/</span><br></pre></td></tr></table></figure><p>最终仓库的文件层级如下，为了防止文章被过容易的盗走（因为仓库里面有md文件，盗文章太容易了。虽然html也能被爬走），我将其设置为私有仓库了</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641c6f01d01c9.png"                      alt="image-20230323232345276"                ></p><p>有了这个仓库之后，就可以进行后续操作了</p><h1 id="3-netlify"><a href="#3-netlify" class="headerlink" title="3.netlify"></a>3.netlify</h1><p>先新增一个site，选择从<code>existing project</code>中import</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641c6f4c5651c.png"                      alt="image-20230323232500319"                ></p><p>这需要你的netlify账户有github的授权。我是直接用github登录的，所以已经有授权了</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641c6fa6bf61b.png"                      alt="image-20230323232630595"                ></p><p>选择hexo配置文件的仓库</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641c6fb3416a2.png"                      alt="image-20230323232643226"                ></p><p>build命令修改为<code>npm run build</code>，其余不动</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641c6fcfaa82f.png"                      alt="image-20230323232711590"                ></p><p>到这里就可以deploy了！就这么简单！过一会成功了之后，就会显示出你的站点链接了！</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641c6fff29cf8.png"                      alt="image-20230323232759075"                ></p><p>需要注意的是，nelify默认提供的二级域名，国内极有可能无法访问</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641c7027a1efe.png"                      alt="image-20230323232839494"                ></p><p>不用担心，你只需要在<code>domain settings</code>里面添加你自己的域名，访问速度就能起飞了</p><h1 id="4-vercel"><a href="#4-vercel" class="headerlink" title="4.vercel"></a>4.vercel</h1><p>和netlify的操作很像，选择git仓库</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641c70aad64e6.png"                      alt="image-20230323233050652"                ></p><p>模板选择hexo，其他设置都没必要动</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641c70ba7d75f.png"                      alt="image-20230323233106194"                ></p><p>vercel会开始部署</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641c70cc83d7d.png"                      alt="image-20230323233124303"                ></p><p>如果没有报错，那就是部署成功了（否则请检查你的仓库里面hexo配置文件是不是少了或者多了什么）</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641c70f32af50.png"                      alt="image-20230323233202981"                ></p><p>完美，也部署好了！</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641c70fc16f81.png"                      alt="image-20230323233211667"                ></p><p>vercel提供的子域名也有无法访问的问题，同样的，绑定了自己的域名就能在国内访问了。</p><h1 id="The-end"><a href="#The-end" class="headerlink" title="The end"></a>The end</h1><p>有问题可以在评论区提出~</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;教你用netlify或vercel完全自动部署hexo&lt;/p&gt;</summary>
    
    
    
    <category term="差生文具多" scheme="https://blog.musnow.top/categories/%E5%B7%AE%E7%94%9F%E6%96%87%E5%85%B7%E5%A4%9A/"/>
    
    
    <category term="Hexo" scheme="https://blog.musnow.top/tags/Hexo/"/>
    
  </entry>
  
  <entry>
    <title>【win】windows初次开机和使用的注意事项</title>
    <link href="https://blog.musnow.top/posts/1529306954/"/>
    <id>https://blog.musnow.top/posts/1529306954/</id>
    <published>2023-03-19T14:51:31.000Z</published>
    <updated>2023-04-03T01:23:39.651Z</updated>
    
    <content type="html"><![CDATA[<p>首次开机和使用一台windows电脑需要注意的那些事</p><span id="more"></span><p>本文所言仅为本人个人观点，且只作为建议和图一乐的素材。电脑归根结底只是个工具，不同人使用习惯不同，按自己熟悉的来用即可。大可无需因为本文而改变你使用电脑的习惯。</p><h1 id="1-跳过联网"><a href="#1-跳过联网" class="headerlink" title="1.跳过联网"></a>1.跳过联网</h1><p>windows11的年代，这已是一个过去式了，还是记录一下吧。</p><p>给不知道的人说一下，大部分笔记本厂家的策略，都是windows激活之后，就不允许退款了。</p><p>在windows10的时代，首次开机的时候，在联网阶段，会有一个跳过联网的选项，我们只需要选择这个选项，就能跳过联网，继续我们的验机过程。这样，发现了电脑有各种各样的的问题或者不满意，还能以电脑没有激活为筹码，直接七天无理由退货。</p><p>可微软的阿三工程师不知道怎么想的，windows11一出来，直接把这个按钮干没了。</p><p>起初，还能通过快捷键or命令行的方式，强行跳过联网，但现在，似乎都不行了。</p><blockquote><p>可能还有跳过联网的方式，但相比win10的一个按钮，显然任何方法都麻烦多了，对小白也不够友好</p></blockquote><p>哎，谁知道微软是怎么想的呢？</p><h1 id="2-用户名"><a href="#2-用户名" class="headerlink" title="2.用户名"></a>2.用户名</h1><p>刚开始使用电脑的时候，电脑的用户名<strong>一定一定一定</strong>要设置成英文的！哪怕你设置成拼音或一串数字，都不要设置成非英文的其他语言！</p><h2 id="软件安装路径不要有中文"><a href="#软件安装路径不要有中文" class="headerlink" title="软件安装路径不要有中文"></a>软件安装路径不要有中文</h2><p>虽然现在，大部分软件对中文的支持都足够友好，但依旧有部分软件（以游戏据多）不支持中文路径的安装目录！</p><p>说人话就是，如果你想安装这些软件，它要求你的安装目录是全英文的</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">C:\Progarm Files\test\</span><br></pre></td></tr></table></figure><p>如果你的安装路径里面带有中文，可能安装不会报错，但是软件打不开、崩溃、报错误，一堆bug就来了</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">C:\Progarm Files\你好\</span><br></pre></td></tr></table></figure><p>为了避免这种bug，我们安装软件的时候，最好都要选择全英文的安装路径。</p><p>多一事不如少一事。你可以赌对很多个支持中文路径的软件，但只要来一个你需要的而不支持中文安装路径软件，就足够让你抓狂。如果你使用的是某些小众软件，恐怕报错贴到百度上都查不到为什么。</p><h2 id="那这又和用户名有啥关系呢？"><a href="#那这又和用户名有啥关系呢？" class="headerlink" title="那这又和用户名有啥关系呢？"></a>那这又和用户名有啥关系呢？</h2><p>windows在初始化的时候，会以你的用户名，在C盘如下路径创建一个文件夹</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">C:\Users\用户名</span><br></pre></td></tr></table></figure><p>注意啊，虽然你打开文件管理器，会发现这个文件夹叫做<code>用户</code>，但实际上他还是英文的，显示为<code>用户</code>是微软对界面做的汉化处理而已。</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6417190ec898e.png"                      alt="image-20230319221542933"                ></p><p>右键这个路径，选择将地址复制成文本，你就会发现用户是汉化处理了</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">C:\Users\moth</span><br></pre></td></tr></table></figure><p>在用户文件夹下，除了会有一些软件的配置文件（如git、vscode、pip）还会有一个隐藏文件夹<code>AppData</code>，这里面存放了大部分软件的缓存文件，甚至有一些软件的安装路径就是在<code>AppData</code>里的！</p><p>如果你的用户名设置成了中文，那很不幸，上面提到的<code>软件安装路径不能有中文</code>的大忌，你将不可避免地遇到了！</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">C:\Users\慕雪</span><br></pre></td></tr></table></figure><p>因为软件会在你的用户目录下创建缓存文件夹，如果一个软件对中文的支持不好，那它就可能因此崩溃！</p><p>在我周边的同学里面，就出现过不少个windows用户名是中文的</p><ul><li>好消息是，目前为止，我们专业学习需要的软件都支持中文路径</li><li>坏消息是，这都大二下了，我们还没有学过一个专业课，还在学基础公共课……来大学快两年了，我还连自己的专业是干嘛的都不知道（事先为何不了解？问就是调剂来的！😒）所用的软件，也不过是编程ide罢了</li></ul><h1 id="3-保持一个良好的文件存放习惯"><a href="#3-保持一个良好的文件存放习惯" class="headerlink" title="3.保持一个良好的文件存放习惯"></a>3.保持一个良好的文件存放习惯</h1><p>可笑的是，这件事，知道的人不需要你告诉他；不在乎的人你告诉他也没用。</p><p>有太多太多的用户（包括我的父母）他们的电脑都中了一个病毒，导致整台电脑只有一个分区，叫做<code>桌面</code></p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/64171ca375563.png"                      alt="image-20230319223059041"                ></p><p>并不是说文件丢桌面不可以，但是丢在桌面上有一个致命的问题：重装的时候，需要格式化C盘。</p><p>windows重装，是对付软件问题的最好方式。重装的时候，大概率是要格式C盘的，而桌面的文件就是存放在C盘上面的。</p><h1 id="未完待续"><a href="#未完待续" class="headerlink" title="未完待续"></a>未完待续</h1><p>想起来啥了就给本文加点料</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;首次开机和使用一台windows电脑需要注意的那些事&lt;/p&gt;</summary>
    
    
    
    <category term="电脑使用小贴士" scheme="https://blog.musnow.top/categories/%E7%94%B5%E8%84%91%E4%BD%BF%E7%94%A8%E5%B0%8F%E8%B4%B4%E5%A3%AB/"/>
    
    
    <category term="windows" scheme="https://blog.musnow.top/tags/windows/"/>
    
  </entry>
  
  <entry>
    <title>【win】windows磁盘管理分区,实现整数容量</title>
    <link href="https://blog.musnow.top/posts/2962575673/"/>
    <id>https://blog.musnow.top/posts/2962575673/</id>
    <published>2023-03-19T13:50:31.000Z</published>
    <updated>2023-04-03T01:23:39.651Z</updated>
    
    <content type="html"><![CDATA[<p>windows磁盘管理分盘分出整数容量</p><span id="more"></span><h1 id="1-问题"><a href="#1-问题" class="headerlink" title="1.问题"></a>1.问题</h1><p>不知道各位在使用windows电脑分区的时候，有没有遇到一个抓狂的问题：那就是windows的分区总是分不出整数！</p><p>不是<code>299.9gb</code>，就是<code>300.1gb</code></p><p>对于强迫症而言，看着那多出来的小数点，别提有多难受了！</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/64171303a3dfe.png"                      alt="image-20230319214854921"                ></p><h1 id="2-解决"><a href="#2-解决" class="headerlink" title="2.解决"></a>2.解决</h1><p>于是，我就百度了一下，到底要怎么设置分区的容量，才能让它分出一个整数gb的盘来？</p><p>最后，我查到了下面这个办法</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">想要整数GB的分区，只需要在原数字上+1就行了</span><br><span class="line">N*1024+1</span><br></pre></td></tr></table></figure><p>比如，我想要把c盘分出去70GB作为数据盘，那我应该在windows磁盘管理<strong>压缩卷</strong>里设置的数字如下</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">70*1024+1</span><br></pre></td></tr></table></figure><p>看，完美的70gb，强迫症有救了！</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/64171303c642e.png"                      alt="image-20230319214825662"                ></p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/64171331ba1d7.png"                      alt="image-20230319215041939"                ></p><p>关注本寒舍，分享更多windows电脑使用的小tips！</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;windows磁盘管理分盘分出整数容量&lt;/p&gt;</summary>
    
    
    
    <category term="电脑使用小贴士" scheme="https://blog.musnow.top/categories/%E7%94%B5%E8%84%91%E4%BD%BF%E7%94%A8%E5%B0%8F%E8%B4%B4%E5%A3%AB/"/>
    
    
    <category term="windows" scheme="https://blog.musnow.top/tags/windows/"/>
    
  </entry>
  
  <entry>
    <title>【Linux】udp | tcp | 协议详解</title>
    <link href="https://blog.musnow.top/posts/4287572457/"/>
    <id>https://blog.musnow.top/posts/4287572457/</id>
    <published>2023-03-17T07:30:55.000Z</published>
    <updated>2023-04-03T01:23:39.556Z</updated>
    
    <content type="html"><![CDATA[<p>本文带你详细了解tcp协议的相关知识</p><span id="more"></span><blockquote><p>本文中部分截图为手写，字丑见谅</p></blockquote><h1 id="1-linux下常用网络命令"><a href="#1-linux下常用网络命令" class="headerlink" title="1.linux下常用网络命令"></a>1.linux下常用网络命令</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cat</span> /etc/servcies <span class="comment"># 系统常用服务和端口</span></span><br></pre></td></tr></table></figure><p>我们自己写网络服务器进程时，绑定的端口不能和系统端口冲突。尽量绑定1024以上的端口，推荐绑定不常用的5位数端口。</p><p>绑定低于1024的端口，会出现权限不足的报错</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ ./tcpServer 100</span><br><span class="line">DEBUG | 1679473830 | muxue | socket create success: 3</span><br><span class="line">FATAL | 1679473830 | muxue | bind: Permission denied:3</span><br></pre></td></tr></table></figure><h2 id="1-1-netstat命令"><a href="#1-1-netstat命令" class="headerlink" title="1.1 netstat命令"></a>1.1 netstat命令</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">netstat</span><br><span class="line">netstat -l <span class="comment"># 只列出listen状态服务</span></span><br><span class="line">netstat -n <span class="comment"># 将显示的信息用数字（id）代替</span></span><br><span class="line">netstat -p <span class="comment"># 显示端口和进程pid的关联</span></span><br><span class="line">netstat -t <span class="comment"># tcp</span></span><br><span class="line">netstat -u <span class="comment"># udp</span></span><br><span class="line">netstat -a <span class="comment"># 显示所有服务</span></span><br></pre></td></tr></table></figure><h2 id="1-2-pidof"><a href="#1-2-pidof" class="headerlink" title="1.2 pidof"></a>1.2 pidof</h2><p>获取某个进程名的进程pid</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pidof 进程名</span><br></pre></td></tr></table></figure><p>比如我想查看<code>sshd</code>的进程id</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ pidof sshd</span><br><span class="line">20706 20703 10775 6067 6009 3339 3338 3272 3269 1340</span><br></pre></td></tr></table></figure><h1 id="2-udp协议"><a href="#2-udp协议" class="headerlink" title="2.udp协议"></a>2.udp协议</h1><p>一下为udp报文格式的结构图</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6414432a16b9f.png"                      alt="image-20230317183835725"                ></p><p>udp采用了定长报文，这也是udp <code>面向数据报</code> 的</p><ul><li>udp采用16位作为ip+端口的存放，源端口和目的端口用于数据的<strong>解包分用</strong>（系统需要知道当前的数据包应该丢给上层的哪一个端口）</li><li>16位udp长度，表示整个数据报 <code>udp首部+udp数据</code> 的最大长度</li><li>16位校验和用于校验报文是否出现错误。如果校验和出错，就会直接丢弃报文</li></ul><p>由于udp的长度标志位只有16位，所以一个udp报文能传输的最大数据是<code>64kb</code> ( 2<sup>2 </sup>)</p><p>如果需要用udp传输大于<code>64kb</code>的数据，则需要在<strong>应用层</strong>进行拆分，在接收方的<strong>应用层</strong>进行合并。</p><h2 id="2-1-理解报头"><a href="#2-1-理解报头" class="headerlink" title="2.1 理解报头"></a>2.1 理解报头</h2><p>所谓报头，其实就是操作系统内核中的一个C语言的结构体。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//示例，不代表真实情况</span></span><br><span class="line"><span class="comment">//udp报头采用了位段</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">udp_hdr</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="type">unsigned</span> <span class="type">int</span> src_port:<span class="number">16</span>;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">int</span> dst_port:<span class="number">16</span>;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">int</span> udp_len:<span class="number">16</span>;</span><br><span class="line">    <span class="type">unsigned</span> <span class="type">int</span> udp_check:<span class="number">16</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>添加报头的本质，其实就是给数据的头部添加上一个<code>struct udp_hdr</code>结构体；</p><p>而解包的时候，也是将指针移动固定长度（8个字节）的空间，将指针强转为<code>struct udp_hdr</code>，即获取到了当前报文的udp报头</p><h2 id="2-2-udp的特点"><a href="#2-2-udp的特点" class="headerlink" title="2.2 udp的特点"></a>2.2 udp的特点</h2><p>udp传输的过程类似于<code>飞鸽传书</code></p><ul><li>无连接：知道对方的<code>ip:端口</code>就能直接传输数据，不需要建立连接</li><li>面向数据报：定长报文，不能灵活控制报文的读取次数和数量<ul><li>一次必须要读取完毕一个完整的udp报文</li><li>假设报文100字节，不能通过10次每次读10字节来获取报文。必须一次读完100字节</li></ul></li><li>不可靠：没有确认机制和重传机制，如果因为各种原因，鸽子在路上出事了，那传输的信息也直接丢失了。udp也不会给应用层返回错误信息。</li></ul><h2 id="2-3-udp缓冲区"><a href="#2-3-udp缓冲区" class="headerlink" title="2.3 udp缓冲区"></a>2.3 udp缓冲区</h2><p>udp支持<code>全双工</code>，udp的socket即可写也可读</p><p>udp没有发送缓冲区，应用层调用<code>sendto</code>会直接将数据交给OS内核（其实就是拷贝），内核再交由网络模组进行后续传输。</p><blockquote><p>由于udp采用了定长报头，其报头较为简单，OS只需要添加上报头即可发送。这个过程很快，所以缓冲区的作用不大。</p></blockquote><p>udp有接收缓冲区，这个接收缓冲区只是一味地接收，并不能保证报文的顺序</p><blockquote><p>因为不保证顺序，所以有可能<strong>乱序</strong>，也是<strong>udp不可靠</strong>的体现</p></blockquote><p>若缓冲区满，新到达的udp数据就会被丢弃。</p><h2 id="2-4-丢包"><a href="#2-4-丢包" class="headerlink" title="2.4 丢包"></a>2.4 丢包</h2><p>一个数据包丢包可能有多种情况</p><ul><li>数据包内容出错（比特位翻转等）</li><li>数据包延迟到达（延迟过久视为丢包）</li><li>数据包在路上被阻塞（到不了）</li><li>数据包在路上由于网络波动而丢失（网络突然抽风了，报文直接不见了）</li></ul><p>udp的报文也是如此，但udp<strong>不可靠</strong>并不是一个贬义词，应该是一个中性词。</p><ul><li>udp不可靠是他的特点，由于udp简单，其不需要进行连接，报头添加的效率快，由此性能消耗小于tcp。</li><li>带来的缺点就是udp不可靠</li></ul><p>在直播场景中，udp的使用很多。同一场直播观看的人数会很多，如果每一个用户都维持一个tcp连接，服务器的负载就太大了。用udp就能直接向该用户广播数据，负载小。</p><h2 id="2-5-基于udp的应用层协议"><a href="#2-5-基于udp的应用层协议" class="headerlink" title="2.5 基于udp的应用层协议"></a>2.5 基于udp的应用层协议</h2><ul><li>NFS: 网络文件系统</li><li>TFTP: 简单文件传输协议</li><li>DHCP: 动态主机配置协议</li><li>BOOTP: 启动协议（用于无盘设备启动）</li><li>DNS: 域名解析协议</li></ul><hr><p>本文往下都是tcp的内容了哦！</p><h1 id="3-tcp协议"><a href="#3-tcp协议" class="headerlink" title="3.tcp协议"></a>3.tcp协议</h1><p>下图为tcp协议报头的一个基本结构图，我们需要了解整个结构，以及每一个部分的作用</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6414766c49315.png"                      alt="image-20230317221627931"                ></p><h2 id="3-1-源和目的端口号"><a href="#3-1-源和目的端口号" class="headerlink" title="3.1 源和目的端口号"></a>3.1 源和目的端口号</h2><p>这部分和udp相同，tcp也需要源端口和目的端口号，以用于找到报文要去的目的地。</p><h2 id="3-2-4位首部长度"><a href="#3-2-4位首部长度" class="headerlink" title="3.2 4位首部长度"></a>3.2 4位首部长度</h2><p>相比于udp的定长报头，tcp采用了不定长的方式。但tcp的报头有<strong>标准的20字节</strong>，所有报头都至少有20字节。</p><p>在这20字节中，有一个<code>4位首部长度</code>，用于标识tcp报文的<strong>真实长度</strong>。</p><p>我们知道，4位二进制可以表示<code>0~15</code>，这不比固定的20字节还少吗？难道说，这4位首部长度标识的是比20字节多余的内容？</p><p>并不是！这4位首部长度的标识是有单位的，每一位实际上代表的是4字节，即tcp报头的最大长度为<code>15*4=60</code>字节。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">由于标准长度也记入4位首部长度，所以4位首部长度的最小值为 0101</span><br></pre></td></tr></table></figure><p>读取tcp报文的时候，只需要先读取20字节，再从这20字节中<strong>取出</strong>4位首部长度，获得报头的实际长度；再重新读取，即获得了<strong>完整</strong>的tcp报头。剩下的部分就是报文携带的数据了（有效载荷）</p><h2 id="3-3-32位序号-x2F-确认序号"><a href="#3-3-32位序号-x2F-确认序号" class="headerlink" title="3.3 32位序号&#x2F;确认序号"></a>3.3 32位序号&#x2F;确认序号</h2><h3 id="3-3-1-如何确认信息被对方收到？"><a href="#3-3-1-如何确认信息被对方收到？" class="headerlink" title="3.3.1 如何确认信息被对方收到？"></a>3.3.1 如何确认信息被对方收到？</h3><blockquote><p>tcp具有确认应答的机制</p></blockquote><p>当我们和对方<strong>微信交流</strong>的时候，怎么样才能确认自己的信息被对方看到了？</p><ul><li>A发 吃饭了吗？</li><li>B回应 吃了</li></ul><p>在这个场景中，只有B给A发出回应，A才能确认自己的消息被B看到了。</p><p>tcp通信也是如此，只有给对方发送的报文收到了对方的<strong>应答</strong>，发送方才能确认自己的报文被对方收到了。</p><p>为此，tcp引入了<code>32位 序号/确认序号</code></p><hr><h3 id="3-3-2-确认应答"><a href="#3-3-2-确认应答" class="headerlink" title="3.3.2 确认应答"></a>3.3.2 确认应答</h3><p>用于确认自己和对方的通信，究竟收到了哪一个报文（序号）以及确认信息发出的顺序。</p><p>比如客户端会向服务器发 <code>吃了吗？吃的什么？好吃吗？晚上想去干什么？</code>，如果没有对报头带上序号，服务器接收到的可能就会是下面这样 <code>好吃吗？晚上想去干什么？吃的什么？吃了吗？</code>，看起来是不是十分怪异？</p><p>所以，为了保证tcp报文的顺序性，以及保证报文被送达到对方。tcp引入了以序号为基础的<code>确认应答</code>机制</p><ul><li>客户端向服务器发送一个报头，并将<strong>序号</strong>设置为1</li><li>服务端收到信息后，回复客户端一个报头，将<strong>确认序号</strong>设置为2（为客户端所发消息的序号<code>+1</code>） </li><li>此时客户端就能确认服务器收到了自己刚刚发出的序号为1的消息</li><li>下次发送消息，客户端会从2号开始发送</li></ul><p>以上是一次通信的过程，如果是多次通信呢？ </p><ul><li>客户端连续向服务器发送了n个消息，服务器应答：1、2、3、4……</li><li>服务器的每次应答会设置<strong>确认序号</strong>，代表n之前的报文被全部收到</li><li>比如假设客户端发送了<code>1-10</code>的报文，而第6个报文出现了丢失，那么服务端就应该设置<code>确认序号</code>为6，代表<strong>6之前</strong>的报文都被正常收到。</li><li>此时客户端发现，明明自己已经都发到10了，服务端还在回应6。这就代表发送过程中，<strong>6号报文丢失了</strong>！于是客户端从6号报文开始，<strong>重发报文</strong>：6、7、8……</li></ul><p>不管是服务端给客户端发信息，还是客户端给服务器发信息，收方都需要对信息进行回应。tcp通信中，<strong>通信双方地位是对等</strong>的！</p><h3 id="3-3-3-为什么有两组确认序号？"><a href="#3-3-3-为什么有两组确认序号？" class="headerlink" title="3.3.3 为什么有两组确认序号？"></a>3.3.3 为什么有两组确认序号？</h3><p>tcp是<strong>全双工</strong>的，通信一方在发送响应信息的同时，可能也会携带新的报文给对方。</p><ul><li>客户端给服务器发了一条消息 <code>吃了吗？</code></li><li>服务器在回复的同时，也带上了新的请求 <code>吃了，你呢？</code> </li><li>服务器的这种策略叫做：<strong>捎带应答</strong></li></ul><p>此时服务端就需要在填充<em>客户端</em>消息的<strong>确认序号</strong>的同时，填充<em>自己</em>所发消息的<strong>序号</strong>。这样才能保证tcp在双向交流中的可靠性！</p><p>所以在tcp报头中，<strong>序号和确认序号</strong>缺一不可！</p><h3 id="3-3-4-没有完美的协议！"><a href="#3-3-4-没有完美的协议！" class="headerlink" title="3.3.4 没有完美的协议！"></a>3.3.4 没有完美的协议！</h3><p>经过上面的过程，我们会发现，总有一条报文，<strong>在收到对方回应之前</strong>，是无法得知对方是否收到信息的。</p><p>这也说明：没有一定可靠的协议！</p><h3 id="3-3-5-按序到达"><a href="#3-3-5-按序到达" class="headerlink" title="3.3.5 按序到达"></a>3.3.5 按序到达</h3><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641543370a163.png"                      alt="image-20230318125057097"                ></p><p>序号除了用于确认应答，还有多个功能</p><ul><li>保证数据的顺序收发问题</li></ul><p>比如一个http的报头，原本的格式应该是下面这样</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">GET / HTTP/1.1</span><br></pre></td></tr></table></figure><p>结果由于传输的过程中乱序了，变成了下面这样</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">HTTP/1.1 GET /</span><br></pre></td></tr></table></figure><p>这种情况，是不能被应用层所正常解析的！数据全都乱了，原本写好的代码也没用了。</p><p>所以，为了避免数据在传输中<code>乱序</code>，tcp的序号就有了新的功能——保证数据的按序到达。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">1.客户端发送了1-5号报文</span><br><span class="line">2.服务端收到了1 3 4 2 5（乱序）</span><br><span class="line">3.服务端在tcp的接收缓冲区中，将报文重排序为1-5</span><br><span class="line">4.将重排序后的正确数据交付给应用层</span><br></pre></td></tr></table></figure><p>但是，如果<strong>只</strong>按顺序来接收数据，那就无法处理<code>优先级</code>问题。<span id="jump">这部分</span>将在后文<strong>6个标记位</strong>详解。</p><p>序号除了可以用于排序，还能用于<code>去重</code>，这部分也将在后文<strong>超时重传</strong>部分解析。</p><h2 id="3-4-16位窗口大小"><a href="#3-4-16位窗口大小" class="headerlink" title="3.4 16位窗口大小"></a>3.4 16位窗口大小</h2><h3 id="3-4-1-发送和接收缓冲区"><a href="#3-4-1-发送和接收缓冲区" class="headerlink" title="3.4.1 发送和接收缓冲区"></a>3.4.1 发送和接收缓冲区</h3><p>tcp同时拥有发送和接收缓冲区。</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6415458533954.png"                      alt="image-20230318130053000"                ></p><p>我们在应用层调用的<code>read/write</code>函数，实际上只是将数据<code>从接收缓冲区中拷贝出来/发送的数据拷贝到发送缓冲区</code>。</p><blockquote><p>如果write包含将数据发送给对方的过程，那么这个函数的调用效率就太低了，影响应用层执行其他代码。</p></blockquote><p>数据并没有被立即送入网络传输，而是由tcp协议自主决定发送数据的长度和发送的时间！这一切，都是由<strong>操作系统</strong>来决定的。这就是为什么tcp又称为<code>传输控制协议</code>！</p><h3 id="3-4-2-接收缓冲区满了咋办"><a href="#3-4-2-接收缓冲区满了咋办" class="headerlink" title="3.4.2 接收缓冲区满了咋办"></a>3.4.2 接收缓冲区满了咋办</h3><p>既然有缓冲区，就肯定会存在缓冲区被写满的问题。</p><ul><li>发送缓冲区满，由操作系统告知应用层，不再往发送缓冲区中写入数据</li><li>接受缓冲区满<ul><li>直接丢弃数据？</li><li>告诉对方，不再给自己发信息？</li></ul></li></ul><p>在实际的tcp收发过程中，由于<strong>接收方缓冲区满</strong>而丢弃数据，是不可接受的。因为数据跨过了茫茫网络，都已经到你机器上了，结果因为你缓冲区满了给它丢掉了，这不是坑人吗？</p><p>虽然出现这种情况，我们可以让发送方重传报文，但这样效率太低！</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/02/202211031203959.jpg"                      alt="QQ图片20220424132543"                ></p><p>所以，我们应该让收发双方知晓对方的缓冲区大小，从而避免这个问题！</p><p>这就是tcp报头中<code>16位窗口大小</code>的作用了！</p><h3 id="3-4-3-告知对方收缓大小"><a href="#3-4-3-告知对方收缓大小" class="headerlink" title="3.4.3 告知对方收缓大小"></a>3.4.3 告知对方收缓大小</h3><p>如下图，在客户端和服务端<code>互通有无</code>的时候，假设服务端的<strong>接收缓冲区</strong>满了，应该告知客户端，让他别再给自己发消息了。</p><p>此时，服务端设置自己的<code>16位窗口大小</code>，以此告知客户端自己的缓冲区<strong>剩余</strong>容量。</p><p>如果对方发来的报文中，<code>16位窗口大小</code>所表示的缓冲区剩余容量已经不足了，发送方就不应该继续发送，而应该等待对方从缓冲区中取走数据。</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/64154988e2f2b.png"                      alt="image-20230318131802239"                ></p><p>这是已经开始通讯的情况，但如果是<strong>第一次</strong>通讯呢？如果客户端一来就发送了一个巨大的数据，直接塞满了服务端的缓冲区，那不是出事了？</p><p>这便是tcp在<strong>三次握手</strong>中要做的事情了，简单来说就是在通信开始前就互相告知自己缓冲区的大小。后文会讲解。</p><h3 id="3-4-4-缓冲区是否独立？"><a href="#3-4-4-缓冲区是否独立？" class="headerlink" title="3.4.4 缓冲区是否独立？"></a>3.4.4 缓冲区是否独立？</h3><ul><li>进程的tcp缓冲区是独立的吗？</li></ul><p>每个进程都有自己的内核空间，内核空间里有tcp缓冲区，所以每个进程都有自己独立的tcp缓冲区</p><ul><li>线程的tcp缓冲区是独立的吗？</li></ul><p>是的！虽然这些线程共享同一个内核TCP缓冲区，但是每个线程使用的缓冲区是独立的，互相之间不会产生冲突。每个线程对自己的缓冲区进行读写操作时，会使用内核提供的同步机制，如互斥锁、信号量等来确保线程之间的缓冲区不会互相干扰，从而实现数据的安全读写。</p><h2 id="3-5-六个标记位"><a href="#3-5-六个标记位" class="headerlink" title="3.5 六个标记位"></a>3.5 六个标记位</h2><p>在4位首部长度右侧，有一块保留长度，和6个标记位。这六个标记位是所有设备都支持的标记位。</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/64156cd9c9751.png"                      alt="image-20230318154843384"                ></p><ul><li>SYN: 连接标记位，用于建立连接（又称同步报文）</li><li>FIN: 表示请求关闭连接，又称为<code>结束报文</code></li><li>ACK：响应报文，代表本次报文中包含对之前报文的确认应答</li><li>PSH：要求对方立马从tcp缓冲区中取走数据</li><li>URG：紧急指针标记位，用于紧急数据的传输</li><li>RST：要求重置连接（双方重新建立一次新的tcp连接）</li></ul><h3 id="3-5-1-8个标记位？"><a href="#3-5-1-8个标记位？" class="headerlink" title="3.5.1 8个标记位？"></a>3.5.1 8个标记位？</h3><p>在部分书籍中，还会出现8个标记位与4位保留长度的说法（下图源自《图解tcp&#x2F;ip第五版》）</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/64156e7dd3169.png"                      alt="image-20230318155543377"                ></p><ul><li>CWR（Congestion Window Reduced）：该标志位用于通知对方自己已经将拥塞窗口缩小。在TCP SYN握手时，发送方会将CWR标志位设置为1，表示它支持ECN（Explicit Congestion Notification）拥塞控制，并且接收到的TCP包的IP头部的ECN被设置为11。如果发送方收到了一个设置了ECE（ECN Echo）标志位的TCP数据包，则它将调整自己的拥塞窗口，就像它从丢失的数据包中快速恢复一样。然后，发送方会在下一个数据包中设置CWR标志位，向接收方表明它已对拥塞做出反应。发送方在每个RTT（Round Trip Time）间隔最多做出一次这种反应。</li><li>ECE（ECN Echo）：该标志位用于通知对方从对方到这边的网络有拥塞。在收到数据包的IP首部中ECN为1时，TCP首部中的ECE会被设置为1。接收方会在所有数据包中设置ECE标志位，以便通知发送方网络发生了拥塞。</li></ul><p>而我百度到的文章提到，tcp给多出来的两个标记位新增了功能：</p><ul><li>除了以上6个标志位，还有一个实验性的标志位NS（Nonce Sum），用于防止TCP发送者的数据包标记被意外或恶意改动。NS标志位仍然是一个实验标志，用于帮助防止发送者的数据包标记被意外或恶意更改。<a class="link"   href="https://www.codetd.com/article/12421677" >[3]<i class="fas fa-external-link-alt"></i></a><a class="link"   href="https://blog.csdn.net/qq_38323666/article/details/85338477" >[4]<i class="fas fa-external-link-alt"></i></a></li><li>TCP标志位中还有两个标志位后来加的一个功能：显式拥塞通知（ECN）。ECN允许拥塞控制的端对端通知而避免丢包。但是，ECN在某些老旧的路由器和操作系统（例如：Windows XP）上不受支持。在TCP连接上使用ECN也是可选的；当ECN被使用时，它必须在连接创建时通过SYN和SYN-ACK段中包含适当选项来协商。 <a class="link"   href="https://www.likecs.com/show-203551574.html" >[2]<i class="fas fa-external-link-alt"></i></a><a class="link"   href="https://www.codetd.com/article/12421677" >[3]<i class="fas fa-external-link-alt"></i></a></li></ul><p>诸如<code>tcp的标记位到底是6个还是8个?</code>这种摸棱两可的问题，在考试中不会问道。</p><p>在学习中，我们只需要掌握所有设备都支持的<strong>6个标记位</strong>即可</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/02/202211031205743.png"                      alt="狗头"                ></p><h3 id="3-5-2-ACK"><a href="#3-5-2-ACK" class="headerlink" title="3.5.2 ACK"></a>3.5.2 ACK</h3><p>该标记位用于标识本条报文是对之前的报文的<code>确认应答</code></p><p>ACK标记位的设置和其他标记位并不冲突，在<code>捎带应答</code>的时候，可以同时设置多个标记位</p><h3 id="3-5-3-SYN-x2F-FIN"><a href="#3-5-3-SYN-x2F-FIN" class="headerlink" title="3.5.3 SYN&#x2F;FIN"></a>3.5.3 SYN&#x2F;FIN</h3><ul><li>SYN：表示请求建立连接，并在建立连接时用于同步序列号，所以又称为<code>同步报文</code>；</li><li>FIN：表示请求关闭连接，又称为<code>结束报文</code>。设置为1时，代表本方希望断开连接。此时双方要交换FIN（四次挥手）才能真正断开tcp连接。</li></ul><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6415723928135.png"                      alt="image-20230318161138819"                ></p><h4 id="3-5-3-1-三次握手"><a href="#3-5-3-1-三次握手" class="headerlink" title="3.5.3.1 三次握手"></a>3.5.3.1 三次握手</h4><p>在三次握手的时候，经历了如下过程</p><ul><li>连接发方A向对方主机B发送SYN报文，请求建立连接（A进入<code>SYN-SENT</code>状态）</li><li>主机B在收到报文后，回应<code>ACK+SYN</code>的报文，在确认应答的同时，请求建立连接（B进入<code>SYN-RCVD</code>状态）</li><li>A收到这条报文后，发送确认应答ACK（A认为连接成功建立 <code>ESTABLISHED</code>）</li><li>B收到A发送的ACK，三次握手完成（B认为连接成功建立  <code>ESTABLISHED</code>）</li></ul><h4 id="3-5-3-2-四次挥手"><a href="#3-5-3-2-四次挥手" class="headerlink" title="3.5.3.2 四次挥手"></a>3.5.3.2 四次挥手</h4><p>在断开连接，四次挥手的时候，经历了如下过程</p><ul><li>A要断开连接，发送FIN（A进入<code>FIN WAIT 1</code>状态）</li><li>B收到了FIN，发送ACK（B进入<code>CLOSE-WAIT</code>半关闭状态）</li><li>A收到了ACK（A进入<code>FIN WAIT 2</code>状态)</li><li>此时只是A要和B单方面分手，<code>A-&gt;B</code>的路被切断了，但是<code>B-&gt;A</code>的还没有，B还能继续给A发数据</li><li>B发完数据了，也和A分手了，B发送FIN（B进入<code>LAST ACK</code>状态）</li><li>A收到FIN，发送回应ACK（A进入<code>TIME WAIT</code>状态，将在<strong>一段时间后</strong>进入<code>CLOSE</code>断连状态）</li><li>B收到了ACK（B进入<code>CLOSE</code>状态）</li><li>连接关闭</li></ul><p>我们不仅需要知道3次握手和4次挥手的过程，还需要知道每一次的状态变化！</p><blockquote><p>3次握手和4次挥手对于应用层而言，都只有1个对应的函数。这些操作都是由tcp自主完成的。</p></blockquote><p>在centos下，可以使用如下命令，查看到<code>TIME WAIT</code>状态默认等待的时间</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ cat /proc/sys/net/ipv4/tcp_fin_timeout</span><br><span class="line">60</span><br></pre></td></tr></table></figure><h3 id="3-5-4-PSH"><a href="#3-5-4-PSH" class="headerlink" title="3.5.4 PSH"></a>3.5.4 PSH</h3><p>PSH标记位的作用是：要求对方立马取走缓冲区中数据</p><p>如下图，S在接收缓冲区满了之后过了很久，还没有取走缓冲区中的数据，C实在忍不住了，给S发一个<code>PSH</code>标记位的报文，要求S立马取走这些数据！</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641578af00365.png"                      alt="image-20230318163912700"                ></p><p>tcp在收到此报文后，将由操作系统告知应用层，取走缓冲区中的数据。</p><p>如果应用层不听操作系统的咋办？<strong>那就代表应用层写的有bug</strong>！人家给你发了那么多东西了你还不处理，有点过分了！</p><h3 id="3-5-5-URG"><a href="#3-5-5-URG" class="headerlink" title="3.5.5 URG"></a>3.5.5 URG</h3><p>URG是<strong>紧急指针标记位</strong>。</p><p>在<a href="#jump">3.3.5 按序到达</a>部分提到过，如果只关注序号，则无法处理优先级问题。有一些数据对于应用层来说，优先级较高。如果tcp只会老老实实的按顺序把数据交付给应用层，那在高优先级的数据也搞不过操作系统对tcp的处理。</p><p>所以，为了能操作优先级，tcp提供了<code>URG</code>标记位，设置了此标记位的报文具有较高优先级。</p><p>应用层有专门的接口可以优先读取带有<code>URG</code>标记位的报文。</p><h4 id="3-5-5-1-16位紧急指针"><a href="#3-5-5-1-16位紧急指针" class="headerlink" title="3.5.5.1 16位紧急指针"></a>3.5.5.1 16位紧急指针</h4><p>为了能标识这个紧急数据在报文中的位置，tcp还提供了<code>16位紧急指针</code>；这个指针的指向便是<strong>紧急数据</strong>在tcp报文中的偏移量。<strong>紧急数据</strong>规定只有1个字节！</p><p>由于紧急指针的数据可以被提前读取，不受tcp缓冲区的约束，所以又被称为<code>带外数据</code></p><p>下图就举了一个紧急指针使用的场景：</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/64157b0e4aeba.png"                      alt="image-20230318164915979"                ></p><p>TCP 在传输数据时是有顺序的，它有<strong>字节号</strong>，URG配合紧急指针，就可以找到紧急数据的<strong>字节号</strong>。</p><p>紧急数据的字节号公式如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">紧急数据字节号(urgSeq)=TCP报文序号(seq)+紧急指针(urgpoint)−1</span><br></pre></td></tr></table></figure><p>比如图中的例子，如果 <code>seq = 10,urgpoint = 5</code>，那么字节序号 <code>urgSeq = 10 + 5 -1 = 14</code></p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641582bf3d3bc.png"                      alt="image-20230318172209050"                ></p><p>知道了字节号后，就可以计算紧急数据字位于<strong>所有传输数据中</strong>的第几个字节了。如果从第0个字节开始算起，那么紧急数据就是第<code>urgSeq - ISN - 1</code>个字节（ISN 表示初始序列号），减1表示不包括第一个SYN段，因为一个SYN段会消耗一个字节号。</p><h3 id="3-5-6-RST"><a href="#3-5-6-RST" class="headerlink" title="3.5.6 RST"></a>3.5.6 RST</h3><p>RST为复位报文，即<code>RESET</code>。</p><p>如下图，如果A给B发送的ACK在传输路途上丢失了，咋办？</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6415911e126cc.png"                      alt="image-20230318182326946"                ></p><p>这时候，就会出现A认为连接已经建立，而B由于没收到A的ACK而处于<code>SYN-RCVD</code>状态。</p><ul><li>此时A开始给B发送数据，B一看，不是说好了要建立连接才能发送数据的吗，你这是在干嘛？</li><li>于是B告知A，发送RST标志位的报文，要求和A重新建立连接（重新进行三次握手）</li><li>重新建立连接成功后，AB再正常发送信息。</li></ul><p>以上只是RST使用的情况之一。我们使用浏览器访问一些网页时，<code>F5刷新</code>就可以理解为浏览器向服务器发送了一个带有RST标记位的报文。</p><h2 id="3-6-为什么是3次握手？"><a href="#3-6-为什么是3次握手？" class="headerlink" title="3.6 为什么是3次握手？"></a>3.6 为什么是3次握手？</h2><p>为什么握手的次数是3次，不是1次、2次、4次、5次？</p><p>在讨论这个问题之前，我们要知道：连接建立是有消耗的！需要维护其缓存区、连接描述符（linux下为文件描述符）等等数据。</p><ul><li>如果是一次握手？</li></ul><p>一次握手，即A给B发送一个SYN，双方就认为连接建立了。</p><p>那么我们直接拿个机器，写个死循环，一直给对方发送SYN，自己直接丢弃文件描述符（不做维护）</p><p>由于服务器并不知道你直接丢弃了文件描述符，其还是要为此次连接维护相关数据，这样会导致服务器的资源在短时间内被大量消耗，最后直接dead了</p><blockquote><p>这种攻击叫做<strong>SYN洪水</strong></p></blockquote><ul><li>如果是二次握手？</li></ul><p>A给B发送一个SYN，B给A发送一个ACK，即认为连接建立。</p><p>这和一次握手其实是相似的，服务器发送完毕ACK之后，就认为连接已经建立，需要维护相关资源。而我们依旧可以直接丢弃，不进行任何维护，最后还是服务器的资源被消耗完了</p><ul><li>三次握手</li></ul><p>双方都必须维护连接的相关资源，这样，哪怕你攻击我的服务器，你也得付出同等的资源消耗。最后就是比谁资源更多呗！</p><p>相比于前两种情况，三次握手能在<strong>验证全双工</strong>的同时，一定程度上避免攻击。</p><p>三次握手还将最后一次ACK丢失的成本嫁接给了客户端（连接发起方）如果最后一次ACK丢失，要由客户端重新发起和服务器的连接。</p><p>注意，三次握手只是<strong>一定程度</strong>上避免攻击。我们依旧可以用很多宿主机“堆料”来和服务器硬碰硬，这是无可避免的情况。</p><ul><li>更多次握手？</li></ul><p>由于三次握手已经满足了我们的要求，更多次握手依旧有被攻击的可能，还降低了效率，完全没必要！</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/64158f71c8c50.png"                      alt="image-20230318181619570"                ></p><h2 id="3-7-超时重传"><a href="#3-7-超时重传" class="headerlink" title="3.7 超时重传"></a>3.7 超时重传</h2><p>为了保证可靠性，如果一个报文长时间未收到对方的ACK回应，则需要进行<code>超时重传</code>。</p><p>linux下每一次尝试的时间间隔为500ms，若500ms内尚未收到对方的ACK，则重发报文，再等待1000ms……以此类推。</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6415924e3984a.png"                      alt="image-20230318182832179"                ></p><p>超时重传还可能遇到下面的情况：</p><ul><li>服务器收到了消息，也发送了ACK，但是ACK在路上丢失了</li><li>客户端没有收到ACK，于是进行超时重传</li><li>服务器再次收到了消息，此时接收缓冲区里出现了两个一样的数据</li></ul><p>但是，我们的报文是有序号的，tcp就可以<strong>直接根据序号去重</strong>，所以，tcp交给应用层的数据是<code>去重+排序</code>之后的数据！</p><hr><p>如果同一个报文超时重传了好几次，还没有收到对方的应答，就会认为对方的服务挂掉了，此时本端会强制断连。</p><p>此时客户端就可以发送一个带有RST标记位的报文，要求和对方重新建立连接。</p><h2 id="3-8-出现了很多CLOSE-WAIT状态的连接？"><a href="#3-8-出现了很多CLOSE-WAIT状态的连接？" class="headerlink" title="3.8 出现了很多CLOSE-WAIT状态的连接？"></a>3.8 出现了很多CLOSE-WAIT状态的连接？</h2><p>在上面提到过，当客户端向服务器发送FIN之后，服务器回复ACK，会进入<code>CLOSE-WAIT</code>状态。此时服务器还能给客户端发送消息，双方都还在维护连接的相关资源。</p><p>如果一个服务出现了很多个处于<code>CLOSE-WAIT</code>状态的连接，就必须要检查一下，应用层的代码里面是不是没有调用<code>close(fd)</code>函数来关闭对应的文件描述符。</p><ul><li>一方的<code>close(fd)</code>就对应了两次挥手</li></ul><p>对方明明都要和你分手了，你还挂着对方当备胎，还要找对方要钱，也太不像话了😂</p><h3 id="3-8-1-活学活用🤣"><a href="#3-8-1-活学活用🤣" class="headerlink" title="3.8.1 活学活用🤣"></a>3.8.1 活学活用🤣</h3><p>230322下午，正准备通过之前写的tcp代码来验证tcp握手和挥手的各个状态的，没想到用命令一看，全是CLOSE-WAIT状态，填满了整个屏幕，这完全没办法写博客啊</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641abf6b4b48b.png"                      alt="image-20230322164218824"                ></p><p>而且这些状态清一色来自<code>python3.10</code>的程序，看到它的时候，我已经基本猜到了是啥进程引发的了——我的<a class="link"   href="https://github.com/Valorant-Shop-CN/" >两个valorant机器人<i class="fas fa-external-link-alt"></i></a>。查了查pid，坐实了这一点</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641abfb02da9d.png"                      alt="image-20230322164328076"                ></p><p>我将数据写入到一个文件里面，统计了一下，一共1200多个<code>CLOSE-WAIT</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">netstat -ntp &gt; <span class="built_in">log</span> <span class="comment"># 将统计结果写入文件log</span></span><br><span class="line">netstat -antp | grep CLOSE_WAIT <span class="comment"># 只统计CLOSE_WAIT状态的链接</span></span><br></pre></td></tr></table></figure><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641abff36dad2.png"                      alt="image-20230322164435422"                ></p><p>这些状态值的远程ip来源虽然有多个，但一个ip出现了多次，于是我就使用 <a class="link"   href="https://www.itdog.cn/http/" >itdog<i class="fas fa-external-link-alt"></i></a> 看了一下其中几个ip的来源，是<code>Anycast/cloudflare.com</code>，也就是很出名的cloudflare-cdn。</p><p>在我的kook-valorant-bot里面，有一项业务是方便开发者使用的valorant登录和商店查询的<strong>api</strong>（使用aiohttp库编写）</p><p>为了统计其上线状态，我使用了<code>uptimerobot</code>定时请求，每5分钟获取一次api的在线情况</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">https://stats.uptimerobot.com/Wl4KwU6Bzz</span><br></pre></td></tr></table></figure><p>嗯，运行状态倒是蛮好的，100%在线</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641ac0f123c14.png"                      alt="image-20230322164848818"                ></p><p>前面提到过，系统是需要消耗资源来维护tcp链接的。如下图，机器人占用了将近400mb的内存，其中肯定有一部分就是被这些没有关闭的tcp链接所占用的</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641ac1b16c065.png"                      alt="image-20230322165201221"                ></p><p>大量<code>CLOSE-WAIT</code>，只可能是一个原因：<code>uptimebot</code>的请求已经结束并发送了FIN，而我的api代码作为服务端，并没有在收到FIN后，对链接进行<code>close</code>，于是链接一直处于<code>CLOSE-WAIT</code>半关闭状态。只有程序关闭（机器人下线）才会被操作系统清空。</p><hr><p>后来又研究了一下，经过他人点醒，才发现上面的结论都是<strong>错的</strong></p><blockquote><p><a class="link"   href="https://segmentfault.com/q/1010000043572705/a-1020000043573118" >https://segmentfault.com/q/1010000043572705/a-1020000043573118<i class="fas fa-external-link-alt"></i></a></p></blockquote><p>其实在<code>netstat</code>里面很明显的一点，表示这一切和uptimebot以及我写的api没有任何关系</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641e705375685.png"                      alt="image-20230325115349060"                ></p><p>那就是这里面<code>Local Address</code>的端口，每一个都是不一样的。如果是我写的api导致的，那么他们的端口都应该是api绑定的端口，且固定才对！</p><p>后来就找到了一个2014年的issue，大概情况就是，python的requests库会维护一个连接处。这些处于<code>close-wait</code>状态的连接，都是requests库维护的。</p><p><a class="link"   href="https://github.com/psf/requests/issues/1973" >https://github.com/psf/requests/issues/1973<i class="fas fa-external-link-alt"></i></a></p><p>好嘛，原来是自己学艺不精，闹了个大笑话。当时找处理aiohttp的web状态的资料找了老半天都没找到……原来一开始方向就错了😶‍🌫️</p><h1 id="4-验证状态"><a href="#4-验证状态" class="headerlink" title="4.验证状态"></a>4.验证状态</h1><p>下面可以用代码来实地查看tcp在传输过程中的各种状态。之前写过一个<a class="link"   href="https://gitee.com/musnow/raspberry-practice/blob/f3fb8669cd32b1ed2cc0a869ea6c05d070baa2c2/code/23-02-15_http/tcpServer.cpp" >简单的http服务器<i class="fas fa-external-link-alt"></i></a>，现在为了方便，直接拿来使用。</p><p>采用如下命令进行netstat的循环监测</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">while :; do netstat -ntp | grep 端口号;sleep 1; echo &quot;########################&quot;; done</span><br></pre></td></tr></table></figure><h2 id="4-1-TIME-WAIT"><a href="#4-1-TIME-WAIT" class="headerlink" title="4.1 TIME-WAIT"></a>4.1 TIME-WAIT</h2><p>在浏览器访问，可以看到服务器返回的html页面</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641e730913703.png"                      alt="image-20230325120528729"                ></p><p>后台可以看到，服务器接收到了请求的报头</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641e75084bbf3.png"                      alt="image-20230325121400802"                ></p><p>并按如下返回<code>response</code></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">DEBUG | 1679717397 | muxue | [sockfd: 4] filePath: web/index.html</span><br><span class="line">######### response header ##########</span><br><span class="line">HTTP/1.0 200 OK</span><br><span class="line">Content-Type: text/html</span><br><span class="line">Content-Length: 362</span><br><span class="line">Set-Cookie: This is my cookie test</span><br><span class="line"></span><br><span class="line">######### response end ##########</span><br></pre></td></tr></table></figure><p>使用<code>netstat</code>命令查看，当前多出了一个处于<code>time wait</code>状态的连接</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641e7529b2ec4.png"                      alt="image-20230325121434464"                ></p><p>这代表四次挥手的第一个FIN是由服务器发出的，这一点在代码中也能体现，服务器accpet到连接后，会交由孙子进程来执行<code>handlerHttpRequest(conet)</code>服务</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 提供服务（孙子进程）</span></span><br><span class="line"><span class="type">pid_t</span> id = fork();</span><br><span class="line"><span class="keyword">if</span>(id == <span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">close</span>(_listenSock);<span class="comment">//因为子进程不需要监听，所以关闭掉监听socket</span></span><br><span class="line">    <span class="comment">//又创建一个子进程，大于0代表是父进程，即创建完子进程后父进程直接退出</span></span><br><span class="line">    <span class="keyword">if</span>(fork()&gt;<span class="number">0</span>)&#123;</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 父进程推出后，子进程被操作系统接管</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 孙子进程执行</span></span><br><span class="line">    <span class="built_in">handlerHttpRequest</span>(conet);</span><br><span class="line">    <span class="built_in">exit</span>(<span class="number">0</span>);<span class="comment">// 服务结束后，退出，子进程会进入僵尸状态等待父进程回收</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 爷爷进程</span></span><br><span class="line"><span class="built_in">close</span>(conet); <span class="comment">//这个close并不会影响孙子进程内部的，因为有写时拷贝</span></span><br><span class="line"><span class="type">pid_t</span> ret = <span class="built_in">waitpid</span>(id, <span class="literal">nullptr</span>, <span class="number">0</span>); <span class="comment">//此时就可以直接用阻塞式等待了</span></span><br><span class="line"><span class="built_in">assert</span>(ret &gt; <span class="number">0</span>);<span class="comment">//ret如果不大于0，则代表等待发生了错误</span></span><br></pre></td></tr></table></figure><p>这个服务函数并不是<code>while(1)</code>的死循环，内部也没有进行socket的close操作，而是发送完毕客户端请求的文件后，直接退出了</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">handlerHttpRequest</span><span class="params">(<span class="type">int</span> sock)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt;     <span class="string">&quot;########### header-start ##########&quot;</span> &lt;&lt; endl;<span class="comment">//打印一个分隔线</span></span><br><span class="line">    <span class="type">char</span> buffer[<span class="number">10240</span>];</span><br><span class="line">    <span class="type">ssize_t</span> s = <span class="built_in">read</span>(sock, buffer, <span class="built_in">sizeof</span>(buffer));</span><br><span class="line">    <span class="keyword">if</span>(s &gt; <span class="number">0</span>)&#123;</span><br><span class="line">        cout &lt;&lt; buffer &lt;&lt; endl;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;########### header-end   ##########&quot;</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">fflush</span>(stdout);</span><br><span class="line">    string path = <span class="built_in">getPath</span>(buffer);</span><br><span class="line">    <span class="comment">// 假设用户请求的是 /a/b 路径</span></span><br><span class="line">    <span class="comment">// 那么服务端处理的时候，就需要添加根目录位置和默认的文件名</span></span><br><span class="line">    <span class="comment">// &lt;root&gt;/a/b/index.html</span></span><br><span class="line">    <span class="comment">// 在本次用例中，根目录为 ./web文件夹，所以完整的文件路径应该是</span></span><br><span class="line">    <span class="comment">// ./web/a/b/index.html</span></span><br><span class="line"></span><br><span class="line">    string resources = ROOT_PATH; <span class="comment">// 根目录路径</span></span><br><span class="line">    resources += path; <span class="comment">// 文件路径</span></span><br><span class="line">    <span class="built_in">logging</span>(DEBUG,<span class="string">&quot;[sockfd: %d] filePath: %s&quot;</span>,sock,resources.<span class="built_in">c_str</span>()); <span class="comment">// 打印用作debug</span></span><br><span class="line"></span><br><span class="line">    string html = <span class="built_in">readFile</span>(resources);<span class="comment">// 打开文件</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 开始响应</span></span><br><span class="line">    string response = <span class="string">&quot;HTTP/1.0 200 OK\r\n&quot;</span>;</span><br><span class="line">    <span class="comment">//如果readFile返回的是404，代表文件路径不存在</span></span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">strcmp</span>(html.<span class="built_in">c_str</span>(),<span class="string">&quot;404&quot;</span>)==<span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        response = <span class="string">&quot;HTTP/1.0 404 NOT FOUND\r\n&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 追加后续字段</span></span><br><span class="line">    response += <span class="string">&quot;Content-Type: text/html\r\n&quot;</span>;</span><br><span class="line">    response += (<span class="string">&quot;Content-Length: &quot;</span> + <span class="built_in">to_string</span>(html.<span class="built_in">size</span>()) + <span class="string">&quot;\r\n&quot;</span>);</span><br><span class="line">    response += <span class="string">&quot;Set-Cookie: This is my cookie test\r\n&quot;</span>;</span><br><span class="line">    response += <span class="string">&quot;\r\n&quot;</span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;######### response header ##########\n&quot;</span> &lt;&lt; response &lt;&lt; <span class="string">&quot;######### response end ##########\n&quot;</span>;</span><br><span class="line">    <span class="built_in">fflush</span>(stdout);</span><br><span class="line">    response += html;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 发送给用户</span></span><br><span class="line">    <span class="built_in">send</span>(sock, response.<span class="built_in">c_str</span>(), response.<span class="built_in">size</span>(), <span class="number">0</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>函数退出了之后，文件描述符就交由了操作系统。一个没有进程使用的文件描述符，会被操作系统直接close关掉。相当于<strong>操作系统</strong>帮我们发出了FIN，就出现了<code>TIME WAIT</code>状态。</p><h3 id="4-1-1-为啥要有这个状态？"><a href="#4-1-1-为啥要有这个状态？" class="headerlink" title="4.1.1 为啥要有这个状态？"></a>4.1.1 为啥要有这个状态？</h3><p>知道了4次挥手的过程后，我们就能知道，<code>TIME-WAIT</code>是4次挥手的发起方才有的状态。</p><p>既然对方已经给我发了FIN，这不就代表对方也想和我分手吗？那我为啥还留着好友不删，非要等等呢？</p><p>这是因为，我们发出的最后一次ACK是否被对方收到，是未知的！</p><ul><li>A给B发送最后一次ACK，B没有收到</li><li>A不TIME-WAIT直接退出，A已经断开连接了，但是B还在维护这个连接</li><li>如果有TIME-WAIT状态，B没有收到ACK，会对FIN进行超时重传</li><li>A再次收到FIN，代表上一次ACK丢了，那就再次发送ACK</li><li>如果A在TIME-WAIT状态什么信息都没有收到，那就代表自己的ACK被B收到了，便可以放心断连</li></ul><p>此时，TIME-WAIT状态保证了最后一次ACK的正常递达</p><p>还有第二种情况：</p><ul><li>C给S发送FIN，准备断连</li><li>S给C发送data，发送完毕后，立马发送FIN</li><li>data和FIN都在路由传输的过程，可能会出现FIN比data早到的情况</li><li>C收到FIN，进入TIME-WAIT状态，期间收到了S发送的data</li></ul><p>此时，TIME-WAIT状态保证了二者之间的消息能都被收到</p><h3 id="4-1-2-等多久？"><a href="#4-1-2-等多久？" class="headerlink" title="4.1.2 等多久？"></a>4.1.2 等多久？</h3><p>这里引入一个新概念：一个报文在双方之间传输花费的时间，被称为这个消息的 MSL（maximun segment lifetime 最大生存时间）</p><p>TIME-WAIT等待的时间需要适中，不同的操作系统，默认等待的时间都是不同的。CentOS下，这个时间是60s</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ cat /proc/sys/net/ipv4/tcp_fin_timeout</span><br><span class="line">60</span><br></pre></td></tr></table></figure><p>一般情况下，设置为<code>MSL*2</code>是最好的，这样能保证双方数据的递达，和最后ACK的递达</p><h2 id="4-2-CLOSE-WAIT"><a href="#4-2-CLOSE-WAIT" class="headerlink" title="4.2 CLOSE-WAIT"></a>4.2 CLOSE-WAIT</h2><p>如果我们在<code>handlerHttpRequest(conet);</code>向客户端发送了html文件后，休眠几秒钟，是否就能看到其他状态呢？</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 发送给用户</span></span><br><span class="line">send(sock, response.c_str(), response.size(), <span class="number">0</span>);   </span><br><span class="line"><span class="comment">// 休眠几秒钟作为测试</span></span><br><span class="line">sleep(<span class="number">20</span>);</span><br></pre></td></tr></table></figure><p>如下，情况又不同了。这次出现的是<code>CLOSE-WAIT</code>状态，代表第一个FIN请求是客户端发出的</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641e78f5d0133.png"                                     ></p><p>这是因为当前的进程没有进行<code>长链接</code>的维护，如果想维护长连接，则服务器应该给客户也返回一个<code>Connection: keep-alive</code>。</p><p>如下图，可以看到客户端发来的<code>http-header</code>里面，是有该字段的。而服务器并没有返回相同的字段，客户端就认为服务器不支持长链接，从而主动发出了<code>FIN</code>。</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641e7981d948c.png"                      alt="image-20230325123306633"                ></p><p>进一步看tcp的状态，当前是有两个父进程为1（采用了孙子进程的写法，父进程退出后会被操作系统接管）的进程在进行休眠，它们同属于<code>295942</code>这个tcp服务器主进程的进程组（<code>PGID</code>相同）</p><p>当这两个进程结束休眠的时候，<code>CLOSE-WAIT</code>状态的连接立马消失了。因为操作系统接管了文件描述符后，进行了close，服务端也发出了fin，四次挥手成功，连接终止。</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641e78f5d0133.png"                                     ></p><h2 id="4-3-ESTABLISHED"><a href="#4-3-ESTABLISHED" class="headerlink" title="4.3 ESTABLISHED"></a>4.3 ESTABLISHED</h2><p>如果我们给response加上长链接的报头，是否可以看到<code>ESTABLISHED</code>状态呢？</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">response += <span class="string">&quot;Connection: keep-alive\r\n&quot;</span>;</span><br></pre></td></tr></table></figure><p>可以看到，确实出现了这个状态，这代表双方成功维护起了长链接（虽然当前情况下，这个长链接并没有起到应有的作用）</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641e7de86ad76.png"                      alt="image-20230325125153006"                ></p><p>进一步轮换，将处理函数改为<code>while(1)</code>的死循环调用，我们应该可以通过一个socket实现多个报文的发送</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 孙子进程执行</span></span><br><span class="line">logging(DEBUG, <span class="string">&quot;new child process&quot;</span>);<span class="comment">//打印一个新进程的提示信息，方便观察结果</span></span><br><span class="line"><span class="keyword">while</span>(<span class="number">1</span>)&#123;</span><br><span class="line">    handlerHttpRequest(conet);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，只出现了一个子进程，对客户端进行服务</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641e7f2a9b670.png"                      alt="image-20230325125715102"                ></p><p>查看日志，能看到，成功实现了长链接通信</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641e7ff8ac7fc.png"                      alt="image-20230325130041133"                ></p><p>如果不采用<code>while(1)</code>死循环进行服务，则客户端的每一次请求，都需要一个新的子进程来服务</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641e8197ea194.png"                      alt="image-20230325130736659"                ></p><p>即便response中带有长链接标识，也会因为fd被操作系统回收而进入<code>TIME-WAIT</code>状态</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641e8132ba5d3.png"                      alt="image-20230325130555495"                ></p><h2 id="4-4-端口不能被bind"><a href="#4-4-端口不能被bind" class="headerlink" title="4.4 端口不能被bind"></a>4.4 端口不能被bind</h2><p>之前在tcp服务器的学习中，出现了如果立马把tcp服务器关了后开，同一个端口无法被bind的情况</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ ./tcpServer 50000 &gt; log</span><br><span class="line">FATAL | 1679720482 | muxue | bind: Address already in use:3</span><br><span class="line">$ ./tcpServer 50000 &gt; log</span><br><span class="line">FATAL | 1679720491 | muxue | bind: Address already in use:3</span><br></pre></td></tr></table></figure><p>经过对tcp协议的学习，现在能知道为何这个端口不能被bind了。使用<code>netstat -ntp</code>命令查看，能看到这个端口上还有处于<code>TIME-WAIT</code>状态的链接，所以系统<strong>不允许</strong>我们bind这个端口。这是操作系统在默认状态下的行为。</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641e80825f8aa.png"                      alt="image-20230325130259120"                ></p><p>前面提到过，centos默认的<code>TIME-WAIT</code>等待时间是60s。只要等待60s，操作系统释放了这个端口上的冗余链接，就能被bind了！</p><p>但是，这样会有很大的问题！请接着往下看</p><h3 id="4-4-1-问题"><a href="#4-4-1-问题" class="headerlink" title="4.4.1 问题"></a>4.4.1 问题</h3><p>假设我现在的服务器进程是直接<code>bind 80</code>端口对外进行服务的，这样他人就能直接通过我服务器的ip，以http协议与我的<strong>服务进程</strong>进行通信。</p><p>以http网页服务为例，经过了很久很久的运行时间</p><ul><li>服务器进程出了恶性bug，导致进程退出了</li><li>服务器压力过大，操作系统直接把服务进程给kill了</li></ul><p>这时候，由于第一个FIN是由服务端发出的，服务器会进入<code>TIME-WAIT</code>状态。</p><p>假设<strong>服务进程</strong>崩溃的时候，有数个用户正在访问你的网页。对于他们而言，崩溃的表现就是，刷新网页，<strong>直接白屏</strong>，显示不出来后续的页面了。</p><p>此时就需要运维老哥赶快ssh连上服务器，重启<strong>服务进程</strong></p><p>为了关照运维老哥的头发，让出错的服务进程快速重启，一般情况下，我们会给这个服务进程增加一个监视进程</p><ul><li>监视进程是个死循环，其要做的功能很单一，所以负载并不大</li><li>监视进程实时查看，每几秒就看一眼服务进程的状态</li><li>服务进程挂掉了，监视进程在下一轮监视时会立马发现，通过 <a href="https://blog.musnow.top/posts/2395550697/#4-3-execl">exec系列函数</a> 直接重启服务进程</li></ul><p>这时候，<code>TIME-WAIT</code>的问题就出现了：服务进程想绑定的是80端口，也只能绑定80端口（不然客户端无法知道服务器端口改变，也依旧无法访问服务）但是80端口还有没有清理的tcp链接，操作不给你bind啊！</p><p>如果等操作系统60s后清除链接再bind，那也太晚了🙅‍♀️</p><p>大型服务进程启动时要干的活很多，所以启动会较慢。等待系统释放<code>TIME-WAIT</code>的链接后再bind，相当于多给服务进程启动增加了60s</p><ul><li>对于一些客户量级巨大的服务，时间就是生命呀！</li><li>用户的耐心都不咋地，拿我自己举栗子吧！当我去访问一些网站时，如果<code>5s</code>之内网页没有加载出来，我就准备x掉那个网页了</li></ul><p>所以，为了避免由于<code>TIME-WAIT/CLOSE-WAIT</code>未释放而无法bind端口的问题，操作系统提供了<strong>端口复用</strong>的接口。让进程可以忽略冗余连接，直接bind这个端口！</p><h3 id="4-4-2-端口复用"><a href="#4-4-2-端口复用" class="headerlink" title="4.4.2 端口复用"></a>4.4.2 端口复用</h3><p>端口复用，复用的是有<code>TIME-WAIT/CLOSE-WAIT</code>这种冗余链接的端口，而不是处于服务状态的端口哈！一个端口只能对应一个服务，老规矩可不能破坏了。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">setsockopt</span><span class="params">(<span class="type">int</span> sockfd, <span class="type">int</span> level, <span class="type">int</span> optname, <span class="type">const</span> <span class="type">void</span> *optval, <span class="type">socklen_t</span> optlen)</span>;</span><br></pre></td></tr></table></figure><p>默认情况下，端口有冗余链接，无法bind</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641e8992278ab.png"                      alt="image-20230325134139050"                ></p><p>只需要在bind函数之前添加上如下代码，就能实现端口复用。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1.1 允许端口被复用</span></span><br><span class="line"><span class="type">int</span> optval = <span class="number">1</span>;</span><br><span class="line">setsockopt(_listenSock, SOL_SOCKET, SO_REUSEADDR, &amp;optval, <span class="keyword">sizeof</span>(optval));</span><br></pre></td></tr></table></figure><p>如下图，即便50000端口存在<code>time-wait</code>的链接，我们依旧可以正常bind这个端口！</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641e89e075269.png"                      alt="image-20230325134257205"                ></p><h2 id="4-5-accpet不影响tcp"><a href="#4-5-accpet不影响tcp" class="headerlink" title="4.5 accpet不影响tcp"></a>4.5 accpet不影响tcp</h2><p>linux给我们提供的接口accpt，并不参与3次握手的阶段</p><p>将http服务的accpet给去掉，来观察这一情况。如下图，服务器直接是一个啥事不干的死循环，不对新来的连接进行<code>accept</code>，此时浏览器访问该服务，依旧会出现两个处于<code>ESTABLISHED</code>的连接</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641fff6bbcb20.png"                      alt="image-20230326161637486"                ></p><p>这便证实了我们的结论：accpet不参与tcp3次握手的过程</p><h2 id="4-6-listen的第二个参数"><a href="#4-6-listen的第二个参数" class="headerlink" title="4.6 listen的第二个参数"></a>4.6 listen的第二个参数</h2><h3 id="4-6-1-概念"><a href="#4-6-1-概念" class="headerlink" title="4.6.1 概念"></a>4.6.1 概念</h3><p>之前学习<a href="https://blog.musnow.top/posts/2171263616/">tcp服务器写法</a>的时候，粗略提到了listen函数第二个参数的作用。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/types.h&gt;</span>          <span class="comment">/* See NOTES */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/socket.h&gt;</span></span></span><br><span class="line"><span class="type">int</span> <span class="title function_">listen</span><span class="params">(<span class="type">int</span> sockfd, <span class="type">int</span> backlog)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//backlog参数限制了能被阻塞等待连接的数量</span></span><br><span class="line"><span class="comment">//如果超过这个数量，则会返回一个ECONNREFUSED错误。亦或者如果协议支持重传，多余的请求会被忽略，后续可以重传</span></span><br></pre></td></tr></table></figure><p>这里的阻塞等待连接是什么意思？还是用前面用到的http服务，以实际情况来看看</p><ul><li>什么情况下，一个连接会被阻塞？</li></ul><p>这一点就涉及到服务器的承受能力了。假设服务器现在很忙，压根没时间去<code>accept</code>一个新的连接，那这个连接就一直存在操作系统的tcp连接中，<strong>而没有进程对它服务</strong>。这种状态，就可以被称为连接的<strong>阻塞等待</strong></p><h3 id="4-6-2-看看具体情况"><a href="#4-6-2-看看具体情况" class="headerlink" title="4.6.2 看看具体情况"></a>4.6.2 看看具体情况</h3><p>假设我将listen的第二个参数设置为了2，服务器是个啥事不干的死循环</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="built_in">listen</span>(_listenSock, <span class="number">2</span>) &lt; <span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">logging</span>(FATAL, <span class="string">&quot;listen: %s&quot;</span>, <span class="built_in">strerror</span>(errno));</span><br><span class="line">    <span class="built_in">exit</span>(LISTEN_ERR);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在浏览器内直接开5个窗口请求这个连接，加上我的手机，一共是6个请求</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6420011b1f157.png"                      alt="image-20230326162356252"                ></p><p>但后台可以看到，再继续增加浏览器请求的数量，依旧都只有两个连接是处于<code>ESTABLISHED</code>状态，和listen的第二个参数正好相同！这两个连接因为没有被服务进程<code>accept</code>，它们就是处于阻塞等待状态的！</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/64200113b694e.png"                      alt="image-20230326162348693"                ></p><h3 id="4-6-3-为什么？"><a href="#4-6-3-为什么？" class="headerlink" title="4.6.3 为什么？"></a>4.6.3 为什么？</h3><p>为什么操作系统要给一个进程维护<strong>阻塞等待</strong>的连接呢？既然这个进程不进行新连接的accept，操作系统为何不直接把这个连接丢弃呢？</p><p>拿生活中非常场景的<code>餐厅排队</code>举例子吧。大家应该都见过一个餐馆在中晚餐高峰期时，门口有人在排队等位吧？特别是<del>河</del>海底捞，每次想去都得提前预定，不然排队的时间吃门口的小零食都要吃饱了。</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6420040c2a47d.png"                      alt="image-20230326163628670"                ></p><p>那么，餐馆为什么要提供排队等位呢？为何服务员不直接告诉新来的客人，馆子里没空位了，请另寻他处呢？</p><ul><li>原因很简单：为了上桌率。</li></ul><p><strong>一个餐馆的上桌率越高，就代表其生意越好</strong>。如果餐馆内部没桌了，但是外头有人排队，这样就能让有客人离开（空出桌子后）立马有新的客人上桌。</p><ul><li>对于我们的服务进程也是一样！</li></ul><p>假设这个服务进程有10个线程对外进行服务，此时来了第11位需要服务的客人。服务的10个线程（桌子）都被坐满了，没人能给11号客人服务。那这时候，操作系统就告诉11位客人：“你在这里稍作等待，我去给你<del>买个橘子</del>取个排队单号”，这时候11号客人就在操作系统为服务进程提供的等候位置上坐了下来，等待服务进程腾出空位来给他服务（链接阻塞等待）</p><p>这时候，有一个用户断开了连接，空出来了一个进程，那么服务进程（餐馆）内的服务员就跑出来，和11号客户说，他可以上桌了（accept）这时候，服务进程就开始给11号客户提供服务了。</p><p>这样一来，只要服务进程有空闲，就能立马有新的进程入座，让服务进程<strong>不至于摸鱼</strong>。提高了服务器资源的利用效率。</p><p>我买了一个服务器，我肯定是希望它在不崩溃的前提下<strong>为越多客户服务越好</strong>，资源最大化嘛！</p><h3 id="4-6-4-该参数应该设置成多少？"><a href="#4-6-4-该参数应该设置成多少？" class="headerlink" title="4.6.4 该参数应该设置成多少？"></a>4.6.4 该参数应该设置成多少？</h3><p>既然我们已经知道了这个参数的作用，那么应该把它设置为多少呢？</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/642007ef3e094.png"                      alt="image-20230326165304255"                ></p><p>餐馆也需要面临这个问题</p><ul><li>如果自己设置的排队等位太少，那么可能会有想排队的客户没有位置坐。</li><li>如果设置的太多，那新来的客户压根不打算排队了，因为他们知道，轮到自己的时候，已经饿扁了</li></ul><p>服务器也是如此</p><ul><li>第二个参数设置的低了，排队的空位太少，超过该参数的链接直接被os拒绝，错过了本来可以提供服务的用户</li><li>第二个参数设置的高了，用户过来排队，等了好久都没等到，于是就报错<code>连接超时</code>了</li><li>设置的太高了，维护的连接也会占用系统资源，服务进程可用资源变少了！</li><li>与其增长队列，还不如增加服务进程的服务能力（扩大店面）</li></ul><p>所以，我们应该根据自己服务的面向用户数量级，设置一个合适的等位数量！这个应该根据具体情况来看的！</p><h3 id="4-6-5-listen和accept"><a href="#4-6-5-listen和accept" class="headerlink" title="4.6.5 listen和accept"></a>4.6.5 listen和accept</h3><p>如下图，我让服务进程只对一个链接进行accept，相当于餐厅里面只有两张桌子。此时新来的链接就会处于等待状态，数量正好是<code>listen</code>的第二个参数（但是我的第二个参数是2，我也不知道为啥会是3个🤣）</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6420098c95af5.png"                      alt="image-20230326165957731"                ></p><h1 id="5-滑动窗口"><a href="#5-滑动窗口" class="headerlink" title="5.滑动窗口"></a>5.滑动窗口</h1><p>tcp中引入了<strong>滑动窗口</strong>的操作</p><h2 id="5-1-概念"><a href="#5-1-概念" class="headerlink" title="5.1 概念"></a>5.1 概念</h2><p>在实际通信中，如果真的只是让双方一发一答，那效率也太低了。所以，一般都是直接一次性发送多条消息，对方也是对多条报文进行ACK的，而且只需要ACK一次（这点在前面序号部分已经讲过原理了）</p><ul><li>一次性可以发送多条报文，但前提是对方有能力收那么多</li><li>窗口大小：一次性可以发送的数据数量（无需等待前面已发报文的ACK，就可以发送这么多）<ul><li>窗口大小是由<strong>对方的接收能力</strong>决定的</li><li>tcp报头中，<strong>16位窗口大小</strong>就是滑动窗口的大小</li><li>S给C所发报头中的窗口大小，既代表S接收缓冲区的大小，又代表C可以一次发送的数据大小</li><li>S接收缓冲区的大小变化，也会导致S给C所发报文中，窗口大小的变化</li></ul></li><li>窗口越大，代表双方通信的<strong>吞吐率</strong>就越大</li><li>发送的数据会保留在发送缓冲区中，发送缓冲区以如下区域构成<ul><li>已发，收到了ACK的报文（可删）</li><li>已发，未收到ACK的报文</li><li>未发，准备发送的报文</li></ul></li></ul><h2 id="5-2-看图"><a href="#5-2-看图" class="headerlink" title="5.2 看图"></a>5.2 看图</h2><p>滑动窗口可以用下图来形象的理解，对图冲的文字就不复述了</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/64203f7797844.png"                      alt="image-20230326204959886"                ></p><p>本人字丑，用pad写就更丑了，请谅解</p><h1 id="6-流量控制"><a href="#6-流量控制" class="headerlink" title="6.流量控制"></a>6.流量控制</h1><p>所谓流量控制，就是发送方根据对方的接收能力来选择发送数据的多少。</p><p>如果B的接收缓冲区满了，会通过报文中的窗口大小告知A，A不再继续发送数据。</p><p>此时，A会在过一会后，向B发送一个<code>窗口探测</code>报文，该报文没有有效载荷，所以不会过多占用接收缓冲区；</p><p>B在收到该报文后，会回应报文，告知A自己的窗口大小，被称为<code>窗口更新</code></p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/64203fc18dba2.png"                      alt="image-20230326205114217"                ></p><h1 id="7-拥塞控制"><a href="#7-拥塞控制" class="headerlink" title="7.拥塞控制"></a>7.拥塞控制</h1><p>前面提到的tcp处理措施，都是为了保证通信双方的主机不会出什么错误，导致数据的丢失。</p><p>但是一直么有提到一点，网络出错了咋办？</p><p>你和对方打电话，结果电线都断了，那还咋电话呢？</p><p>为了避免通信给网络造成太大的负担，tcp除了考虑对方的接受能力以外，还需要考虑网络的承载能力</p><h2 id="7-1-如何确认网络出问题？"><a href="#7-1-如何确认网络出问题？" class="headerlink" title="7.1 如何确认网络出问题？"></a>7.1 如何确认网络出问题？</h2><p>如果双方通信的时候，出现了丢包，我们真的能确认网络出现问题了吗？</p><ul><li>答案是否定的。</li></ul><p>你和朋友之间打电话，突然对方的声音卡了一下，你就能下结论，是的电话线断了吗？</p><ul><li>实际上，只有你完全听不到对方声音了，才能认为是通信出了问题。</li></ul><p>网络也是一样，只有出现<code>大面积丢包</code>，才能认为是网络出了问题。</p><p>我们知道，tcp基于字节流，一次性可以发送大量的信息，要是一个进程的tcp连接一建立，就开始往网络里面塞一大堆的信息，把网络给整堵塞了，那好吗？</p><p>一个进程这么干，那多几个进程加入，网络直接雪上加霜。</p><h2 id="7-2-慢启动"><a href="#7-2-慢启动" class="headerlink" title="7.2 慢启动"></a>7.2 慢启动</h2><p>所以，为了避免这种情况，tcp添加了慢启动机制。</p><p>说白了就是：刚开始发送的少，逐渐增多</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/641a57740be43.png"                                     ></p><p>整个过程如下：</p><ul><li>拥塞窗口从<strong>一个段</strong>的大小开始（约1kb）</li><li>拥塞窗口有一个阈值<code>ssthresh</code>，默认为对方的窗口大小，这在3次挥手的时候已经确定了</li><li>收到<strong>一次</strong>ACK，且<code>拥塞窗口&lt;阈值</code>，直接将现有拥塞窗口大小加倍【指数增长】<ul><li>也可以理解为，<strong>一个ACK</strong>就加1</li><li>比如第一次发送了1000个消息，那么收到对方的ACK后，直接将拥塞窗口大小加倍，为2000，下一次发送就发2000的消息</li></ul></li><li>收到ACK，<code>拥塞窗口&gt;=阈值</code>，窗口值+1【线性增长】</li><li>超时，阈值<code>ssthresh</code>设置为<code>拥塞窗口/2</code>，拥塞窗口置为1（从头开始，避免大面积的重传）</li><li>拥塞窗口始终小于接收器窗口</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">实际传输的数据大小=min(拥塞窗口,对方窗口大小)</span><br></pre></td></tr></table></figure><p>这便是慢启动的机制，上面贴的图能形象的展示这一点</p><h1 id="8-延迟应答"><a href="#8-延迟应答" class="headerlink" title="8.延迟应答"></a>8.延迟应答</h1><p>收到消息后，等一会再给对方应答</p><p>此时等待的是应用层取走接收缓冲区中的数据，这样回应ACK的时候，缓冲区的容量更富裕，ACK中携带的窗口大小也就更大，下次对方就能发送更多数据，提高了tcp通信的效率！</p><p>需要注意的是，窗口大小的增加，是与网络拥塞无关的，二者是tcp在传输中都要考虑的两个问题</p><blockquote><p>在保证不拥塞网络的前提下，传输更多数据</p></blockquote><p>要知道，网络环境复杂多变，一次性发送更多数据，是优于多次发送少量数据的</p><p>一般延迟应答有如下两个策略</p><ul><li>隔N个包应答一次</li><li>隔一定时间应答一次（避免对方进行超时重传）</li></ul><p>这两个策略都是可行的</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文带你详细了解tcp协议的相关知识&lt;/p&gt;</summary>
    
    
    
    <category term="玩上Linux" scheme="https://blog.musnow.top/categories/%E7%8E%A9%E4%B8%8ALinux/"/>
    
    
    <category term="Linux" scheme="https://blog.musnow.top/tags/Linux/"/>
    
    <category term="tcp" scheme="https://blog.musnow.top/tags/tcp/"/>
    
    <category term="udp" scheme="https://blog.musnow.top/tags/udp/"/>
    
  </entry>
  
  <entry>
    <title>【Git】解决git status中文乱码问题</title>
    <link href="https://blog.musnow.top/posts/3422840311/"/>
    <id>https://blog.musnow.top/posts/3422840311/</id>
    <published>2023-03-16T04:35:28.000Z</published>
    <updated>2023-04-03T01:23:39.646Z</updated>
    
    <content type="html"><![CDATA[<p>解决git status中文乱码问题</p><span id="more"></span><h2 id="起因"><a href="#起因" class="headerlink" title="起因"></a>起因</h2><p>在项目下输入git status之后，出现如下情况：</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6413fd7f3709b.png"                                     ></p><p>在线搜索了一下，发现是git添加了如下特性：</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6413fd83b9a65.png"                                     ></p><p>git参考文档：<a class="link"   href="https://git-scm.com/docs/git-config" >https://git-scm.com/docs/git-config<i class="fas fa-external-link-alt"></i></a></p><h2 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h2><p>根据上面的文档得知，git对超过0x80的字符都会以八进制输出，由于中文以UTF-8保存，所以上面的文件路径输出了一堆的八进制<a class="link"   href="https://so.csdn.net/so/search?q=%E8%BD%AC%E4%B9%89&spm=1001.2101.3001.7020" >转义<i class="fas fa-external-link-alt"></i></a>代码，这对用户极其不友好。</p><p>文档给出的方案是-z，通过-z选项的确可以输出正确的中文，但是却缺少了详细信息：</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6413fd87a51b8.png"                                     ></p><p>所有的输出全在一行，对用户也不好友。</p><p>查阅git status命令文档可知：</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6413fd8b68d4a.png"                                     ></p><p>该选项会以NUL为一行的几位，但是终端不能识别NUL结尾，所以终端不会<a class="link"   href="https://so.csdn.net/so/search?q=%E8%87%AA%E5%8A%A8%E6%8D%A2%E8%A1%8C&spm=1001.2101.3001.7020" >自动换行<i class="fas fa-external-link-alt"></i></a>。</p><h2 id="解决"><a href="#解决" class="headerlink" title="解决"></a>解决</h2><p>那么最终的解决办法：关闭这个特性。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --global core.quotePath false</span><br></pre></td></tr></table></figure><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6413fd8f4efc9.png"                                     ></p><p>再次执行git status：</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6413fd92c83e6.png"                                     ></p><p>一切回归正常。</p><p>然后git add python添加所有修改过的文件：</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6413fd96574d1.png"                                     ></p><h2 id="更多"><a href="#更多" class="headerlink" title="更多"></a>更多</h2><p>默认情况下，git会将入库的文件的行尾符替换为LF，这是因为git原生的工作环境是linux&#x2F;unix，而我目前工作环境是windows，所以这个也挺正常的。当下次在windows下检出分支的时候，会自动将LF替换为<a class="link"   href="https://so.csdn.net/so/search?q=CRLF&spm=1001.2101.3001.7020" >CRLF<i class="fas fa-external-link-alt"></i></a>。</p><p>这个警告并不会带来什么问题，完全可以忽略。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">版权声明：本文为CSDN博主「lengye7」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。</span><br><span class="line">原文链接：https://blog.csdn.net/lengye7/article/details/129208899</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;解决git status中文乱码问题&lt;/p&gt;</summary>
    
    
    
    <category term="差生文具多" scheme="https://blog.musnow.top/categories/%E5%B7%AE%E7%94%9F%E6%96%87%E5%85%B7%E5%A4%9A/"/>
    
    
    <category term="git" scheme="https://blog.musnow.top/tags/git/"/>
    
  </entry>
  
  <entry>
    <title>【C++】将浮点数每一位拆分并输出</title>
    <link href="https://blog.musnow.top/posts/398857040/"/>
    <id>https://blog.musnow.top/posts/398857040/</id>
    <published>2023-03-13T11:14:28.000Z</published>
    <updated>2023-04-03T01:23:39.630Z</updated>
    
    <content type="html"><![CDATA[<p>将浮点数每一位拆分并输出</p><span id="more"></span><h1 id="1-问题"><a href="#1-问题" class="headerlink" title="1.问题"></a>1.问题</h1><blockquote><p>来源 <a class="link"   href="https://ask.csdn.net/questions/7901417" >https://ask.csdn.net/questions/7901417<i class="fas fa-external-link-alt"></i></a></p></blockquote><p>题目要求如下，将浮点数拆分，不允许使用将浮点数整数倍扩大的办法（那样太简单了）</p><p><img                       lazyload                     alt="image"                     data-src="https://img1.imgtp.com/2023/03/28/vdW50l2p.png"                      alt="auto-orient1"                ></p><h1 id="2-办法"><a href="#2-办法" class="headerlink" title="2.办法"></a>2.办法</h1><h2 id="2-1-库函数modf"><a href="#2-1-库函数modf" class="headerlink" title="2.1 库函数modf"></a>2.1 库函数modf</h2><p>C语言有一个库函数modf，可以将浮点数的整数部分和小数部分拆分开来、</p><blockquote><p><a class="link"   href="https://www.runoob.com/cprogramming/c-function-modf.html" >https://www.runoob.com/cprogramming/c-function-modf.html<i class="fas fa-external-link-alt"></i></a></p></blockquote><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">double</span> <span class="title function_">modf</span><span class="params">(<span class="type">double</span> x, <span class="type">double</span> *integer)</span></span><br></pre></td></tr></table></figure><p>其中参数x是原有浮点数，参数<code>integer</code>是输出型参数，会存放x的整数部分。</p><p>函数的返回值是x的小数部分</p><h2 id="2-2-代码"><a href="#2-2-代码" class="headerlink" title="2.2 代码"></a>2.2 代码</h2><p>第一版本代码如下</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;cmath&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="type">double</span> num;</span><br><span class="line">    cin &gt;&gt; num;</span><br><span class="line"></span><br><span class="line">    <span class="type">double</span> int_part;</span><br><span class="line">    <span class="type">double</span> frac_part = <span class="built_in">modf</span>(num, &amp;int_part);</span><br><span class="line"></span><br><span class="line">    string int_str = <span class="built_in">to_string</span>((<span class="type">int</span>)int_part);<span class="comment">//获取到整数部分并打印</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">char</span> c : int_str) &#123;</span><br><span class="line">        cout &lt;&lt; c &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;. &quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> i = <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span> n = <span class="number">2</span>;<span class="comment">//小数点后n位</span></span><br><span class="line">    <span class="keyword">while</span> (i &lt; n) &#123;</span><br><span class="line">        frac_part *= <span class="number">10</span>;</span><br><span class="line">        <span class="type">double</span> int_part;</span><br><span class="line">        frac_part = <span class="built_in">modf</span>(frac_part, &amp;int_part);</span><br><span class="line">        cout &lt;&lt; (<span class="type">int</span>)int_part &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">        i++;</span><br><span class="line">    &#125;</span><br><span class="line">    cout&lt;&lt;endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这样已经做到了将整数和小数部分给分离打印了</p><h2 id="2-3-精度缺失问题"><a href="#2-3-精度缺失问题" class="headerlink" title="2.3 精度缺失问题"></a>2.3 精度缺失问题</h2><p>但是，测试可以看到，小数的最后一位出现了可能会存在的精度缺失</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/640f0e7945693.png"                      alt="img"                ></p><h1 id="3-解决"><a href="#3-解决" class="headerlink" title="3.解决"></a>3.解决</h1><p>所以还需要用另外一个办法来处理</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;cmath&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="comment">//https://ask.csdn.net/questions/7901417</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="type">double</span> num;</span><br><span class="line">    cin &gt;&gt; num;</span><br><span class="line"></span><br><span class="line">    <span class="type">double</span> int_part;</span><br><span class="line">    <span class="type">double</span> frac_part = <span class="built_in">modf</span>(num, &amp;int_part);</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;int_part: &quot;</span>&lt;&lt; int_part &lt;&lt;<span class="string">&quot; | frac_part: &quot;</span> &lt;&lt;frac_part &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="type">char</span> str[<span class="number">10</span>];</span><br><span class="line">    <span class="built_in">sprintf</span>(str, <span class="string">&quot;%.3f&quot;</span>, frac_part); <span class="comment">//将小数部分打印到字符串中</span></span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;str:  &quot;</span> &lt;&lt; str &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 打印整数部分</span></span><br><span class="line">    string int_str = <span class="built_in">to_string</span>((<span class="type">int</span>)int_part);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">char</span> c : int_str) &#123;</span><br><span class="line">        cout &lt;&lt; c &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;. &quot;</span>;</span><br><span class="line">    <span class="comment">// 打印小数部分</span></span><br><span class="line">    <span class="type">int</span> n = <span class="number">2</span>;<span class="comment">//小数点后n位</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;n;i++) </span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 因为打印到字符串中的数据包含了最开始的0.</span></span><br><span class="line">        <span class="comment">// 前两个字符就是0. 应该从第三个字符开始打印</span></span><br><span class="line">        cout &lt;&lt; str[<span class="number">2</span>+i] &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，问题已解决，符合题目要求！</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/640f0e4ca915c.png"                      alt="img"                ></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;将浮点数每一位拆分并输出&lt;/p&gt;</summary>
    
    
    
    <category term="把题目都给刷干净" scheme="https://blog.musnow.top/categories/%E6%8A%8A%E9%A2%98%E7%9B%AE%E9%83%BD%E7%BB%99%E5%88%B7%E5%B9%B2%E5%87%80/"/>
    
    
    <category term="C++" scheme="https://blog.musnow.top/tags/C/"/>
    
    <category term="OJ刷题" scheme="https://blog.musnow.top/tags/OJ%E5%88%B7%E9%A2%98/"/>
    
  </entry>
  
  <entry>
    <title>【图】拓扑排序</title>
    <link href="https://blog.musnow.top/posts/1720780208/"/>
    <id>https://blog.musnow.top/posts/1720780208/</id>
    <published>2023-03-12T01:00:49.000Z</published>
    <updated>2023-04-03T01:23:39.613Z</updated>
    
    <content type="html"><![CDATA[<p>本文搬运自csdn</p><span id="more"></span><h3 id="文章目录"><a href="#文章目录" class="headerlink" title="文章目录"></a>文章目录</h3><ul><li><a href="#%E6%8B%93%E6%89%91%E6%8E%92%E5%BA%8F">拓扑排序</a><ul><li><a href="#%E4%BB%80%E4%B9%88%E6%98%AF%E6%8B%93%E6%89%91%E6%8E%92%E5%BA%8F">什么是拓扑排序？</a></li><li><a href="#%E6%80%8E%E4%B9%88%E6%8B%93%E6%89%91%E6%8E%92%E5%BA%8F">怎么拓扑排序？</a></li><li><a href="#%E6%8B%93%E6%89%91%E6%8E%92%E5%BA%8F%E5%AE%9E%E7%8E%B0">拓扑排序实现</a></li></ul></li><li><a href="#%E5%85%B3%E9%94%AE%E8%B7%AF%E5%BE%84">关键路径</a><ul><li><a href="#%E4%BB%80%E4%B9%88%E6%98%AF%E5%85%B3%E9%94%AE%E8%B7%AF%E5%BE%84">什么是关键路径？</a></li><li><a href="#%E6%80%8E%E4%B9%88%E6%B1%82%E5%85%B3%E9%94%AE%E8%B7%AF%E5%BE%84">怎么求关键路径？</a></li><li><a href="#%E5%85%B3%E9%94%AE%E8%B7%AF%E5%BE%84%E5%AE%9E%E7%8E%B0">关键路径实现</a></li><li><a href="#%E6%B1%82%E5%85%B3%E9%94%AE%E8%B7%AF%E5%BE%84%E8%BF%87%E7%A8%8B%E7%A4%BA%E4%BE%8B">求关键路径过程示例</a></li></ul></li></ul><h1 id="拓扑排序"><a href="#拓扑排序" class="headerlink" title="拓扑排序"></a><a class="link"   href="https://so.csdn.net/so/search?q=%E6%8B%93%E6%89%91%E6%8E%92%E5%BA%8F&spm=1001.2101.3001.7020" >拓扑排序<i class="fas fa-external-link-alt"></i></a></h1><h2 id="什么是拓扑排序？"><a href="#什么是拓扑排序？" class="headerlink" title="什么是拓扑排序？"></a>什么是拓扑排序？</h2><p>  在图论中，<strong>拓扑排序</strong>是一个<strong>有向无环图</strong>的所有顶点的线性序列(获得拓扑有序序列)。且该序列必须满足下面两个条件：</p><ol><li>每个顶点出现且只出现一次。</li><li>若存在一条从顶点 A 到顶点 B 的路径，那么在序列中顶点 A 出现在顶点 B 的前面。</li></ol><p>有向无环图（DAG）才有拓扑排序，非DAG图没有拓扑排序一说。</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/640d23c77df53.png"                                     >  </p><h2 id="怎么拓扑排序？"><a href="#怎么拓扑排序？" class="headerlink" title="怎么拓扑排序？"></a>怎么拓扑排序？</h2><p><strong>拓扑排序步骤：</strong></p><ol><li>在有向图中选一个没有前驱的顶点且输出之。</li><li>从图中删除该顶点和所有以它为尾的弧。</li></ol><p>  重复上述两步，直至全部顶点均已输出，或者当前图中不存在无前驱的顶点为止。后一种情况则说明有向图中存在环。</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/eb618be1ff4873833c8469dae9093d1e.png"                                     ><br>  图中，V1 和 V6 没有前驱，则可任选一个。假设先输出 V6，在删除 V6 及弧 &lt;V6, V4&gt;,&lt;V6, V5&gt; 之后，只有顶点 V1 没有前驱，输出 V1 且删去 V1 及弧 &lt;V1, V2&gt;,&lt;V1, V3&gt; 和 &lt;V1, V4&gt;，之后 V3 和 V4 都没有前驱。依此类推，可从中任选一个继续进行。整个拓扑排序的过程如上图。</p><h2 id="拓扑排序实现"><a href="#拓扑排序实现" class="headerlink" title="拓扑排序实现"></a>拓扑排序实现</h2><p>  我们采用<strong>邻接表</strong>作有向图的存储结构，且在头结点中增加一个存放顶点入度的数组。入度为零的顶点即为没有前驱的顶点，删除顶点以及它为尾的弧的操作，则可换以弧头顶点的入度减 1 来实现。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">Status <span class="title function_">TopologicalSort</span><span class="params">(ALGraph G)</span>&#123;</span><br><span class="line"><span class="comment">//有向图G采用邻接表存储结构 </span></span><br><span class="line"><span class="comment">//若G无回路，则输出G的顶点的一个拓扑序列并返回OK，否则ERROR </span></span><br><span class="line">FindInDegree(G, indegree);<span class="comment">//对各顶点求入度indegree[0..vernum-1] </span></span><br><span class="line">InitStack(S);</span><br><span class="line"><span class="keyword">for</span>(i=<span class="number">0</span>;i&lt;G.vexnum;i++)<span class="comment">//建零入度顶点栈S </span></span><br><span class="line"><span class="keyword">if</span>(!indegree[i])<span class="comment">//入度为0者进栈 </span></span><br><span class="line">Push(S, i);</span><br><span class="line">count = <span class="number">0</span>;<span class="comment">//对输出顶点计数 </span></span><br><span class="line"><span class="keyword">while</span>(!StackEmpty(S))&#123;</span><br><span class="line">Pop(S, i);printf(i, G.vertices[i].data);count++;<span class="comment">//输出i号顶点并计数 </span></span><br><span class="line"><span class="keyword">for</span>(p=G.vertices[i].firstarc; p; p=p-&gt;nextarc)&#123;</span><br><span class="line">k = p-&gt;adjvex;<span class="comment">//对i号顶点的每个邻接点的入度减1 </span></span><br><span class="line"><span class="keyword">if</span>(!(--indegree[k]))<span class="comment">//若入度减为0，则入栈 </span></span><br><span class="line">Push(S, k);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span>(count&lt;G.vexnum)<span class="comment">//该有向图有回路 </span></span><br><span class="line"><span class="keyword">return</span> ERROR;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"><span class="keyword">return</span> OK;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  对有 n 个顶点和 e 条弧的有向图而言，建立求各顶点的入度的时间复杂度 O(e)；建零入度顶点栈的时间复杂度为 O(n)；在拓扑排序过程中，若有向图无环，则每个顶点进一次栈，出一次栈，入度减 1 的操作在 WHILE 语句中总共执行 e 次，所以，总的**时间复杂度为 O(n+e)**。<br>  当有向图中无环时，也可利用深度优先遍历进行拓扑排序，因为图中无环，则由图中某点出发进行深度优先搜索遍历时，最先退出 DFS 函数的顶点即出度为零的顶点，是拓扑有序序列中最后一个顶点。由此，按退出 DFS 函数的先后记录下来的顶点序列(如同求强连通分量时 finished 数组中的顶点序列)即为逆向的拓扑有序序列。  </p><h1 id="关键路径"><a href="#关键路径" class="headerlink" title="关键路径"></a>关键路径</h1><h2 id="什么是关键路径？"><a href="#什么是关键路径？" class="headerlink" title="什么是关键路径？"></a>什么是关键路径？</h2><p><strong>AOE网：</strong> 在一个表示工程的带权有向图中，用顶点表示事件（如V1），用有向边表示活动（如&lt;V1,V2&gt; &#x3D; a1），边上的权值表示活动的持续时间，称这样的有向图为边表示的活动的网。<br><strong>源点：</strong> 在AOE网中，没有入边的顶点称为源点；如顶点V1。<br><strong>终点：</strong> 在AOE网中，没有出边的顶点称为终点；如顶点V9。<br><strong>AOE网的性质：</strong></p><ol><li>只有在进入某顶点的活动都已经结束，该顶点所代表的事件才发生；例如，V5 事件发生需要 a4 和 a5 两个活动都结束。</li><li>只有在某顶点所代表的事件发生后，从该顶点出发的各活动才开始；例如，V5 事件结束，活动 a7 和 a8 活动才能开始。<br><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/d52c8bebe169544eea3f93136b617d61.jpeg"                                     ></li></ol><p>  在AOE网中，所有活动都完成才能到达终点，因此完成整个工程所必须花费的时间（即最短工期）应该为源点到终点的最大路径长度。具有最大路径长度的路径称为<strong>关键路径</strong>。关键路径上的活动称为<strong>关键活动</strong>。<br>  假设开始点是 V1，从 V1 到 Vi 的最长路径长度叫做事件 Vi 的<strong>最早发生事件</strong>。这个时间决定了所有以 Vi 为尾的弧所表示的活动的<strong>最早开始时间</strong>。我们用 e(i) 表示活动 ai 的最早开始时间。还可以定义一个活动的<strong>最迟开始时间</strong> l(i)，这是在不推迟整个过程完成的前提下，活动 ai 最迟必须开始进行的时间。两者之差 l(i)-e(i) 意味着完成活动 ai 的时间余量。我们把 l(i)&#x3D;e(i) 的活动叫做关键活动。</p><p><strong>向关键路径要时间，向非关键路径要资源。</strong></p><ol><li><strong>从前往后，计算工期与每项活动的最早开始时间；</strong></li><li><strong>从后往前，倒推每项活动最晚开始时间。</strong></li><li><strong>关键路径：最早开始时间&#x3D;最晚开始时间</strong></li></ol><h2 id="怎么求关键路径？"><a href="#怎么求关键路径？" class="headerlink" title="怎么求关键路径？"></a>怎么求关键路径？</h2><p>ve(j)：最早发生时间<br>vl(j)：最迟发生时间</p><ol><li>输入 e 条弧&lt;j, k&gt;，建立 AOE-网的存储结构；</li><li>从源点 v0 出发，令 <code>ve[0]=0</code>，按拓扑有序求其余各顶点的最早发现时间 <code>ve[i] (1≤i≤n-1)</code>。如果得到的拓扑有序序列中顶点个数小于网中顶点数 n，则说明网中存在环，不能求关键路径，算法终止；否则执行步骤(3)。</li><li>从汇点 vn 出发，令 <code>vl[n-1]=ve[n-1]</code>，按逆拓扑有序求其余各顶点的最迟发生时间<code> vl[i] (n-2≥i≥2)</code>；</li><li>根据各顶点的 ve 和 vl 值，求每条弧 s 的最早开始时间 e(s) 和最迟开始时间 l(s)。若某条弧满足条件 e(s)&#x3D;l(s)，则为关键活动。</li></ol><p>  根据上述算法，计算各顶点的 ve 值是在拓扑排序的过程中进行的，需对拓扑排序的算法作如下修改：</p><ol><li>在拓扑排序之前设初值，令 <code>ve[i]=0 (0≤i≤n-1)</code>；</li><li>在算法中增加一个计算 vj 的直接后继 vk 的最早发生时间的操作：若 <code>ve[j]+dut(&lt;j, k&gt;) &gt; ve[k]</code>，则 <code>ve[k]=ve[j]+dut(&lt;j, k&gt;)</code>；</li><li>为了能按逆拓扑有序序列的顺序计算各顶点的 vl 值，需记下在拓扑排序的过程中求得的拓扑有序序列，这需要在拓扑排序算法中，增设一个栈以记录拓扑有序序列，则在计算求得各顶点的 ve 值之后，从栈顶至栈底便为逆拓扑有序序列。</li></ol><h2 id="关键路径实现"><a href="#关键路径实现" class="headerlink" title="关键路径实现"></a>关键路径实现</h2><p><strong>改写的拓扑排序代码：</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">Status <span class="title function_">TopologicalOrder</span><span class="params">(ALGraph G, Stack &amp;T)</span>&#123;</span><br><span class="line"><span class="comment">//有向图G采用邻接表存储结构，求各顶点事件的最早发生时间 ve(全局变量) </span></span><br><span class="line"><span class="comment">//T为拓扑序列顶点栈，S为零入度顶点栈 </span></span><br><span class="line"><span class="comment">//若G无回路，则用栈T返回G的一个拓扑序列，且函数值为OK，否则ERROR </span></span><br><span class="line">FindInDegree(G, indegree);<span class="comment">//对各顶点求入度indegree[0..vernum-1] </span></span><br><span class="line">InitStack(S);<span class="comment">//建零入度顶点栈S </span></span><br><span class="line"><span class="keyword">for</span>(i=<span class="number">0</span>;i&lt;G.vexnum;i++)<span class="comment">//建零入度顶点栈S </span></span><br><span class="line"><span class="keyword">if</span>(!indegree[i])<span class="comment">//入度为0者进栈 </span></span><br><span class="line">Push(S, i);</span><br><span class="line">InitStack(T);count = <span class="number">0</span>;ve[<span class="number">0.</span>.G.vexnum-<span class="number">1</span>] = <span class="number">0</span>;<span class="comment">//初始化 </span></span><br><span class="line"><span class="keyword">while</span>(!StackEmpty(S))&#123;</span><br><span class="line">Pop(S, j);Push(T, j);count++;<span class="comment">//j号顶点入T栈并计数 </span></span><br><span class="line"><span class="keyword">for</span>(p=G.vertices[j].firstarc; p; p=p-&gt;nextarc)&#123;</span><br><span class="line">k = p-&gt;adjvex;<span class="comment">//对j号顶点的每个邻接点的入度减1 </span></span><br><span class="line"><span class="keyword">if</span>(--indegree[k] == <span class="number">0</span>)<span class="comment">//若入度减为0，则入栈 </span></span><br><span class="line">Push(S, k);</span><br><span class="line"><span class="keyword">if</span>(ve[j]+ *(p-&gt;info)&gt;ve[k])</span><br><span class="line">ve[k] = ve[j] + *(p-&gt;info);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span>(count &lt; G.vexnum)<span class="comment">//该有向图有回路 </span></span><br><span class="line"><span class="keyword">return</span> ERROR;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"><span class="keyword">return</span> OK;</span><br><span class="line">&#125; </span><br></pre></td></tr></table></figure><p><strong>关键路径算法：</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">Status <span class="title function_">CriticalPath</span><span class="params">(ALGraph G)</span>&#123;</span><br><span class="line"><span class="comment">//G为有向图，输出G的各项关键活动 </span></span><br><span class="line"><span class="keyword">if</span>(!TopologicalOrder(G, T))</span><br><span class="line"><span class="keyword">return</span> ERROR;</span><br><span class="line">vl[<span class="number">0.</span>.G.vexnum-<span class="number">1</span>] = ve[G.vexnum-<span class="number">1</span>];<span class="comment">//初始化顶点事件的最迟发生事件 </span></span><br><span class="line"><span class="keyword">while</span>(!StackEmpty(T))&#123;<span class="comment">//按拓扑逆序求各顶点的vl值 </span></span><br><span class="line"><span class="keyword">for</span>(Pop(T, j),p=G.vertices[j].firstarc; p; p=p-&gt;nextarc)&#123;</span><br><span class="line">k = p-&gt;adjvex;dut = *(p-&gt;info);</span><br><span class="line"><span class="keyword">if</span>(vl[k]-dut &lt; vl[j])</span><br><span class="line">vl[j] = vl[k]-dut;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">for</span>(j=<span class="number">0</span>;j&lt;G.vexnum;j++)&#123;<span class="comment">//求ee，el和关键活动 </span></span><br><span class="line"><span class="keyword">for</span>(p=G.vertices[j].firstarc; p; p=p-&gt;nextarc)&#123;</span><br><span class="line">k = p-&gt;adjvex;dut = *(p-&gt;info);</span><br><span class="line">ee = ve[j];el = vl[k]-dut;</span><br><span class="line">tag = (ee==el)?<span class="string">&#x27;*&#x27;</span>:<span class="string">&#x27;&#x27;</span>;</span><br><span class="line">printf(j, k, dut, ee, el, tag);<span class="comment">//输出关键活动 </span></span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125; </span><br></pre></td></tr></table></figure><p>  上面两种算法的时间复杂度均为 O(n+e)，计算弧的活动最早开始时间和最迟开始时间的时间复杂度为 O(e)，所以总的求关键路径的**时间复杂度为 O(n+e)**。  </p><h2 id="求关键路径过程示例"><a href="#求关键路径过程示例" class="headerlink" title="求关键路径过程示例"></a>求关键路径过程示例</h2><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/f0dcb74c0eff3d6f56e8415a2664e1dd.png"                                     ><br><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/d52c8bebe169544eea3f93136b617d61.jpeg"                                     > </p><p>上图的关键活动为 a1，a4，a7，a8，a10 和 a11。它们构成两条关键路径：(V1，V2，V5，V7，V9) 和 (V1，V2，V5，V8，V9)。  </p><p><strong>实践证明：用 AOE-网来估算某些工程完成的时间是非常有用的。只有在不改变网的关键路径的情况下，提高关键活动的速度才有效。若网中有几条关键路径，那么，单是提高一条关键路径上的关键活动的速度，还不能导致整个工程缩短工期，而必须提高同时在几条关键路径上的活动速度。</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">版权声明：本文为CSDN博主「qw&amp;amp;jy」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。</span><br><span class="line">原文链接：https://blog.csdn.net/qq_43448856/article/details/119959241</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文搬运自csdn&lt;/p&gt;</summary>
    
    
    
    <category term="数据结构太复杂" scheme="https://blog.musnow.top/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%A4%AA%E5%A4%8D%E6%9D%82/"/>
    
    
    <category term="数据结构" scheme="https://blog.musnow.top/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
    
    <category term="Java" scheme="https://blog.musnow.top/tags/Java/"/>
    
    <category term="图" scheme="https://blog.musnow.top/tags/%E5%9B%BE/"/>
    
  </entry>
  
  <entry>
    <title>【教程】Typora+PicGo+又拍云uss搭建博客图床</title>
    <link href="https://blog.musnow.top/posts/3648256488/"/>
    <id>https://blog.musnow.top/posts/3648256488/</id>
    <published>2023-03-12T00:35:28.000Z</published>
    <updated>2023-04-03T01:23:39.648Z</updated>
    
    <content type="html"><![CDATA[<p>Typora+PicGo+又拍云UOS搭建博客图床</p><span id="more"></span><h1 id="1-又拍云uos配置"><a href="#1-又拍云uos配置" class="headerlink" title="1.又拍云uos配置"></a>1.又拍云uos配置</h1><h2 id="1-1-创建"><a href="#1-1-创建" class="headerlink" title="1.1 创建"></a>1.1 创建</h2><p>进入控制台后，点击云存储，创建云存储服务。配置你的服务名称，操作员名称，和操作员的权限。</p><p>请保存操作员的密码，后续配置picgo用得到</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/51c2ef0cca2e70e32b09857f5358c54c.png"                      alt="image-20230312084831623"                ></p><h2 id="1-2-加速域名"><a href="#1-2-加速域名" class="headerlink" title="1.2 加速域名"></a>1.2 加速域名</h2><p>点击如下图齿轮进入配置页面</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/8e0fadfea5eb848011682a22a44f83ba.png"                      alt="image-20230312083903790"                ></p><p>在下图绑定你的域名（需要已备案域名）</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/625bc16e55372917417c26d5c3e819c2.png"                      alt="image-20230312084009866"                ></p><h2 id="1-3-https"><a href="#1-3-https" class="headerlink" title="1.3 https"></a>1.3 https</h2><p>随后在下图所示处配置域名的证书。如果你没有ssl证书，可以在又拍云的控制台主页申请一个免费的<code>let’s encrpy</code>证书</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/24b7d19b0bea93e1b84a24389afaf237.png"                      alt="image-20230312084046578"                ></p><h2 id="1-4-访问控制"><a href="#1-4-访问控制" class="headerlink" title="1.4 访问控制"></a>1.4 访问控制</h2><p>在访问控制里面可以配置各种形式的控制。对于博客图床而言，referer防盗链是一定要打开的，其余根据自己的需求进行配置</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">IP黑白名单 <span class="comment"># 白名单：允许的ip,黑名单：不允许的ip</span></span><br><span class="line"><span class="comment"># 如果发现某一个ip的请求次数过多，有恶意请求嫌疑，可以暂时将这个ip设置为黑名单</span></span><br><span class="line">CORS 跨域共享 <span class="comment"># 参考http://ruanyifeng.com/blog/2016/04/cors.html</span></span><br><span class="line">地区访问限制 <span class="comment"># 禁止或允许特定地区的限制</span></span><br><span class="line">Referer 防盗链 <span class="comment"># 重点，建议开启白名单，只将自己的域名添加到白名单中（建议开启）</span></span><br><span class="line">User-Agent 防盗链 <span class="comment"># 限制只有某种类型的客户端、浏览器可以访问资源</span></span><br><span class="line">Token 防盗链 <span class="comment"># 有些麻烦，请自行了解。对于静态博客而言，并不方便</span></span><br><span class="line">回源鉴权 <span class="comment"># 如果源站服务器有鉴权操作，可以在这里配置</span></span><br><span class="line">IP 访问限制 <span class="comment"># 对单个ip的请求速率进行限制（建议开启）</span></span><br><span class="line">CC 防护 <span class="comment"># 防护cc攻击</span></span><br><span class="line">WAF 保护 <span class="comment"># Web Application Firewall</span></span><br></pre></td></tr></table></figure><h1 id="2-picgo配置"><a href="#2-picgo配置" class="headerlink" title="2.picgo配置"></a>2.picgo配置</h1><p>相比于阿里云oss，这里的picgo配置就挺明了了</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/be5e600f6ec7f568f6c9722d88291f7e.png"                      alt="image-20230312084344938"                ></p><p>在picgo的<strong>上传区</strong>测试一下是否能正常上传</p><h1 id="3-typora"><a href="#3-typora" class="headerlink" title="3.typora"></a>3.typora</h1><p>typora的偏好设置中，图像一栏选择上传图片，勾选如下几项。</p><p>上传服务设置为<code>picgo(app)</code>，路径填写为你picgo的安装路径。</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/59936c6ea7a652bd02ca47fa81b94f7e.png"                      alt="image-20230312084419417"                ></p><p>随后点击左下角验证图片上传，出现如下所示代表配置成功</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/42c1bf3d071335235ff8c6d201a5a66e.png"                      alt="image-20230312084524751"                ></p><p>此时我们的图床就已经配置好了</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;Typora+PicGo+又拍云UOS搭建博客图床&lt;/p&gt;</summary>
    
    
    
    <category term="差生文具多" scheme="https://blog.musnow.top/categories/%E5%B7%AE%E7%94%9F%E6%96%87%E5%85%B7%E5%A4%9A/"/>
    
    
    <category term="图床" scheme="https://blog.musnow.top/tags/%E5%9B%BE%E5%BA%8A/"/>
    
    <category term="typora" scheme="https://blog.musnow.top/tags/typora/"/>
    
  </entry>
  
  <entry>
    <title>【建站】hexo搭建博客+建站相关教程</title>
    <link href="https://blog.musnow.top/posts/1888648770/"/>
    <id>https://blog.musnow.top/posts/1888648770/</id>
    <published>2023-03-12T00:28:14.000Z</published>
    <updated>2023-04-03T01:23:39.567Z</updated>
    
    <content type="html"><![CDATA[<p>关于HEXO建站相关博客汇总</p><span id="more"></span><h1 id="1-hexo部署和主题自定义"><a href="#1-hexo部署和主题自定义" class="headerlink" title="1.hexo部署和主题自定义"></a>1.hexo部署和主题自定义</h1><p><a href="https://blog.musnow.top/posts/138502038/">【教程】hexo搭建博客+keep主题美化</a></p><h1 id="2-配置图床"><a href="#2-配置图床" class="headerlink" title="2.配置图床"></a>2.配置图床</h1><p>以下为typora+picgo配置图床的教程</p><p><a href="https://blog.musnow.top/posts/373497921/">【Picgo】正确重命名文件，避免重复上传</a></p><h2 id="2-1-阿里云oss"><a href="#2-1-阿里云oss" class="headerlink" title="2.1 阿里云oss"></a>2.1 阿里云oss</h2><p><a href="https://blog.musnow.top/posts/1825409965/">【教程】Typora+PicGo+阿里云OSS搭建博客图床</a></p><h2 id="2-2-又拍云uos"><a href="#2-2-又拍云uos" class="headerlink" title="2.2 又拍云uos"></a>2.2 又拍云uos</h2><p>相比于阿里云oss的bucket只有防盗链，又拍云的uos还有ip黑白名单，cc防护，单ip访问限制等等功能。对于博客图床防盗刷而言，很有用。</p><p>但又拍云uos需要<strong>域名已备案</strong>才能自定义域名，否则只能使用官方提供的测试域名。如果你的域名已备案，个人更推荐使用又拍云uos作为博客图床。</p><p><a href="https://blog.musnow.top/posts/3648256488/">【教程】Typora+PicGo+又拍云USS搭建博客图床</a></p><h2 id="2-3-自建图床lsky"><a href="#2-3-自建图床lsky" class="headerlink" title="2.3 自建图床lsky"></a>2.3 自建图床lsky</h2><p>自建图床的优势在于，<strong>图片都在你服务器的本地</strong>，可以直接tar打包进行备份，或者手动从服务器下载到电脑作为备份。</p><p>个人不太喜欢oss的一点，就是大多数oss服务商都没有提供批量下载oss内部文件的选项。估计是考虑到存储服务器的负载问题，所以没有提供这个功能。毕竟批量下载大量文件，是很耗资源的。</p><p>虽然可以用python代码来实现批量下载，但服务商能直接提供批量下载的功能，还是更方面嘛！</p><p>兰空图床是自建图床中比较方便的一个，且支持picgo上传</p><p><a href="https://blog.musnow.top/posts/370869610/">【Docker】配置lsky pro兰空图床</a> </p>]]></content>
    
    
    <summary type="html">&lt;p&gt;关于HEXO建站相关博客汇总&lt;/p&gt;</summary>
    
    
    
    <category term="差生文具多" scheme="https://blog.musnow.top/categories/%E5%B7%AE%E7%94%9F%E6%96%87%E5%85%B7%E5%A4%9A/"/>
    
    
    <category term="Hexo" scheme="https://blog.musnow.top/tags/Hexo/"/>
    
  </entry>
  
  <entry>
    <title>【docker】通过配置文件，修改已有容器的端口映射</title>
    <link href="https://blog.musnow.top/posts/1996139841/"/>
    <id>https://blog.musnow.top/posts/1996139841/</id>
    <published>2023-03-11T11:45:33.000Z</published>
    <updated>2023-04-03T01:23:39.621Z</updated>
    
    <content type="html"><![CDATA[<p>docker修改已有容器的端口映射</p><span id="more"></span><blockquote><p>参考 <a class="link"   href="https://www.jb51.net/article/257510.htm" >https://www.jb51.net/article/257510.htm<i class="fas fa-external-link-alt"></i></a></p></blockquote><h1 id="1-需求"><a href="#1-需求" class="headerlink" title="1.需求"></a>1.需求</h1><p>之前我在我的nas上部署了一个centos容器，当时脑子抽了，没想起来要多映射几个端口。只映射了容器的22端口（ssh）</p><p>对于应用开发而言，没有能够直接访问的端口可太蛋疼了。在centos里面开frp穿透出来访问吧，又太麻烦。</p><p>如果在绿联的控制台里面修改端口隐射，<strong>会直接创建一个新的容器</strong>，原有容器的信息会丢失。现在这个centos系统已经用了一段时间了，开发环境都配置好了。创建一个新容器就是得重头配置，虽然不是啥难事，但是太耗时间了！！！😥</p><p>于是就想着，能否通过修改配置文件的方式，实现修改容器的端口映射。</p><h1 id="2-操作"><a href="#2-操作" class="headerlink" title="2.操作"></a>2.操作</h1><h2 id="2-1-开启ssh并关闭docker服务"><a href="#2-1-开启ssh并关闭docker服务" class="headerlink" title="2.1 开启ssh并关闭docker服务"></a>2.1 开启ssh并关闭docker服务</h2><p>修改之前，<strong>需要开启绿联nas的ssh服务</strong>。绿联nas如何开启ssh参考 <a class="link"   href="https://www.bilibili.com/video/BV1J84y1C7Zu/?spm_id_from=333.337.search-card.all.click" >视频<i class="fas fa-external-link-alt"></i></a></p><p>链接上ssh之后，先使用<code>docker ps</code>，查看需要修改容器的id。注意，这里的id并不是完整的容器id，先记录下来。</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/4531c3a572e0afe317768fee05254943.png"                      alt="image-20230311193038037"                ></p><p>图中也能看到，当前这个centos容器只映射了22端口</p><hr><p>进入绿联的客户端，关闭docker服务</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/aa3879c8c27f9e13e4300376eec8176b.png"                      alt="image-20230311192756106"                ></p><p>命令行里面再次执行<code>docker ps</code>，确认docker服务确实已经关闭</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ docker ps</span><br><span class="line">Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?</span><br></pre></td></tr></table></figure><p>如果显示如上，则代表docker并没有在运行。</p><hr><h2 id="2-2-找到docker安装路径"><a href="#2-2-找到docker安装路径" class="headerlink" title="2.2 找到docker安装路径"></a>2.2 找到docker安装路径</h2><p>接下来要做的，就是查看docker安装的位置。对于群晖、威联通、绿联这种nas而言，其系统内对docker安装挂载的位置是不同的。比如绿联的nas自带了16gb的闪存，但docker的安装位置并不在这里面。</p><p>一般的centos系统（以我的云服务器为例），docker的安装位置应该是如下路径</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/var/lib/docker</span><br></pre></td></tr></table></figure><p>ls查看内部文件，如下</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># ls /var/lib/docker</span><br><span class="line">containers  image  network  overlay2  plugins  swarm  tmp  trust  volumes</span><br></pre></td></tr></table></figure><p>而绿联的系统并没有这个路径</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">can&#x27;t open &#x27;/var/lib/docker&#x27;: No such file or directory</span><br></pre></td></tr></table></figure><p>查看<code>/var/lib</code>，并没有发现docker文件夹</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># ls /var/lib/</span><br><span class="line">dbus   nginx  samba</span><br></pre></td></tr></table></figure><p>查看<code>/var</code>，发现了一个dockerd，猜测这是docker相关文件存放的位置</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># ls /var</span><br><span class="line">all_aria2.txt    dockerd          factory   </span><br></pre></td></tr></table></figure><p>里面只有一个json文件</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># ls /var/dockerd</span><br><span class="line">daemon.json</span><br></pre></td></tr></table></figure><p>cat命令查看，果不其然，是docker的安装路径</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># cat /var/dockerd/daemon.json</span><br><span class="line">&#123;&quot;data-root&quot;:&quot;/mnt/media_rw/2550c297-7334-455f-9fce-e0a00aad40c8/.ugreen_nas/6&quot;,&quot;log-level&quot;:&quot;warn&quot;,&quot;iptables&quot;:true,&quot;api-cors-header&quot;:&quot;*&quot;,&quot;host375&quot;],&quot;registry-mirrors&quot;:[&quot;https://registry.docker-cn.com&quot;]&#125;</span><br></pre></td></tr></table></figure><p>这就是docker的安装位置了</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/mnt/media_rw/2550c297-7334-455f-9fce-e0a00aad40c8/.ugreen_nas/6</span><br></pre></td></tr></table></figure><p>ls查看，文件结构和云服务器的docker安装位置基本一样，这代表我们找对了！</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"># ls /mnt/media_rw/2550c297-7334-455f-9fce-e0a00aad40c8/.ugreen_nas/6</span><br><span class="line">buildkit    containerd  containers  image       network     overlay2    plugins     runtimes    swarm    </span><br><span class="line">tmp        trust       volumes</span><br></pre></td></tr></table></figure><h2 id="2-3-找到对应容器的配置文件路径"><a href="#2-3-找到对应容器的配置文件路径" class="headerlink" title="2.3 找到对应容器的配置文件路径"></a>2.3 找到对应容器的配置文件路径</h2><p>docker路径下的<code>containers</code>文件夹，包含了不同容器的配置<strong>文件夹</strong>。ls查看，发现都是一些看不懂的字符串。实际上，这里的字符串就是容器的id</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"># ls /mnt/media_rw/2550c297-7334-455f-9fce-e0a00aad40c8/.ugreen_nas/6/containers</span><br><span class="line">2b8f29d7b4b0ad624fb02048cb91c569e6e52409fe246e1c741bdd13ed7242cf  9113e1ec4740726feb9623a897f81b9066bc1b63811bfc0a65875d8a2c46c603</span><br><span class="line">47aa763o2da18686c6301047ef57f24fb611a2ebea2066f0dbk68205a40777e3</span><br></pre></td></tr></table></figure><p>前面提到，<code>docker ps</code>显示的容器id并不是完整的。因为完整的很长很长，不可能在ps命令里面全显示出来。</p><p>我需要修改的目标容器，缩短的id如下，对应的正是<code>containers</code>文件夹里面的第一个文件夹</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">2b8f29d7b4b0</span><br></pre></td></tr></table></figure><p>看看这个文件夹里面有啥东西</p><p><font color=Red>注意</font>：此时命令已经很长很长。避免出错，建议开个记事本，在里面把命令写好，再复制到终端中执行！</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ls -l /mnt/media_rw/2550c297-7334-455f-9fce-e0a00aad40c8/.ugreen_nas/6/containers/2b8f29d7b4b0ad624fb02048cb91c569e6e52409fe246e1c741bdd13ed7242cf</span><br></pre></td></tr></table></figure><p>得到的文件结构如下，我们需要修改的是<code>config.v2.json</code>和<code>hostconfig.json</code></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">-rw-r-----    1 root     root         87802 Mar 11 18:12 2b8f29d7b4b0ad624fb02048cb91c569e6e52409fe246e1c741bdd13ed7242cf-json.log</span><br><span class="line">drwx------    2 root     root          4096 Feb 23 18:42 checkpoints</span><br><span class="line">-rw-------    1 root     root          3300 Mar 11 18:12 config.v2.json</span><br><span class="line">-rw-r--r--    1 root     root          1925 Mar 11 18:12 hostconfig.json</span><br><span class="line">-rw-r--r--    1 root     root            13 Mar 11 18:12 hostname</span><br><span class="line">-rw-r--r--    1 root     root           174 Mar 11 18:12 hosts</span><br><span class="line">drwx--x---    2 root     root          4096 Feb 23 18:43 mounts</span><br><span class="line">-rw-r--r--    1 root     root            83 Mar 11 18:12 resolv.conf</span><br><span class="line">-rw-r--r--    1 root     root            71 Mar 11 18:12 resolv.conf.hash</span><br></pre></td></tr></table></figure><h2 id="2-4-备份"><a href="#2-4-备份" class="headerlink" title="2.4 备份"></a>2.4 备份</h2><p>在linux的命令行中操作，备份是必不可少的！使用cp命令，把配置文件复制一份，并在名字中添加上<code>.bak</code>予以区别</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cp 源文件 目标文件</span><br></pre></td></tr></table></figure><p>以下是cp命令备份<code>config.v2.json</code>文件的命令示例</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cp /mnt/media_rw/2550c297-7334-455f-9fce-e0a00aad40c8/.ugreen_nas/6/containers/2b8f29d7b4b0ad624fb02048cb91c569e6e52409fe246e1c741bdd13ed7242cf/config.v2.json /mnt/media_rw/2550c297-7334-455f-9fce-e0a00aad40c8/.ugreen_nas/6/containers/2b8f29d7b4b0ad624fb02048cb91c569e6e52409fe246e1c741bdd13ed7242cf/config.v2.bak.json</span><br></pre></td></tr></table></figure><p>备份了我们需要修改的两个文件后，接下来就是修改配置文件了</p><h2 id="2-5-修改配置文件"><a href="#2-5-修改配置文件" class="headerlink" title="2.5 修改配置文件"></a>2.5 修改配置文件</h2><h3 id="2-5-1-hostconfig-json"><a href="#2-5-1-hostconfig-json" class="headerlink" title="2.5.1  hostconfig.json"></a>2.5.1  hostconfig.json</h3><p>使用vim来进行编辑（编辑之前，可以用cat先复制出内容，放到vsc等编辑器中格式化，方便找到对应的位置）</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim /mnt/media_rw/2550c297-7334-455f-9fce-e0a00aad40c8/.ugreen_nas/6/containers/2b8f29d7b4b0ad624fb02048cb91c569e6e52409fe246e1c741bdd13ed7242cf/hostconfig.json</span><br></pre></td></tr></table></figure><p>如下便是要修改的字段，可以看到里面只有对22端口的映射</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">&quot;PortBindings&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;22/tcp&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">            <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;HostIp&quot;</span><span class="punctuation">:</span> <span class="string">&quot;0.0.0.0&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;HostPort&quot;</span><span class="punctuation">:</span> <span class="string">&quot;22222&quot;</span></span><br><span class="line">            <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;HostIp&quot;</span><span class="punctuation">:</span> <span class="string">&quot;::&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;HostPort&quot;</span><span class="punctuation">:</span> <span class="string">&quot;22222&quot;</span></span><br><span class="line">            <span class="punctuation">&#125;</span></span><br><span class="line">        <span class="punctuation">]</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>我们照猫画虎，直接添加上另外两个端口的映射。</p><p>在英文输入法下，按<code>i</code>进入vim的编辑模式，通过键盘移动光标到指定位置，修改文件</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">&quot;PortBindings&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;22/tcp&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">            <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;HostIp&quot;</span><span class="punctuation">:</span> <span class="string">&quot;0.0.0.0&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;HostPort&quot;</span><span class="punctuation">:</span> <span class="string">&quot;22222&quot;</span></span><br><span class="line">            <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;HostIp&quot;</span><span class="punctuation">:</span> <span class="string">&quot;::&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;HostPort&quot;</span><span class="punctuation">:</span> <span class="string">&quot;22222&quot;</span></span><br><span class="line">            <span class="punctuation">&#125;</span></span><br><span class="line">        <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;50000/tcp&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">            <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;HostIp&quot;</span><span class="punctuation">:</span> <span class="string">&quot;0.0.0.0&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;HostPort&quot;</span><span class="punctuation">:</span> <span class="string">&quot;50000&quot;</span></span><br><span class="line">            <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;HostIp&quot;</span><span class="punctuation">:</span> <span class="string">&quot;::&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;HostPort&quot;</span><span class="punctuation">:</span> <span class="string">&quot;50000&quot;</span></span><br><span class="line">            <span class="punctuation">&#125;</span></span><br><span class="line">        <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;50001/tcp&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">            <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;HostIp&quot;</span><span class="punctuation">:</span> <span class="string">&quot;0.0.0.0&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;HostPort&quot;</span><span class="punctuation">:</span> <span class="string">&quot;50001&quot;</span></span><br><span class="line">            <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;HostIp&quot;</span><span class="punctuation">:</span> <span class="string">&quot;::&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;HostPort&quot;</span><span class="punctuation">:</span> <span class="string">&quot;50001&quot;</span></span><br><span class="line">            <span class="punctuation">&#125;</span></span><br><span class="line">        <span class="punctuation">]</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>编辑好了之后，按esc退出编辑模式，<code>:wq</code>保存并退出</p><hr><h3 id="2-5-2-config-v2-json"><a href="#2-5-2-config-v2-json" class="headerlink" title="2.5.2 config.v2.json"></a>2.5.2 config.v2.json</h3><p>这个文件需要修改的是如下字段</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">&quot;ExposedPorts&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;22/tcp&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span><span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>添加上刚刚在<code>hostconfig.json</code>里面新增的端口</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">&quot;ExposedPorts&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;22/tcp&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;50000/tcp&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;50001/tcp&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>同样<code>:wq</code>保存并退出</p><h2 id="2-6-现在就已经修改好啦！"><a href="#2-6-现在就已经修改好啦！" class="headerlink" title="2.6 现在就已经修改好啦！"></a>2.6 现在就已经修改好啦！</h2><p>相同的位置还可以修改容器的hostname。</p><p>用docker安装的centos镜像，是不能在centos系统中修改容器hostname的（修改可以，但是不会生效）</p><h1 id="3-重启docker，查看是否修改成功"><a href="#3-重启docker，查看是否修改成功" class="headerlink" title="3.重启docker，查看是否修改成功"></a>3.重启docker，查看是否修改成功</h1><p>在客户端里面重启docker服务之后，使用docker ps命令查看端口映射是否修改成功</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/04cbabad9ae0b3f2dbe29136281063b8.png"                      alt="image-20230311195511866"                ></p><p>可以看到，多出了50000和50001端口，修改成功！</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;docker修改已有容器的端口映射&lt;/p&gt;</summary>
    
    
    
    <category term="docker真好玩" scheme="https://blog.musnow.top/categories/docker%E7%9C%9F%E5%A5%BD%E7%8E%A9/"/>
    
    
    <category term="Linux" scheme="https://blog.musnow.top/tags/Linux/"/>
    
    <category term="CentOS" scheme="https://blog.musnow.top/tags/CentOS/"/>
    
    <category term="Docker" scheme="https://blog.musnow.top/tags/Docker/"/>
    
  </entry>
  
  <entry>
    <title>【js】用html+javascript静态页面实现伪重定向</title>
    <link href="https://blog.musnow.top/posts/1472038549/"/>
    <id>https://blog.musnow.top/posts/1472038549/</id>
    <published>2023-03-10T11:50:31.000Z</published>
    <updated>2023-04-03T01:23:39.644Z</updated>
    
    <content type="html"><![CDATA[<p>javascript代码实现一个伪重定向</p><span id="more"></span><h1 id="1-前言"><a href="#1-前言" class="headerlink" title="1.前言"></a>1.前言</h1><p>本站的域名之前有修改过，<strong>但一些朋友的友链，和开往里面的链接尚未更新</strong>。旧的域名又米有备案，不能解析到国内服务器上用nginx进行30x转发。</p><p>这时候我就想到了一招，用js的代码来实现一个url的切换，相当于一个伪重定向。因为是静态文件，能直接部署到netlify等平台上。</p><p>百度了一下js是否支持这种语法，嘿，支持！</p><p>那不直接开整！</p><h1 id="2-代码"><a href="#2-代码" class="headerlink" title="2.代码"></a>2.代码</h1><p>关于http的30x状态码，其代表站点域名或路径更改</p><ul><li>301重定向：永久</li><li>302重定向：临时</li></ul><p>html代码部分很简单，设置body和站点标题都为301重定向，告诉访问者，站点已经换了新的域名</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span><span class="tag">&lt;<span class="name">title</span>&gt;</span>301 Moved Permanently<span class="tag">&lt;/<span class="name">title</span>&gt;</span><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">center</span>&gt;</span><span class="tag">&lt;<span class="name">h1</span>&gt;</span>301 Moved Permanently<span class="tag">&lt;/<span class="name">h1</span>&gt;</span><span class="tag">&lt;/<span class="name">center</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><p><code>script</code>部分，通过<code>window</code>获取当前的域名，判断域名是否为目标域名（下文代码中的<code>旧域名</code>）</p><p>如果不是，则什么都不做。如果是，则覆盖url到新域名。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--放在head后，body前--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">var</span> path = <span class="variable language_">window</span>.<span class="property">location</span>.<span class="property">href</span>;</span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;current path:&quot;</span>,path)<span class="comment">//打印到控制台</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 睡眠函数</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">function</span> <span class="title function_">sleep</span> (time) &#123;</span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve</span>) =&gt;</span> <span class="built_in">setTimeout</span>(resolve, time));</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 睡500ms,之后执行下面的内容</span></span></span><br><span class="line"><span class="language-javascript">    <span class="title function_">sleep</span>(<span class="number">500</span>).<span class="title function_">then</span>(<span class="function">() =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// 如果url不是预先设定好的这个，则啥都不做</span></span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">if</span> (path!=<span class="string">&quot;旧域名1&quot;</span> &amp;&amp; path!=<span class="string">&quot;旧域名2&quot;</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">          <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;url not match, do nothing&quot;</span>)</span></span><br><span class="line"><span class="language-javascript">        &#125;</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">else</span>&#123;<span class="comment">// 否则重定向</span></span></span><br><span class="line"><span class="language-javascript">          <span class="variable language_">window</span>.<span class="property">location</span>.<span class="title function_">replace</span>(<span class="string">&quot;https://blog.musnow.top/&quot;</span>);</span></span><br><span class="line"><span class="language-javascript">        &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    &#125;)</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><p>这里我还用sleep函数休眠了500ms，否则切换很快，访客不注意的话，还不一定能发现301重定向。</p><hr><p>如果你不需要sleep，那代码更少</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">var</span> path = <span class="variable language_">window</span>.<span class="property">location</span>.<span class="property">href</span>;</span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;current path:&quot;</span>,path)</span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 如果url不是预先设定好的这两个，则啥都不做</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span> (path!=<span class="string">&quot;旧域名&quot;</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;url not match, do nothing&quot;</span>)</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">else</span>&#123;<span class="comment">// 否则重定向</span></span></span><br><span class="line"><span class="language-javascript">       <span class="variable language_">window</span>.<span class="property">location</span>.<span class="title function_">replace</span>(<span class="string">&quot;https://blog.musnow.top/&quot;</span>);</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><h1 id="3-netlify部署"><a href="#3-netlify部署" class="headerlink" title="3.netlify部署"></a>3.netlify部署</h1><p>将刚刚写好的代码写入一个<code>index.html</code>文件，再放入一个文件夹（netlify的上传只能上传文件夹）</p><p>如果你想，还可以在文件夹里面放一个<code>favicon.ico</code>作为临时的站点logo</p><p>打开netlify，找到Add new site，选择手动部署</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/640b1d697d4b2.png"                      alt="image-20230310200548549"                ></p><p>在这里上传文件夹</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/640b1d7ed3638.png"                      alt="image-20230310200727885"                ></p><p>部署好了之后，可以在domain settings里面配置域名为你的旧域名</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/640b1dfe2efe5.png"                      alt="image-20230310200935284"                ></p><h1 id="4-测试"><a href="#4-测试" class="headerlink" title="4.测试"></a>4.测试</h1><p>先访问旧域名，显示301重定向的信息</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/640b1e203e735.png"                      alt="image-20230310201009342"                ></p><p>休眠的时间到了之后，就会被重定向到当前的站点</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/640b1e6d4a595.png"                      alt="image-20230310201122790"                ></p><p>目的达成！</p><h1 id="5-添加上尾随参数"><a href="#5-添加上尾随参数" class="headerlink" title="5.添加上尾随参数"></a>5.添加上尾随参数</h1><p>如果你的站点有部署umami或者百度统计等<strong>访问统计</strong>服务，则可以在源码中跳转的链接里面加上来源</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">window</span>.<span class="property">location</span>.<span class="title function_">replace</span>(<span class="string">&quot;https://blog.musnow.top/?utm_source=旧域名&quot;</span>);</span><br></pre></td></tr></table></figure><p>这样，在访问统计中，就能得知有多少用户用了旧域名访问了你的站点</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/640b207623dc3.png"                      alt="image-20230310201912571"                ></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;javascript代码实现一个伪重定向&lt;/p&gt;</summary>
    
    
    
    <category term="差生文具多" scheme="https://blog.musnow.top/categories/%E5%B7%AE%E7%94%9F%E6%96%87%E5%85%B7%E5%A4%9A/"/>
    
    
    <category term="hexo" scheme="https://blog.musnow.top/tags/hexo/"/>
    
  </entry>
  
  <entry>
    <title>【Python】pip下载使用国内镜像源</title>
    <link href="https://blog.musnow.top/posts/783373963/"/>
    <id>https://blog.musnow.top/posts/783373963/</id>
    <published>2023-03-10T00:31:00.000Z</published>
    <updated>2023-04-03T01:23:39.560Z</updated>
    
    <content type="html"><![CDATA[<p>pip下载使用国内镜像源</p><span id="more"></span><h2 id="1-如何使用"><a href="#1-如何使用" class="headerlink" title="1.如何使用"></a>1.如何使用</h2><p>在直接用pip安装包的时候，有时候会非常非常慢</p><p>可以用下面的命令，在安装的时候使用清华的镜像源</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-i https://pypi.tuna.tsinghua.edu.cn/simple</span><br></pre></td></tr></table></figure><p>比如下面的操作</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip3.10 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple</span><br></pre></td></tr></table></figure><p>速度直接起飞</p><h2 id="2-修改配置文件"><a href="#2-修改配置文件" class="headerlink" title="2.修改配置文件"></a>2.修改配置文件</h2><p>在用户的家目录下创建一个<code>.pip</code>文件夹，并在里面创建一个<code>pip.conf</code></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[global]</span><br><span class="line">index-url = https://pypi.tuna.tsinghua.edu.cn/simple</span><br><span class="line">[install]</span><br><span class="line">trusted-host = https://pypi.tuna.tsinghua.edu.cn</span><br></pre></td></tr></table></figure><p>保存后，查看是否配置成功</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ pip3.10 config list</span><br><span class="line">global.index-url=&#x27;https://pypi.tuna.tsinghua.edu.cn/simple&#x27;</span><br><span class="line">install.trusted-host=&#x27;https://pypi.tuna.tsinghua.edu.cn&#x27;</span><br></pre></td></tr></table></figure><p>这样就配置好了</p><hr><p>windows下pip配置文件路径如下（如果没有此文件，创建之）</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">C:\Users\用户名\pip\pip.ini</span><br></pre></td></tr></table></figure><p>输入如下内容</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[global]</span><br><span class="line">index-url = https://pypi.tuna.tsinghua.edu.cn/simple</span><br><span class="line">[install]</span><br><span class="line">trusted-host = pypi.tuna.tsinghua.edu.cn</span><br></pre></td></tr></table></figure><h2 id="3-镜像源汇总"><a href="#3-镜像源汇总" class="headerlink" title="3.镜像源汇总"></a>3.镜像源汇总</h2><p>目前我测试能直接访问的镜像源有下面几个。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">清华 https://pypi.tuna.tsinghua.edu.cn/simple</span><br><span class="line">豆瓣 https://pypi.doubanio.com/simple/</span><br><span class="line">中科大 https://mirrors.bfsu.edu.cn/pypi/web/simple/</span><br><span class="line">阿里云 https://mirrors.aliyun.com/pypi/simple/</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;pip下载使用国内镜像源&lt;/p&gt;</summary>
    
    
    
    <category term="速通Python" scheme="https://blog.musnow.top/categories/%E9%80%9F%E9%80%9APython/"/>
    
    
    <category term="Python" scheme="https://blog.musnow.top/tags/Python/"/>
    
  </entry>
  
  <entry>
    <title>【Linux】浏览器写代码！部署code-server远程vscode网页</title>
    <link href="https://blog.musnow.top/posts/2203891398/"/>
    <id>https://blog.musnow.top/posts/2203891398/</id>
    <published>2023-03-09T13:45:33.000Z</published>
    <updated>2023-04-03T01:23:39.621Z</updated>
    
    <content type="html"><![CDATA[<p>部署code-server远程vscode网页，在浏览器上写代码！</p><span id="more"></span><p>参考文档 <a class="link"   href="https://developer.aliyun.com/article/876967#slide-7" >https://developer.aliyun.com/article/876967#slide-7<i class="fas fa-external-link-alt"></i></a></p><h1 id="1-什么是code-server？"><a href="#1-什么是code-server？" class="headerlink" title="1.什么是code-server？"></a>1.什么是code-server？</h1><p>注意，这不是在linux系统上安装vscode软件（和windows一样的vscode客户端）</p><blockquote><p>开源仓库 <a class="link"   href="https://github.com/coder/code-server" >https://github.com/coder/code-server<i class="fas fa-external-link-alt"></i></a></p></blockquote><p>code-server是一个社区项目（非微软官方）其作用是在你的linux主机（云服务器）上部署一个可以在<strong>浏览器</strong>上访问的vscode页面，类似官方的<code>vscode.dev</code></p><blockquote><p>微软官方推出的vscode网页版 <a class="link"   href="https://vscode.dev/" >https://vscode.dev/<i class="fas fa-external-link-alt"></i></a> </p><p>它可以通过浏览器的api访问你<strong>电脑上的本地文件</strong>，此时浏览器就是一个vscode客户端</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6409e2f59a83a.png"                      alt="image-20230309214517648"                ></p></blockquote><p>code-server同理，其访问的是你linux服务器上的文件，作用类似于vscode插件<code>remote ssh</code>，让我们可以用浏览器，在任何电脑、<strong>平板</strong>上进行远程开发，无须下载vscode客户端或者ssh软件！</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6409edd1712e7.png"                      alt="image-20230309223145036"                ></p><h2 id="1-1-官方的vscode-server"><a href="#1-1-官方的vscode-server" class="headerlink" title="1.1 官方的vscode-server?"></a>1.1 官方的vscode-server?</h2><p>微软官方确实有一个vscode-server，在官网可以下载。我测试了之后，发现它是打了一个隧道，通过<code>vscode.dev</code>的子域名访问，会转发到你的主机上。</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6409f5464d748.png"                      alt="image-20230309230334118"                ></p><p>在我这里的测试，<strong>发现压根连不上微软的服务器</strong>。想想其实也很正常，因为vscode-server的文档页面连中文都没有，再加上微软的服务器，国内用不了也是意料之中</p><h1 id="2-安装"><a href="#2-安装" class="headerlink" title="2.安装"></a>2.安装</h1><h2 id="2-1-下载安装包"><a href="#2-1-下载安装包" class="headerlink" title="2.1 下载安装包"></a>2.1 下载安装包</h2><p>截至本文创作时间<code>23-03-09</code>，code-server的最新版本是<code>4.10.1</code>，可以用下面的命令获取到linux下的安装包</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">wget https://github.com/cdr/code-server/releases/download/v4.10.1/code-server-4.10.1-linux-amd64.tar.gz </span><br></pre></td></tr></table></figure><p>如果上面这个命令你无法访问，可以去github的releases里面手动下载安装包</p><blockquote><p><a class="link"   href="https://github.com/coder/code-server/releases/" >https://github.com/coder/code-server/releases/<i class="fas fa-external-link-alt"></i></a></p></blockquote><p>我的系统是amd64，即x86架构。在github里面也是找<code>linux amd64</code>的包进行安装</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ uname -a</span><br><span class="line">Linux 1c2261732150 5.10.120 #0 SMP Fri Jan 6 08:05:47 2023 x86_64 x86_64 x86_64 GNU/Linux</span><br></pre></td></tr></table></figure><p>如果你使用的是树莓派等设备，请用<code>uname -a</code>确认你的系统架构，并下载对应的包</p><hr><p>github的release下载慢，可以用下面这个网站加速（用迅雷下，速度起飞）</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">https://doget.nocsdn.com/#/</span><br></pre></td></tr></table></figure><p>下载完成后，将压缩包用xftp传输到服务器上</p><h2 id="2-2-解压"><a href="#2-2-解压" class="headerlink" title="2.2 解压"></a>2.2 解压</h2><p>使用下面的命令解压刚刚下载好的压缩包</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tar -zxvf code-server-4.10.1-linux-amd64.tar.gz</span><br></pre></td></tr></table></figure><p>给解压出来的文件夹改个短名字vsc</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mv code-server-4.10.1-linux-amd64 vsc</span><br></pre></td></tr></table></figure><p>cd进入这个文件夹</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cd vsc</span><br></pre></td></tr></table></figure><p><code>ls -l</code>，基本文件如下</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">total 792</span><br><span class="line">drwxr-xr-x   8 muxue muxue   4096 Mar  4 12:26 .</span><br><span class="line">drwx------   6 muxue muxue   4096 Mar  9 22:42 ..</span><br><span class="line">drwxr-xr-x   2 muxue muxue   4096 Mar  4 12:26 bin</span><br><span class="line">drwxr-xr-x   3 muxue muxue   4096 Mar  4 12:26 lib</span><br><span class="line">-rw-r--r--   1 muxue muxue   1084 Mar  3 17:13 LICENSE</span><br><span class="line">drwxr-xr-x 243 muxue muxue  12288 Mar  4 12:26 node_modules</span><br><span class="line">-rw-r--r--   1 muxue muxue 480100 Mar  4 12:26 npm-shrinkwrap.json</span><br><span class="line">drwxr-xr-x   5 muxue muxue   4096 Mar  3 17:13 out</span><br><span class="line">-rw-r--r--   1 muxue muxue   5235 Mar  4 12:24 package.json</span><br><span class="line">-rwxr-xr-x   1 muxue muxue   4877 Mar  3 17:13 postinstall.sh</span><br><span class="line">-rw-r--r--   1 muxue muxue   3056 Mar  3 17:13 README.md</span><br><span class="line">drwxr-xr-x   3 muxue muxue   4096 Mar  3 17:14 src</span><br><span class="line">-rw-r--r--   1 muxue muxue 180977 Mar  3 17:13 ThirdPartyNotices.txt</span><br><span class="line">drwxr-xr-x   2 muxue muxue   4096 Mar  3 17:14 typings</span><br><span class="line">-rw-r--r--   1 muxue muxue  76688 Mar  4 12:26 yarn.lock</span><br></pre></td></tr></table></figure><p>直接进入bin文件夹，我们需要用的可执行文件就在里头</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ ls</span><br><span class="line">code-server</span><br></pre></td></tr></table></figure><p>这个<code>code-server</code>就是我们要的可执行文件</p><h2 id="2-3-启动code-server"><a href="#2-3-启动code-server" class="headerlink" title="2.3 启动code-server"></a>2.3 启动code-server</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">export PASSWORD=&quot;配置一个密码&quot;</span><br><span class="line">code-server --port 8888 --host 0.0.0.0 --auth password</span><br></pre></td></tr></table></figure><p>上面这个命令，启动之后，会创建一个端口为8888的web服务，此时使用<code>ip:8888</code>的方式，应该就可以访问到你的code-server服务了！</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">[2023-03-09T14:52:07.081Z] info  Wrote default config file to ~/.config/code-server/config.yaml</span><br><span class="line">[2023-03-09T14:52:07.516Z] info  code-server 4.10.0 ac1fba8bde0c3f29bf6bc27d3d7d75cb2390a7c2</span><br><span class="line">[2023-03-09T14:52:07.517Z] info  Using user-data-dir ~/.local/share/code-server</span><br><span class="line">[2023-03-09T14:52:07.532Z] info  Using config file ~/.config/code-server/config.yaml</span><br><span class="line">[2023-03-09T14:52:07.532Z] info  HTTP server listening on http://0.0.0.0:8888/</span><br><span class="line">[2023-03-09T14:52:07.532Z] info    - Authentication is enabled</span><br><span class="line">[2023-03-09T14:52:07.532Z] info      - Using password from $PASSWORD</span><br><span class="line">[2023-03-09T14:52:07.532Z] info    - Not serving HTTPS</span><br></pre></td></tr></table></figure><p>出现下面的页面，那就是安装成功了！输入你刚刚设置的密码，就可以像使用vscode一样使用code-server了！</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6409f2f7b9a46.png"                      alt="image-20230309225342649"                ></p><h2 id="2-4-配置文件"><a href="#2-4-配置文件" class="headerlink" title="2.4 配置文件"></a>2.4 配置文件</h2><p>首次启动了之后，会在你的用户路径下生成一个<code>.config/code-server</code>文件夹，里面有一个<code>config.yaml</code></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ ls .config/code-server</span><br><span class="line">config.yaml</span><br></pre></td></tr></table></figure><p>打开这个文件，可以在里面配置code-server的密码，以及https访问。其余的配置项我还没有关注过，可以去看看github上的文档</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">bind-addr: 127.0.0.1:8080</span><br><span class="line">auth: password</span><br><span class="line">password: 64f1881b805917a449d80874</span><br><span class="line">cert: false</span><br></pre></td></tr></table></figure><p>默认的密码巨长，改成你自己的即可。在这里还可以修改默认绑定的ip和端口，后续启动code-server，就不需要手动指定了，直接运行即可</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./code-server</span><br></pre></td></tr></table></figure><h2 id="2-5-tmux后台运行"><a href="#2-5-tmux后台运行" class="headerlink" title="2.5 tmux后台运行"></a>2.5 tmux后台运行</h2><blockquote><p>nohup运行code-server有一点问题，所以要用tmux</p></blockquote><p>如果没有这个软件，先安装</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo yum install tmux</span><br></pre></td></tr></table></figure><p>创建一个新会话，你可以理解为这是一个脱离于bash的独立shell页面，即便当前bash退出了，code-server也不会退出</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tmux new -s vscode_online</span><br></pre></td></tr></table></figure><p>开启新会话后，进入vsc的bin目录，启动code-server，命令是不变的</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./code-server</span><br></pre></td></tr></table></figure><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/640a78b909107.png"                      alt="image-20230310082417260"                ></p><p>随后直接关掉当前bash，此时就已经实现了code-server的后台运行</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 接入会话</span></span><br><span class="line">tmux a -t vscode_online</span><br><span class="line"></span><br><span class="line"><span class="comment"># 结束会话</span></span><br><span class="line">tmux kill-session -t vscode_online</span><br></pre></td></tr></table></figure><h1 id="3-一些问题"><a href="#3-一些问题" class="headerlink" title="3.一些问题"></a>3.一些问题</h1><h2 id="3-1-nginx转发遇到1006错误"><a href="#3-1-nginx转发遇到1006错误" class="headerlink" title="3.1 nginx转发遇到1006错误"></a>3.1 nginx转发遇到1006错误</h2><p>我在采用nginx转发的时候，遇到了这个问题</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6409e922986ac.png"                      alt="image-20230309221146320"                ></p><p>个人猜测，这大概率是因为我在<code>config.yaml</code>里面配置了<code>code-server</code>的https导致的</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">cert:</span> <span class="literal">false</span> <span class="comment"># 关闭https</span></span><br></pre></td></tr></table></figure><p>将cert修改回false，再次测试，发现还是有这个问题！</p><blockquote><p>实际上，如果你进行了nginx转发，并不需要配置code-server的https</p><p>保持默认的http即可</p></blockquote><hr><p>后来发现是我的nginx转发设置有问题，正确的转发配置如下</p><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">location</span> / &#123;</span><br><span class="line"><span class="attribute">proxy_pass</span>   http://127.0.0.1:8888;</span><br><span class="line"><span class="attribute">proxy_set_header</span> Host <span class="variable">$host</span>;</span><br><span class="line"><span class="attribute">proxy_set_header</span> Upgrade <span class="variable">$http_upgrade</span>;</span><br><span class="line"><span class="attribute">proxy_set_header</span> Connection upgrade;</span><br><span class="line"><span class="attribute">proxy_set_header</span> Accept-Encoding gzip;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面给出一个nginx的https的完整配置文件！</p><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">server</span> &#123;</span><br><span class="line">    <span class="attribute">listen</span> <span class="number">443</span> ssl;</span><br><span class="line">    <span class="attribute">server_name</span>  www.example.com;  <span class="comment"># 域名</span></span><br><span class="line">    <span class="comment"># 注意证书文件位置，是从/etc/nginx/下开始算起的</span></span><br><span class="line">    <span class="attribute">ssl_certificate</span> cert/example.com.crt; </span><br><span class="line">    <span class="attribute">ssl_certificate_key</span> cert/example.com.key;</span><br><span class="line">    <span class="attribute">ssl_session_timeout</span> <span class="number">5m</span>;</span><br><span class="line">    <span class="attribute">ssl_protocols</span> TLSv1 TLSv1.<span class="number">1</span> TLSv1.<span class="number">2</span>;</span><br><span class="line">    <span class="attribute">ssl_ciphers</span> ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;</span><br><span class="line">    <span class="attribute">ssl_prefer_server_ciphers</span> <span class="literal">on</span>;</span><br><span class="line"></span><br><span class="line">    <span class="attribute">client_max_body_size</span> <span class="number">1024m</span>;</span><br><span class="line"></span><br><span class="line">    <span class="section">location</span> / &#123;</span><br><span class="line">       <span class="attribute">proxy_set_header</span> Host <span class="variable">$host</span>;</span><br><span class="line">        <span class="attribute">proxy_set_header</span> Upgrade <span class="variable">$http_upgrade</span>;</span><br><span class="line">        <span class="attribute">proxy_set_header</span> Connection upgrade;</span><br><span class="line">        <span class="attribute">proxy_set_header</span> Accept-Encoding gzip;</span><br><span class="line">        <span class="comment"># 当下是直接用yum安装的nginx，可以直接用本地ip访问</span></span><br><span class="line">        <span class="comment"># 如果是docker安装的nginx，需要改为公网ip</span></span><br><span class="line">        <span class="attribute">proxy_pass</span> http://127.0.0.1:8888;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment"># 强制重定向http -&gt; https</span></span><br><span class="line"><span class="section">server</span> &#123;</span><br><span class="line">    <span class="attribute">listen</span> <span class="number">80</span>; <span class="comment"># 监听80端口</span></span><br><span class="line">    <span class="attribute">server_name</span>  www.example.com;  <span class="comment"># 域名</span></span><br><span class="line">    <span class="comment">#把http的域名请求转成https</span></span><br><span class="line">    <span class="attribute">return</span> <span class="number">301</span> https://<span class="variable">$host</span><span class="variable">$request_uri</span>; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="3-2-C-x2F-C-插件"><a href="#3-2-C-x2F-C-插件" class="headerlink" title="3.2 C&#x2F;C++插件"></a>3.2 C&#x2F;C++插件</h2><p>由于code-server是一个第三方项目，所以一些官方的插件（如pylance）直接在code-server的插件商店里面搜是没有的</p><p>我们需要去微软的vscode官网上下载插件的VSIX文件，使用本地文件的方式进行安装</p><blockquote><p><a class="link"   href="https://marketplace.visualstudio.com/VSCode" >https://marketplace.visualstudio.com/VSCode<i class="fas fa-external-link-alt"></i></a></p></blockquote><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6409f4d54a065.png"                      alt="image-20230309230141057"                ></p><p>不过，pylance用这个办法依旧无法安装。看来有些插件只有官方的vsc才能安装</p><h2 id="3-3-本地文件"><a href="#3-3-本地文件" class="headerlink" title="3.3 本地文件"></a>3.3 本地文件</h2><p>如果你用过vscode的<code>remote-ssh</code>插件连过你这个服务器，用户目录下面会有两个文件夹</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">.vsc</span><br><span class="line">.vscode-server</span><br></pre></td></tr></table></figure><p>这两个文件夹是vscode的插件<code>remote-ssh</code>生成的，和本文安装的code-server无关！</p><h2 id="3-4-tmux常用语句"><a href="#3-4-tmux常用语句" class="headerlink" title="3.4 tmux常用语句"></a>3.4 tmux常用语句</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">tmux new　　创建默认名称的会话（在tmux命令模式使用new命令可实现同样的功能，其他命令同理，后文不再列出tmux终端命令）</span><br><span class="line"></span><br><span class="line">tmux new -s mysession　　创建名为mysession的会话</span><br><span class="line"></span><br><span class="line">tmux ls　　显示会话列表</span><br><span class="line"></span><br><span class="line">tmux a　　连接上一个会话</span><br><span class="line"></span><br><span class="line">tmux a -t mysession　　连接指定会话mysession</span><br><span class="line"></span><br><span class="line">tmux rename -t s1 s2　　重命名会话s1为s2</span><br><span class="line"></span><br><span class="line">tmux kill-session　　关闭上次打开的会话</span><br><span class="line"></span><br><span class="line">tmux kill-session -t s1　关闭会话s1</span><br><span class="line"></span><br><span class="line">tmux kill-session -a -t s1　　关闭除s1外的所有会话</span><br><span class="line"></span><br><span class="line">tmux kill-server　　关闭所有会话</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;部署code-server远程vscode网页，在浏览器上写代码！&lt;/p&gt;</summary>
    
    
    
    <category term="差生文具多" scheme="https://blog.musnow.top/categories/%E5%B7%AE%E7%94%9F%E6%96%87%E5%85%B7%E5%A4%9A/"/>
    
    
    <category term="Linux" scheme="https://blog.musnow.top/tags/Linux/"/>
    
    <category term="vscode" scheme="https://blog.musnow.top/tags/vscode/"/>
    
    <category term="CentOS" scheme="https://blog.musnow.top/tags/CentOS/"/>
    
  </entry>
  
  <entry>
    <title>【Docker】限制已运行容器的Cpu和内存</title>
    <link href="https://blog.musnow.top/posts/2589797340/"/>
    <id>https://blog.musnow.top/posts/2589797340/</id>
    <published>2023-03-06T09:45:33.000Z</published>
    <updated>2023-04-03T01:23:39.621Z</updated>
    
    <content type="html"><![CDATA[<p>docker限制已运行容器的Cpu和内存</p><span id="more"></span><h2 id="1-问题描述"><a href="#1-问题描述" class="headerlink" title="1.问题描述"></a>1.问题描述</h2><p>最近云服务器的内存经常不够用，而且是<strong>莫名其妙</strong>的增多，在腾讯云的控制台里面看，4g的内存占用了3.2g，就卡到连ssh都连不上了</p><blockquote><p>PS: 已换过网络和设备，确认不是网络问题导致无法ssh</p></blockquote><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6405c2f8c3a35.png"                      alt="img"                ></p><p>实在没辙了，只能把我的几个不热门的kook-bot移植到replit白嫖，再限制一下lsky图床docker镜像的内存用量</p><blockquote><p>其实主要是<code>nsfw-api</code>的内存用量，我发现有人故意给我的图床上传h图。lsky后台由于鉴黄不通过，不允许上传，也看不到是谁干的。隔这压力测试呢？</p><p>我的图床基本只对自己的博客使用。开放游客上传，也只是方便大家临时上传一些图片，还请大家手下留情！！！😥</p></blockquote><p>这个操作我做了几次，也算是一个高频操作了。在此记录一下如何更改一个正在运行中容器的内存限制</p><h2 id="2-修改内存限制"><a href="#2-修改内存限制" class="headerlink" title="2.修改内存限制"></a>2.修改内存限制</h2><p>先使用ps命令查看当前容器和对应的id</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker ps</span><br></pre></td></tr></table></figure><p>可以看到，lsky图床和nsfw的镜像分别是第二个和第三个</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6405c3777d10c.png"                      alt="image-20230306184202020"                ></p><p>再用stats命令查看当前的状态</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker stats</span><br></pre></td></tr></table></figure><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6405c43bed272.png"                      alt="image-20230306184518474"                ></p><p>可以看到，图床的内存没有进行限制，nsfw的内存已经被限制到了618mb，当前已用565mb，基本要满了</p><p>重启一下容器，发现初始化的时候只需要100mb左右的内存，合计着现在就有人在往图床里面上传图片？🤣🤣🤣</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6405c412e1f22.png"                      alt="image-20230306184437446"                ></p><p>这怎么行，直接给它内存限制干到512mb，能省一点内存是一点，不能因为图床导致我服务器卡死。</p><p>修改限制，要使用的是<code>container update</code>命令</p><blockquote><p><a class="link"   href="https://www.yiibai.com/docker/container_update.html#:~:text=docker%20container,update%20%E5%91%BD%E4%BB%A4%E7%94%A8%E4%BA%8E%E6%9B%B4%E6%96%B0%E4%B8%80%E4%B8%AA%E6%88%96%E5%A4%9A%E4%B8%AA%E5%AE%B9%E5%99%A8%E7%9A%84%E9%85%8D%E7%BD%AE%E3%80%82" >参考文档<i class="fas fa-external-link-alt"></i></a></p><table><thead><tr><th>名称,简写</th><th>默认值</th><th>描述</th></tr></thead><tbody><tr><td>–blkio-weight</td><td>0</td><td>阻塞IO(相对权重)，介于<code>10</code>和<code>1000</code>之间，或<code>0</code>禁用(默认为<code>0</code>)</td></tr><tr><td>–cpu-period</td><td>0</td><td>限制CPU CFS(完全公平的调度程序)周期</td></tr><tr><td>–cpu-quota</td><td>0</td><td>限制CPU CFS(完全公平的调度程序)配额</td></tr><tr><td>–cpu-rt-period</td><td>0</td><td>限制CPU实时周期(以微秒为单位)</td></tr><tr><td>–cpu-rt-runtime</td><td>0</td><td>以微秒为单位限制CPU实时运行时间</td></tr><tr><td>–cpu-shares, -c</td><td>0</td><td>CPU份额(相对权重)</td></tr><tr><td>–cpuset-cpus</td><td></td><td>允许执行的CPU(0-3,0)</td></tr><tr><td>–cpuset-mems</td><td></td><td>允许执行的内存率(0-3,0.1)</td></tr><tr><td>–kernel-memory</td><td></td><td>内核内存限制</td></tr><tr><td>–memory, -m</td><td></td><td>内存限制</td></tr><tr><td>–memory-reservation</td><td></td><td>内存软限制</td></tr><tr><td>–memory-swap</td><td></td><td>交换限制等于内存加交换：<code>-1</code>以启用无限制的交换</td></tr><tr><td>–restart</td><td></td><td>重新启动在容器退出时应用的策略</td></tr><tr><td>模板如下</td><td></td><td></td></tr></tbody></table></blockquote><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker container update 容器名 --memory=&quot;2g&quot;  --memory-swap=&quot;-1&quot;</span><br></pre></td></tr></table></figure><p>上面的命令会限制容器的内存为2gb，交换内存设置为<code>-1</code>（以启用无限制的交换）</p><p>比如我现在想修改nsfw容器的内存限制，应该如下操作</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker container update nsfw-api --memory=&quot;0.5g&quot; </span><br></pre></td></tr></table></figure><p>然后发现报错了</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Error response from daemon: Cannot update container 418fc2a79fa7ad2e637babe17424ee60e9027ef4a7fada3f279864e76bdaaa10: Memory limit should be smaller than already set memoryswap limit, update the memoryswap at the same time</span><br></pre></td></tr></table></figure><p>大概意思是让我们同时更新交换内存</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker container update nsfw-api --memory=&quot;0.5g&quot;  --memory-swap=&quot;-1&quot;</span><br></pre></td></tr></table></figure><p>现在就ok了，执行成功会显示镜像的名字</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nsfw-api</span><br></pre></td></tr></table></figure><p>在<code>stats</code>看一下，发现已经修改成功了</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker stats</span><br></pre></td></tr></table></figure><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6405c73f35462.png"                      alt="image-20230306185809744"                ></p><p>修改其他容器的效果也是一样的，目的已经达到啦！</p><h2 id="3-修改cpu限制"><a href="#3-修改cpu限制" class="headerlink" title="3.修改cpu限制"></a>3.修改cpu限制</h2><p>下面的命令意思是限制容器只能使用主机上两个cpu</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker container update  容器名 --cpus=2</span><br></pre></td></tr></table></figure><p>但我没有理解这个<code>两个cpu</code>是什么含义。考虑到大部分时期，cpu负载都不会很大，暂时先不设置这个了</p><blockquote><p><a class="link"   href="https://www.cnblogs.com/sparkdev/p/8052522.html" >https://www.cnblogs.com/sparkdev/p/8052522.html<i class="fas fa-external-link-alt"></i></a></p><p><a class="link"   href="https://zhuanlan.zhihu.com/p/388012843" >https://zhuanlan.zhihu.com/p/388012843<i class="fas fa-external-link-alt"></i></a></p></blockquote>]]></content>
    
    
    <summary type="html">&lt;p&gt;docker限制已运行容器的Cpu和内存&lt;/p&gt;</summary>
    
    
    
    <category term="docker真好玩" scheme="https://blog.musnow.top/categories/docker%E7%9C%9F%E5%A5%BD%E7%8E%A9/"/>
    
    
    <category term="Docker" scheme="https://blog.musnow.top/tags/Docker/"/>
    
  </entry>
  
  <entry>
    <title>【Linux】修改linux下的时区和系统时间</title>
    <link href="https://blog.musnow.top/posts/809108065/"/>
    <id>https://blog.musnow.top/posts/809108065/</id>
    <published>2023-03-04T01:30:55.000Z</published>
    <updated>2023-04-03T01:23:39.555Z</updated>
    
    <content type="html"><![CDATA[<p>用配置文件，修改linux系统下的时区（所用系统 CentOS8）</p><span id="more"></span><h2 id="1-问题来源"><a href="#1-问题来源" class="headerlink" title="1.问题来源"></a>1.问题来源</h2><p>在linux上使用python的logging模块的时候，发现了一个问题，那就是模块里面的<code>%(asctime)s</code>打印的时间并非东八区的时间，而是格林尼治时间，比东八区的时间少了8小时</p><p>对于日志来说，这怎么行？总不能每次看日志的时候，自己手动给时间加8小时吧！</p><h2 id="2-解决"><a href="#2-解决" class="headerlink" title="2.解决"></a>2.解决</h2><p>百度了一下后，发现是我系统的时区问题。这个Centos系统是用docker安装的，内部的时区没有正常设置</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">date</span><br></pre></td></tr></table></figure><p>通过date命令可以查看系统当前的时间</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Sat Mar  4 01:29:59 UTC 2023</span><br></pre></td></tr></table></figure><p>会发现是3月4日的凌晨1点，但实际上我是在上午9点写下的这篇博客</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/6402a0423fa7b.png"                      alt="image-20230304093451196"                ></p><p>这就需要我们修改时区了！</p><hr><p>刚开始，我尝试用<code>tzselect</code>命令进行时区的选择，再选择了上海时区后，系统的时间还是有问题。然后发现，需要修改文件配置，才能让时间生效</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">/etc/timezone <span class="comment"># 时区的配置</span></span><br><span class="line">/etc/localtime <span class="comment"># 时间</span></span><br><span class="line">/usr/share/zoneinfo/Asia <span class="comment">#这里边放着亚洲主要城市的时间</span></span><br></pre></td></tr></table></figure><p>而我的系统中，第一个文件压根不存在！</p><p>要用下面的两个命令来修改配置文件（root下执行）</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">echo &quot;Asia/Shanghai&quot; &gt; /etc/timezone</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">rm -rf /etc/localtime</span><br><span class="line">ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime</span><br></pre></td></tr></table></figure><p>修改完毕，再次执行<code>date</code>命令，可以看到时间已经正常了</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Sat Mar  4 09:31:33 CST 2023</span><br></pre></td></tr></table></figure><p>python的logging模块中打印的时间也正常了</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[23-03-04 09:31:39] DEBUG:log.py:debug:9 | test in main</span><br><span class="line">[23-03-04 09:31:39] ERROR:log.py:exception:24 | Exception occurred</span><br><span class="line">Traceback (most recent call last):</span><br><span class="line">  File &quot;/home/kook/code/py-test/log.py&quot;, line 32, in test</span><br><span class="line">    a = 10/0</span><br><span class="line">ZeroDivisionError: division by zero</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;用配置文件，修改linux系统下的时区（所用系统 CentOS8）&lt;/p&gt;</summary>
    
    
    
    <category term="玩上Linux" scheme="https://blog.musnow.top/categories/%E7%8E%A9%E4%B8%8ALinux/"/>
    
    
    <category term="Linux" scheme="https://blog.musnow.top/tags/Linux/"/>
    
  </entry>
  
  <entry>
    <title>【Python】解决SSL certificate problem unable to get local issuer certificate</title>
    <link href="https://blog.musnow.top/posts/3348986841/"/>
    <id>https://blog.musnow.top/posts/3348986841/</id>
    <published>2023-02-24T01:19:00.000Z</published>
    <updated>2023-04-03T01:23:39.560Z</updated>
    
    <content type="html"><![CDATA[<p>CentOS8 出现 SSL certificate problem: unable to get local issuer certificate 解决办法</p><span id="more"></span><h1 id="1-错误来源"><a href="#1-错误来源" class="headerlink" title="1.错误来源"></a>1.错误来源</h1><p>我在一个docker安装的<code>Centos8.5</code>系统中启动我自己写的kook机器人时，遇到了下面的bug</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/02/63f80f61bd112.png"                      alt="原图"                ></p><p>其大概意思是没有办法获取到本地的<code>issuer certificate</code>。如果你知道证书和CA的含义，那么大概能猜到这个报错是什么意思。</p><blockquote><p>所有站点的ssl证书，都需要经过CA机构的认证和颁发。操作系统会默认<strong>内嵌</strong>已有CA机构的公钥，以用于解密CA颁发的证书中的数字签名。</p></blockquote><p>因为CA机构是内嵌在操作系统本地的，如果一个证书的颁发机构不在本地已有CA中，该站点就会被报不安全。</p><p>而无法获取本地<code>issuer certificate</code>，我猜测意思就是没有办法获取道操作系统内嵌的CA</p><h1 id="2-解决"><a href="#2-解决" class="headerlink" title="2.解决"></a>2.解决</h1><p>刚开始我尝试过通过python的ssl模组取消证书验证、更新本地证书和openssl，都没有解决这个问题。</p><p>在Kook平台<code>khl.py</code>服务器的大佬帮助下，最终定位并解决了这个问题。</p><h2 id="2-1-更新本地ca包"><a href="#2-1-更新本地ca包" class="headerlink" title="2.1 更新本地ca包"></a>2.1 更新本地ca包</h2><p>先 <code>yum install ca-certificates</code>，已经装过的话那就是 <code>yum update ca-certificates</code> 如果不行再 <code>pip3 install certifi</code></p><p>操作完毕上面的步骤，发现本地已经安装了最新版本的<code>ca-certificates</code>，python也有安装<code>certifi</code>包</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">$ yum update ca-certificates</span><br><span class="line">Repository extras is listed more than once in the configuration</span><br><span class="line">Warning: failed loading &#x27;/etc/yum.repos.d/epel.repo&#x27;, skipping.</span><br><span class="line">Last metadata expiration check: 8:42:37 ago on Thu 23 Feb 2023 03:09:14 PM UTC.</span><br><span class="line">Dependencies resolved.</span><br><span class="line">Nothing to do.</span><br><span class="line">Complete!</span><br><span class="line"></span><br><span class="line">$ pip3.10 install  certifi</span><br><span class="line">Defaulting to user installation because normal site-packages is not writeable</span><br><span class="line">Requirement already satisfied: certifi in ./.local/lib/python3.10/site-packages (2022.12.7)</span><br><span class="line"></span><br><span class="line">$ pip3.10 install  certifi -U</span><br><span class="line">Defaulting to user installation because normal site-packages is not writeable</span><br><span class="line">Requirement already satisfied: certifi in ./.local/lib/python3.10/site-packages (2022.12.7)</span><br></pre></td></tr></table></figure><h2 id="2-2-查看本地主机的链接"><a href="#2-2-查看本地主机的链接" class="headerlink" title="2.2 查看本地主机的链接"></a>2.2 查看本地主机的链接</h2><p>于是尝试使用如下命令，看看主机是否能成功连接到<code>kaiheila.cn</code>(kookapp.cn) 并认证ssl证书</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">openssl s_client -connect www.kookapp.cn:443 -showcerts</span><br></pre></td></tr></table></figure><p>输出了如下的信息</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br></pre></td><td class="code"><pre><span class="line">depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA</span><br><span class="line">verify return:1</span><br><span class="line">depth=1 C = US, O = &quot;DigiCert, Inc.&quot;, CN = RapidSSL Global TLS RSA4096 SHA256 2022 CA1</span><br><span class="line">verify return:1</span><br><span class="line">depth=0 CN = *.kookapp.cn</span><br><span class="line">verify return:1</span><br><span class="line">CONNECTED(00000003)</span><br><span class="line">---</span><br><span class="line">Certificate chain</span><br><span class="line"> 0 s:CN = *.kookapp.cn</span><br><span class="line">   i:C = US, O = &quot;DigiCert, Inc.&quot;, CN = RapidSSL Global TLS RSA4096 SHA256 2022 CA1</span><br><span class="line">-----BEGIN CERTIFICATE-----</span><br><span class="line">MIIHkTCCBXmgAwIBAgIQCUlurlf6m1VXsuISNqlT8DANBgkqhkiG9w0BAQsFADBc</span><br><span class="line">MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xNDAyBgNVBAMT</span><br><span class="line">K1JhcGlkU1NMIEdsb2JhbCBUTFMgUlNBNDA5NiBTSEEyNTYgMjAyMiBDQTEwHhcN</span><br><span class="line">MjIwNjE1MDAwMDAwWhcNMjMwNjE1MjM1OTU5WjAXMRUwEwYDVQQDDAwqLmtvb2th</span><br><span class="line">cHAuY24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDB1uHkQ/oEkGNl</span><br><span class="line">oVa6wHz424VznxL+eUt6AAfNhtIXeone8rtnJqeoDJ24lmBYUrL00tChCp4rdTdd</span><br><span class="line">crqPppDYSEY7+/U0hf39pDrmsGKZeeOu7JvANjtvqLFdXsqe5CnXtxAo5QUXB9gc</span><br><span class="line">kttoLTZZX5O1Gyi6mqDtsFCTaVpsQmZWVcbA21zLAdAZJyUyzM2VyfCUPQHs+VFE</span><br><span class="line">rcmA9SN8nkppLyf00lIHCWR6v9HYC2XgjHN2JX8ARJwTEddhHjvleMc/SlHncU6f</span><br><span class="line">pZ8itWx8NzBL6MwPKEjmHgKp0VYBMY6PHrSR/j/XtgOweodp1JBIr6yLLlyCu/lJ</span><br><span class="line">ksCwMUIZAgMBAAGjggOSMIIDjjAfBgNVHSMEGDAWgBTwnIX9op99j8lou9XUiU0d</span><br><span class="line">vtOQ/zAdBgNVHQ4EFgQUYe6lb4/s4J82aqWfNDaev2MD8k4wIwYDVR0RBBwwGoIM</span><br><span class="line">Ki5rb29rYXBwLmNuggprb29rYXBwLmNuMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUE</span><br><span class="line">FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwgZ8GA1UdHwSBlzCBlDBIoEagRIZCaHR0</span><br><span class="line">cDovL2NybDMuZGlnaWNlcnQuY29tL1JhcGlkU1NMR2xvYmFsVExTUlNBNDA5NlNI</span><br><span class="line">QTI1NjIwMjJDQTEuY3JsMEigRqBEhkJodHRwOi8vY3JsNC5kaWdpY2VydC5jb20v</span><br><span class="line">UmFwaWRTU0xHbG9iYWxUTFNSU0E0MDk2U0hBMjU2MjAyMkNBMS5jcmwwPgYDVR0g</span><br><span class="line">BDcwNTAzBgZngQwBAgEwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2Vy</span><br><span class="line">dC5jb20vQ1BTMIGHBggrBgEFBQcBAQR7MHkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9v</span><br><span class="line">Y3NwLmRpZ2ljZXJ0LmNvbTBRBggrBgEFBQcwAoZFaHR0cDovL2NhY2VydHMuZGln</span><br><span class="line">aWNlcnQuY29tL1JhcGlkU1NMR2xvYmFsVExTUlNBNDA5NlNIQTI1NjIwMjJDQTEu</span><br><span class="line">Y3J0MAkGA1UdEwQCMAAwggF/BgorBgEEAdZ5AgQCBIIBbwSCAWsBaQB3AOg+0No+</span><br><span class="line">9QY1MudXKLyJa8kD08vREWvs62nhd31tBr1uAAABgWbUskcAAAQDAEgwRgIhAL1q</span><br><span class="line">ZKig2NqYtlAVnaXtJ7Tk+OuROrPHGR8t2zyNTb6xAiEAsLL5yuvJ/WtIj0bLwbKR</span><br><span class="line">0/nFe/Juub3aZjrNvUhzZIIAdQA1zxkbv7FsV78PrUxtQsu7ticgJlHqP+Eq76gD</span><br><span class="line">wzvWTAAAAYFm1LJ6AAAEAwBGMEQCIDK3qkxuTUB1Dngycy9/LECyVXjlpi2eJBAC</span><br><span class="line">6oZPJQ/VAiBs1iupiYwavmD2m6QrDzzZrW2MJWx4LMxCtvf6pmN3NwB3ALc++yTf</span><br><span class="line">nE26dfI5xbpY9Gxd/ELPep81xJ4dCYEl7bSZAAABgWbUsnkAAAQDAEgwRgIhAL5v</span><br><span class="line">fkeeQAwowpkUhf0U/Z2fFiL8IY+QsCyED40zw0d1AiEA02UjHM3tztsrMn2xyzmT</span><br><span class="line">kVgXnly/OozP+LjdwdsTa6gwDQYJKoZIhvcNAQELBQADggIBAGdBom7y2NXOZL7K</span><br><span class="line">xtZFBFQCBJrEQOHZvuN4Etkmotb+0aVnrEZ/Qc5+zl86YqURcmOZgPhzSjuVfTvU</span><br><span class="line">sJJ1TkC/tMYofoo1Db7B5yRxQmjEYRNLFAZ8rbP/pd5Js8ZGWW7RcxqG+zebqDDD</span><br><span class="line">2CWvpiJNStu/yjWwdsFXQUwU0XOGUfwJDHD0eSBaa3uclAn3kUqF82l2X9qzvy9E</span><br><span class="line">exGjPOSWyNuksjeScr32OyNUrwo7RUYJU4Ztl8xaiaxcvg7u2/o9WWzPhHMVI6ij</span><br><span class="line">rJbj6Wn+BMe3SMYcR/L+foItjWAuOqOVB7IKO32JgCkP1vyl8AWs0up8ddI/9jn3</span><br><span class="line">px8ii0HphKJnkCPZNmLnnOhFXQjoPy2PRmKqusP0kleJw1ZpQXsQpm3yZqFggyFH</span><br><span class="line">8S4ii83kkHBynC89dlwlBIkF29Ds4mYjau6mZZxE69By7ptvFvcYREYslaRMxd16</span><br><span class="line">6PEf5mp5jBKSiI2iYcP8PQOudhObpkuw+KHz8ntkf++HeluuEiqsKyiyyiXm5dmR</span><br><span class="line">E/9uIUjPcQia5ohs2VpAgOLhLG6yphWCHQ6awFj9q3Ce1Mm5yXyZFvLXWGwoJj6j</span><br><span class="line">CVXAZDchwX4uiHW2fI3GHz1pTPg0L3NE/M2CHnQWJlh9cgOljmaZXCeKmLdA9phD</span><br><span class="line">+NQho5xVElRiF+rijlrkMutS7p1Y</span><br><span class="line">-----END CERTIFICATE-----</span><br><span class="line"> 1 s:C = US, O = &quot;DigiCert, Inc.&quot;, CN = RapidSSL Global TLS RSA4096 SHA256 2022 CA1</span><br><span class="line">   i:C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA</span><br><span class="line">-----BEGIN CERTIFICATE-----</span><br><span class="line">MIIFyzCCBLOgAwIBAgIQCgWbJfVLPYeUzGYxR3U4ozANBgkqhkiG9w0BAQsFADBh</span><br><span class="line">MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3</span><br><span class="line">d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD</span><br><span class="line">QTAeFw0yMjA1MDQwMDAwMDBaFw0zMTExMDkyMzU5NTlaMFwxCzAJBgNVBAYTAlVT</span><br><span class="line">MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE0MDIGA1UEAxMrUmFwaWRTU0wgR2xv</span><br><span class="line">YmFsIFRMUyBSU0E0MDk2IFNIQTI1NiAyMDIyIENBMTCCAiIwDQYJKoZIhvcNAQEB</span><br><span class="line">BQADggIPADCCAgoCggIBAKY5PJhwCX2UyBb1nelu9APen53D5+C40T+BOZfSFaB0</span><br><span class="line">v0WJM3BGMsuiHZX2IHtwnjUhLL25d8tgLASaUNHCBNKKUlUGRXGztuDIeXb48d64</span><br><span class="line">k7Gk7u7mMRSrj+yuLSWOKnK6OGKe9+s6oaVIjHXY+QX8p2I2S3uew0bW3BFpkeAr</span><br><span class="line">LBCU25iqeaoLEOGIa09DVojd3qc/RKqr4P11173R+7Ub05YYhuIcSv8e0d7qN1sO</span><br><span class="line">1+lfoNMVfV9WcqPABmOasNJ+ol0hAC2PTgRLy/VZo1L0HRMr6j8cbR7q0nKwdbn4</span><br><span class="line">Ar+ZMgCgCcG9zCMFsuXYl/rqobiyV+8U37dDScAebZTIF/xPEvHcmGi3xxH6g+dT</span><br><span class="line">CjetOjJx8sdXUHKXGXC9ka33q7EzQIYlZISF7EkbT5dZHsO2DOMVLBdP1N1oUp0/</span><br><span class="line">1f6fc8uTDduELoKBRzTTZ6OOBVHeZyFZMMdi6tA5s/jxmb74lqH1+jQ6nTU2/Mma</span><br><span class="line">hGNxUuJpyhUHezgBA6sto5lNeyqc+3Cr5ehFQzUuwNsJaWbDdQk1v7lqRaqOlYjn</span><br><span class="line">iomOl36J5txTs0wL7etCeMRfyPsmc+8HmH77IYVMUOcPJb+0gNuSmAkvf5QXbgPI</span><br><span class="line">Zursn/UYnP9obhNbHc/9LYdQkB7CXyX9mPexnDNO7pggNA2jpbEarLmZGi4grMmf</span><br><span class="line">AgMBAAGjggGCMIIBfjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBTwnIX9</span><br><span class="line">op99j8lou9XUiU0dvtOQ/zAfBgNVHSMEGDAWgBQD3lA1VtFMu2bwo+IbG8OXsj3R</span><br><span class="line">VTAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC</span><br><span class="line">MHYGCCsGAQUFBwEBBGowaDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNl</span><br><span class="line">cnQuY29tMEAGCCsGAQUFBzAChjRodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20v</span><br><span class="line">RGlnaUNlcnRHbG9iYWxSb290Q0EuY3J0MEIGA1UdHwQ7MDkwN6A1oDOGMWh0dHA6</span><br><span class="line">Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RDQS5jcmwwPQYD</span><br><span class="line">VR0gBDYwNDALBglghkgBhv1sAgEwBwYFZ4EMAQEwCAYGZ4EMAQIBMAgGBmeBDAEC</span><br><span class="line">AjAIBgZngQwBAgMwDQYJKoZIhvcNAQELBQADggEBAAfjh/s1f5dDdfm0sNm74/dW</span><br><span class="line">MbbsxfYV1LoTpFt+3MSUWvSbiPQfUkoV57b5rutRJvnPP9mSlpFwcZ3e1nSUbi2o</span><br><span class="line">ITGA7RCOj23I1F4zk0YJm42qAwJIqOVenR3XtyQ2VR82qhC6xslxtNf7f2Ndx2G7</span><br><span class="line">Mem4wpFhyPDT2P6UJ2MnrD+FC//ZKH5/ERo96ghz8VqNlmL5RXo8Ks9rMr/Ad9xw</span><br><span class="line">Y4hyRvAz5920myUffwdUqc0SvPlFnahsZg15uT5HkK48tHR0TLuLH8aRpzh4KJ/Y</span><br><span class="line">p0sARNb+9i1R4Fg5zPNvHs2BbIve0vkwxAy+R4727qYzl3027w9jEFC6HMXRaDc=</span><br><span class="line">-----END CERTIFICATE-----</span><br><span class="line">---</span><br><span class="line">Server certificate</span><br><span class="line">subject=CN = *.kookapp.cn</span><br><span class="line"></span><br><span class="line">issuer=C = US, O = &quot;DigiCert, Inc.&quot;, CN = RapidSSL Global TLS RSA4096 SHA256 2022 CA1</span><br><span class="line"></span><br><span class="line">---</span><br><span class="line">No client certificate CA names sent</span><br><span class="line">Peer signing digest: SHA256</span><br><span class="line">Peer signature type: RSA-PSS</span><br><span class="line">Server Temp Key: X25519, 253 bits</span><br><span class="line">---</span><br><span class="line">SSL handshake has read 3993 bytes and written 392 bytes</span><br><span class="line">Verification: OK</span><br><span class="line">---</span><br><span class="line">New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384</span><br><span class="line">Server public key is 2048 bit</span><br><span class="line">Secure Renegotiation IS NOT supported</span><br><span class="line">Compression: NONE</span><br><span class="line">Expansion: NONE</span><br><span class="line">No ALPN negotiated</span><br><span class="line">Early data was not sent</span><br><span class="line">Verify return code: 0 (ok)</span><br><span class="line">---</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>在这里面能看到如下内容</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">SSL handshake has read 3993 bytes and written 392 bytes</span><br><span class="line">Verification: OK</span><br></pre></td></tr></table></figure><p>也就是说，我们的本地主机是能成功认证<code>kookapp.cn</code>的ssl证书的，那么问题就出现在python的ssl模块中了！</p><h2 id="2-3-检测python的ssl模块"><a href="#2-3-检测python的ssl模块" class="headerlink" title="2.3 检测python的ssl模块"></a>2.3 检测python的ssl模块</h2><p>python开启一个REPL，输入如下的两行代码</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> ssl</span><br><span class="line">ssl.create_default_context().cert_store_stats()</span><br></pre></td></tr></table></figure><p>输出的内容如下</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; import ssl</span><br><span class="line">&gt;&gt;&gt; ssl.create_default_context().cert_store_stats()</span><br><span class="line">&#123;&#x27;x509&#x27;: 0, &#x27;crl&#x27;: 0, &#x27;x509_ca&#x27;: 0&#125;</span><br></pre></td></tr></table></figure><p>这里就能看出来问题了，全都是0！难怪说没有办法获取本地的CA</p><p>作为对比，一台<strong>正常的</strong>windows11电脑输出如下</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; import ssl</span><br><span class="line">&gt;&gt;&gt; ssl.create_default_context().cert_store_stats()</span><br><span class="line">&#123;&#x27;x509&#x27;: 87, &#x27;crl&#x27;: 0, &#x27;x509_ca&#x27;: 84&#125;</span><br></pre></td></tr></table></figure><h2 id="2-4-解决错误"><a href="#2-4-解决错误" class="headerlink" title="2.4 解决错误"></a>2.4 解决错误</h2><p>找到了错误，那就得想办法解决！</p><p>python开启一个REPL，输入如下的两行代码</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">import ssl</span><br><span class="line">ssl.get_default_verify_paths()</span><br></pre></td></tr></table></figure><p>输出内容如下，这是本地python查找ca证书的位置</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; import ssl</span><br><span class="line">&gt;&gt;&gt; ssl.get_default_verify_paths()</span><br><span class="line">DefaultVerifyPaths(cafile=None, capath=&#x27;/usr/local/openssl-1.1.1/ssl/certs&#x27;, openssl_cafile_env=&#x27;SSL_CERT_FILE&#x27;, openssl_cafile=&#x27;/usr/local/openssl-1.1.1/ssl/cert.pem&#x27;, openssl_capath_env=&#x27;SSL_CERT_DIR&#x27;, openssl_capath=&#x27;/usr/local/openssl-1.1.1/ssl/certs&#x27;)</span><br></pre></td></tr></table></figure><p>其中<code>/usr/local/openssl-1.1.1/ssl/cert.pem</code>中应该会保存CA的信息</p><p>查看本地的这个文件的内容</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ls /usr/local/openssl-1.1.1/ssl/cert.pem</span><br></pre></td></tr></table></figure><p>好家伙，发现文件压根不存在！</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ls: cannot access &#x27;/usr/local/openssl-1.1.1/ssl/cert.pem&#x27;: No such file or directory</span><br></pre></td></tr></table></figure><p>看看openssl的版本和路径</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">openssl  version -d</span><br></pre></td></tr></table></figure><p>输出如下</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">OPENSSLDIR: &quot;/etc/pki/tls&quot;</span><br></pre></td></tr></table></figure><p>于是看看这个目录下有没有我们需要的<code>cert.pem</code></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ ls /etc/pki/tls</span><br><span class="line">cert.pem  certs  ct_log_list.cnf  misc  openssl.cnf  private</span><br></pre></td></tr></table></figure><p>有！那么接下来要做的，就是创建一个软连接，将其弄到&#x2F;usr&#x2F;local中！</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ln -s /etc/pki/tls/cert.pem /usr/local/openssl-1.1.1/ssl/cert.pem</span><br></pre></td></tr></table></figure><h1 id="3-完美！"><a href="#3-完美！" class="headerlink" title="3.完美！"></a>3.完美！</h1><p>做完这一切后，再次运行机器人，这次么有报错了！</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ py3 main.py</span><br><span class="line">[FileManage] load all files</span><br><span class="line">[BOT] Start at: [23-02-24 00:25:29]</span><br><span class="line">[BOT.TASK] fetch_public_channel success</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>也成功响应了命令！</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/02/63f812d3abf94.png"                      alt="image-20230224092852118"                ></p><p>似乎这个问题在mac下更常见？由于没有使用过mac设备，本文不解释相关内容</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/02/63f81349c99b9.png"                      alt="image-20230224093050205"                ></p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/03/640d558b80b56.png"                                     ></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;CentOS8 出现 SSL certificate problem: unable to get local issuer certificate 解决办法&lt;/p&gt;</summary>
    
    
    
    <category term="速通Python" scheme="https://blog.musnow.top/categories/%E9%80%9F%E9%80%9APython/"/>
    
    
    <category term="Python" scheme="https://blog.musnow.top/tags/Python/"/>
    
    <category term="CentOS" scheme="https://blog.musnow.top/tags/CentOS/"/>
    
  </entry>
  
  <entry>
    <title>【Nas】绿联安装CentOS8容器</title>
    <link href="https://blog.musnow.top/posts/336198679/"/>
    <id>https://blog.musnow.top/posts/336198679/</id>
    <published>2023-02-23T14:25:57.000Z</published>
    <updated>2023-04-03T01:23:39.626Z</updated>
    
    <content type="html"><![CDATA[<p>绿联安装CentOS8容器</p><span id="more"></span><h1 id="1-创建容器"><a href="#1-创建容器" class="headerlink" title="1.创建容器"></a>1.创建容器</h1><p>先在客户端安装容器，随后用xshell进入绿联的ssh（如何<a class="link"   href="https://www.bilibili.com/video/BV1J84y1C7Zu/?spm_id_from=333.337.search-card.all.click" >开启ssh参考视频<i class="fas fa-external-link-alt"></i></a>）</p><blockquote><p>简单说来，就是在客户端里面开启远程调试，将远程调试的验证码和下面的密码拼起来，就是绿联ssh的root密码</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">L#W$%W1uGa</span><br></pre></td></tr></table></figure><p>端口是922，利用putty或者xshell等软件链接（推荐xshell）</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssh 绿联本地ip:922</span><br></pre></td></tr></table></figure><p>会出现提示框要你输入账户和密码，账户写root，密码用上面拼好的密码，直接复制后，<code>shift+ins</code>粘贴到终端后回车</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">注意，终端内填写密码是不会有任何提示的，需要盲敲</span><br></pre></td></tr></table></figure></blockquote><p>执行如下命令，以<strong>特权模式</strong>创建一个centos容器，并将容器内的22端口映射给主机的22222端口</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">docker run -d \</span><br><span class="line">-p 22222:22 \</span><br><span class="line">--name centos \</span><br><span class="line">--privileged=true \</span><br><span class="line">centos:latest \</span><br><span class="line">/usr/sbin/init</span><br></pre></td></tr></table></figure><p>搞定了之后，进绿联客户端，停止容器运行，设置一个文件路径给容器内部，方便docker容器和nas资料互传（不设置也不是不行）</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/02/63f74c586cb56.png"                      alt="image-20230223192152276"                ></p><p>注意，在客户端配置的时候，需要把所有能给的选项（<code>-i -t 高级选项</code>）全都勾上，避免docker安装的centos缺少权限；</p><hr><p>回到绿联的ssh终端，用下面的命令进入容器内部</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker exec -it centos /bin/bash</span><br></pre></td></tr></table></figure><p>你也可以用客户端里面的终端功能，但是那个太简陋了，不太好用。既然都已经能ssh连上绿联了，那完全没必要用客户端</p><h2 id="1-1-判断是否成功以特权模式安装"><a href="#1-1-判断是否成功以特权模式安装" class="headerlink" title="1.1 判断是否成功以特权模式安装"></a>1.1 判断是否成功以特权模式安装</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl </span><br></pre></td></tr></table></figure><p>执行此命令，看看是否有如下输出。如果没有，代表没有成功以特权模式安装。<code>systemctl</code>在linux中很常用，且开启容器的ssh也需要使用此命令！</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/02/63f74e0d90e2c.png"                      alt="image-20230223192918079"                ></p><p>如果没能成功使用，请重试上面的步骤</p><h1 id="2-更新软件"><a href="#2-更新软件" class="headerlink" title="2.更新软件"></a>2.更新软件</h1><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum update -y &amp;&amp; yum upgrade -y</span><br></pre></td></tr></table></figure><p>第一次执行此命令的时候，大概率会出现下面的报错</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Error: Failed to download metadata for repo &#x27;appstream&#x27;: Cannot prepare internal mirrorlist</span><br></pre></td></tr></table></figure><p>参考 <a class="link"   href="https://blog.csdn.net/weixin_43252521/article/details/124409151" >Error: Failed to download metadata for repo ‘appstream‘: Cannot prepare internal mirrorlist<i class="fas fa-external-link-alt"></i></a> 博客，用下面的命令解决这个问题</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cd /etc/yum.repos.d/</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sed -i &#x27;s/mirrorlist/#mirrorlist/g&#x27; /etc/yum.repos.d/CentOS-*</span><br><span class="line">sed -i &#x27;s|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g&#x27; /etc/yum.repos.d/CentOS-*</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum makecache</span><br></pre></td></tr></table></figure><p>执行<code>makecache</code>后，系统会获取一些内容，让后我们重新指向如下命令</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum update -y &amp;&amp; yum upgrade -y</span><br></pre></td></tr></table></figure><p>会进行一连串的更新，直到出现下面的complete窗口，代表成功！</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/02/63f74f439f368.png"                      alt="image-20230223193425765"                ></p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/02/63f74edeaacc9.png"                      alt="image-20230223193247217"                ></p><h1 id="3-ssh链接"><a href="#3-ssh链接" class="headerlink" title="3.ssh链接"></a>3.ssh链接</h1><h2 id="3-1-安装passwd"><a href="#3-1-安装passwd" class="headerlink" title="3.1 安装passwd"></a>3.1 安装passwd</h2><p>一个linux系统，不能ssh链接可不行，我们需要更新一下内容，实现ssh链接</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum install passwd</span><br></pre></td></tr></table></figure><p>先安装passwd命令，设置root的密码</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">passwd</span><br></pre></td></tr></table></figure><p>会弹出来让你输入密码，盲敲就行</p><h2 id="3-2-安装vim和openssh"><a href="#3-2-安装vim和openssh" class="headerlink" title="3.2 安装vim和openssh"></a>3.2 安装vim和openssh</h2><p>用下面的命令安装vim和openssh</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum install vim openssh-server openssh-clients -y</span><br></pre></td></tr></table></figure><p>安装完毕后，修改ssh的配置文件（vim的使用请百度）</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/ssh/sshd_config</span><br></pre></td></tr></table></figure><p><strong>取消注释</strong>下面的字段即可开启ssh</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Port 22</span><br><span class="line">ListenAddress 0.0.0.0</span><br><span class="line">ListenAddress ::</span><br><span class="line"></span><br><span class="line">PermitRootLogin yes</span><br><span class="line">PubkeyAuthentication yes</span><br><span class="line">AuthorizedKeysFile .ssh/authorized_keys</span><br></pre></td></tr></table></figure><p>修改好了之后，退出终端；</p><p>用下面的命令重启ssh服务，并设置开机启动ssh</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">systemctl restart sshd.service</span><br><span class="line">systemctl enable sshd.service</span><br></pre></td></tr></table></figure><p>如果一切正常的话，这两个命令应该什么都不会输出！</p><h2 id="3-3-尝试ssh链接"><a href="#3-3-尝试ssh链接" class="headerlink" title="3.3 尝试ssh链接"></a>3.3 尝试ssh链接</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssh 绿联本地ip:22222</span><br></pre></td></tr></table></figure><p>会弹出来让你验证登录，到这里就是ok了</p><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/02/63f750c5f3325.png"                      alt="image-20230223194054648"                ></p><h2 id="3-4-配置ls命令颜色"><a href="#3-4-配置ls命令颜色" class="headerlink" title="3.4 配置ls命令颜色"></a>3.4 配置ls命令颜色</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/profile</span><br></pre></td></tr></table></figure><p>在末尾添加如下字段</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">export LS_OPTIONS=&#x27;--color=auto&#x27;</span><br><span class="line">alias ls=&#x27;ls $LS_OPTIONS&#x27;</span><br><span class="line">alias ll=&#x27;ls $LS_OPTIONS -lA&#x27;</span><br></pre></td></tr></table></figure><p>随后重启容器即可</p><h2 id="3-5-配置yum阿狸源"><a href="#3-5-配置yum阿狸源" class="headerlink" title="3.5 配置yum阿狸源"></a>3.5 配置yum阿狸源</h2><p>备份原有源</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tar -zcvf CentOS-bk.tar.gz /etc/yum.repos.d/CentOS-*</span><br></pre></td></tr></table></figure><p>看看本地的centos是什么版本</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cat /etc/redhat-release</span><br></pre></td></tr></table></figure><p>我这里的版本是8.5</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">CentOS Linux release 8.5.2111</span><br></pre></td></tr></table></figure><p>替换阿里源</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-8.repo</span><br><span class="line">curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-8.repo</span><br></pre></td></tr></table></figure><p>重新生成缓存</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">yum clean all</span><br><span class="line">yum makecache</span><br></pre></td></tr></table></figure><p><img                       lazyload                     alt="image"                     data-src="https://img.musnow.top/i/2023/02/63f7562e6d44a.png"                      alt="image-20230223200200975"                ></p><p>现在速度快多了！</p><h1 id="开用！"><a href="#开用！" class="headerlink" title="开用！"></a>开用！</h1><p>后续的使用就不再多说拉！既然你想安装centos，想必对linux的了解肯定比我多😂</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;绿联安装CentOS8容器&lt;/p&gt;</summary>
    
    
    
    <category term="捣鼓折腾nas" scheme="https://blog.musnow.top/categories/%E6%8D%A3%E9%BC%93%E6%8A%98%E8%85%BEnas/"/>
    
    
    <category term="CentOS" scheme="https://blog.musnow.top/tags/CentOS/"/>
    
    <category term="nas" scheme="https://blog.musnow.top/tags/nas/"/>
    
  </entry>
  
</feed>
