<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>Willy Geek</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://blog.gk99.site/</id>
  <link href="https://blog.gk99.site/" rel="alternate"/>
  <link href="https://blog.gk99.site/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, Willy Geek</rights>
  <title>Willy Geek's Blog</title>
  <updated>2026-07-17T05:55:55.742Z</updated>
  <entry>
    <author>
      <name>Willy Geek</name>
    </author>
    <category term="offline sentry docker docker-compose" scheme="https://blog.gk99.site/tags/offline-sentry-docker-docker-compose/"/>
    <content>
      <![CDATA[<h2 id="1、Sentry是什么"><a href="#1、Sentry是什么" class="headerlink" title="1、Sentry是什么"></a>1、Sentry是什么</h2><p><img src="/assets/2021-09-10-install-sentry-offline/image-20210910111113167.png" alt="image-20210910111113167"> </p><p>如官网Logo所言：</p><blockquote><p>Application Monitoring and Error Tracking Software.</p></blockquote><p>应用监控以及错误追踪软件。</p><p>至于Sentry到底有多好，可能只有亲身体验一下就知道了（完善的bug追踪，完整的复现流程breadcrumbs，客户机型的纪录，ip的纪录etc..）</p><h2 id="2、为什么用Sentry"><a href="#2、为什么用Sentry" class="headerlink" title="2、为什么用Sentry"></a>2、为什么用Sentry</h2><ul><li><p>Sentry采用Client&#x2F;Server方式。</p></li><li><p>客户端提供了多个平台的SDK支持，如Android、iOS、Vue、React、Go、Java、NodeJS，开源并且方便快速集成。</p></li><li><p>服务端开源，可自行搭建，也可通过官网提供的基础服务集成到自己的应用里面， 只有高级服务付费，基础服务例如最常用的bug追踪是免费的。</p></li></ul><h2 id="3、为什么需要离线安装Sentry"><a href="#3、为什么需要离线安装Sentry" class="headerlink" title="3、为什么需要离线安装Sentry"></a>3、为什么需要离线安装Sentry</h2><p>公司服务器机子在内网环境，无法连接到外网。而且官网仅提供在线安装的教程，并没提供离线安装的教程，其实离线安装只不过多了些额外的步骤，比如需要外网的内容一同打到镜像里面，把wal2json拉到本地等。</p><!--truncate--><h2 id="4、离线安装环境准备"><a href="#4、离线安装环境准备" class="headerlink" title="4、离线安装环境准备"></a>4、离线安装环境准备</h2><ul><li><p>离线安装Docker，这里有篇不错的教程，可以参考一下， <a href="https://yeasy.gitbook.io/docker_practice/install/offline">Linux离线安装Docker</a>，这里需要注意最好保证有网络的服务器跟无网络的服务器保持相同的系统版本，例如同版本下的CentOS、Ubuntu，不然会有版本兼容问题。</p></li><li><p>离线安装Docker-Compose，同样的，也有教程， 可以参考一下， <a href="https://www.jianshu.com/p/6f9ce31b9aa7">centos7离线安装docker-compose</a>. 简单来说， 就是把docker-compose的可执行文件放在<code>/usr/local/bin</code>目录下。</p></li><li><p>下载Sentry的Release包， 当前用的是<a href="https://github.com/getsentry/onpremise/releases/tag/21.8.0">21.8.0</a>版本。</p></li><li><p>前期准备完成。</p></li></ul><h2 id="5、离线安装额外准备"><a href="#5、离线安装额外准备" class="headerlink" title="5、离线安装额外准备"></a>5、离线安装额外准备</h2><ul><li><p>打开步骤4的Sentry安装包，先观察一下脚本安装步骤，入口在<code>./install.sh</code>有些地方需要外网下载的部分。</p></li><li><p>打开<code>cron/Dockerfile</code>这个文件，大概动作就是添加cron依赖，然后拷贝入口文件，但这个添加cron动作是需要联网操作的。</p></li><li><figure class="highlight shell"><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">ARG BASE_IMAGE</span><br><span class="line">FROM $&#123;BASE_IMAGE&#125;</span><br><span class="line">RUN apt-get update &amp;&amp; apt-get install -y --no-install-recommends cron &amp;&amp; \</span><br><span class="line">    rm -r /var/lib/apt/lists/*</span><br><span class="line">COPY entrypoint.sh /entrypoint.sh</span><br><span class="line">ENTRYPOINT [&quot;/entrypoint.sh&quot;]</span><br></pre></td></tr></table></figure></li><li><p>然后在<code>docker-compose.yml</code>，发现几个镜像，调用了上面的Dockerfile打包逻辑：</p></li><li><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></pre></td><td class="code"><pre><span class="line">snuba-cleanup:</span><br><span class="line">    &lt;&lt;: *snuba_defaults</span><br><span class="line">    image: snuba-cleanup-onpremise-local</span><br><span class="line">    build:</span><br><span class="line">      context: ./cron</span><br><span class="line">      args:</span><br><span class="line">        BASE_IMAGE: &quot;$SNUBA_IMAGE&quot;</span><br><span class="line">    command: &#x27;&quot;*/5 * * * * gosu snuba snuba cleanup --storage errors --dry-run False&quot;&#x27;</span><br><span class="line">symbolicator-cleanup:</span><br><span class="line">    &lt;&lt;: *restart_policy</span><br><span class="line">    image: symbolicator-cleanup-onpremise-local</span><br><span class="line">    build:</span><br><span class="line">      context: ./cron</span><br><span class="line">      args:</span><br><span class="line">        BASE_IMAGE: &quot;$SYMBOLICATOR_IMAGE&quot;</span><br><span class="line">    command: &#x27;&quot;55 23 * * * gosu symbolicator symbolicator cleanup&quot;&#x27;</span><br><span class="line">sentry-cleanup:</span><br><span class="line">    &lt;&lt;: *sentry_defaults</span><br><span class="line">    image: sentry-cleanup-onpremise-local</span><br><span class="line">    build:</span><br><span class="line">      context: ./cron</span><br><span class="line">      args:</span><br><span class="line">        BASE_IMAGE: &quot;$SENTRY_IMAGE&quot;</span><br><span class="line">    entrypoint: &quot;/entrypoint.sh&quot;</span><br><span class="line">    command: &#x27;&quot;0 0 * * * gosu sentry sentry cleanup --days $SENTRY_EVENT_RETENTION_DAYS&quot;&#x27;</span><br></pre></td></tr></table></figure></li><li><p>主要就是给sentry、snuba、symbolicator这几个镜像，添加cron定时任务模块，如果我们要在离线环境安装，肯定是不行的，于是我们改写一下这三个镜像，提前把定时任务打进镜像里面。举其中一个镜像当例子，新建Dockerfile：</p></li><li><figure class="highlight dockerfile"><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">FROM</span> getsentry/sentry:<span class="number">21.8</span>.<span class="number">0</span></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> apt-get update &amp;&amp; apt-get install -y --no-install-recommends cron &amp;&amp; \</span></span><br><span class="line"><span class="language-bash">    <span class="built_in">rm</span> -r /var/lib/apt/lists/*</span></span><br></pre></td></tr></table></figure><p>然后docker build出来，然后用docker tag给他一个原来一样的名字，如: <code>getsentry/sentry:21.8.0</code>，另外两个同理build出镜像出来，便可以拉到离线环境的服务器里面了。其他需要依赖的镜像，例如alpine、redis、zookeeper， 无需其他额外操作，就按照<code>docker-compose.yml</code>里面的指定版本进行安装导出即可，然后拉到离线环境的服务器里面。</p></li><li><p>打开install-wal2json.sh这个脚本：</p><figure class="highlight shell"><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></pre></td><td class="code"><pre><span class="line">echo &quot;$&#123;_group&#125;Downloading and installing wal2json ...&quot;</span><br><span class="line"></span><br><span class="line">FILE_TO_USE=&quot;../postgres/wal2json/wal2json.so&quot;</span><br><span class="line">ARCH=$(uname -m)</span><br><span class="line">FILE_NAME=&quot;wal2json-Linux-$ARCH-glibc.so&quot;</span><br><span class="line"></span><br><span class="line">docker_curl() &#123;</span><br><span class="line">    # The environment variables can be specified in lower case or upper case.</span><br><span class="line">    # The lower case version has precedence. http_proxy is an exception as it is only available in lower case.</span><br><span class="line">    docker run --rm -e http_proxy -e https_proxy -e HTTPS_PROXY -e no_proxy -e NO_PROXY curlimages/curl:7.77.0 &quot;$@&quot;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">if [[ $WAL2JSON_VERSION == &quot;latest&quot; ]]; then</span><br><span class="line">    VERSION=$(</span><br><span class="line">        docker_curl https://api.github.com/repos/getsentry/wal2json/releases/latest |</span><br><span class="line">        grep &#x27;&quot;tag_name&quot;:&#x27; |</span><br><span class="line">        sed -E &#x27;s/.*&quot;([^&quot;]+)&quot;.*/\1/&#x27;</span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line">    if [[ ! $VERSION ]]; then</span><br><span class="line">        echo &quot;Cannot find wal2json latest version&quot;</span><br><span class="line">        exit 1</span><br><span class="line">    fi</span><br><span class="line">else</span><br><span class="line">    VERSION=$WAL2JSON_VERSION</span><br><span class="line">fi</span><br><span class="line"></span><br><span class="line">mkdir -p ../postgres/wal2json</span><br><span class="line">if [ ! -f &quot;../postgres/wal2json/$VERSION/$FILE_NAME&quot; ]; then</span><br><span class="line">    mkdir -p &quot;../postgres/wal2json/$VERSION&quot;</span><br><span class="line">    docker_curl -L \</span><br><span class="line">        &quot;https://github.com/getsentry/wal2json/releases/download/$VERSION/$FILE_NAME&quot; \</span><br><span class="line">        &gt; &quot;../postgres/wal2json/$VERSION/$FILE_NAME&quot;</span><br><span class="line">fi</span><br><span class="line">cp &quot;../postgres/wal2json/$VERSION/$FILE_NAME&quot; &quot;$FILE_TO_USE&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">echo &quot;$&#123;_endgroup&#125;&quot;</span><br></pre></td></tr></table></figure><p>观察分析发现，这里面也是需要联网操作，我们需要从github release把wal2json下载下来，然后把<a href="https://github.com/getsentry/wal2json/releases/download/0.0.2/wal2json-Linux-x86_64-glibc.so">wal2json-Linux-x86_64-glibc.so</a>这个丢到&#x2F;postgres&#x2F;wal2json&#x2F;0.0.2&#x2F;这个目录下即可，然后还要在<code>.env</code>文件里面，把wal2json的版本从latest改为0.0.2。</p></li></ul><h2 id="6、安装sentry"><a href="#6、安装sentry" class="headerlink" title="6、安装sentry"></a>6、安装sentry</h2><p>  需要联网下载的东西，然后把所需的镜像image，也拉到了离线环境，这里顺便提一下，如何离线安装镜像</p><p>在有联网的机器上，我们可以通过docker save命令，把已经pull下来的镜像，导出来，举个栗子：</p><p>docker save nginx &gt; nginx.tar</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"></span><br><span class="line">导出来之后，把`nginx.tar`拉到离线机器上，通过docker load命令，导入镜像：</span><br><span class="line"></span><br><span class="line">```shell</span><br><span class="line">docker load &lt; nginx.tar</span><br></pre></td></tr></table></figure><p>  我们在前一步已经准备就绪，接下来，运行</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./install.sh</span><br></pre></td></tr></table></figure><p>  如无意外，将会自动配置各种环境。</p><p>然后，用docker-compose把容器跑起来即可：</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-compose up -d</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://blog.gk99.site/2021/09/10/2021-09-10-install-sentry-offline/</id>
    <link href="https://blog.gk99.site/2021/09/10/2021-09-10-install-sentry-offline/"/>
    <published>2021-09-10T02:00:00.000Z</published>
    <summary>
      <![CDATA[<h2 id="1、Sentry是什么"><a href="#1、Sentry是什么" class="headerlink" title="1、Sentry是什么"></a>1、Sentry是什么</h2><p><img src="/assets/2021-09-10-insta]]>
    </summary>
    <title>离线安装Sentry</title>
    <updated>2026-07-17T05:55:55.742Z</updated>
  </entry>
  <entry>
    <author>
      <name>Willy Geek</name>
    </author>
    <category term="DIY project Kindle jailbreak" scheme="https://blog.gk99.site/tags/DIY-project-Kindle-jailbreak/"/>
    <content>
      <![CDATA[<p><img src="/assets/2020-06-08-recent-projects-02/311591625042_.pic.jpg" alt="311591625042_.pic"></p><p>  不知哪里的灵感，看着角落里的Kindle(kt2)，突然想给他越个狱，然后把改造成成墨水屏的时钟，再配个屏保图片，完美~！</p><!--truncate--><p>准备材料：</p><ul><li>1.Kindle一个</li><li>2.撬棒一根</li><li>3.USB转TTL连接器</li><li>4.接线数根</li></ul><p>越狱步骤：</p><pre><code>1. 由于kindle的系统自动更新到了最新版本，开始尝试通过软件越狱的，但都说版本过高，只能通过拆机越狱2. 拆机越狱需要拆机，还要用到串口调试。所以拆开后，还要焊线，把串口调试的两个焊点接上TTL串口调试器，相当于找到了越狱的后门，通过硬件强制越狱。由于焊点小，所以找了手机店的师傅进行焊线，五块钱搞定3. 详细的越狱步骤，有先辈出的一些教程，如[这个](https://www.luu.moe/90/)，还有[这个](http://www.yunjialeguanwang.com/archives/4670.html)，步骤都大体相同。在这过程中，令我很意外的是，kingdle的root密码竟然是mario，难道kindle系统的开发者也是任天堂的粉丝？</code></pre><p>越狱完之后，装了屏保插件，可以自由地设置自己的屏保图片，但对屏保图片还是有一定要求的，例如固定宽高啊，去掉色彩，很幸运的是，书伴为我们制作了ps<a href="https://bookfere.com/post/470.html">工作流</a>，导入到ps之后，点几下就能生成符合kindle要求的屏保图片。</p><p><img src="/assets/2020-06-08-recent-projects-02/301591624753_.pic-1669134.jpg" alt="301591624753_.pic"></p><p>但这样还不够，我们的目标是要做成墨水屏时钟。而且有可能还要做成一个应用，能够定时刷新时间，还要定时获取屏保图片</p><p>于是又开始了新的寻找，很幸运的是，找到了<a href="https://www.mobileread.com/forums/member.php?u=69624">NiLuJe</a>大神写的渲染插件，能够方便地渲染png图片，还有文字等。</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">但图片从哪里来呢？</span><br><span class="line"></span><br><span class="line">于是在服务器上写了接收图片的服务</span><br><span class="line"></span><br><span class="line">还写了个前端页面，方便自己上传想要更换的图片</span><br><span class="line"></span><br><span class="line">到了kindle客户端这边，需要定时从服务端获取图片，并刷新时间</span><br><span class="line"></span><br><span class="line">绕了一大圈，终于把需求给实现了</span><br></pre></td></tr></table></figure><p>但是，整一个流程下来，kindle必须关闭屏保，处于一直运行状态，费时又费电，不到一天时间，满电的kindle就耗光了</p><p>既然耗电，那一直插着充电不就行了吗？</p><p>可一直插着充电不美观，而且伤电池</p><p>如果不能一直插着充电，就必须舍弃之前所做的这套流程</p><p>那怎么办？</p><p>抛弃多余的东西，抛弃耗电的流程</p><p>让它安静地做个电子相册好了</p><p>然后扔到角落里</p><p>继续吃灰</p><p>End~</p>]]>
    </content>
    <id>https://blog.gk99.site/2020/06/08/2020-06-08-recent-projects-02/</id>
    <link href="https://blog.gk99.site/2020/06/08/2020-06-08-recent-projects-02/"/>
    <published>2020-06-08T13:00:00.000Z</published>
    <summary>
      <![CDATA[<p><img src="/assets/2020-06-08-recent-projects-02/311591625042_.pic.jpg" alt="311591625042_.pic"></p>
<p>  不知哪里的灵感，看着角落里的Kindle(kt2)，突然想给他越]]>
    </summary>
    <title>最近做的一些硬件diy项目-02-给Kindle越个狱</title>
    <updated>2026-07-17T05:55:55.739Z</updated>
  </entry>
  <entry>
    <author>
      <name>Willy Geek</name>
    </author>
    <category term="DIY project Arduino Raspberry" scheme="https://blog.gk99.site/tags/DIY-project-Arduino-Raspberry/"/>
    <content>
      <![CDATA[<p><img src="/assets/2020-06-07-recent-projects-01/d7c9b151d24d5f6b20b7590603ec65.gif" alt="d7c9b151d24d5f6b20b7590603ec65"></p><p>最初的想法是做一个无线充电板，上面是实木，下面是环氧树脂，无线充电的线圈嵌在实木板底下，然后底下粘贴rgb灯条作指示。但由于手工不到家，给实木板挖孔的时候，挖穿了两块板:joy:，加之无线充发热量大，于是就放弃对无线充的改造</p><h4 id="使用到的材料："><a href="#使用到的材料：" class="headerlink" title="使用到的材料："></a>使用到的材料：</h4><ul><li>1.环氧树脂A&#x2F;B胶</li><li>2.黑胡桃木薄片</li><li>3.Type-C 母头接口</li><li>4.Attiny85单片机</li><li>5.WS2812B全彩灯条（一米144颗灯珠）</li><li>6.Arduino开发板（给Attiny85烧录用）</li><li>7.杜邦线若干</li></ul><!--truncate--><h4 id="软件部分："><a href="#软件部分：" class="headerlink" title="软件部分："></a>软件部分：</h4><ul><li>Arduino IDE</li></ul><h4 id="把大象装进冰箱，只要三步。而我们的改造步骤也是只要三步："><a href="#把大象装进冰箱，只要三步。而我们的改造步骤也是只要三步：" class="headerlink" title="把大象装进冰箱，只要三步。而我们的改造步骤也是只要三步："></a>把大象装进冰箱，只要三步。而我们的改造步骤也是只要三步：</h4><ol><li><p>天才第一步，先让灯亮起来。具体连线过程呢，可参考这个<a href="http://www.i-element.org/ws2812/">教程</a>。软件方面，选用的是NeoPixel这个库，方便驱动RGB灯珠。在NeoPixel的example里面，附送了很多例子，随便抄一个跑马灯过来就👌</p></li><li><p>天才第二步，把代码塞进小芯片里面。把Arduino塞进我们的成品是不太可能的了，那有没有办法把它变小呢？办法当然是有的，就是把代码烧录到Attiny85里，具体烧录过程呢，可参考这个<a href="https://create.arduino.cc/projecthub/alaspuresujay/use-an-attiny85-with-arduino-ide-07740c">教程</a>。其中，记得要改变一下Attiny85的时钟频率，要改成8Mhz的，然后再引导一下，不然不兼容NeoPixel这个库</p><p><img src="/assets/2020-06-07-recent-projects-01/IMG_0578.jpg" alt="IMG_0578"></p></li><li><p>天才第三步，封装起来。手工粗糙，在下就不表辣~</p></li></ol><h3 id="收获"><a href="#收获" class="headerlink" title="收获"></a>收获</h3><ol><li>最初的想法是做无线充的，在对无线充改造的过程中，遇到很多坑。举个栗子，想要获取无线充的充电状态，必须从无线充板子上获取充电状态的“信号”。</li><li>而在单片机开发中，无非分为两种信号，模拟信号和数字信号。</li><li>模拟信号简单理解为连续的信号，例如人耳听到的声音，有高有低而且连续。而数字信号呢，简单理解为以一定数值为单位的不连续信号，计算机接收声音信号，是根据一定的时间间隔，把接收的声音信号，分成一段一段的信号，每一段都截取对应时间的声音强度，拼接起来就成了连续的声音。这个过程也叫采样，多久时间分一段，这个叫采样频率，频率越高，存储这些信息的空间也就越大</li><li>一开始把无线充LED状态灯的输出，作为模拟信号的输入，一直在那分析，什么正弦波，余弦波，pwm信号灯。由于LED状态灯使用了pwm脉冲信号做呼吸灯效果，研究了大半天，都无法确定无线充的状态到底是充电，还是在闲置状态。钻牛角不可怕，可怕的是钻一直下去。</li><li>意识到钻牛角尖了，立马改变方向。我们不是还有个数字信号吗？立马把LED状态灯的输出作为数字信号输入，害，结果一下就出来了。。。</li><li>单片机的闪存有上限，同一时间只能控制一定数量的RGB灯珠，再多就没有足够内存空间进行存储。不像平常电脑上的软件开发，有足量的内存空间，所以还要时刻注意内存使用，变量的分配</li><li>相较于使用51、stm32单片机，个人觉得arduino对新手更加友好一些。墙裂推荐佑來老师推出的<a href="https://www.bilibili.com/video/BV1YW411Z76E">Arduino教学</a>，对新手超级友好。结合了很多实例，跟着实作的话肯定能学习到很多</li></ol>]]>
    </content>
    <id>https://blog.gk99.site/2020/06/08/2020-06-07-recent-projects-01/</id>
    <link href="https://blog.gk99.site/2020/06/08/2020-06-07-recent-projects-01/"/>
    <published>2020-06-08T03:00:00.000Z</published>
    <summary>
      <![CDATA[<p><img src="/assets/2020-06-07-recent-projects-01/d7c9b151d24d5f6b20b7590603ec65.gif" alt="d7c9b151d24d5f6b20b7590603ec65"></p>
<p>最初的想法是做一]]>
    </summary>
    <title>最近做的一些硬件diy项目-01-流彩灯展示板</title>
    <updated>2026-07-17T05:55:55.739Z</updated>
  </entry>
  <entry>
    <author>
      <name>Willy Geek</name>
    </author>
    <category term="zelda link" scheme="https://blog.gk99.site/tags/zelda-link/"/>
    <content>
      <![CDATA[<p><img src="/assets/2020-03-03-thoughts-of-zelda/ryan.jpg" alt="ryan"></p><p>正如标题所说, 这篇感想是在打了140小时的塞尔达写下的。说下目前完成的情况：通关了120座神庙，以及DLC的所有神庙，领了摩托车，打了盖侬，走完主剧情任务，其他支线任务尚未完成，如若全部完成的话可能要200h+了吧。 </p><p>目前没有继续肝下去的打算，个人是倾向神庙解谜这块的，对于打怪、收集呀哈哈这些兴趣度不高，打完所有神庙之后也无心继续肝下去了。</p><p>当然在网上也看到其他爱好者，例如速通爱好者，呀哈哈爱好者，研究游戏中各种bug操作爱好者，御风飞行爱好者等等，相信他们也是找到了属于自己的游戏乐趣。这个游戏就是面向很多爱好向的人群，可以体验RPG的剧情化，也可以体验收集的乐趣，也可以体验解谜向的乐趣，还可以动作游戏的爽快感。</p><!--truncate--><p>个人觉得在RPG这块还是稍弱了点，剧情简短些，加上开放世界的设定，剧情也是松散了些，并无过多的关联性。成也开放性，败也在开放性啊。</p><p>对于收集这块，九百多个的呀哈哈，可谓塞尔达历史上最多的一次收集任务了。不过放心，玩家不用收集那么多的呀哈哈，在旷野之息这部游戏中，收集呀哈哈就是为了兑换武器的卡槽，只要收集一定数量的呀哈哈（可能几十个就够了），就能正常通关主剧情任务。呀哈哈遍布游戏世界的每一个角落，低头沉思或是仰望星空，都有可能遇到一只呀哈哈，拨开草丛又或是跋山涉水，也可能遇到一只呀哈哈。惊喜总在不经意之间出现，像是给玩家不断探索的奖励糖果。</p><p>任天堂的大部分游戏，都延续着收集向这个要素，从最初的初代马里奥收集金币，再到后来的马里奥3d世界，到后来的三角力量，再到这作【旷野之息】，都流淌这种血液。</p><p>从心理学上来说，这种设计就是不断给玩家以正向奖励，不断在大脑产生多巴胺，让玩家沉浸其中，并从中快感与成就感。不管是现如今的一些手机小游戏，还是3A大作，其实都有意无意地安排这些设定于游戏之中，让玩家沉浸其中。</p><p>【旷野之息】也可作为一款动作游戏来体验其中的操作快感。作为手残党，笔者总是练不会盾反，抓不住盾反的时机。练会并掌握这个时机的玩家，就能够体验到这苦难后的甘甜辣。在游戏设定中，玩家持剑拿盾，如果刚好用盾抵挡了敌人的攻击，则会触发林可时间（子弹时间），此时相当于进入无敌时间，而敌人则进入静止状态，任你砍杀，爽快感一流。但练不会的话，又或者手残的快，就可能被敌人瞬间砍倒了。</p><p>在这波设定中，又会成为一个分水岭，一波是学不会盾反的玩家，一波是掌握盾反节奏的硬核玩家。但塞尔达并不强制所有玩家都得学会这个技能，只是告诉你有这个技能，至于要不要用，就看玩家了。</p><p>玩家也可以通过其他一些奇淫巧技来打败敌人，例如用铁盒砸死怪物，用炸弹送进巨型敌人嘴里然后炸死，扔剑到敌人附近然后引雷劈死敌人，更简单的就是静止敌人死命砍过去。在塞尔达中，打倒敌人很多时候远不止一种方法，常常引导玩家去思考，去解决问题。</p><p>至于引导玩家思考，做得更精妙可谓是游戏中设计的“神庙”了，这也是笔者喜欢这款游戏的一个重要原因。通过简单的几个道具，开局通过一个小机关来引导玩家使用某种道具，例如需要静止才能进入下一关的机关，玩家学会这种操作之后，后续就会通过更加复杂的设计，来引导玩家去深度地思考，并独立地解决所有关卡，获得最后的胜利。很多时候，迷宫无需任何文字，玩家都能通过迷宫的一些设计获得灵感或是解题思路。有时一个神庙需要苦思冥想一两个小时才能通过，虽然过程很痛苦，但过后获得的成就感无与伦比，只有玩过之后才知道。</p><p>先写到这里. 后续再更新</p><p>塞尔达传说</p><p>下一篇博客见~！</p><h5 id="原创文章，转载请注明出处-！感谢支持"><a href="#原创文章，转载请注明出处-！感谢支持" class="headerlink" title="原创文章，转载请注明出处~！感谢支持"></a>原创文章，转载请注明出处~！感谢支持</h5>]]>
    </content>
    <id>https://blog.gk99.site/2020/03/03/2020-03-03-thoughts-of-zelda/</id>
    <link href="https://blog.gk99.site/2020/03/03/2020-03-03-thoughts-of-zelda/"/>
    <published>2020-03-03T10:00:00.000Z</published>
    <summary>
      <![CDATA[<p><img src="/assets/2020-03-03-thoughts-of-zelda/ryan.jpg" alt="ryan"></p>
<p>正如标题所说, 这篇感想是在打了140小时的塞尔达写下的。说下目前完成的情况：通关了120座神庙，以及DLC的所有神庙，领]]>
    </summary>
    <title>打了140小时[塞尔达传说:旷野之息]的感想</title>
    <updated>2026-07-17T05:55:55.740Z</updated>
  </entry>
  <entry>
    <author>
      <name>Willy Geek</name>
    </author>
    <category term="raspberry weather python" scheme="https://blog.gk99.site/tags/raspberry-weather-python/"/>
    <content>
      <![CDATA[<p><img src="/assets/2019-10-21-build-IoT-system-with-raspberry/s-tsuchiya-sPLLVFJXlb8-unsplash.jpg" alt="s-tsuchiya-sPLLVFJXlb8-unsplash"></p><h3 id="前情"><a href="#前情" class="headerlink" title="前情"></a>前情</h3><p>惬意下午，阳光暖暖，清理杂物的时候找到台吃灰已久的树莓派，想要拿来做点小东西。于是想到利用它来做环境监测，把家里的温湿度收集起来，然后传到同样在吃灰的阿里服务器，最后用前端页面展示出来</p><p>Perfect~！  </p><p>所以想到了以下需求：  </p><ul><li>树莓派每小时收集一次温湿度，然后上传到服务器</li><li>服务器接收环境信息，存储到数据库里面</li><li>后端提供三个Get请求接口，以提供一天内、一周内以及最新的环境信息，提供一个Post请求接口，以接收树莓派传过来的数据</li><li>前端展示最新收集的环境信息，使用图表展示一天内、一周内的环境信息变化波动图</li></ul><p>需求不多，但怎么算也是个软硬件结合，前后端结合的小工程，想想还有点小兴奋呢，嘿嘿嘿😜！</p><!--truncate--><h3 id="前期准备"><a href="#前期准备" class="headerlink" title="前期准备"></a>前期准备</h3><p>硬件准备：</p><ul><li>树莓派3B</li><li>DHT11温湿度传感器（后升级为更精准的DHT22）</li><li>杜邦线</li></ul><p>软件准备：</p><ul><li>VSCode（code）</li><li>FileZilla(传文件到pi)</li><li>iTerm（ssh）</li></ul><h3 id="树莓派读取传感器数据"><a href="#树莓派读取传感器数据" class="headerlink" title="树莓派读取传感器数据"></a>树莓派读取传感器数据</h3><p><mark><a href="http://shumeipai.nxez.com/2018/05/16/dht11-temperature-and-humidity-sensor-raspberry-pi.html">这篇教程</a></mark>讲得灰常详细，推荐查看</p><h3 id="传送环境数据到服务器"><a href="#传送环境数据到服务器" class="headerlink" title="传送环境数据到服务器"></a>传送环境数据到服务器</h3><p>在<mark><a href="https://github.com/adafruit/Adafruit_Python_DHT">Adafruit</a></mark>这个工程底下有个example，修改一下<a href="https://github.com/adafruit/Adafruit_Python_DHT/blob/master/examples/simpletest.py">simpletest.py</a>, 加入我们的Post请求，即可。</p><p>使用pip导入requests库：</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">pip install requests</span><br></pre></td></tr></table></figure><p>然后发起Post请求即可：</p><p>完成从树莓派到服务器的请求</p><h3 id="存储到数据库里"><a href="#存储到数据库里" class="headerlink" title="存储到数据库里"></a>存储到数据库里</h3><p>后端选择了Go语言，同时选择gin作web框架，gorm做数据库框架，即可完成上述需求。</p><h3 id="前端页面进行展示"><a href="#前端页面进行展示" class="headerlink" title="前端页面进行展示"></a>前端页面进行展示</h3><p>前端选择React、<a href="https://github.com/alibaba/BizCharts">BizCharts</a>框架进行搭建，具体过程就不过多赘述。<br>最终效果可点击以下链接：</p><p><mark><a href="https://willygeek.xyz:8070/">当前天气</a></mark></p><p><mark><a href="https://willygeek.xyz:8070/history">过去一天天气</a></mark></p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>未来要做的需求：</p><ul><li>1.利用爬虫抓取天气预报，很多时候，我们急切想知道的是未来，而不是过去</li><li>2.过去可以给我们预知未来的勇气，所以想要通过深度学习，结合过去的天气，预知未来的天气。当然了，这肯定没有通过云图观察来的那么准确，就当作入门深度学习</li></ul><p><strong>其实整合起来以上所有的流程，就是一条完整的IoT物联网产业链，对，产业链，每个环节都可以做成一个商业化的产品。例如传感器这块，就已经有了米家的温湿度传感器。后台这块，就已经有了阿里云物联网平台，以接收不同平台不同设备上传上来的数据，做成SaaS平台(服务云平台)，硬件厂家无需开发后台，使用第三方现成的SaaS平台，即可对接自家的硬件，实现万物互联。在前端(客户端)展示这块，小米的米家还有苹果的HomeKit，都是体验还有互联非常棒的产品。</strong></p><h3 id="Q-A"><a href="#Q-A" class="headerlink" title="Q&amp;A"></a>Q&amp;A</h3><p>Q: 既然都有成熟的产品，那为什么自己还要做一套呢？</p><p><strong>A: 市场上确实有完整的产品，花个几十块钱就能买个传感器，提供完整的功能。而动手去做，可以体验实现每个流程带来的成就感，从硬件读取数据，从硬件传输数据到服务器，再到前端从服务器获取数据，每个细节都有优化的空间，新的想法也在这过程中闪现，是个非常不错的体验过程。</strong></p><p>Q: 接下来想做点什么好玩的东西？</p><p><strong>A: 利用这个吃灰的树莓派，做一个AirPlay音箱、做一个复古游戏机（安装模拟机）、做一个智能音箱、做一个HiFi音箱、bala bala …</strong></p><p>原创不易，转载请声明~~！</p>]]>
    </content>
    <id>https://blog.gk99.site/2019/10/21/2019-10-21-build-IoT-system-with-raspberry/</id>
    <link href="https://blog.gk99.site/2019/10/21/2019-10-21-build-IoT-system-with-raspberry/"/>
    <published>2019-10-21T09:00:28.000Z</published>
    <summary>
      <![CDATA[<p><img src="/assets/2019-10-21-build-IoT-system-with-raspberry/s-tsuchiya-sPLLVFJXlb8-unsplash.jpg" alt="s-tsuchiya-sPLLVFJXlb8-unsplash"><]]>
    </summary>
    <title>利用树莓派做一套属于自己的IoT物联网</title>
    <updated>2026-07-17T05:55:55.741Z</updated>
  </entry>
  <entry>
    <author>
      <name>Willy Geek</name>
    </author>
    <category term="WebRTC video chat" scheme="https://blog.gk99.site/tags/WebRTC-video-chat/"/>
    <content>
      <![CDATA[<h3 id="前情"><a href="#前情" class="headerlink" title="前情"></a>前情</h3><p>首先，要知道如何通过WebRTC建立简单的视频通话，首先得了解到底什么是WebRTC，可以先看下维基百科的说明：</p><p><code>WebRTC (Web Real-Time Communication) is a free, open-source project that provides web browsers and mobile applications with real-time communication (RTC) via simple application programming interfaces (APIs). It allows audio and video communication to work inside web pages by allowing direct peer-to-peer communication, eliminating the need to install plugins or download native apps.</code></p><p>当然了，WebRTC不仅广泛用于音视频通话，还可以结合其他场景用于视频会议、远程控制等。今天我们要做的就是搭建一套最简单的视频通话系统，包括前端以及后台（其实就是信令服务器），来熟悉WebRTC的整个通信流程。</p><p>在开始之前，建议读者先深入学习这篇来自WebRTC团队的入门教程<mark><a href="https://www.html5rocks.com/en/tutorials/webrtc/basics/">Getting Started with WebRTC</a></mark>,<br>其中介绍了WebRTC的历史、WebRTC基本概念（<strong>RTCPeerConnection</strong>、<strong>RTCDataChannel</strong>、<strong>MediaStream</strong>）、信令、网络拓扑等。</p><!--truncate--><p>熟悉上述的一些基本概念之后，便可以开始体验WebRTC的一些例子来深入地理解WebRTC的使用，推荐的还是官方出品的。<mark><a href="https://webrtc.github.io/samples/">WebRTC samples</a></mark></p><p>展示的是基本API的使用，具体实现还需要点击到Github查看源码，需要一定的html基础。重点可看RTCPeerConnection这一部分。</p><h3 id="WebRTC通信流程"><a href="#WebRTC通信流程" class="headerlink" title="WebRTC通信流程"></a>WebRTC通信流程</h3><p>通过上面的一些教程，我们知道建立一个通话，需要一个信令服务器(转发信令)，一个STUN&#x2F;TURN服务器（查找客户端的公网ip，如果穿透不成功则用转发），而具体这些服务器怎么联结在一起的呢，具体的操作流程到底是怎样子的呢？</p><h4 id="1-通过信令服务器转发SDP"><a href="#1-通过信令服务器转发SDP" class="headerlink" title="1.通过信令服务器转发SDP"></a>1.通过信令服务器转发SDP</h4><p>首先，可以参考MDN的<a href="https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Signaling_and_video_calling">Singal and video calling</a>里面的这个流程图：<br><img src="/assets/2019-07-30-basic-webrtc-video-chat/WebRTC_-_Signaling_Diagram.svg" alt="Signaling transaction flow"></p><p>上述流程可以简单总结为：</p><ul><li>A.发起方（caller）创建pc（RTCPeerConnection），通过getUserMedia()采集麦克风&#x2F;摄像头的音视频流（mediaStream），然后添加到pc上</li><li>B.发起方通过pc.createOffer()，创建offer，然后调用<mark>setLocalDescription(offer)</mark>设为本地的描述</li><li>C.发起方通过信令服务器（signaling server）发送offer给接收方（callee）。在这一过程中，如果接收方还没在线，信令服务器需要暂时保存offer，等接收方在线了，再发送</li><li>D.接收方创建pc，然后采集音视频流，然后把mediaStream添加到pc上</li><li>E.接收方把信令服务器传过来的offer，通过<mark>setRemoteDescription(offer)</mark>,设为远端的描述</li><li>F.接收方通过pc.createAnswew()，创建answer，然后调用<mark>setLocalDescription(answer)</mark>设为本地的描述</li><li>G.接收方通过信令服务器发送answer给发起方，发起方通过<mark>setRemoteDescription（answer）</mark>设为远端的描述，</li></ul><p>信令服务器在上述流程做的工作，其实就两个，转发offer还有转发answer，offer还有answer都属于SDP（Session Description Protocol）会话描述协议，具体详细内容通过查阅<a href="https://webrtchacks.com/sdp-anatomy/">Anatomy of a WebRTC SDP</a>来了解WebRTC里的SDP协议。</p><h4 id="2-通过信令服务器转发candidate"><a href="#2-通过信令服务器转发candidate" class="headerlink" title="2.通过信令服务器转发candidate"></a>2.通过信令服务器转发candidate</h4><p>在上图中，还有一个细节未提到的是，就是生成icecandidate并转发的流程。icecandiate是在调用createOffer()还有createAnswer()之后生成的，icecandidate代表客户端的候选地址，是通过ice机制获取当前客户端的候选地址，再借用MDN的流程图：</p><p>![ICE candidate exchange process](&#x2F;assets&#x2F;2019-07-30-basic-webrtc-video-chat&#x2F;WebRTC - ICE Candidate Exchange.svg)</p><p>上述流程可以简单总结为：</p><ul><li>a.把发起方生成的icecandidate，通过信令服务器发送给接收方。如果这时接收方不在线的话，需要信令服务器缓存这些icecandidate,等待接收方在线时，再发送</li><li>b.把接收方生成的icecandidate，通过信令服务器发送给发起方</li></ul><p>所以信令服务器又多了项工作，就是转发icecandidate。</p><h4 id="3-我们的STUN-TURN服务器呢？"><a href="#3-我们的STUN-TURN服务器呢？" class="headerlink" title="3.我们的STUN&#x2F;TURN服务器呢？"></a>3.我们的STUN&#x2F;TURN服务器呢？</h4><p>如果两个客户端在同一内网的情况下，理论上是不需要STUN&#x2F;TURN，只需要配置一台信令服务器，找到内网各自的地址，交换offer&#x2F;answer，通过上述两个步骤，就能实现音视频通话。可现实生活中，很多时候我们的客户端是处于不同的内网下的，或者说处于不同的NAT下的，无法知道对方的公网地址，这时就需要STUN&#x2F;TURN服务器啦。</p><p>在上一篇博客里面，提到过</p><p>可前面两个步骤中，没提到STUN&#x2F;TURN服务器，我们的STUN&#x2F;TURN服务器应该在哪配置呢？其实我们在一开始创建pc的时候，就需要把TURN&#x2F;STUN服务器的地址配置进去</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></pre></td><td class="code"><pre><span class="line">const configuration = &#123; iceServers: [&#123;</span><br><span class="line">                          urls: &quot;tun:turn.services.mozilla.com&quot;,</span><br><span class="line">                          username: &quot;louis@mozilla.com&quot;, </span><br><span class="line">                          credential: &quot;webrtcdemo&quot;</span><br><span class="line">                      &#125;, &#123;</span><br><span class="line">                          urls: [</span><br><span class="line">                                  &quot;stun:stun.example.com&quot;,</span><br><span class="line">                                  &quot;stun:stun-1.example.com&quot;</span><br><span class="line">                          ]</span><br><span class="line">                      &#125;]</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">const pc = new RTCPeerConnection(configuration);</span><br></pre></td></tr></table></figure><p>配置完iceServers后，pc在createOffer或者createAnswer之后，就会通过iceServers即STUN&#x2F;TURN服务器，查找自己的公网ip，并生成相应的candidate候选地址（包括内网以及外网的候选地址），然后通过信令服务器转发给对方，这样就可以互相知道对方的公网ip以及各自的内网ip。候选地址会生成多个，但最后会根据候选地址的优先级，最终选择最优的一对候选地址，形成通道，以传输音视频流。</p><p>更多关于ICE STUN TURN的相关内容，可查阅：<mark><a href="https://www.avaya.com/blogs/archives/2014/08/understanding-webrtc-media-connections-ice-stun-and-turn.html">Understanding WebRTC Media Connections: ICE, STUN and TURN</a></mark></p>]]>
    </content>
    <id>https://blog.gk99.site/2019/07/30/2019-07-30-basic-webrtc-video-chat/</id>
    <link href="https://blog.gk99.site/2019/07/30/2019-07-30-basic-webrtc-video-chat/"/>
    <published>2019-07-30T02:00:28.000Z</published>
    <summary>
      <![CDATA[<h3 id="前情"><a href="#前情" class="headerlink" title="前情"></a>前情</h3><p>首先，要知道如何通过WebRTC建立简单的视频通话，首先得了解到底什么是WebRTC，可以先看下维基百科的说明：</p>
<p><code>]]>
    </summary>
    <title>通过WebRTC建立简单的视频通话</title>
    <updated>2026-07-17T05:55:55.740Z</updated>
  </entry>
  <entry>
    <author>
      <name>Willy Geek</name>
    </author>
    <category term="Ubuntu" scheme="https://blog.gk99.site/tags/Ubuntu/"/>
    <category term="coturn" scheme="https://blog.gk99.site/tags/coturn/"/>
    <content>
      <![CDATA[<h4 id="什么是coturn"><a href="#什么是coturn" class="headerlink" title="什么是coturn?"></a>什么是coturn?</h4><h4 id=""><a href="#" class="headerlink" title=""></a><img src="/assets/2019-07-23-install-coturn-in-ubuntu/jordan-harrison-40XgDxBfYXM-unsplash.jpg" alt="jordan-harrison-40XgDxBfYXM-unsplash"></h4><p>coturn是可用作穿透服务器（STUN）及转发服务器（TURN），在WebRTC通话中扮演的角色是一个“红娘”。打个比方，在互联网的两端，有一对准备约会的情侣，他们需要进行视频通话，但互相不知道对方的网络地址，这时coturn就告诉他们各自的地址以及对方的地址，然后就可以进行通话啦。 </p><p>大部分的情况下，都可以穿透成功，也就是双方之间可以不经过任何服务器就能实现通话，在穿透不成功的情况下，就需要经过coturn服务器进行数据流的转发，也就是所有的数据经过服务器，对服务器的负载比较大。</p><!--truncate--><p>至于通话过程中，什么情况选择穿透，什么情况选择转发，取决于双方各自所处的网络环境，更具体的话就是取决于各自所处的NAT网络类型，以下是不同环境下对应所需的方式：</p><ul><li>对称NAT - 对称NAT ：TURN</li><li>对称NAT - 端口限制型NAT : TURN</li><li>对称NAT - 地址限制型NAT : STUN（部分可行）</li><li>对称NAT - 全锥形NAT : STUN</li><li>其他： STUN</li></ul><h4 id="如何知道自己网络或者服务器所处的NAT类型？"><a href="#如何知道自己网络或者服务器所处的NAT类型？" class="headerlink" title="如何知道自己网络或者服务器所处的NAT类型？"></a>如何知道自己网络或者服务器所处的NAT类型？</h4><p>Github上有个简单的开源库，叫<a href="https://github.com/jtriley/pystun">pystun</a>, 简单安装就能或者NAT类型以及公网ip</p><h4 id="Ubuntu上怎么安装coturn"><a href="#Ubuntu上怎么安装coturn" class="headerlink" title="Ubuntu上怎么安装coturn"></a>Ubuntu上怎么安装coturn</h4><p>1.先更新源：</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 apt update</span><br></pre></td></tr></table></figure><p>2.安装coturn:</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 apt install coturn</span><br></pre></td></tr></table></figure><p>3.让服务可以在重启后启动，修改<code>/etc/default/coturn</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">TURNSERVER_ENABLED=1</span><br></pre></td></tr></table></figure><p>4.修改监听端口，修改<code>/etc/turnserver.conf</code>，修改监听端口为443，找到下面的配置，去掉注释，并改为443，如果443端口被占用，可以使用默认的3478：</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">listening-port=443</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">user=your-username:your-password</span><br></pre></td></tr></table></figure><p>5.服务器端的防火墙开启443或者3478端口</p><h4 id="为什么是443端口？"><a href="#为什么是443端口？" class="headerlink" title="为什么是443端口？"></a>为什么是443端口？</h4><p>在一些极其严格的网络环境下，可能客户端网络的防火墙只给开80&#x2F;443端口，为了穿透成功，服务器端开放443端口为最保守的方案，意思就是说如果防火墙把443都封了的话，那你就别想上网了。更多细节，可参考这篇<a href="https://bloggeek.me/ignore-default-protocol-ports/">You Better Ignore the Default Protocol Ports You Implement</a></p><p>443为https端口，如果在服务器上已经装了http服务例如nginx，443可能会被占用，这时coturn可以装到另外一台服务器上，避免端口冲突；</p><h4 id="怎么测试我们成功安装好了呢？"><a href="#怎么测试我们成功安装好了呢？" class="headerlink" title="怎么测试我们成功安装好了呢？"></a>怎么测试我们成功安装好了呢？</h4><p>进入在线<a href="https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/">网页测试</a>，<code>STUN or TURN URI</code>一栏输入: stun:公网ip:3478，然后点击Add server, 再点击Gather candidates, 如果成功会输出客户端的公网ip，及可用端口号。turn的配置则需要填入上述的账号密码。</p><h4 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h4><p>以上是P2P通信中，cuturn的介绍、配置、使用以及测试。如果喜欢本站的话，记得收藏哟。</p><h5 id="原创文章，转载请注明出处-！感谢支持"><a href="#原创文章，转载请注明出处-！感谢支持" class="headerlink" title="原创文章，转载请注明出处~！感谢支持"></a>原创文章，转载请注明出处~！感谢支持</h5>]]>
    </content>
    <id>https://blog.gk99.site/2019/07/23/2019-07-23-install-coturn-in-ubuntu/</id>
    <link href="https://blog.gk99.site/2019/07/23/2019-07-23-install-coturn-in-ubuntu/"/>
    <published>2019-07-23T02:00:28.000Z</published>
    <summary>
      <![CDATA[<h4 id="什么是coturn"><a href="#什么是coturn" class="headerlink" title="什么是coturn?"></a>什么是coturn?</h4><h4 id=""><a href="#" class="headerlink" ti]]>
    </summary>
    <title>Ubuntu下安装coturn</title>
    <updated>2026-07-17T05:55:55.740Z</updated>
  </entry>
  <entry>
    <author>
      <name>Willy Geek</name>
    </author>
    <content>
      <![CDATA[<h4 id="缘起"><a href="#缘起" class="headerlink" title="缘起"></a>缘起</h4><p><img src="/assets/2019-07-11-how-to-root-360-p2/misha-feshchak-syCXK9WndqQ-unsplash.jpg" alt="misha-feshchak-syCXK9WndqQ-unsplash"></p><p>办公室刚好有个同事留下的360路由器，突发奇想，何不装个ss在上面? 这样所有连接他的设备都不用翻墙了。嘿嘿嘿。。。  </p><p>恰好搜到这款路由器有个刷机教程，<a href="https://bbs.360.cn/thread-14654564-1-1.html">360路由器5G版（型号P2）小白刷机教程</a>，教程很长。。超详细。。但总结下来，其实就六个步骤：</p><ul><li>1.恢复出厂设置</li><li>2.将路由器固件“升级”为调试版固件（如果升级不成功，先降级，再升级）</li><li>3.将breed文件传到路由器</li><li>4.刷breed</li><li>5.备份eeprom</li><li>6.刷入第三方固件</li><li>End</li></ul><!--truncate--><p>以上步骤，就步骤3在windows下会麻烦一些，详细教程里面就有介绍在windows如何开启Telnet ，还有用hfs起一个ftp服务让路由器下载breed文件，而在*nix环境就方便多了，在树莓派下也能操作。</p><p>举个栗子：</p><p>电脑端是Linux环境下，在路由器升级到调试版固件后，通过<a href="https://linux.cn/article-9369-1.html">Telnet</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">telnet 192.168.0.1</span><br></pre></td></tr></table></figure><p>然后用python起一个简单的http服务，方便传输，cd到breed文件所在目录：</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">python -m SimpleHTTPServer 8080</span><br></pre></td></tr></table></figure><p>如果是python3，则为:</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">python3 -m http.server 8080</span><br></pre></td></tr></table></figure><p>然后就可以在路由器端，通过wget拿到电脑端的breed文件：</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 http://电脑的内网ip:8080/breed文件名</span><br></pre></td></tr></table></figure><p>通过以上骚操作，解决了breed文件传输问题。</p><p>刷完breed，就是随意装第三方固件。例如潘多拉、老毛子、OpenWrt等等，其实都是基于OpenWrt改的。</p><p>在这推荐hiboy固件，内置各种不可描述的插件，开箱即用，超赞。</p><h4 id="缘落"><a href="#缘落" class="headerlink" title="缘落"></a>缘落</h4><p>其他路由器刷机其实跟上述过程差不多，步骤大同小异。在路由器装完ss之后，以为是能够从此起飞了，遨游全世界。但事实上很不稳定，容易断，就又回到电脑端开启ss的时代。- -！</p><p>第三方固件可不止ss一个插件，还有其他很多实用的小插件，例如做内网穿透的frp、ngrok，还有离线下载的插件，可做文件服务器的插件，巴拉巴拉。。</p><p>上帝为你关上一扇门的同时，还为你开了一扇窗。</p><h5 id="原创文章，转载请注明出处-！感谢支持"><a href="#原创文章，转载请注明出处-！感谢支持" class="headerlink" title="原创文章，转载请注明出处~！感谢支持"></a>原创文章，转载请注明出处~！感谢支持</h5>]]>
    </content>
    <id>https://blog.gk99.site/2019/07/11/2019-07-11-how-to-root-360-p2/</id>
    <link href="https://blog.gk99.site/2019/07/11/2019-07-11-how-to-root-360-p2/"/>
    <published>2019-07-11T02:00:28.000Z</published>
    <summary>
      <![CDATA[<h4 id="缘起"><a href="#缘起" class="headerlink" title="缘起"></a>缘起</h4><p><img src="/assets/2019-07-11-how-to-root-360-p2/misha-feshchak-syCXK9W]]>
    </summary>
    <title>360路由器P2刷固件</title>
    <updated>2026-07-17T05:55:55.739Z</updated>
  </entry>
  <entry>
    <author>
      <name>Willy Geek</name>
    </author>
    <category term="博客搭建" scheme="https://blog.gk99.site/tags/%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA/"/>
    <content>
      <![CDATA[<p><img src="/assets/2019-07-10-how-to-build-the-blog/glenn-carstens-peters-npxXWgQ33ZQ-unsplash.jpg" alt="glenn-carstens-peters-npxXWgQ33ZQ-unsplash"></p><h4 id="为什么自己搭建博客，不是有现成的博客平台吗"><a href="#为什么自己搭建博客，不是有现成的博客平台吗" class="headerlink" title="为什么自己搭建博客，不是有现成的博客平台吗?"></a>为什么自己搭建博客，不是有现成的博客平台吗?</h4><p>市面上，确实有很多现成的、优秀的博客平台，例如思否、掘金、简书、CSDN等，各个平台的定位、以及内容都各不相同，还有非常棒的内容推荐，会给你推荐相近的内容，还有粉丝互粉等各种功能，内容 + 社交 成了这些平台的基本要素。</p><p>而本人关注的是内容，同时又是自（fei）闭(zhai)症患者，只想安静地为大家分享好玩有趣的东西，仅此而已。当然了，评论系统是开放的，需要翻墙登陆Facebook方可评论。</p><h4 id="用什么搭的博客？"><a href="#用什么搭的博客？" class="headerlink" title="用什么搭的博客？"></a>用什么搭的博客？</h4><!--truncate--><p>目前选定的是Facebook开源的<a href="https://github.com/facebook/docusaurus">docusaurus</a>, 其他可以快速搭建博客的平台还有Hexo、WordPress等。WordPress比较齐全，想要什么功能通过插件就能导进来，定制化比较高，多了解WordPress再学点html知识，就能开个淘宝店来接活了。Hexo比较轻量级，比较纯粹一些，比较拿来做博客，可使用网友做好的主题，套上去就能出一套很好看的博客系统了。</p><p>而docusaurus，其实是专门拿来做文档说明的，顺便送套博客系统。被docusaurus倾心的一个点是，字体渲染好看，代码高亮好看，专注于文档。</p><p>嗯。颜值就是正义。</p><h4 id="怎么部署到服务器的？"><a href="#怎么部署到服务器的？" class="headerlink" title="怎么部署到服务器的？"></a>怎么部署到服务器的？</h4><p>docusaurus通过一条命令，就能生成静态html</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">npm run build</span><br></pre></td></tr></table></figure><p>然后通过rsync命令同步更新到服务器，选择rsync的原因是增量同步，意思就是说，每次新增或改变的内容，只会上传改变的内容到服务器上，得劲！</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">rsync build/* -r 用户名@服务器ip:/var/www</span><br></pre></td></tr></table></figure><p>再用Git把项目管理起来，每次都能记录下修改内容，同时推送到Git服务器上（目前用的github私有仓库）。</p><p>感谢观看。下一篇博客见~</p>]]>
    </content>
    <id>https://blog.gk99.site/2019/07/10/2019-07-10-how-to-build-the-blog/</id>
    <link href="https://blog.gk99.site/2019/07/10/2019-07-10-how-to-build-the-blog/"/>
    <published>2019-07-10T02:00:28.000Z</published>
    <summary>
      <![CDATA[<p><img src="/assets/2019-07-10-how-to-build-the-blog/glenn-carstens-peters-npxXWgQ33ZQ-unsplash.jpg" alt="glenn-carstens-peters-npxXWgQ33ZQ]]>
    </summary>
    <title>怎么搭的博客</title>
    <updated>2026-07-17T05:55:55.739Z</updated>
  </entry>
  <entry>
    <author>
      <name>Willy Geek</name>
    </author>
    <category term="Hack" scheme="https://blog.gk99.site/tags/Hack/"/>
    <content>
      <![CDATA[<p><img src="/assets/2019-06-03-the-first-time-touch-hacker/jefferson-santos-9SoCnyQmkzI-unsplash.jpg" alt="jefferson-santos-9SoCnyQmkzI-unsplash"></p><p>那年冬天，跟老板还有另一位同事，一起在公司楼下喝了散伙酒。风萧萧兮易水寒，酒过几巡便都散了，没有太多的遗憾，也没有太多的唏嘘，挥手告别，各自安好。  </p><p>次日，就帮着技术总监收拾小小的办公室，眼看着办公桌一张张搬离，心里才开始有点不舍。看到他眼里似乎挂着朦胧的雾水般，但又不好去打扰，想必内心已是洪水汹涌，生怕他人来触动。  </p><!--truncate--><p>告别了总监，告别了这座城市，归家。  </p><p>在那之前的一个月，已经预感到了这一刻，所以提前做好了准备，打算实现年轻时的一个梦—成为一名黑客。在这之前也做了些疯狂的梦，可都没实现，例如徒步背包到拉萨，但半路遇险，终没完成。  </p><p>拉萨梦在心中也是酝酿许久，而踏出第一步也只是冲动的一个决定，感谢那时候的不顾一切，感谢年轻时候的那份自由，年级越大就越不敢奋不顾身去做某事了。  </p><p>也感谢这件事，让我多了份追梦的勇气。年轻时候，就幻想着像电影里面的黑客一样，轻松攻破层层障碍，绕过所有的机关，最终找到潘多拉盒子的钥匙。随着岁月的推移，这份幻想也就深深地刻在脑海里。  </p><p>是时候把这份幻想，变成现实了。于是在那个有些风雨的冬季，抱着破解的一些教程书，啃着难懂的知识点，虽然很硬，但都咬下来了。  </p><p>离职后，没急着找下一份工作，继续坚持着学习关于破解的知识。回到家后，是要躲避邻居的眼神的，因为那些眼神似乎带着轻蔑，高材生，还待业在家，渍渍渍。没办法，在这种环境下，人都会变得敏感起来。  </p><p>没办法，只好把自己藏起来。就像电影的黑客总会把自己隐藏在无人问之的角落里，完成一场漂亮的攻击后，总能把身后的线索抛掉，颇有大隐隐于市的王者风范。 </p><p>突然的一个午后，收到技术群发出的一个任务，急需某App抢红包的插件。刚好那几天在研究那个App，于是爽快地接下了这单子。第一次接单，不太会开价，就让对方开了。当你在乎的东西为你带来的更多是成就感或是荣誉感时，金钱就显得没那么重要了。  </p><p>折腾了好几天，日日夜夜地寻找线索，但以失败告终。能够找到的漏洞，似乎人家都提前把他给封死了。人家可不会那么容易给你入侵的机会，但这更提起了破解的兴趣。就像打游戏，遇到的妖魔鬼怪越强大，就越能激发战斗的士气。  </p><p>不过很遗憾，经过了几天的研究破解，但最终没把漏洞打下来，单子的Deadline已到，老板过来说，算了，我找下家好了。想要挽回点什么，但又无能为力，真的有点难受。  </p><p>时间快到了除夕，一个人待在自己设计的“工作室”里，其实就一台电脑，一个屏幕，一个手机，一套键鼠罢了。失落感渐渐弥漫在空气中，多想手机里的一个消息，或是屏幕里的一个弹幕能够打破这僵局，但都没有。  </p><p>很长一段时间里，都充斥着这种氛围。想想别的黑客，如George Hotz，人家是怎样熬过这些孤独寂寞，最终把一个一个漏洞找到的。崇拜者们可能看到他的伟大牛逼的成果，但可能不知这寻找过程中伴随而来的孤独寂寞感，但George Hotz可能并不觉得，因为那是他喜爱的，热爱的东西，并愿意打破规则的人，社会新潮需要这样的人出现。  </p><p>而此时想想自己，就因为这一点点的挫折，振奋不前，真不配做一个合格的hacker，真想给自己一巴掌。这还是自己所热爱的东西吗，不禁自问  </p><p>重拾心情，拾起勇气，改变思路，重新踏上寻找漏洞之路。寻找漏洞很多时候，是需要靠运气的。条条大路通罗马，但谁能找到最近的一条路，有时就是要靠点运气，但运气不是所有的人都有，运气是属于足够努力的那些人。运气不够，肯定是自己还不够努力  </p><p>嗯，就是这样。改变思路，不断尝试，不断试错。终于在除夕夜的晚上，插件完成了它的第一笔自动抢红包，像是亲手养大的儿子，终于完成他的学业一般，油然而生的自豪感  </p><p>烟花随着春晚节目的伴奏声中，徐徐绽放，似乎为这一刻庆祝着  </p><p>浮华褪尽，人比烟花寂寞  </p><p>在理性的世界，加入感性的文字，增添了不少美感  </p><p>感谢看官的耐心阅读</p><h5 id="原创文章，转载请注明出处-！感谢支持"><a href="#原创文章，转载请注明出处-！感谢支持" class="headerlink" title="原创文章，转载请注明出处~！感谢支持"></a>原创文章，转载请注明出处~！感谢支持</h5><p>​</p>]]>
    </content>
    <id>https://blog.gk99.site/2019/06/03/2019-06-03-the-first-time-touch-hacker/</id>
    <link href="https://blog.gk99.site/2019/06/03/2019-06-03-the-first-time-touch-hacker/"/>
    <published>2019-06-03T15:03:01.000Z</published>
    <summary>
      <![CDATA[<p><img src="/assets/2019-06-03-the-first-time-touch-hacker/jefferson-santos-9SoCnyQmkzI-unsplash.jpg" alt="jefferson-santos-9SoCnyQmkzI-uns]]>
    </summary>
    <title>离黑客最近的一次</title>
    <updated>2026-07-17T05:55:55.741Z</updated>
  </entry>
  <entry>
    <author>
      <name>Willy Geek</name>
    </author>
    <category term="Vim" scheme="https://blog.gk99.site/tags/Vim/"/>
    <content>
      <![CDATA[<p>突然想捡起Vim，为了能在服务器进行一些简单配置，于是在谷歌上搜索<code>how to learn vim</code>时，找到一个不错的教程：<a href="https://medium.com/actualize-network/how-to-learn-vim-a-four-week-plan-cd8b376a9b85">How To Learn Vim: A Four Week Plan</a>（Medium的文章，国内访问可能要翻墙）。  </p><p>如果看不到上面链接也没关系，在Mac&#x2F;Linux下的终端，输入  </p><pre><code>vimtutor</code></pre><!--truncate--><p>进入Vim交互式教程即可。</p><p>个人觉得能达到第一个水平就够了，也就是能在服务器进行一些简单配置，所以完成First week的任务便可(即上面的交互式教程，每天练一次，直到能够5分钟内完成)。如果要把Vim配置成IDE的话，就要折腾一番咯，但就目前来说没必要，毕竟VS Code配合一些插件用起来挺香，唯一缺点就是需要脱离键盘去操作鼠标，优点就是不用折腾vimrc。  </p><p>大道至简。  </p><p>下一篇博客见~！</p><h5 id="原创文章，转载请注明出处-！感谢支持"><a href="#原创文章，转载请注明出处-！感谢支持" class="headerlink" title="原创文章，转载请注明出处~！感谢支持"></a>原创文章，转载请注明出处~！感谢支持</h5>]]>
    </content>
    <id>https://blog.gk99.site/2019/06/02/2019-06-02-review-Vim/</id>
    <link href="https://blog.gk99.site/2019/06/02/2019-06-02-review-Vim/"/>
    <published>2019-06-02T15:03:01.000Z</published>
    <summary>
      <![CDATA[<p>突然想捡起Vim，为了能在服务器进行一些简单配置，于是在谷歌上搜索<code>how to learn vim</code>时，找到一个不错的教程：<a href="https://medium.com/actualize-network/how-to-learn-vim-]]>
    </summary>
    <title>重新复习Vim</title>
    <updated>2026-07-17T05:55:55.740Z</updated>
  </entry>
  <entry>
    <author>
      <name>Willy Geek</name>
    </author>
    <content>
      <![CDATA[<p><img src="/assets/2019-06-01-program-language-suggest/alex-chumak-zGuBURGGmdY-unsplash.jpg" alt="alex-chumak-zGuBURGGmdY-unsplash"></p><h3 id="TypeScript入门教程"><a href="#TypeScript入门教程" class="headerlink" title="TypeScript入门教程"></a><a href="https://ts.xcatliu.com/">TypeScript入门教程</a></h3><p>官方的TS教程很全面，但不适合新手上路。所以推荐这个，简单易懂，前提最好有ES6基础。熟练ES6的话，一个小时内应该就可以看完。阮一峰老师的<a href="https://es6.ruanyifeng.com/">ECMAScript 6 入门</a></p><h3 id="Python-100天从新手到大师"><a href="#Python-100天从新手到大师" class="headerlink" title="Python - 100天从新手到大师"></a><a href="https://github.com/jackfrued/Python-100-Days">Python - 100天从新手到大师</a></h3><p>从知乎推荐找到的一个Python教程，从Python基础、进阶、web开发、爬虫，再到数据处理和机器学习，最后补充了团队项目开发经验，很全，很细心，讲解+练习为主，由浅入深，看了前十天，精简细致，主要还是以上手练习为主。</p><!--truncate--><h3 id="Swift4-2-and-iOS11"><a href="#Swift4-2-and-iOS11" class="headerlink" title="Swift4.2 and iOS11"></a><a href="https://youtu.be/71pyOB4TPRE">Swift4.2 and iOS11</a></h3><p>来自斯坦福大学的Swift教程，从浅入深，从可视化界面编辑，到Swift进阶使用，带学生走进Swift的世界。需要有一定的英语听力能力。</p><h3 id="Go"><a href="#Go" class="headerlink" title="Go"></a><a href="https://golangbot.com/learn-golang-series/">Go</a></h3><p>Go作为新起之秀，其快速编译，语法简洁等特性深深吸引了我，于是找到了Github上深受好评的<a href="https://github.com/Alikhll/golang-developer-roadmap">roadmap</a>，roadmap提到的入门课程便是这篇教程。</p><h2 id="FAQ-自问自答环节"><a href="#FAQ-自问自答环节" class="headerlink" title="FAQ 自问自答环节"></a>FAQ 自问自答环节</h2><p>为什么要学这么多语言? </p><p><code>因为2019的目标是想成为全栈开发</code>  </p><p>为什么选这些语言？  </p><p><code>每种语言都只是工具，为了完成特定任务选用特定的工具，没有孰好孰坏之说，个人更偏向简洁的语言，以及语言带来的愉悦感。从简洁性来说，上面的语言都是各平台的佼佼者。从愉悦感来说，TS给JS弥补了非常多的不足，带来了静态检查，方便的代码提示等，Swift则带来了更安全的开发体验。至于GO还有Python, 深入体验一段时间过后，再回来补充</code><br>正值六一儿童节，希望自己以及屏幕前的看官们能够永葆童心，保持对未知世界永不停歇的渴求，下一篇博客见</p><h5 id="原创文章，转载请注明出处-！感谢支持"><a href="#原创文章，转载请注明出处-！感谢支持" class="headerlink" title="原创文章，转载请注明出处~！感谢支持"></a>原创文章，转载请注明出处~！感谢支持</h5>]]>
    </content>
    <id>https://blog.gk99.site/2019/06/01/2019-06-01-program-language-suggest/</id>
    <link href="https://blog.gk99.site/2019/06/01/2019-06-01-program-language-suggest/"/>
    <published>2019-06-01T06:16:32.000Z</published>
    <summary>
      <![CDATA[<p><img src="/assets/2019-06-01-program-language-suggest/alex-chumak-zGuBURGGmdY-unsplash.jpg" alt="alex-chumak-zGuBURGGmdY-unsplash"></p>
<]]>
    </summary>
    <title>编程语言的推荐</title>
    <updated>2026-07-17T05:55:55.741Z</updated>
  </entry>
</feed>
