<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>浣熊窝</title>
  
  <subtitle>Hello, world!</subtitle>
  <link href="https://blog.wanghaoyu.cn/atom.xml" rel="self"/>
  
  <link href="https://blog.wanghaoyu.cn/"/>
  <updated>2024-02-27T06:16:02.140Z</updated>
  <id>https://blog.wanghaoyu.cn/</id>
  
  <author>
    <name>Roccoon</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>通过阿里云盘备份群晖NAS</title>
    <link href="https://blog.wanghaoyu.cn/2021/12/12/nas-backup-to-aliyun-drive/"/>
    <id>https://blog.wanghaoyu.cn/2021/12/12/nas-backup-to-aliyun-drive/</id>
    <published>2021-12-12T12:43:13.000Z</published>
    <updated>2024-02-27T06:16:02.140Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>最近在在研究如何对NAS进行备份，在网上找了很久，基本上是通过Hyper Backup备份到移动硬盘备份或者另一台群晖NAS。但这并不能满足我的需求：</p><ul><li>备份到移动硬盘：移动硬盘的容量一般在1T~2T，容量基本满足需求，但是无法移动存储，且存储介质不能保证高可靠，一旦移动硬盘故障，就会导致数据丢失。</li><li>备份到另一台NAS：备份数据高可靠，但是费钱，而且异地备份需要有第二个家，就更缺钱了。</li></ul><p>因此调研了一下国内云厂商的对象存储（Object Storage Service, OSS)，能够提供高可靠存储且价格勉强能够接受。一般情况下，100元/年可以购买到约100G/年的存储空间，下图是京东云的OSS价格</p><p><img src="/.cn//01.png" alt="京东云OSS价格"></p><p>综上所属，我最终选择了京东云的OSS，并购买了150GB容量+50GB的下行流量。</p><p>就在我购买后的一个月，我发现了更廉价的异地备份方案，就是通过阿里云盘进行备份数据。</p><p><a href="https://pages.aliyundrive.com/mobile-page/web/beinvited.html?code=c75afe3">阿里云盘邀请链接</a></p><a id="more"></a><h2 id="阿里云盘介绍"><a href="#阿里云盘介绍" class="headerlink" title="阿里云盘介绍"></a>阿里云盘介绍</h2><p>阿里云盘是阿里云推出的一款不限速的云盘，用户默认空间是500GB，足够大多数用户使用。</p><p>阿里云盘支持分享，不过分享之前需要进行实名认证，还是比较讨厌的。如果要分享一些不太合适的文件/视频，建议不要使用阿里云盘分享。</p><p>阿里云盘还支持做任务增加容量，不过增加的容量一般是有有效期的（一般是1年），目前我的云盘容量大约5TB，足够用很长时间了。</p><p>现在通过邀请注册阿里云盘，还可以额外获得500GB容量（有效期1年）。</p><p><a href="https://pages.aliyundrive.com/mobile-page/web/beinvited.html?code=c75afe3">阿里云盘邀请链接</a></p><h2 id="基于阿里云盘搭建WebDAV"><a href="#基于阿里云盘搭建WebDAV" class="headerlink" title="基于阿里云盘搭建WebDAV"></a>基于阿里云盘搭建WebDAV</h2><p>目前有两个开源项目可以把阿里云盘变成WebDAV，通过WebDAV接口就可以让群晖NAS进行备份，黑白群晖均支持。</p><p>仓库地址：</p><ul><li><a href="https://github.com/zxbu/webdav-aliyundriver">zxbu/webdav-aliyundriver</a></li><li><a href="https://github.com/messense/aliyundrive-webdav">messense/aliyundrive-webdav</a></li></ul><p>从我的使用体验来看，<a href="https://github.com/zxbu/webdav-aliyundriver">zxbu/webdav-aliyundriver</a>比较稳定，但是比较吃资源，容器正常状况下大约占用300MB+内存。<a href="https://github.com/messense/aliyundrive-webdav">messense/aliyundrive-webdav</a>比较轻量，运行时占用约50MB。</p><p>两者的配置方法是类似的，都是需要通过Docker，如果没有Docker的用户需要到<code>套件中心</code>中进行安装。若NAS本身不支持Docker，可以将容器部署在局域网的其他设备中国，如PC或者路由器等。</p><blockquote><p>PS: 以下操作全部是基于<a href="https://github.com/zxbu/webdav-aliyundriver">zxbu/webdav-aliyundriver</a>，<a href="https://github.com/messense/aliyundrive-webdav">messense/aliyundrive-webdav</a>的操作也是相似的。</p></blockquote><ol><li><p>下载Docker镜像</p><p> 打开Docker套件，选择<code>注册表</code>，在右上角的搜索框中输入<code>webdav-aliyundriver</code>或者<code>aliyundrive-webdav</code>，在搜索结果中选择对应的镜像进行下载。<br> <img src="/.cn//02.png" alt="下载镜像"></p></li><li><p>启动镜像</p><p> 打开Docker套件，选择<code>映像</code>，并选择对应的镜像，点击上方启动按钮。<br> <img src="/.cn//03.png" alt="启动镜像"></p></li><li><p>配置镜像</p><ol><li><p>根据自己的需要设置容器的名称，并且点击下放的高级设置<br> <img src="/.cn//04.png" alt="配置名称"></p></li><li><p>在弹出高级设置界面中，勾选<code>启用自动重新启动</code>。<br> <img src="/.cn//05.png" alt="配置名称"></p></li><li><p>选择<code>存储空间</code>标签，点击<code>添加文件夹</code>，在弹出的界面中可以选择/创建一个空文件夹作为容器的配置文件夹。并将文件夹挂载到<code>/etc/aliyun-driver/</code>，<strong>一定不要勾选只读</strong>。<br> <img src="/.cn//06.png" alt="存储空间"></p></li><li><p>选择<code>端口设置</code>标签，<code>本地端口</code>是你后续要访问webdav的端口，可以自己随意设置，<code>容器端口</code>必须是<code>8080</code>，协议是<code>TCP</code>。<br><img src="/.cn//07.png" alt="端口设置"></p></li><li><p>选择<code>环境</code>标签，添加以下四个环境变量：</p><ul><li><p>变量名：<code>ALIYUNDRIVE_AUTH_USER_NAME</code>，WebDAV访问时需要填写的用户名</p></li><li><p>变量名：<code>ALIYUNDRIVE_AUTH_PASSWORD</code>，WebDAV访问时需要填写的密码</p></li><li><p>变量名：<code>ALIYUNDRIVE_REFRESH_TOKEN</code>，登陆阿里云盘的token，获取方法可以参考：<a href="https://github.com/zxbu/webdav-aliyundriver#%E6%B5%8F%E8%A7%88%E5%99%A8%E8%8E%B7%E5%8F%96refreshtoken%E6%96%B9%E5%BC%8F">通过浏览器获取Refresh Token</a></p></li><li><p>变量名：<code>TZ</code>，值：<code>Asia/Shanghai</code>，设置时区为北京时间</p><p><img src="/.cn//08.png" alt="环境设置"></p></li></ul></li><li><p>点击<code>应用</code>完成容器配置</p></li></ol></li><li><p>启动容器</p><p> 点击<code>下一步</code>，再点击<code>完成</code>即可启动容器。<br> <img src="/.cn//09.png" alt="启动容器"></p></li></ol><h2 id="设置Hyper-Backup"><a href="#设置Hyper-Backup" class="headerlink" title="设置Hyper Backup"></a>设置Hyper Backup</h2><blockquote><p>如果没有Hyper Backup的用户需要到<code>套件中心</code>中进行安装。</p></blockquote><ol><li><p>创建备份任务</p><ol><li><p>打开Hyper Backup套件，点击左上方加号，选择<code>数据备份任务</code><br> <img src="/.cn//10.png" alt="创建备份任务"></p></li><li><p>备份目标选择<code>文件服务器</code>——<code>WebDAV</code>，并点击下一步<br> <img src="/.cn//11.png" alt="选择WebDAV"></p></li><li><p>配置WebDAV文件服务器信息</p><ul><li><p>服务器地址：如果在NAS的Docker中部署的WebDAV，就填写<code>http://127.0.0.1:8080</code>，端口为之前启动Docker时对外暴露的端口。若在外部Docker部署的WebDAV，则需要填写对应服务器的IP地址和端口。</p></li><li><p>用户账号：启动容器时配置<code>ALIYUNDRIVE_AUTH_USER_NAME</code>环境变量对应的值</p></li><li><p>密码：启动容器时配置<code>ALIYUNDRIVE_AUTH_PASSWORD</code>环境变量对应的值</p></li><li><p>文件夹：选择一个阿里云盘中的文件夹作为备份地址</p></li><li><p>目录：输入NAS备份目录名称</p><p><img src="/.cn//12.png" alt="配置备份目标"></p></li></ul></li><li><p>选择需要备份的共享文件夹，可以按子目录选择/排除，也可以配置文件过滤器进行自定义过滤。<br> <img src="/.cn//13.png" alt="选择需要备份的共享文件夹"></p></li><li><p>选择需要备份的应用套件，注意，有些套件会要求必须备份某些共享文件夹。<br> <img src="/.cn//14.png" alt="选择需要备份的套件"></p></li><li><p>设置备份任务的执行周期，<strong>强烈建议开启客户端加密</strong>，因为不确定阿里云盘是否会扫描你的备份数据，加密后的密码一定要记牢，一旦丢失就无法找回。<br> <img src="/.cn//15.png" alt="设置备份任务的执行周期"></p></li><li><p>设置备份版本替换规则，建议使用Smart Recycle，会自动根据时间保留各个版本，时间距离越近，保留的版本越多，时间距离越远，保留的版本越少。</p><p> <img src="/.cn//16.png" alt="设置备份任务的执行周期"></p></li></ol></li><li><p>开始备份</p><p> 点击立即备份，就开始执行备份任务了，可以在阿里云盘中看到对应备份文件<br> <img src="/.cn//17.png" alt="备份中"></p></li></ol><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>阿里云盘的容量速度还是非常不错的，完美的平衡了容量和价格，并且是高可靠异地存储，哪怕NAS机械故障后，也能找回数据。</p><p>数据无价，大家保存好自己的数据，一旦丢失，就再也找不回来了。</p>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;最近在在研究如何对NAS进行备份，在网上找了很久，基本上是通过Hyper Backup备份到移动硬盘备份或者另一台群晖NAS。但这并不能满足我的需求：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;备份到移动硬盘：移动硬盘的容量一般在1T~2T，容量基本满足需求，但是无法移动存储，且存储介质不能保证高可靠，一旦移动硬盘故障，就会导致数据丢失。&lt;/li&gt;
&lt;li&gt;备份到另一台NAS：备份数据高可靠，但是费钱，而且异地备份需要有第二个家，就更缺钱了。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此调研了一下国内云厂商的对象存储（Object Storage Service, OSS)，能够提供高可靠存储且价格勉强能够接受。一般情况下，100元/年可以购买到约100G/年的存储空间，下图是京东云的OSS价格&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/.cn//01.png&quot; alt=&quot;京东云OSS价格&quot;&gt;&lt;/p&gt;
&lt;p&gt;综上所属，我最终选择了京东云的OSS，并购买了150GB容量+50GB的下行流量。&lt;/p&gt;
&lt;p&gt;就在我购买后的一个月，我发现了更廉价的异地备份方案，就是通过阿里云盘进行备份数据。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://pages.aliyundrive.com/mobile-page/web/beinvited.html?code=c75afe3&quot;&gt;阿里云盘邀请链接&lt;/a&gt;&lt;/p&gt;</summary>
    
    
    
    
    <category term="阿里云盘" scheme="https://blog.wanghaoyu.cn/tags/%E9%98%BF%E9%87%8C%E4%BA%91%E7%9B%98/"/>
    
    <category term="群晖" scheme="https://blog.wanghaoyu.cn/tags/%E7%BE%A4%E6%99%96/"/>
    
    <category term="Synology" scheme="https://blog.wanghaoyu.cn/tags/Synology/"/>
    
  </entry>
  
  <entry>
    <title>MPQUIC实验环境</title>
    <link href="https://blog.wanghaoyu.cn/2020/05/19/mpquic-vm/"/>
    <id>https://blog.wanghaoyu.cn/2020/05/19/mpquic-vm/</id>
    <published>2020-05-19T15:06:26.000Z</published>
    <updated>2024-02-27T06:16:02.140Z</updated>
    
    <content type="html"><![CDATA[<h2 id="准备"><a href="#准备" class="headerlink" title="准备"></a>准备</h2><ul><li>已经安装<strong>KVM</strong>或者<strong>Virtual Box</strong>的Ubuntu 16.04 64bit。</li><li>下载MPQUIC镜像，<a href="https://multipath-quic.org/kvm-mpquic.vmdk.zip">下载地址</a>，下载后解压。</li></ul><h2 id="配置环境"><a href="#配置环境" class="headerlink" title="配置环境"></a>配置环境</h2><h3 id="创建虚拟机"><a href="#创建虚拟机" class="headerlink" title="创建虚拟机"></a>创建虚拟机</h3><ol><li>在KVM或者Virtual Box中创建一个虚拟机，内存推荐2G以上。</li><li>虚拟机硬盘为下载的MPQUIC镜像。</li><li>设置虚拟机的网卡为<strong>e1000</strong>，其他的网卡可能不支持。</li><li>启动虚拟机，用户名和密码均为<code>mininet</code>。</li><li>测试虚拟机是否可以上网。</li></ol><h3 id="部署测试环境"><a href="#部署测试环境" class="headerlink" title="部署测试环境"></a>部署测试环境</h3><ol><li><p>挂载内存盘，<strong>虚拟机每次重启都要进行该步骤</strong>。</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></pre></td><td class="code"><pre><span class="line">bash ~/mount_tmpfs.sh</span><br><span class="line"><span class="comment"># 或者</span></span><br><span class="line">sudo mkdir -p /mnt/tmpfs</span><br><span class="line">sudo mount -t tmpfs -o size=256M tmpfs /mnt/tmpfs</span><br></pre></td></tr></table></figure></li><li><p>创建ssh密钥</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></pre></td><td class="code"><pre><span class="line">ssh-keygen <span class="comment"># 一直回车即可，之前创建过的可以忽略</span></span><br><span class="line"><span class="comment"># 设置ssh免密登录</span></span><br><span class="line">ssh-copy-id mininet@localhost</span><br></pre></td></tr></table></figure></li><li><p>安装依赖</p> <figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install python-dev python-pip python-numpy</span><br></pre></td></tr></table></figure></li><li><p>下载测试脚本</p> <figure class="highlight sh"><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="built_in">cd</span> ~/git</span><br><span class="line">git <span class="built_in">clone</span> https://github.com/qdeconinck/minitopo-experiences.git</span><br></pre></td></tr></table></figure></li></ol><h3 id="运行测试脚本"><a href="#运行测试脚本" class="headerlink" title="运行测试脚本"></a>运行测试脚本</h3><ol><li><p>修改脚本参数，将待运行的脚本中的<code>REMOTE_SERVER_RUNNER_PORT</code>的值修改为<code>[&quot;22&quot;]</code>。</p> <figure class="highlight sh"><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="comment"># 以quic_mptcp_https_tests_expdes_wsp_highbdp_loss_quic.py脚本为例</span></span><br><span class="line">vim ~/git/minitopo-experiences/experiences/quic_mptcp_https_tests_expdes_wsp_highbdp_loss_quic.py</span><br></pre></td></tr></table></figure></li><li><p>运行脚本。脚本运行很慢，且无法用<code>Ctrl-C</code>终止，建议使用<code>tmux</code>或者<code>screen</code>等软件运行。</p> <figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python ~/git/minitopo-experiences/experiences/quic_mptcp_https_tests_expdes_wsp_highbdp_loss_quic.py</span><br></pre></td></tr></table></figure></li><li><p>查看运行结果。运行结果保存在<code>/mnt/tmpfs</code>中，测试结束后应及时复制出来，因为<strong>该目录为内存盘，重启后数据会丢失</strong>。</p></li></ol>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;准备&quot;&gt;&lt;a href=&quot;#准备&quot; class=&quot;headerlink&quot; title=&quot;准备&quot;&gt;&lt;/a&gt;准备&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;已经安装&lt;strong&gt;KVM&lt;/strong&gt;或者&lt;strong&gt;Virtual Box&lt;/strong&gt;的Ubuntu 16</summary>
      
    
    
    
    <category term="环境搭建" scheme="https://blog.wanghaoyu.cn/categories/%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/"/>
    
    
    <category term="MPQUIC" scheme="https://blog.wanghaoyu.cn/tags/MPQUIC/"/>
    
  </entry>
  
  <entry>
    <title>群晖VideoStation支持DTS、EAC3、TrueHD</title>
    <link href="https://blog.wanghaoyu.cn/2020/03/31/synology-videostation/"/>
    <id>https://blog.wanghaoyu.cn/2020/03/31/synology-videostation/</id>
    <published>2020-03-31T04:19:33.000Z</published>
    <updated>2024-02-27T06:16:02.150Z</updated>
    
    <content type="html"><![CDATA[<h2 id="更新日志"><a href="#更新日志" class="headerlink" title="更新日志"></a>更新日志</h2><table><thead><tr><th>时间</th><th>修改内容</th></tr></thead><tbody><tr><td>2020.07.08</td><td>更新ffmpeg-wrapper，修复无法显示ASS字幕的问题</td></tr><tr><td>2020.04.07</td><td>修改安装命令</td></tr><tr><td>2020.03.31</td><td>撰写文章</td></tr></tbody></table><h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>由于群晖没有获得DTS的版权，因此群晖自带的VideoStation从某个版本开始不支持DTS、EAC3、TrueHD的音频格式，严重影响使用体验。搜索该问题，得到的答案均为将VideoStation降级到2.3.4-1468，但是这个版本的VideoStation不支持HEVC和4K解码，比较鸡肋。本文将利用第三方的ffmpeg替换VideoStation中自带的ffmpeg，实现对全视频格式的支持。</p><h2 id="操作方法"><a href="#操作方法" class="headerlink" title="操作方法"></a>操作方法</h2><h3 id="安装ffmpeg"><a href="#安装ffmpeg" class="headerlink" title="安装ffmpeg"></a>安装ffmpeg</h3><ol><li><p>下载对应版本的ffmpeg安装包，其中DS918+对应的版本是<strong>apollolake</strong>，具体每个机型对应的版本可以在以下连接中找到：<a href="https://github.com/SynoCommunity/spksrc/wiki/Architecture-per-Synology-model">https://github.com/SynoCommunity/spksrc/wiki/Architecture-per-Synology-model</a></p><p>下载地址：<a href="https://github.com/th0ma7/synology/tree/master/packages">https://github.com/th0ma7/synology/tree/master/packages</a></p></li><li><p>登录DSM，打开<strong>套件中心</strong>，点击<strong>手动安装</strong>，选择刚刚下载的文件进行安装。</p></li><li><p>在DSM中打开<strong>控制面板</strong>，选择<strong>终端机和SNMP</strong>，勾选<strong>启动SSH功能</strong>，并调整对应的端口号。</p></li><li><p>用XSheel等软件以SSH的方式登录到NAS中，执行以下命令：</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></pre></td><td class="code"><pre><span class="line">sudo chmod +s /var/packages/ffmpeg/target/bin/ffmpeg</span><br><span class="line">sudo chmod +s /var/packages/ffmpeg/target/bin/ffprobe</span><br><span class="line">sudo chmod +s /var/packages/ffmpeg/target/bin/vainfo</span><br></pre></td></tr></table></figure></li></ol><h2 id="安装VideoStation"><a href="#安装VideoStation" class="headerlink" title="安装VideoStation"></a>安装VideoStation</h2><ol><li><p>登录DSM，打开<strong>套件中心</strong>，安装<strong>最新版的VideoStation</strong>，本文撰写时，版本为2.4.7-1603，经测试，以下版本适用于本方法，其他版本未测试：<code>2.4.6-1594</code>，<code>2.4.7-1603</code></p></li><li><p>用XSheel等软件以SSH的方式登录到NAS中，执行以下命令：</p></li></ol><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><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 备份</span></span><br><span class="line">sudo  sed -i<span class="string">&#x27;-BACKUP&#x27;</span> -e <span class="string">&#x27;s/eac3/ZAAP/&#x27;</span> -e <span class="string">&#x27;s/dts/ZAP/&#x27;</span> -e <span class="string">&#x27;s/truehd/ZAPZAP/&#x27;</span> /var/packages/VideoStation/target/lib/libsynovte.so</span><br><span class="line"><span class="comment"># 注意：ffmpeg的备份名称ffmpeg-BACKUP应与ffmpeg自定义脚本中$bin2保持一致</span></span><br><span class="line">sudo mv /var/packages/VideoStation/target/bin/ffmpeg /var/packages/VideoStation/target/bin/ffmpeg-BACKUP</span><br><span class="line">sudo mv /var/packages/VideoStation/target/bin/ffprobe /var/packages/VideoStation/target/bin/ffprobe-BACKUP</span><br><span class="line">sudo mv /var/packages/VideoStation/target/bin/vainfo /var/packages/VideoStation/target/bin/vainfo-BACKUP</span><br><span class="line"></span><br><span class="line"><span class="comment"># 更新/var/packages/VideoStation/target/ffmpeg为自定义脚本，调用第三方的ffmpeg</span></span><br><span class="line"><span class="comment"># 具体脚本信息见附录</span></span><br><span class="line">sudo su</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;H4sICJ6oBF8AA2ZmbXBlZy13cmFwcGVyAO1abXPbNhL+LP4KhKVf5Jimybn7ogwz8dlKqklcayzHnU7ioyASlFhTJEtQVFzH/70LAqT4KjltrzO9qWeSSMCziwfAg8Vine9eaDMv0GaYLiQpJqkp64YsSZYfzg/7j1KP2IsQycqhgxOCDl7u/aTuLdU9B+19P9i7HOxNDvpIRcojTWKCl57zxL7pMnr9GmnJMtJcdxmR+Ql4k564VyvCMS67BvRX5Ia+g9Q10o1T+EaJg2Sq/ffkSNG+aejix0T7WieLhMTLjEG8LPerJV8nNHFIHEs9RhnJR0dH6P3ow4fz70cfLhB8k6Xevef7SL0ZXl/CJOyF5zuR58jIeK05JNWCle+zwZIYRygbEU1G7xhaknBsL8zpKsBLglQs5ntAtZMj+hCEMOCDpWkH0G6vEqRCl3WAVBfpUynA9HcYGlMJqJmKIrG1X6bUVN5IfKqmrDy+GSBVf5KlfPKm/PbtpcImI4E0dFNLcaxF2L7Hc0LFWmkJjuckybTDWxjWqGFvPYeEkwQnXhg0LdT/nJ2//ziG5ZhT87AvSakdOsQ25fdXoMA1rChBnz4h5TukzhN0iu7uJCeUbEwJV40XSD3V60u9Hl14bgL/LsPUIybrhC/M7UvzUFY9OcMD8NUrsFissW0Tv2wnmhqWor1qTzOWZXMqeNeseXPV2C3bue12btVkiaOyUQGD9gLY8xz4ak6zI6XoGwUMxP4DwkWfAJ4KUiZiy4zuXqFkQWAhez2x+tPd2x3F4QwEmCI4IWGMVEp8YicW1w9FSkYGmhfh2iJBEnuEIiG3bAyLCzh0kUNcvPITMwij2AsSaw2nJSIxNfVBEN6TB1MHytmusiCxIBiCRMAOQsEXuLgglP8xbTHKH+VMfEoYdfz3WmrX41I86pf0VyiUUGxLXJ1OGBAe5XnIHF9fnQ8nEzS5Obu+QdfDW6TAFYMuJgoEMnSosEDYR+PRBWLhhsdVEYYcLzan8FfGX1Z4ozyVILZ6Nmy3jZMi9gPsSUtZrLGWJMFwX7DY+PMvSI3RwQm3OJgKXhfD29H5MLsiFN4li57Lq9vRkF8eYiGADIFjpAKBRzbvT2/uIE5mASsgaxaz2EedpvlHY/NRT2nRyj8+N6RRWj/vMBiPKHRz4sXIW9uDEOLXKob706KE3G/6U9puJ9q77AzhFwfYf/iVOAwCoR2c6Kf8ZwMVrrZDswDndU3Xa59VrdkoNUdeRAan8EFEx9PG1OrGpeZW44xhWgnbIpIWlwOE0hTjCFzcof39UpQVUSNDXGIPTtNpJeJSG/vEWouwDRqDW1hc6irh97q5NjVtflBpHSzMkyPWOt04WWxzAgYFHMj7JDs6fPAndPbDRaVp8bRhWNmOlF1MHJPNF7ht3MAYGwcDN4yXGGJNqhvHi/Uq8kPsHMNZojg22R++AUU83D7MM52xKLWJsHWHYs8zUFk15c6yTtLaLZw2bv3KENWrviLXos8Oo4eGalstcw4tfUa9L/eascQNlkKseiZC35u5GNsVEZangXOnDNTPe/OJtPcWB73e27YVuDajdv/lzob7ro3Ercus182ryzXr2lA8a9/MentBb1aVC+6UCu6QSb19M/EtGWTZcV3Iwi9rZnlFQhva65B+m0XamppSMl9C1mHxE9p5a1VQ7bPcislvuXZMhYnv0cRKHiKyi0wB3M6nC1aj1ITxp4ZPswvUSrxlJ6MKqJ3NNohg0g6prM02EmXM9hXpptCKaDCwHOIn+Dk8OHI3m1ZcC6carsKMQtadWMFqOSPxLm5l7HZ2W5A1fq1Inh0Fjge57Qr7FkvKSWwlMYasopNmp0FH5vNMeJ5l7YTz+JeGnmMFZA5ZX0qspDOlbQA7ouAOWB5EO2EZqxm8Fu6tIAwsePC4MTwtOnm1QNuZ7QYKbtuA4qH/xVquvnjB3PplRVaQe3u/Ng5sPmwruH3g7dCuGkO+EJUyQ/liaXakzcJE5dkoXOYlC56ZiJcdS09YllLLTPLBZitXsNZPjX/d59l6OVHgyEpikW/RbuO09KatZC7dbkuJSNsrOEvAPZaDy1R7/CzDK/ezPFAe4aH7dPxZXuDYWeOYsOMTZJUi6E3iFTl+LtjFQPNYk3kJs1JqIwGe+cRhb2kIK+QLsVn8g/c0q6S+3KNTScpXf1MOoqtZ7EXFDvSkvPj59u3lePiOv4tZkU/OO86vLobn4sEsHOVd47Prs8vbCXSJpqzsCwOKCiS0Sr3MW+VtjfZf76jIon1JIr5g727YL38G/O8l7/6l5EFc+Ui3OQsgwTN6dFh9PPYLVmclLP5mwnmJ42Io6uM/Xp+N5Y5V0quefxxzz5k33eRThsPMZt0yHljz5dHz5RHYZ64QnCgpr6ebygtRnclK76xUBBSLaru0xl6y+Z7Jeh6TCOBhkJKYwmlALrufnBfyrqG/fhW2w6zWFkYkgJiJYBcS8PQN9h+DmNjhPICY44AbXvd4rvEoSLHvOSirYrnhKnDQGhSNQA82oZQx8oJolezyKOJo+TcY/LcXwx/y32B0RgdhM7m5uPp4gxqymiawoll18N87SEzblDcejYdoclsTn94UH+8wmmc3U6WOWlQJ90W3KsvWRtnaENZGp7XR0LQYidVaKqHAyNufpfVeWei9bUrv1aXOjsk/cv+byP120iV3o0vu+nPkntI/IvdO60LuhazFSLnci2NgpN9y+f0j9/97uV9dj9515RVNWf112qEJbHnLaoCmEkIBfnhYhqiV5ev3keonSD/NXyd/4hIycp272PxdWtEl7f7fC78BTGYfI90hAAA=&quot;</span> | base64 -d | gunzip &gt; /var/packages/VideoStation/target/bin/ffmpeg</span><br><span class="line"><span class="built_in">exit</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 更新ffprobe vainfo</span></span><br><span class="line">sudo ln -s /var/packages/ffmpeg/target/bin/ffprobe /var/packages/VideoStation/target/bin/ffprobe</span><br><span class="line">sudo ln -s /var/packages/ffmpeg/target/bin/vainfo /var/packages/VideoStation/target/bin/vainfo</span><br><span class="line"></span><br><span class="line"><span class="comment"># 解除对EAC3 DTS TrueHD的屏蔽</span></span><br><span class="line">sudo sed -i<span class="string">&#x27;-BACKUP&#x27;</span> -e <span class="string">&#x27;s/eac3/ZAAP/&#x27;</span> -e <span class="string">&#x27;s/dts/ZAP/&#x27;</span> -e <span class="string">&#x27;s/truehd/ZAPZAP/&#x27;</span> /var/packages/VideoStation/target/lib/libsynovte.so</span><br><span class="line"></span><br><span class="line"><span class="comment"># 调整权限信息</span></span><br><span class="line">sudo chmod +x /var/packages/VideoStation/target/bin/ffmpeg</span><br><span class="line">sudo chmod +s /var/packages/VideoStation/target/bin/ffmpeg</span><br><span class="line">sudo chown root:VideoStation /var/packages/VideoStation/target/bin/ffmpeg</span><br></pre></td></tr></table></figure><a id="more"></a><h2 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h2><p>这里有两个视频文件，分别时EAC3和DTS格式的视频文件，可以在DSM的FileStation中右击播放，查看是否成功。<br>EAC3：<a href="https://gofile.me/51V3S/WbIDHIvUU">https://gofile.me/51V3S/WbIDHIvUU</a><br>DTS：<a href="https://gofile.me/51V3S/xp1jYf0W2">https://gofile.me/51V3S/xp1jYf0W2</a></p><h2 id="关于调试"><a href="#关于调试" class="headerlink" title="关于调试"></a>关于调试</h2><p>通过脚本可知，本程序会将ffmpeg的日志输出到<code>/tmp/ffmpeg.log</code>文件中，有需要的可以查看。</p><h2 id="附录1：ffmpeg-wrapper脚本内容"><a href="#附录1：ffmpeg-wrapper脚本内容" class="headerlink" title="附录1：ffmpeg-wrapper脚本内容"></a>附录1：ffmpeg-wrapper脚本内容</h2><p>由于未知原因，在将ASS等格式的高级字幕转换成SRT格式字幕时，ffmpeg包所提供的ffmpeg可执行文件提示权限不足，导致字幕转换失败。在新版本的ffmpeg-wrapper利用VideoStation自带的ffmpeg可执行文件对字幕进行处理，从而ASS等高级字幕可以播放。由于PGS等图像格式的字幕无法转成文本格式，因此VideoStation不支持PGS等图像格式字幕。</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><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><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line">rev=<span class="string">&quot;12&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">_log</span></span>()&#123;</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;<span class="subst">$(date &#x27;+%Y-%m-%d %H:%M:%S&#x27;)</span> - <span class="variable">$&#123;streamid&#125;</span> - <span class="variable">$1</span>&quot;</span> &gt;&gt; /tmp/ffmpeg.log</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">_log_para</span></span>()&#123;</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span> | fold -w 120 | sed <span class="string">&quot;s/^.*$/<span class="subst">$(date &#x27;+%Y-%m-%d %H:%M:%S&#x27;)</span> - <span class="variable">$&#123;streamid&#125;</span> -          = &amp;/&quot;</span> &gt;&gt; /tmp/ffmpeg.log</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">_term</span></span>()&#123;</span><br><span class="line">rm /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr</span><br><span class="line">_log <span class="string">&quot;*** KILLCHILD ***&quot;</span></span><br><span class="line"><span class="built_in">kill</span> -TERM <span class="string">&quot;<span class="variable">$childpid</span>&quot;</span> 2&gt;/dev/null</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">trap</span> _term SIGTERM</span><br><span class="line"></span><br><span class="line">arch=`uname -a | sed <span class="string">&#x27;s/.*synology_//&#x27;</span> | cut -d <span class="string">&#x27;_&#x27;</span> -f 1`</span><br><span class="line">nas=`uname -a | sed <span class="string">&#x27;s/.*synology_//&#x27;</span> | cut -d <span class="string">&#x27;_&#x27;</span> -f 2`</span><br><span class="line">pid=$$</span><br><span class="line">paramvs=<span class="variable">$@</span></span><br><span class="line">stream=<span class="string">&quot;<span class="variable">$&#123;@: -1&#125;</span>&quot;</span></span><br><span class="line">streamid=<span class="string">&quot;FFM<span class="variable">$pid</span>&quot;</span></span><br><span class="line">bin1=/var/packages/ffmpeg/target/bin/ffmpeg</span><br><span class="line">bin2=/var/packages/VideoStation/target/bin/ffmpeg-BACKUP</span><br><span class="line">args=()</span><br><span class="line"></span><br><span class="line">vcodec=<span class="string">&quot;KO&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> [[ <span class="variable">$#</span> -gt 0 ]]</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line"><span class="keyword">case</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span> <span class="keyword">in</span></span><br><span class="line">-i)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line">movie=<span class="string">&quot;<span class="variable">$1</span>&quot;</span></span><br><span class="line">args+=(<span class="string">&quot;-i&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">-hwaccel)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line">hwaccel=<span class="string">&quot;<span class="variable">$1</span>&quot;</span></span><br><span class="line">args+=(<span class="string">&quot;-hwaccel&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">-scodec)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line">scodec=<span class="string">&quot;<span class="variable">$1</span>&quot;</span></span><br><span class="line">args+=(<span class="string">&quot;-scodec&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">-f)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line">fcodec=<span class="string">&quot;<span class="variable">$1</span>&quot;</span></span><br><span class="line">args+=(<span class="string">&quot;-f&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">-map)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line">args+=(<span class="string">&quot;-map&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">idmap=`<span class="built_in">echo</span> <span class="variable">$1</span> | cut -d : -f 2`</span><br><span class="line"><span class="keyword">if</span> [ <span class="string">&quot;<span class="variable">$vcodec</span>&quot;</span> = <span class="string">&quot;KO&quot;</span> ]; <span class="keyword">then</span></span><br><span class="line">vcodec=`/var/packages/ffmpeg/target/bin/ffprobe -v error -select_streams <span class="variable">$idmap</span> -show_entries stream=codec_name -of default=noprint_wrappers=1:nokey=1 <span class="string">&quot;<span class="variable">$movie</span>&quot;</span> | head -n 1`</span><br><span class="line">vcodecprofile=`/var/packages/ffmpeg/target/bin/ffprobe -v error -select_streams <span class="variable">$idmap</span> -show_entries stream=profile -of default=noprint_wrappers=1:nokey=1 <span class="string">&quot;<span class="variable">$movie</span>&quot;</span> | head -n 1`</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">acodec=`/var/packages/ffmpeg/target/bin/ffprobe -v error -select_streams <span class="variable">$idmap</span> -show_entries stream=codec_name -of default=noprint_wrappers=1:nokey=1 <span class="string">&quot;<span class="variable">$movie</span>&quot;</span> | head -n 1`</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line">;;</span><br><span class="line">*)</span><br><span class="line">args+=(<span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">;;</span><br><span class="line"><span class="keyword">esac</span></span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"></span><br><span class="line">_log <span class="string">&quot;*** PROCESS START REV <span class="variable">$rev</span> DS<span class="variable">$nas</span> (<span class="variable">$arch</span>) PID <span class="variable">$pid</span> ***&quot;</span></span><br><span class="line"></span><br><span class="line">streamdir=`dirname <span class="string">&quot;<span class="variable">$stream</span>&quot;</span>`</span><br><span class="line">device=`cat <span class="variable">$&#123;streamdir&#125;</span>/video_metadata | jq -r <span class="string">&#x27;.device&#x27;</span>`</span><br><span class="line">_log <span class="string">&quot;DEVICE   = <span class="variable">$device</span>&quot;</span></span><br><span class="line">_log <span class="string">&quot;MOVIE    = <span class="variable">$movie</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">set</span> -- <span class="string">&quot;<span class="variable">$&#123;args[@]&#125;</span>&quot;</span></span><br><span class="line"></span><br><span class="line">argsnew=()</span><br><span class="line">args1sv=()</span><br><span class="line">args2sv=()</span><br><span class="line">args1vs=()</span><br><span class="line">args2vs=()</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> [[ <span class="variable">$#</span> -gt 0 ]]</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line"><span class="keyword">case</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span> <span class="keyword">in</span></span><br><span class="line">-ss)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line">argsnew+=(<span class="string">&quot;-ss&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args1sv+=(<span class="string">&quot;-ss&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args1sv+=(<span class="string">&quot;-noaccurate_seek&quot;</span>)</span><br><span class="line">args1vs+=(<span class="string">&quot;-ss&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args1vs+=(<span class="string">&quot;-noaccurate_seek&quot;</span>)</span><br><span class="line">args2sv+=(<span class="string">&quot;-analyzeduration&quot;</span> <span class="string">&quot;10000000&quot;</span>)</span><br><span class="line">args2vs+=(<span class="string">&quot;-analyzeduration&quot;</span> <span class="string">&quot;10000000&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">-i)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line">argsnew+=(<span class="string">&quot;-i&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args1sv+=(<span class="string">&quot;-i&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2sv+=(<span class="string">&quot;-i&quot;</span> <span class="string">&quot;pipe:0&quot;</span> <span class="string">&quot;-map&quot;</span> <span class="string">&quot;0&quot;</span>)</span><br><span class="line">args1vs+=(<span class="string">&quot;-i&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2vs+=(<span class="string">&quot;-i&quot;</span> <span class="string">&quot;pipe:0&quot;</span> <span class="string">&quot;-map&quot;</span> <span class="string">&quot;0&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">-vf)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line"><span class="keyword">if</span> [ <span class="string">&quot;<span class="variable">$hwaccel</span>&quot;</span> = <span class="string">&quot;vaapi&quot;</span> ] &amp;&amp; [ <span class="string">&quot;<span class="variable">$vcodecprofile</span>&quot;</span> = <span class="string">&quot;Main 10&quot;</span> ]; <span class="keyword">then</span></span><br><span class="line">scale_w=`<span class="built_in">echo</span> <span class="string">&quot;<span class="variable">$&#123;1&#125;</span>&quot;</span> | sed -e <span class="string">&#x27;s/.*=w=//g&#x27;</span> | sed -e <span class="string">&#x27;s/:h=.*//g&#x27;</span>`</span><br><span class="line">scale_h=`<span class="built_in">echo</span> <span class="string">&quot;<span class="variable">$&#123;1&#125;</span>&quot;</span> | sed -e <span class="string">&#x27;s/.*:h=//g&#x27;</span>`</span><br><span class="line"><span class="keyword">if</span> <span class="built_in">let</span> <span class="variable">$&#123;scale_w&#125;</span> AND <span class="built_in">let</span> <span class="variable">$&#123;scale_h&#125;</span>; <span class="keyword">then</span></span><br><span class="line">argsnew+=(<span class="string">&quot;-vf&quot;</span> <span class="string">&quot;scale_vaapi=w=<span class="variable">$&#123;scale_w&#125;</span>:h=<span class="variable">$&#123;scale_h&#125;</span>:format=nv12,hwupload,setsar=sar=1&quot;</span>)</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">argsnew+=(<span class="string">&quot;-vf&quot;</span> <span class="string">&quot;scale_vaapi=format=nv12,hwupload,setsar=sar=1&quot;</span>)</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">argsnew+=(<span class="string">&quot;-vf&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line">args2sv+=(<span class="string">&quot;-vf&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args1vs+=(<span class="string">&quot;-vf&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">-vcodec)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line">argsnew+=(<span class="string">&quot;-vcodec&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args1sv+=(<span class="string">&quot;-vcodec&quot;</span> <span class="string">&quot;copy&quot;</span>)</span><br><span class="line">args2sv+=(<span class="string">&quot;-vcodec&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args1vs+=(<span class="string">&quot;-vcodec&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2vs+=(<span class="string">&quot;-vcodec&quot;</span> <span class="string">&quot;copy&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">-acodec)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line"><span class="keyword">if</span> [ <span class="string">&quot;<span class="variable">$1</span>&quot;</span> = <span class="string">&quot;libfaac&quot;</span> ]; <span class="keyword">then</span></span><br><span class="line">argsnew+=(<span class="string">&quot;-acodec&quot;</span> <span class="string">&quot;aac&quot;</span>)</span><br><span class="line">args1sv+=(<span class="string">&quot;-acodec&quot;</span> <span class="string">&quot;aac&quot;</span>)</span><br><span class="line">args2vs+=(<span class="string">&quot;-acodec&quot;</span> <span class="string">&quot;aac&quot;</span>)</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">argsnew+=(<span class="string">&quot;-acodec&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args1sv+=(<span class="string">&quot;-acodec&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2vs+=(<span class="string">&quot;-acodec&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line">args2sv+=(<span class="string">&quot;-acodec&quot;</span> <span class="string">&quot;copy&quot;</span>)</span><br><span class="line">args1vs+=(<span class="string">&quot;-acodec&quot;</span> <span class="string">&quot;copy&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">-ab)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line">argsnew+=(<span class="string">&quot;-ab&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args1sv+=(<span class="string">&quot;-ab&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2vs+=(<span class="string">&quot;-ab&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">-ac)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line">argsnew+=(<span class="string">&quot;-ac&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args1sv+=(<span class="string">&quot;-ac&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2vs+=(<span class="string">&quot;-ac&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">-f)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line">argsnew+=(<span class="string">&quot;-f&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args1sv+=(<span class="string">&quot;-f&quot;</span> <span class="string">&quot;mpegts&quot;</span>)</span><br><span class="line">args2sv+=(<span class="string">&quot;-f&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args1vs+=(<span class="string">&quot;-f&quot;</span> <span class="string">&quot;mpegts&quot;</span>)</span><br><span class="line">args2vs+=(<span class="string">&quot;-f&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">-segment_format)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line">argsnew+=(<span class="string">&quot;-segment_format&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2vs+=(<span class="string">&quot;-segment_format&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2sv+=(<span class="string">&quot;-segment_format&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">-segment_list_type)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line">argsnew+=(<span class="string">&quot;-segment_list_type&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2vs+=(<span class="string">&quot;-segment_list_type&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2sv+=(<span class="string">&quot;-segment_list_type&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">-hls_seek_time)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line">argsnew+=(<span class="string">&quot;-hls_seek_time&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2vs+=(<span class="string">&quot;-hls_seek_time&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2sv+=(<span class="string">&quot;-hls_seek_time&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">-segment_time)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line">argsnew+=(<span class="string">&quot;-segment_time&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2vs+=(<span class="string">&quot;-segment_time&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2sv+=(<span class="string">&quot;-segment_time&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">-segment_time_delta)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line">argsnew+=(<span class="string">&quot;-segment_time_delta&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2vs+=(<span class="string">&quot;-segment_time_delta&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2sv+=(<span class="string">&quot;-segment_time_delta&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">-segment_start_number)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line">argsnew+=(<span class="string">&quot;-segment_start_number&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2vs+=(<span class="string">&quot;-segment_start_number&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2sv+=(<span class="string">&quot;-segment_start_number&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">-individual_header_trailer)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line">argsnew+=(<span class="string">&quot;-individual_header_trailer&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2vs+=(<span class="string">&quot;-individual_header_trailer&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2sv+=(<span class="string">&quot;-individual_header_trailer&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">-avoid_negative_ts)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line">argsnew+=(<span class="string">&quot;-avoid_negative_ts&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2vs+=(<span class="string">&quot;-avoid_negative_ts&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2sv+=(<span class="string">&quot;-avoid_negative_ts&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">-break_non_keyframes)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line">argsnew+=(<span class="string">&quot;-break_non_keyframes&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2vs+=(<span class="string">&quot;-break_non_keyframes&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2sv+=(<span class="string">&quot;-break_non_keyframes&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">-max_muxing_queue_size)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line">args2vs+=(<span class="string">&quot;-max_muxing_queue_size&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args2sv+=(<span class="string">&quot;-max_muxing_queue_size&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">-map)</span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line">argsnew+=(<span class="string">&quot;-map&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args1sv+=(<span class="string">&quot;-map&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args1vs+=(<span class="string">&quot;-map&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">;;</span><br><span class="line">*)</span><br><span class="line">argsnew+=(<span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line"><span class="keyword">if</span> [ <span class="string">&quot;<span class="variable">$stream</span>&quot;</span> = <span class="string">&quot;<span class="variable">$1</span>&quot;</span> ]; <span class="keyword">then</span></span><br><span class="line">args1sv+=(<span class="string">&quot;-bufsize&quot;</span> <span class="string">&quot;1024k&quot;</span> <span class="string">&quot;pipe:1&quot;</span>)</span><br><span class="line">args2sv+=(<span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args1vs+=(<span class="string">&quot;-bufsize&quot;</span> <span class="string">&quot;1024k&quot;</span> <span class="string">&quot;pipe:1&quot;</span>)</span><br><span class="line">args2vs+=(<span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">args2sv+=(<span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">args1vs+=(<span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line">;;</span><br><span class="line"><span class="keyword">esac</span></span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"></span><br><span class="line">sed -i -e <span class="string">&quot;s/&#123;\&quot;PID\&quot;:<span class="variable">$&#123;pid&#125;</span>,\&quot;hardware_transcode\&quot;:true,/&#123;\&quot;PID\&quot;:<span class="variable">$&#123;pid&#125;</span>,\&quot;hardware_transcode\&quot;:false,/&quot;</span> /tmp/VideoStation/enabled</span><br><span class="line"></span><br><span class="line">startexectime=`date +%s`</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [ <span class="string">&quot;<span class="variable">$scodec</span>&quot;</span> = <span class="string">&quot;subrip&quot;</span> ]; <span class="keyword">then</span></span><br><span class="line"></span><br><span class="line">_log <span class="string">&quot;FFMPEG   = <span class="variable">$bin2</span>&quot;</span></span><br><span class="line">_log <span class="string">&quot;CODEC    = <span class="variable">$scodec</span>&quot;</span></span><br><span class="line">_log <span class="string">&quot;PARAMVS  =&quot;</span></span><br><span class="line">_log_para <span class="string">&quot;<span class="variable">$paramvs</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$bin2</span> <span class="string">&quot;<span class="variable">$&#123;args[@]&#125;</span>&quot;</span> &amp;&gt; /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr &amp;</span><br><span class="line"></span><br><span class="line"><span class="keyword">elif</span> [ <span class="string">&quot;<span class="variable">$fcodec</span>&quot;</span> = <span class="string">&quot;mjpeg&quot;</span> ]; <span class="keyword">then</span></span><br><span class="line"></span><br><span class="line">_log <span class="string">&quot;FFMPEG   = <span class="variable">$bin2</span>&quot;</span></span><br><span class="line">_log <span class="string">&quot;CODEC    = <span class="variable">$fcodec</span>&quot;</span></span><br><span class="line">_log <span class="string">&quot;PARAMVS  =&quot;</span></span><br><span class="line">_log_para <span class="string">&quot;<span class="variable">$paramvs</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$bin2</span> <span class="string">&quot;<span class="variable">$&#123;args[@]&#125;</span>&quot;</span> &amp;&gt; /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr &amp;</span><br><span class="line"></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"></span><br><span class="line">_log <span class="string">&quot;VCODEC   = <span class="variable">$vcodec</span> (<span class="variable">$vcodecprofile</span>)&quot;</span></span><br><span class="line">_log <span class="string">&quot;ACODEC   = <span class="variable">$acodec</span>&quot;</span></span><br><span class="line">_log <span class="string">&quot;PARAMVS  =&quot;</span></span><br><span class="line">_log_para <span class="string">&quot;<span class="variable">$paramvs</span>&quot;</span></span><br><span class="line">_log <span class="string">&quot;MODE     = WRAP&quot;</span></span><br><span class="line">_log <span class="string">&quot;FFMPEG   = <span class="variable">$bin1</span>&quot;</span></span><br><span class="line">_log <span class="string">&quot;PARAMWP  =&quot;</span></span><br><span class="line">param1=<span class="variable">$&#123;argsnew[@]&#125;</span></span><br><span class="line">_log_para <span class="string">&quot;<span class="variable">$param1</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$bin1</span> <span class="string">&quot;<span class="variable">$&#123;argsnew[@]&#125;</span>&quot;</span> &amp;&gt; /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr &amp;</span><br><span class="line"></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">childpid=$!</span><br><span class="line">_log <span class="string">&quot;CHILDPID = <span class="variable">$childpid</span>&quot;</span></span><br><span class="line"><span class="built_in">wait</span> <span class="variable">$childpid</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> grep <span class="string">&quot;Conversion failed!&quot;</span> /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr || grep <span class="string">&quot;Error opening filters!&quot;</span> /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr || grep <span class="string">&quot;Unrecognized option&quot;</span> /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr || grep <span class="string">&quot;Invalid data found when processing input&quot;</span> /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr; <span class="keyword">then</span></span><br><span class="line"></span><br><span class="line">_log <span class="string">&quot;*** CHILD END ***&quot;</span></span><br><span class="line">startexectime=`date +%s`</span><br><span class="line">_log <span class="string">&quot;STDOUT   =&quot;</span></span><br><span class="line">_log_para <span class="string">&quot;`tail -n 15 /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr`&quot;</span></span><br><span class="line">_log <span class="string">&quot;MODE     = PIPE SV&quot;</span></span><br><span class="line">_log <span class="string">&quot;FFMPEG1  = <span class="variable">$bin1</span>&quot;</span></span><br><span class="line">_log <span class="string">&quot;FFMPEG2  = <span class="variable">$bin2</span>&quot;</span></span><br><span class="line">_log <span class="string">&quot;PARAM1   =&quot;</span></span><br><span class="line">param1=<span class="variable">$&#123;args1sv[@]&#125;</span></span><br><span class="line">_log_para <span class="string">&quot;<span class="variable">$param1</span>&quot;</span></span><br><span class="line">_log <span class="string">&quot;PARAM2   =&quot;</span></span><br><span class="line">param2=<span class="variable">$&#123;args2sv[@]&#125;</span></span><br><span class="line">_log_para <span class="string">&quot;<span class="variable">$param2</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$bin1</span> <span class="string">&quot;<span class="variable">$&#123;args1sv[@]&#125;</span>&quot;</span> | <span class="variable">$bin2</span> <span class="string">&quot;<span class="variable">$&#123;args2sv[@]&#125;</span>&quot;</span> &amp;&gt; /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr &amp;</span><br><span class="line"></span><br><span class="line">childpid=$!</span><br><span class="line">_log <span class="string">&quot;CHILDPID = <span class="variable">$childpid</span>&quot;</span></span><br><span class="line"><span class="built_in">wait</span> <span class="variable">$childpid</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> grep <span class="string">&quot;Conversion failed!&quot;</span> /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr || grep <span class="string">&quot;Error opening filters!&quot;</span> /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr || grep <span class="string">&quot;Unrecognized option&quot;</span> /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr || grep <span class="string">&quot;Invalid data found when processing input&quot;</span> /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr; <span class="keyword">then</span></span><br><span class="line"></span><br><span class="line">_log <span class="string">&quot;*** CHILD END ***&quot;</span></span><br><span class="line">startexectime=`date +%s`</span><br><span class="line">_log <span class="string">&quot;STDOUT   =&quot;</span></span><br><span class="line">_log_para <span class="string">&quot;`tail -n 15 /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr`&quot;</span></span><br><span class="line">_log <span class="string">&quot;MODE     = PIPE VS&quot;</span></span><br><span class="line">_log <span class="string">&quot;FFMPEG1  = <span class="variable">$bin2</span>&quot;</span></span><br><span class="line">_log <span class="string">&quot;FFMPEG2  = <span class="variable">$bin1</span>&quot;</span></span><br><span class="line">_log <span class="string">&quot;PARAM1   =&quot;</span></span><br><span class="line">param1=<span class="variable">$&#123;args1vs[@]&#125;</span></span><br><span class="line">_log_para <span class="string">&quot;<span class="variable">$param1</span>&quot;</span></span><br><span class="line">_log <span class="string">&quot;PARAM2   =&quot;</span></span><br><span class="line">param2=<span class="variable">$&#123;args2vs[@]&#125;</span></span><br><span class="line">_log_para <span class="string">&quot;<span class="variable">$param2</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$bin2</span> <span class="string">&quot;<span class="variable">$&#123;args1vs[@]&#125;</span>&quot;</span> | <span class="variable">$bin1</span> <span class="string">&quot;<span class="variable">$&#123;args2vs[@]&#125;</span>&quot;</span> &amp;&gt; /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr &amp;</span><br><span class="line"></span><br><span class="line">childpid=$!</span><br><span class="line">_log <span class="string">&quot;CHILDPID = <span class="variable">$childpid</span>&quot;</span></span><br><span class="line"><span class="built_in">wait</span> <span class="variable">$childpid</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> grep <span class="string">&quot;Conversion failed!&quot;</span> /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr || grep <span class="string">&quot;Error opening filters!&quot;</span> /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr || grep <span class="string">&quot;Unrecognized option&quot;</span> /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr || grep <span class="string">&quot;Invalid data found when processing input&quot;</span> /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr; <span class="keyword">then</span></span><br><span class="line"></span><br><span class="line">_log <span class="string">&quot;*** CHILD END ***&quot;</span></span><br><span class="line">startexectime=`date +%s`</span><br><span class="line">_log <span class="string">&quot;STDOUT   =&quot;</span></span><br><span class="line">_log_para <span class="string">&quot;`tail -n 15 /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr`&quot;</span></span><br><span class="line">_log <span class="string">&quot;MODE     = ORIG&quot;</span></span><br><span class="line">_log <span class="string">&quot;FFMPEG   = <span class="variable">$bin2</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$bin2</span> <span class="string">&quot;<span class="variable">$&#123;args[@]&#125;</span>&quot;</span> &amp;&gt; /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr &amp;</span><br><span class="line"></span><br><span class="line">childpid=$!</span><br><span class="line">_log <span class="string">&quot;CHILDPID = <span class="variable">$childpid</span>&quot;</span></span><br><span class="line"><span class="built_in">wait</span> <span class="variable">$childpid</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">stopexectime=`date +%s`</span><br><span class="line"><span class="keyword">if</span> <span class="built_in">test</span> $((stopexectime-startexectime)) -lt 10; <span class="keyword">then</span></span><br><span class="line">_log <span class="string">&quot;STDOUT   =&quot;</span></span><br><span class="line">_log_para <span class="string">&quot;`tail -n 15 /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr`&quot;</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">_log <span class="string">&quot;*** CHILD END ***&quot;</span></span><br><span class="line">_log <span class="string">&quot;*** PROCESS END ***&quot;</span></span><br><span class="line"></span><br><span class="line">rm /tmp/ffmpeg-<span class="variable">$&#123;streamid&#125;</span>.stderr</span><br></pre></td></tr></table></figure><h2 id="附录2：原ffmpeg-wrapper脚本内容-已经弃用"><a href="#附录2：原ffmpeg-wrapper脚本内容-已经弃用" class="headerlink" title="附录2：原ffmpeg-wrapper脚本内容(已经弃用)"></a>附录2：原ffmpeg-wrapper脚本内容(已经弃用)</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><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line">bin=<span class="string">&quot;exec /var/packages/ffmpeg/target/bin/ffmpeg&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># DEBUG</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;<span class="variable">$@</span>&quot;</span> &gt;&gt; /tmp/ffmpeg.log</span><br><span class="line"></span><br><span class="line">args=()</span><br><span class="line"><span class="keyword">while</span> [[ <span class="variable">$#</span> -gt 0 ]]</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line"><span class="keyword">case</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span> <span class="keyword">in</span></span><br><span class="line">    -vprofile)</span><br><span class="line">       <span class="built_in">shift</span></span><br><span class="line">       args+=(<span class="string">&quot;-x264profile&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">       ;;</span><br><span class="line">    -vf)</span><br><span class="line">       <span class="built_in">shift</span></span><br><span class="line">       <span class="comment">#vf=$(echo &quot;$1&quot; | sed -e &#x27;s/format=nv12.*scale_vaapi/scale_vaapi/g&#x27; -e &#x27;s/$/:format=nv12/&#x27;)</span></span><br><span class="line">           <span class="comment"># DEBUG</span></span><br><span class="line">       vf=$(<span class="built_in">echo</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span> | tee -a /tmp/ffmpeg.log | sed -e <span class="string">&#x27;s/format=nv12.*scale_vaapi/scale_vaapi/g&#x27;</span> -e <span class="string">&#x27;s/$/:format=nv12/</span></span><br><span class="line"><span class="string">&#x27;</span>)</span><br><span class="line">           <span class="built_in">echo</span> <span class="string">&quot;vf: [<span class="variable">$vf</span>]&quot;</span> &gt;&gt; /tmp/ffmpeg.log</span><br><span class="line">       args+=(<span class="string">&quot;-vf&quot;</span> <span class="string">&quot;<span class="variable">$vf</span>&quot;</span>)</span><br><span class="line">       ;;</span><br><span class="line">    -i)</span><br><span class="line">       <span class="built_in">shift</span></span><br><span class="line">       movie=<span class="string">&quot;<span class="variable">$1</span>&quot;</span></span><br><span class="line">       args+=(<span class="string">&quot;-i&quot;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">       <span class="comment"># DEBUG</span></span><br><span class="line">       <span class="built_in">echo</span> <span class="string">&quot;movie=[<span class="variable">$movie</span>]&quot;</span> &gt;&gt; /tmp/ffmpeg.log</span><br><span class="line">       ;;</span><br><span class="line">    *)</span><br><span class="line">       args+=(<span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line">       ;;</span><br><span class="line"><span class="keyword">esac</span></span><br><span class="line"><span class="built_in">shift</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">set</span> -- <span class="string">&quot;<span class="variable">$&#123;args[@]&#125;</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># DEBUG</span></span><br><span class="line"><span class="built_in">echo</span> <span class="variable">$bin</span> <span class="string">&quot;<span class="variable">$@</span>&quot;</span> &gt;&gt; /tmp/ffmpeg.log</span><br><span class="line"><span class="built_in">echo</span> &gt;&gt; /tmp/ffmpeg.log</span><br><span class="line"><span class="variable">$bin</span> <span class="string">&quot;<span class="variable">$@</span>&quot;</span> 2&gt;&gt;/tmp/ffmpeg.log</span><br><span class="line"></span><br><span class="line"><span class="comment">#$bin &quot;$@&quot;</span></span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;更新日志&quot;&gt;&lt;a href=&quot;#更新日志&quot; class=&quot;headerlink&quot; title=&quot;更新日志&quot;&gt;&lt;/a&gt;更新日志&lt;/h2&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;时间&lt;/th&gt;
&lt;th&gt;修改内容&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;2020.07.08&lt;/td&gt;
&lt;td&gt;更新ffmpeg-wrapper，修复无法显示ASS字幕的问题&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020.04.07&lt;/td&gt;
&lt;td&gt;修改安装命令&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020.03.31&lt;/td&gt;
&lt;td&gt;撰写文章&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;引言&quot;&gt;&lt;a href=&quot;#引言&quot; class=&quot;headerlink&quot; title=&quot;引言&quot;&gt;&lt;/a&gt;引言&lt;/h2&gt;&lt;p&gt;由于群晖没有获得DTS的版权，因此群晖自带的VideoStation从某个版本开始不支持DTS、EAC3、TrueHD的音频格式，严重影响使用体验。搜索该问题，得到的答案均为将VideoStation降级到2.3.4-1468，但是这个版本的VideoStation不支持HEVC和4K解码，比较鸡肋。本文将利用第三方的ffmpeg替换VideoStation中自带的ffmpeg，实现对全视频格式的支持。&lt;/p&gt;
&lt;h2 id=&quot;操作方法&quot;&gt;&lt;a href=&quot;#操作方法&quot; class=&quot;headerlink&quot; title=&quot;操作方法&quot;&gt;&lt;/a&gt;操作方法&lt;/h2&gt;&lt;h3 id=&quot;安装ffmpeg&quot;&gt;&lt;a href=&quot;#安装ffmpeg&quot; class=&quot;headerlink&quot; title=&quot;安装ffmpeg&quot;&gt;&lt;/a&gt;安装ffmpeg&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;下载对应版本的ffmpeg安装包，其中DS918+对应的版本是&lt;strong&gt;apollolake&lt;/strong&gt;，具体每个机型对应的版本可以在以下连接中找到：&lt;a href=&quot;https://github.com/SynoCommunity/spksrc/wiki/Architecture-per-Synology-model&quot;&gt;https://github.com/SynoCommunity/spksrc/wiki/Architecture-per-Synology-model&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;下载地址：&lt;a href=&quot;https://github.com/th0ma7/synology/tree/master/packages&quot;&gt;https://github.com/th0ma7/synology/tree/master/packages&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;登录DSM，打开&lt;strong&gt;套件中心&lt;/strong&gt;，点击&lt;strong&gt;手动安装&lt;/strong&gt;，选择刚刚下载的文件进行安装。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;在DSM中打开&lt;strong&gt;控制面板&lt;/strong&gt;，选择&lt;strong&gt;终端机和SNMP&lt;/strong&gt;，勾选&lt;strong&gt;启动SSH功能&lt;/strong&gt;，并调整对应的端口号。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;用XSheel等软件以SSH的方式登录到NAS中，执行以下命令：&lt;/p&gt;
&lt;figure class=&quot;highlight sh&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;sudo chmod +s /var/packages/ffmpeg/target/bin/ffmpeg&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;sudo chmod +s /var/packages/ffmpeg/target/bin/ffprobe&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;sudo chmod +s /var/packages/ffmpeg/target/bin/vainfo&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;安装VideoStation&quot;&gt;&lt;a href=&quot;#安装VideoStation&quot; class=&quot;headerlink&quot; title=&quot;安装VideoStation&quot;&gt;&lt;/a&gt;安装VideoStation&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;登录DSM，打开&lt;strong&gt;套件中心&lt;/strong&gt;，安装&lt;strong&gt;最新版的VideoStation&lt;/strong&gt;，本文撰写时，版本为2.4.7-1603，经测试，以下版本适用于本方法，其他版本未测试：&lt;code&gt;2.4.6-1594&lt;/code&gt;，&lt;code&gt;2.4.7-1603&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;用XSheel等软件以SSH的方式登录到NAS中，执行以下命令：&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&quot;highlight sh&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;24&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;# 备份&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;sudo  sed -i&lt;span class=&quot;string&quot;&gt;&amp;#x27;-BACKUP&amp;#x27;&lt;/span&gt; -e &lt;span class=&quot;string&quot;&gt;&amp;#x27;s/eac3/ZAAP/&amp;#x27;&lt;/span&gt; -e &lt;span class=&quot;string&quot;&gt;&amp;#x27;s/dts/ZAP/&amp;#x27;&lt;/span&gt; -e &lt;span class=&quot;string&quot;&gt;&amp;#x27;s/truehd/ZAPZAP/&amp;#x27;&lt;/span&gt; /var/packages/VideoStation/target/lib/libsynovte.so&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;# 注意：ffmpeg的备份名称ffmpeg-BACKUP应与ffmpeg自定义脚本中$bin2保持一致&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;sudo mv /var/packages/VideoStation/target/bin/ffmpeg /var/packages/VideoStation/target/bin/ffmpeg-BACKUP&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;sudo mv /var/packages/VideoStation/target/bin/ffprobe /var/packages/VideoStation/target/bin/ffprobe-BACKUP&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;sudo mv /var/packages/VideoStation/target/bin/vainfo /var/packages/VideoStation/target/bin/vainfo-BACKUP&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;# 更新/var/packages/VideoStation/target/ffmpeg为自定义脚本，调用第三方的ffmpeg&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;# 具体脚本信息见附录&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;sudo su&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;built_in&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&amp;quot;H4sICJ6oBF8AA2ZmbXBlZy13cmFwcGVyAO1abXPbNhL+LP4KhKVf5Jimybn7ogwz8dlKqklcayzHnU7ioyASlFhTJEtQVFzH/70LAqT4KjltrzO9qWeSSMCziwfAg8Vine9eaDMv0GaYLiQpJqkp64YsSZYfzg/7j1KP2IsQycqhgxOCDl7u/aTuLdU9B+19P9i7HOxNDvpIRcojTWKCl57zxL7pMnr9GmnJMtJcdxmR+Ql4k564VyvCMS67BvRX5Ia+g9Q10o1T+EaJg2Sq/ffkSNG+aejix0T7WieLhMTLjEG8LPerJV8nNHFIHEs9RhnJR0dH6P3ow4fz70cfLhB8k6Xevef7SL0ZXl/CJOyF5zuR58jIeK05JNWCle+zwZIYRygbEU1G7xhaknBsL8zpKsBLglQs5ntAtZMj+hCEMOCDpWkH0G6vEqRCl3WAVBfpUynA9HcYGlMJqJmKIrG1X6bUVN5IfKqmrDy+GSBVf5KlfPKm/PbtpcImI4E0dFNLcaxF2L7Hc0LFWmkJjuckybTDWxjWqGFvPYeEkwQnXhg0LdT/nJ2//ziG5ZhT87AvSakdOsQ25fdXoMA1rChBnz4h5TukzhN0iu7uJCeUbEwJV40XSD3V60u9Hl14bgL/LsPUIybrhC/M7UvzUFY9OcMD8NUrsFissW0Tv2wnmhqWor1qTzOWZXMqeNeseXPV2C3bue12btVkiaOyUQGD9gLY8xz4ak6zI6XoGwUMxP4DwkWfAJ4KUiZiy4zuXqFkQWAhez2x+tPd2x3F4QwEmCI4IWGMVEp8YicW1w9FSkYGmhfh2iJBEnuEIiG3bAyLCzh0kUNcvPITMwij2AsSaw2nJSIxNfVBEN6TB1MHytmusiCxIBiCRMAOQsEXuLgglP8xbTHKH+VMfEoYdfz3WmrX41I86pf0VyiUUGxLXJ1OGBAe5XnIHF9fnQ8nEzS5Obu+QdfDW6TAFYMuJgoEMnSosEDYR+PRBWLhhsdVEYYcLzan8FfGX1Z4ozyVILZ6Nmy3jZMi9gPsSUtZrLGWJMFwX7DY+PMvSI3RwQm3OJgKXhfD29H5MLsiFN4li57Lq9vRkF8eYiGADIFjpAKBRzbvT2/uIE5mASsgaxaz2EedpvlHY/NRT2nRyj8+N6RRWj/vMBiPKHRz4sXIW9uDEOLXKob706KE3G/6U9puJ9q77AzhFwfYf/iVOAwCoR2c6Kf8ZwMVrrZDswDndU3Xa59VrdkoNUdeRAan8EFEx9PG1OrGpeZW44xhWgnbIpIWlwOE0hTjCFzcof39UpQVUSNDXGIPTtNpJeJSG/vEWouwDRqDW1hc6irh97q5NjVtflBpHSzMkyPWOt04WWxzAgYFHMj7JDs6fPAndPbDRaVp8bRhWNmOlF1MHJPNF7ht3MAYGwcDN4yXGGJNqhvHi/Uq8kPsHMNZojg22R++AUU83D7MM52xKLWJsHWHYs8zUFk15c6yTtLaLZw2bv3KENWrviLXos8Oo4eGalstcw4tfUa9L/eascQNlkKseiZC35u5GNsVEZangXOnDNTPe/OJtPcWB73e27YVuDajdv/lzob7ro3Ercus182ryzXr2lA8a9/MentBb1aVC+6UCu6QSb19M/EtGWTZcV3Iwi9rZnlFQhva65B+m0XamppSMl9C1mHxE9p5a1VQ7bPcislvuXZMhYnv0cRKHiKyi0wB3M6nC1aj1ITxp4ZPswvUSrxlJ6MKqJ3NNohg0g6prM02EmXM9hXpptCKaDCwHOIn+Dk8OHI3m1ZcC6carsKMQtadWMFqOSPxLm5l7HZ2W5A1fq1Inh0Fjge57Qr7FkvKSWwlMYasopNmp0FH5vNMeJ5l7YTz+JeGnmMFZA5ZX0qspDOlbQA7ouAOWB5EO2EZqxm8Fu6tIAwsePC4MTwtOnm1QNuZ7QYKbtuA4qH/xVquvnjB3PplRVaQe3u/Ng5sPmwruH3g7dCuGkO+EJUyQ/liaXakzcJE5dkoXOYlC56ZiJcdS09YllLLTPLBZitXsNZPjX/d59l6OVHgyEpikW/RbuO09KatZC7dbkuJSNsrOEvAPZaDy1R7/CzDK/ezPFAe4aH7dPxZXuDYWeOYsOMTZJUi6E3iFTl+LtjFQPNYk3kJs1JqIwGe+cRhb2kIK+QLsVn8g/c0q6S+3KNTScpXf1MOoqtZ7EXFDvSkvPj59u3lePiOv4tZkU/OO86vLobn4sEsHOVd47Prs8vbCXSJpqzsCwOKCiS0Sr3MW+VtjfZf76jIon1JIr5g727YL38G/O8l7/6l5EFc+Ui3OQsgwTN6dFh9PPYLVmclLP5mwnmJ42Io6uM/Xp+N5Y5V0quefxxzz5k33eRThsPMZt0yHljz5dHz5RHYZ64QnCgpr6ebygtRnclK76xUBBSLaru0xl6y+Z7Jeh6TCOBhkJKYwmlALrufnBfyrqG/fhW2w6zWFkYkgJiJYBcS8PQN9h+DmNjhPICY44AbXvd4rvEoSLHvOSirYrnhKnDQGhSNQA82oZQx8oJolezyKOJo+TcY/LcXwx/y32B0RgdhM7m5uPp4gxqymiawoll18N87SEzblDcejYdoclsTn94UH+8wmmc3U6WOWlQJ90W3KsvWRtnaENZGp7XR0LQYidVaKqHAyNufpfVeWei9bUrv1aXOjsk/cv+byP120iV3o0vu+nPkntI/IvdO60LuhazFSLnci2NgpN9y+f0j9/97uV9dj9515RVNWf112qEJbHnLaoCmEkIBfnhYhqiV5ev3keonSD/NXyd/4hIycp272PxdWtEl7f7fC78BTGYfI90hAAA=&amp;quot;&lt;/span&gt; | base64 -d | gunzip &amp;gt; /var/packages/VideoStation/target/bin/ffmpeg&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;built_in&quot;&gt;exit&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;# 更新ffprobe vainfo&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;sudo ln -s /var/packages/ffmpeg/target/bin/ffprobe /var/packages/VideoStation/target/bin/ffprobe&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;sudo ln -s /var/packages/ffmpeg/target/bin/vainfo /var/packages/VideoStation/target/bin/vainfo&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;# 解除对EAC3 DTS TrueHD的屏蔽&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;sudo sed -i&lt;span class=&quot;string&quot;&gt;&amp;#x27;-BACKUP&amp;#x27;&lt;/span&gt; -e &lt;span class=&quot;string&quot;&gt;&amp;#x27;s/eac3/ZAAP/&amp;#x27;&lt;/span&gt; -e &lt;span class=&quot;string&quot;&gt;&amp;#x27;s/dts/ZAP/&amp;#x27;&lt;/span&gt; -e &lt;span class=&quot;string&quot;&gt;&amp;#x27;s/truehd/ZAPZAP/&amp;#x27;&lt;/span&gt; /var/packages/VideoStation/target/lib/libsynovte.so&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;comment&quot;&gt;# 调整权限信息&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;sudo chmod +x /var/packages/VideoStation/target/bin/ffmpeg&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;sudo chmod +s /var/packages/VideoStation/target/bin/ffmpeg&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;sudo chown root:VideoStation /var/packages/VideoStation/target/bin/ffmpeg&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;</summary>
    
    
    
    <category term="Synology" scheme="https://blog.wanghaoyu.cn/categories/Synology/"/>
    
    
    <category term="群晖" scheme="https://blog.wanghaoyu.cn/tags/%E7%BE%A4%E6%99%96/"/>
    
    <category term="Synology" scheme="https://blog.wanghaoyu.cn/tags/Synology/"/>
    
    <category term="VideoStation" scheme="https://blog.wanghaoyu.cn/tags/VideoStation/"/>
    
    <category term="DTS" scheme="https://blog.wanghaoyu.cn/tags/DTS/"/>
    
    <category term="EAC3" scheme="https://blog.wanghaoyu.cn/tags/EAC3/"/>
    
    <category term="TrueHD" scheme="https://blog.wanghaoyu.cn/tags/TrueHD/"/>
    
  </entry>
  
  <entry>
    <title>Ubuntu18.04安装rTorrent</title>
    <link href="https://blog.wanghaoyu.cn/2019/11/09/rtorrent-installation/"/>
    <id>https://blog.wanghaoyu.cn/2019/11/09/rtorrent-installation/</id>
    <published>2019-11-09T13:13:47.000Z</published>
    <updated>2024-02-27T06:16:02.140Z</updated>
    
    <content type="html"><![CDATA[<h1 id="libtorrent"><a href="#libtorrent" class="headerlink" title="libtorrent"></a>libtorrent</h1><h2 id="安装依赖"><a href="#安装依赖" class="headerlink" title="安装依赖"></a>安装依赖</h2><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></pre></td><td class="code"><pre><span class="line">sudo apt install </span><br><span class="line">            automake \</span><br><span class="line">            gcc \</span><br><span class="line">            g++ \</span><br><span class="line">            libtool \</span><br><span class="line">            pkg-config \</span><br><span class="line">            libcrypto++-dev \</span><br><span class="line">            libssl-dev</span><br></pre></td></tr></table></figure><h2 id="下载源代码并解压"><a href="#下载源代码并解压" class="headerlink" title="下载源代码并解压"></a>下载源代码并解压</h2><figure class="highlight shell"><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">wget http://rtorrent.net/downloads/libtorrent-0.13.8.tar.gz</span><br><span class="line">tar zxvf libtorrent-0.13.8.tar.gz</span><br></pre></td></tr></table></figure><h2 id="编译安装"><a href="#编译安装" class="headerlink" title="编译安装"></a>编译安装</h2><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></pre></td><td class="code"><pre><span class="line">cd libtorrent-0.13.8</span><br><span class="line">./autogen.sh</span><br><span class="line">./configure</span><br><span class="line">make -j4</span><br><span class="line">sudo make install</span><br></pre></td></tr></table></figure><a id="more"></a><h1 id="rTorrent"><a href="#rTorrent" class="headerlink" title="rTorrent"></a>rTorrent</h1><h2 id="安装依赖-1"><a href="#安装依赖-1" class="headerlink" title="安装依赖"></a>安装依赖</h2><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></pre></td><td class="code"><pre><span class="line">sudo apt install </span><br><span class="line">            libncurses-dev \</span><br><span class="line">            libxmlrpc-core-c3-dev \</span><br><span class="line">            libcurl4-openssl-dev \</span><br><span class="line">            libcurlpp-dev</span><br></pre></td></tr></table></figure><h2 id="下载源代码并解压-1"><a href="#下载源代码并解压-1" class="headerlink" title="下载源代码并解压"></a>下载源代码并解压</h2><figure class="highlight shell"><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">wget http://rtorrent.net/downloads/rtorrent-0.9.8.tar.gz</span><br><span class="line">tar zxvf rtorrent-0.9.8.tar.gz</span><br></pre></td></tr></table></figure><h2 id="编译安装-1"><a href="#编译安装-1" class="headerlink" title="编译安装"></a>编译安装</h2><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></pre></td><td class="code"><pre><span class="line">cd rtorrent-0.9.8</span><br><span class="line">./autogen.sh</span><br><span class="line">./configure --with-xmlrpc-c --with-libcurl</span><br><span class="line">make -j4</span><br><span class="line">sudo make install</span><br></pre></td></tr></table></figure><h2 id="rTorrent配置文件"><a href="#rTorrent配置文件" class="headerlink" title="rTorrent配置文件"></a>rTorrent配置文件</h2><p>rTorrent的配置文件在<code>~/.rtorrent.rc</code>，在启动前，需要对rTorrent进行相关的配置，下面是我的配置，仅供参考。具体配置的相关教程请参考<a href="https://github.com/rakshasa/rtorrent/wiki/Config-Guide">rTorrent配置文件</a></p><figure class="highlight vala"><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><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#############################################################################</span></span><br><span class="line"><span class="meta"># A minimal rTorrent configuration that provides the basic features</span></span><br><span class="line"><span class="meta"># you want to have in addition to the built-in defaults.</span></span><br><span class="line"><span class="meta">#</span></span><br><span class="line"><span class="meta"># See https://github.com/rakshasa/rtorrent/wiki/CONFIG-Template</span></span><br><span class="line"><span class="meta"># for an up-to-date version.</span></span><br><span class="line"><span class="meta">#############################################################################</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">## Instance layout (base paths)</span></span><br><span class="line">method.insert = cfg.basedir,  <span class="keyword">private</span>|<span class="keyword">const</span>|<span class="keyword">string</span>, (cat,<span class="string">&quot;/home/rtorrent/rtorrent/&quot;</span>)</span><br><span class="line">method.insert = cfg.download, <span class="keyword">private</span>|<span class="keyword">const</span>|<span class="keyword">string</span>, (cat,<span class="string">&quot;/mnt/download/&quot;</span>)</span><br><span class="line">method.insert = cfg.logs,     <span class="keyword">private</span>|<span class="keyword">const</span>|<span class="keyword">string</span>, (cat,(cfg.basedir),<span class="string">&quot;log/&quot;</span>)</span><br><span class="line">method.insert = cfg.logfile,  <span class="keyword">private</span>|<span class="keyword">const</span>|<span class="keyword">string</span>, (cat,(cfg.logs),<span class="string">&quot;rtorrent-&quot;</span>,(system.time),<span class="string">&quot;.log&quot;</span>)</span><br><span class="line">method.insert = cfg.session,  <span class="keyword">private</span>|<span class="keyword">const</span>|<span class="keyword">string</span>, (cat,(cfg.basedir),<span class="string">&quot;.session/&quot;</span>)</span><br><span class="line">method.insert = cfg.watch,    <span class="keyword">private</span>|<span class="keyword">const</span>|<span class="keyword">string</span>, (cat,(cfg.basedir),<span class="string">&quot;watch/&quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">## Create instance directories</span></span><br><span class="line">execute.throw = sh, -c, (cat,\</span><br><span class="line">    <span class="string">&quot;mkdir -p \&quot;&quot;</span>,(cfg.download),<span class="string">&quot;\&quot; &quot;</span>,\</span><br><span class="line">    <span class="string">&quot;\&quot;&quot;</span>,(cfg.logs),<span class="string">&quot;\&quot; &quot;</span>,\</span><br><span class="line">    <span class="string">&quot;\&quot;&quot;</span>,(cfg.session),<span class="string">&quot;\&quot; &quot;</span>,\</span><br><span class="line">    <span class="string">&quot;\&quot;&quot;</span>,(cfg.watch),<span class="string">&quot;/load\&quot; &quot;</span>,\</span><br><span class="line">    <span class="string">&quot;\&quot;&quot;</span>,(cfg.watch),<span class="string">&quot;/start\&quot; &quot;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">## Listening port for incoming peer traffic (fixed; you can also randomize it)</span></span><br><span class="line">network.port_range.<span class="keyword">set</span> = <span class="number">50000</span><span class="number">-50000</span></span><br><span class="line">network.port_random.<span class="keyword">set</span> = no</span><br><span class="line"></span><br><span class="line"><span class="meta">## Tracker-less torrent and UDP tracker support</span></span><br><span class="line"><span class="meta">## (conservative settings for &#x27;private&#x27; trackers, change for &#x27;public&#x27;)</span></span><br><span class="line">dht.mode.<span class="keyword">set</span> = disable</span><br><span class="line">protocol.pex.<span class="keyword">set</span> = no</span><br><span class="line"></span><br><span class="line">trackers.use_udp.<span class="keyword">set</span> = yes</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">## Peer settings</span></span><br><span class="line">throttle.max_uploads.<span class="keyword">set</span> = <span class="number">100</span></span><br><span class="line">throttle.max_uploads.global.<span class="keyword">set</span> = <span class="number">250</span></span><br><span class="line"></span><br><span class="line">throttle.global_up.max_rate.<span class="keyword">set</span> = <span class="number">131072</span></span><br><span class="line">throttle.global_down.max_rate.<span class="keyword">set</span> = <span class="number">2097152</span></span><br><span class="line"></span><br><span class="line">throttle.min_peers.normal.<span class="keyword">set</span> = <span class="number">20</span></span><br><span class="line">throttle.max_peers.normal.<span class="keyword">set</span> = <span class="number">60</span></span><br><span class="line">throttle.min_peers.seed.<span class="keyword">set</span> = <span class="number">30</span></span><br><span class="line">throttle.max_peers.seed.<span class="keyword">set</span> = <span class="number">80</span></span><br><span class="line">trackers.numwant.<span class="keyword">set</span> = <span class="number">80</span></span><br><span class="line"></span><br><span class="line">protocol.encryption.<span class="keyword">set</span> = allow_incoming,try_outgoing,enable_retry</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">## Limits for file handle resources, this is optimized for</span></span><br><span class="line"><span class="meta">## an `ulimit` of 1024 (a common default). You MUST leave</span></span><br><span class="line"><span class="meta">## a ceiling of handles reserved for rTorrent&#x27;s internal needs!</span></span><br><span class="line">network.http.max_open.<span class="keyword">set</span> = <span class="number">50</span></span><br><span class="line">network.max_open_files.<span class="keyword">set</span> = <span class="number">600</span></span><br><span class="line">network.max_open_sockets.<span class="keyword">set</span> = <span class="number">300</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">## Memory resource usage (increase if you have a large number of items loaded,</span></span><br><span class="line"><span class="meta">## and/or the available resources to spend)</span></span><br><span class="line">pieces.memory.max.<span class="keyword">set</span> = <span class="number">4096</span>M</span><br><span class="line">network.xmlrpc.size_limit.<span class="keyword">set</span> = <span class="number">2</span>M</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">## Basic operational settings (no need to change these)</span></span><br><span class="line">session.path.<span class="keyword">set</span> = (cat, (cfg.session))</span><br><span class="line">directory.<span class="keyword">default</span>.<span class="keyword">set</span> = (cat, (cfg.download))</span><br><span class="line">log.execute = (cat, (cfg.logs), <span class="string">&quot;execute.log&quot;</span>)</span><br><span class="line">log.xmlrpc = (cat, (cfg.logs), <span class="string">&quot;xmlrpc.log&quot;</span>)</span><br><span class="line">execute.nothrow = sh, -c, (cat, <span class="string">&quot;echo &gt;&quot;</span>,\</span><br><span class="line">    <span class="string">&quot;/tmp/rtorrent.pid&quot;</span>, <span class="string">&quot; &quot;</span>,(system.pid))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">## Other operational settings (check &amp; adapt)</span></span><br><span class="line">encoding.add = utf8</span><br><span class="line">system.umask.<span class="keyword">set</span> = <span class="number">0027</span></span><br><span class="line">system.cwd.<span class="keyword">set</span> = (directory.<span class="keyword">default</span>)</span><br><span class="line">network.http.dns_cache_timeout.<span class="keyword">set</span> = <span class="number">25</span></span><br><span class="line">schedule2 = monitor_diskspace, <span class="number">15</span>, <span class="number">60</span>, ((close_low_diskspace, <span class="number">1000</span>M))</span><br><span class="line"><span class="meta">#pieces.hash.on_completion.set = no</span></span><br><span class="line"><span class="meta">#view.sort_current = seeding, greater=d.ratio=</span></span><br><span class="line"><span class="meta">#keys.layout.set = qwerty</span></span><br><span class="line"><span class="meta">#network.http.capath.set = &quot;/etc/ssl/certs&quot;</span></span><br><span class="line"><span class="meta">#network.http.ssl_verify_peer.set = 0</span></span><br><span class="line"><span class="meta">#network.http.ssl_verify_host.set = 0</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">## Some additional values and commands</span></span><br><span class="line">method.insert = system.startup_time, value|<span class="keyword">const</span>, (system.time)</span><br><span class="line">method.insert = d.data_path, simple,\</span><br><span class="line">    <span class="string">&quot;if=(d.is_multi_file),\</span></span><br><span class="line"><span class="string">        (cat, (d.directory), /),\</span></span><br><span class="line"><span class="string">        (cat, (d.directory), /, (d.name))&quot;</span></span><br><span class="line">method.insert = d.session_file, simple, <span class="string">&quot;cat=(session.path), (d.hash), .torrent&quot;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">## Watch directories (add more as you like, but use unique schedule names)</span></span><br><span class="line"><span class="meta">## Add torrent</span></span><br><span class="line">schedule2 = watch_load, <span class="number">11</span>, <span class="number">10</span>, ((load.verbose, (cat, (cfg.watch), <span class="string">&quot;load/*.torrent&quot;</span>)))</span><br><span class="line"><span class="meta">## Add &amp; download straight away</span></span><br><span class="line">schedule2 = watch_start, <span class="number">10</span>, <span class="number">10</span>, ((load.start_verbose, (cat, (cfg.watch), <span class="string">&quot;start/*.torrent&quot;</span>)))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">## Run the rTorrent process as a daemon in the background</span></span><br><span class="line"><span class="meta">## (and control via XMLRPC sockets)</span></span><br><span class="line">system.daemon.<span class="keyword">set</span> = <span class="literal">false</span></span><br><span class="line"><span class="meta">#network.scgi.open_local =&quot;/tmp/rpc.socket&quot;</span></span><br><span class="line"><span class="meta">#execute.nothrow = chmod,770,&quot;/tmp/rpc.socket&quot;</span></span><br><span class="line">scgi_port = <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">5000</span></span><br><span class="line"></span><br><span class="line"><span class="meta">## Logging:</span></span><br><span class="line"><span class="meta">##   Levels = critical error warn notice info debug</span></span><br><span class="line"><span class="meta">##   Groups = connection_* dht_* peer_* rpc_* storage_* thread_* tracker_* torrent_*</span></span><br><span class="line">print = (cat, <span class="string">&quot;Logging to &quot;</span>, (cfg.logfile))</span><br><span class="line">log.open_file = <span class="string">&quot;log&quot;</span>, (cfg.logfile)</span><br><span class="line">log.add_output = <span class="string">&quot;notice&quot;</span>, <span class="string">&quot;log&quot;</span></span><br><span class="line"><span class="meta">#log.add_output = &quot;tracker_debug&quot;, &quot;log&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">### END of rtorrent.rc ###</span></span><br></pre></td></tr></table></figure><h2 id="配置自动启动"><a href="#配置自动启动" class="headerlink" title="配置自动启动"></a>配置自动启动</h2><p>将下方内容写入<code>/etc/systemd/system/rtorrent.service</code>，然后可以使用systemd在后台启动rtorrent，并可以通过<code>tmux a -t rtorrent</code>查看rtorrent的运行状态。</p><figure class="highlight ini"><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"><span class="section">[Unit]</span></span><br><span class="line"><span class="attr">Description</span>=rTorrent</span><br><span class="line"><span class="attr">Wants</span>=network-<span class="literal">on</span>line.target</span><br><span class="line"><span class="attr">After</span>=network-<span class="literal">on</span>line.target</span><br><span class="line"></span><br><span class="line"><span class="section">[Service]</span></span><br><span class="line"><span class="attr">User</span>=rtorrent</span><br><span class="line"><span class="attr">Group</span>=rtorrent</span><br><span class="line"><span class="attr">Type</span>=forking</span><br><span class="line"><span class="attr">PIDFile</span>=/tmp/rtorrent.pid</span><br><span class="line"><span class="attr">ExecStart</span>=/usr/bin/tmux new-session -s rtorrent -d /usr/local/bin/rtorrent</span><br><span class="line"><span class="attr">ExecStop</span>=/usr/bin/tmux send-keys -t rtorrent C-q</span><br><span class="line"></span><br><span class="line"><span class="section">[Install]</span></span><br><span class="line"><span class="attr">WantedBy</span>=multi-user.target</span><br></pre></td></tr></table></figure><h1 id="Flood-UI"><a href="#Flood-UI" class="headerlink" title="Flood-UI"></a>Flood-UI</h1><p>Flood-UI是一个现代化的rTorrent界面，用户可以使用web查看、操作rTorrent。</p><h2 id="安装依赖-2"><a href="#安装依赖-2" class="headerlink" title="安装依赖"></a>安装依赖</h2><p>安装依赖需要注意，Ubuntu包管理中的nodejs版本比较低，若想使用最新版本的nodejs，请参考以下链接进行安装：<a href="https://nodejs.org/en/download/package-manager/">NodeJS安装教程</a></p><figure class="highlight properties"><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"># 配置安装nodejs 13.x</span></span><br><span class="line"><span class="attr">curl</span> <span class="string">-sL https://deb.nodesource.com/setup_13.x | sudo -E bash -</span></span><br><span class="line"><span class="attr">sudo</span> <span class="string">apt-get install -y nodejs</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装其他依赖</span></span><br><span class="line"><span class="attr">sudo</span> <span class="string">apt install \</span></span><br><span class="line">            <span class="attr">git</span> <span class="string">\</span></span><br><span class="line">            <span class="meta">node-gyp</span> <span class="string">\</span></span><br><span class="line">            <span class="attr">mediainfo</span></span><br></pre></td></tr></table></figure><h2 id="下载源代码"><a href="#下载源代码" class="headerlink" title="下载源代码"></a>下载源代码</h2><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">git clone https://github.com/Flood-UI/flood.git</span><br></pre></td></tr></table></figure><h2 id="编译安装-2"><a href="#编译安装-2" class="headerlink" title="编译安装"></a>编译安装</h2><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></pre></td><td class="code"><pre><span class="line">cd flood</span><br><span class="line">npm install</span><br><span class="line">cp config.template.js config.js</span><br><span class="line">vim config.js # 根据需要编辑配置文件</span><br><span class="line">npm run-script build</span><br></pre></td></tr></table></figure><h2 id="启动flood"><a href="#启动flood" class="headerlink" title="启动flood"></a>启动flood</h2><p>Flood-UI的默认地址为<code>127.0.0.1:3000</code>，如果需要修改，请在<code>config.js</code>中配置。</p><figure class="highlight coffeescript"><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">npm</span> start</span><br></pre></td></tr></table></figure><p>运行命令后，在浏览器访问<code>127.0.0.1:3000</code>，之后再网页中配置用户名及密码，rtorrent的cgi地址默认是<code>127.0.0.1:5000</code>，改配置在<code>~/.rtorrent.rc</code>中配置。</p>]]></content>
    
    
    <summary type="html">&lt;h1 id=&quot;libtorrent&quot;&gt;&lt;a href=&quot;#libtorrent&quot; class=&quot;headerlink&quot; title=&quot;libtorrent&quot;&gt;&lt;/a&gt;libtorrent&lt;/h1&gt;&lt;h2 id=&quot;安装依赖&quot;&gt;&lt;a href=&quot;#安装依赖&quot; class=&quot;headerlink&quot; title=&quot;安装依赖&quot;&gt;&lt;/a&gt;安装依赖&lt;/h2&gt;&lt;figure class=&quot;highlight shell&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;sudo apt install &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            automake \&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            gcc \&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            g++ \&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            libtool \&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            pkg-config \&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            libcrypto++-dev \&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            libssl-dev&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&quot;下载源代码并解压&quot;&gt;&lt;a href=&quot;#下载源代码并解压&quot; class=&quot;headerlink&quot; title=&quot;下载源代码并解压&quot;&gt;&lt;/a&gt;下载源代码并解压&lt;/h2&gt;&lt;figure class=&quot;highlight shell&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;wget http://rtorrent.net/downloads/libtorrent-0.13.8.tar.gz&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;tar zxvf libtorrent-0.13.8.tar.gz&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;h2 id=&quot;编译安装&quot;&gt;&lt;a href=&quot;#编译安装&quot; class=&quot;headerlink&quot; title=&quot;编译安装&quot;&gt;&lt;/a&gt;编译安装&lt;/h2&gt;&lt;figure class=&quot;highlight shell&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;cd libtorrent-0.13.8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;./autogen.sh&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;./configure&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;make -j4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;sudo make install&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;</summary>
    
    
    
    <category term="环境搭建" scheme="https://blog.wanghaoyu.cn/categories/%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/"/>
    
    
    <category term="Ubuntu" scheme="https://blog.wanghaoyu.cn/tags/Ubuntu/"/>
    
    <category term="rTorrent" scheme="https://blog.wanghaoyu.cn/tags/rTorrent/"/>
    
    <category term="PT" scheme="https://blog.wanghaoyu.cn/tags/PT/"/>
    
  </entry>
  
  <entry>
    <title>Nginx启用gzip功能</title>
    <link href="https://blog.wanghaoyu.cn/2018/07/19/nginx-gzip/"/>
    <id>https://blog.wanghaoyu.cn/2018/07/19/nginx-gzip/</id>
    <published>2018-07-18T16:22:05.000Z</published>
    <updated>2024-02-27T06:16:02.140Z</updated>
    
    <content type="html"><![CDATA[<p>启用gzip只需要在相应的server或者location中添加以下参数：</p><figure class="highlight awk"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 开启gzip</span></span><br><span class="line">gzip on;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启用gzip压缩的最小文件，小于设置值的文件将不会压缩</span></span><br><span class="line">gzip_min_length <span class="number">1</span>k;</span><br><span class="line"></span><br><span class="line"><span class="comment"># gzip 压缩级别，1-10，数字越大压缩的越好，也越占用CPU时间，后面会有详细说明</span></span><br><span class="line">gzip_comp_level <span class="number">6</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 进行压缩的文件类型。javascript有多种形式。其中的值可以在 mime.types 文件中找到。</span></span><br><span class="line">gzip_types text<span class="regexp">/plain application/</span>javascript application<span class="regexp">/x-javascript text/</span>css application<span class="regexp">/xml text/</span>javascript application<span class="regexp">/x-httpd-php image/</span>jpeg image<span class="regexp">/gif image/</span>png font<span class="regexp">/ttf font/</span>otf image/svg+xml;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 是否在http header中添加Vary: Accept-Encoding，建议开启</span></span><br><span class="line">gzip_vary on;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 禁用IE 6 gzip</span></span><br><span class="line">gzip_disable <span class="string">&quot;MSIE [1-6]\.&quot;</span>;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;启用gzip只需要在相应的server或者location中添加以下参数：&lt;/p&gt;
&lt;figure class=&quot;highlight awk&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br</summary>
      
    
    
    
    <category term="知识点" scheme="https://blog.wanghaoyu.cn/categories/%E7%9F%A5%E8%AF%86%E7%82%B9/"/>
    
    
    <category term="Nginx" scheme="https://blog.wanghaoyu.cn/tags/Nginx/"/>
    
    <category term="Gzip" scheme="https://blog.wanghaoyu.cn/tags/Gzip/"/>
    
  </entry>
  
  <entry>
    <title>Git保存用户名密码</title>
    <link href="https://blog.wanghaoyu.cn/2018/07/19/git-http-passwrod/"/>
    <id>https://blog.wanghaoyu.cn/2018/07/19/git-http-passwrod/</id>
    <published>2018-07-18T16:00:38.000Z</published>
    <updated>2024-02-27T06:16:02.110Z</updated>
    
    <content type="html"><![CDATA[<p>使用下面的命令，可以让GIT将用户名和密码信息保存在硬盘上。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --global credential.helper store</span><br></pre></td></tr></table></figure><p>如果只想保存某个仓库的用户名密码信息，可以去掉<code>--global</code>参数。</p><p>需要注意的是，该密码是明文存储，保存在<code>~/.git-credentials</code>文件中。<br>该文件内容类似于一下方式：</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">https:<span class="regexp">//u</span>ser:pass@example.com</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;使用下面的命令，可以让GIT将用户名和密码信息保存在硬盘上。&lt;/p&gt;
&lt;figure class=&quot;highlight sh&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;</summary>
      
    
    
    
    <category term="知识点" scheme="https://blog.wanghaoyu.cn/categories/%E7%9F%A5%E8%AF%86%E7%82%B9/"/>
    
    
    <category term="Git" scheme="https://blog.wanghaoyu.cn/tags/Git/"/>
    
  </entry>
  
  <entry>
    <title>浣熊的工具箱</title>
    <link href="https://blog.wanghaoyu.cn/2018/06/19/roccoon-tools/"/>
    <id>https://blog.wanghaoyu.cn/2018/06/19/roccoon-tools/</id>
    <published>2018-06-19T04:19:33.000Z</published>
    <updated>2024-02-27T06:16:02.140Z</updated>
    
    <content type="html"><![CDATA[<h2 id="日期文本转换"><a href="#日期文本转换" class="headerlink" title="日期文本转换"></a>日期文本转换</h2><h3 id="文本-Rightarrow-日期"><a href="#文本-Rightarrow-日期" class="headerlink" title="文本 $\Rightarrow$ 日期"></a>文本 $\Rightarrow$ 日期</h3><p>可以使用<code>DATE(year, month, day)</code>函数来实现，具体如下：</p><table><thead><tr><th></th><th>A</th><th>B</th></tr></thead><tbody><tr><td>1</td><td>20170102</td><td>=DATE(LEFT(A1, 4), MID(A1, 5, 2), RIGHT(A1, 2))</td></tr></tbody></table><p>结果为：</p><table><thead><tr><th></th><th>A</th><th>B</th></tr></thead><tbody><tr><td>1</td><td>20170102</td><td>2017/01/02</td></tr></tbody></table><p>备注：</p><ul><li><code>LEFT(text, [num_chars])</code>表示从text中选取最__左侧__的num_chars个字符。</li><li><code>RIGHT(text, [num_chars])</code>表示从text中选取最__右侧__的num_chars个字符。</li><li><code>MID(text, start_num, num_chars)</code>表示从start_num开始返回num_chars个字符。</li><li>可以点击<a href="https://support.office.com/zh-cn/article/DATE-%E5%87%BD%E6%95%B0-E36C0C8C-4104-49DA-AB83-82328B832349">这里</a>查看关于__DATE函数__的更多用法。</li></ul><h3 id="日期-Rightarrow-文本"><a href="#日期-Rightarrow-文本" class="headerlink" title="日期 $\Rightarrow$ 文本"></a>日期 $\Rightarrow$ 文本</h3><p>可以使用<code>TEXT(text, format)</code>函数来实现，具体如下：</p><table><thead><tr><th></th><th>A</th><th>B</th></tr></thead><tbody><tr><td>1</td><td>2017-01-02</td><td>=TEXT(A1, “yyyymmdd”)</td></tr></tbody></table><p>结果为：</p><table><thead><tr><th></th><th>A</th><th>B</th></tr></thead><tbody><tr><td>1</td><td>2017-01-02</td><td>20170102</td></tr></tbody></table><p>备注：可以点击<a href="https://support.office.com/zh-cn/article/TEXT-%E5%87%BD%E6%95%B0-20D5AC4D-7B94-49FD-BB38-93D29371225C">这里</a>查看关于<strong>TEXT函数</strong>的更多用法。</p><a id="more"></a><h2 id="日期计算"><a href="#日期计算" class="headerlink" title="日期计算"></a>日期计算</h2><h3 id="加减月份"><a href="#加减月份" class="headerlink" title="加减月份"></a>加减月份</h3><p>使用<code>EDATE(start_date, months)</code>函数可以计算某个日期(start_date)之前、之后几个月的日期。</p><table><thead><tr><th></th><th>A</th><th>B</th><th>C</th></tr></thead><tbody><tr><td>1</td><td>2017-01-02</td><td>5</td><td>=EDATE(A1, B1)</td></tr></tbody></table><p>结果为：</p><table><thead><tr><th></th><th>A</th><th>B</th><th>C</th></tr></thead><tbody><tr><td>1</td><td>2017-01-02</td><td>5</td><td>2017-06-02</td></tr></tbody></table><p>备注：</p><ul><li>要求start_date必须是日期，如果不是日期，可以通过<a href="#%E6%97%A5%E6%9C%9F%E6%96%87%E6%9C%AC%E8%BD%AC%E6%8D%A2">这里</a>进行转换。</li><li>EDATE的输出也是日期格式。</li><li>可以点击<a href="https://support.office.com/zh-cn/article/EDATE-%E5%87%BD%E6%95%B0-3c920eb2-6e66-44e7-a1f5-753ae47ee4f5">这里</a>查看关于<strong>EDATE函数</strong>的更多用法。</li></ul>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;日期文本转换&quot;&gt;&lt;a href=&quot;#日期文本转换&quot; class=&quot;headerlink&quot; title=&quot;日期文本转换&quot;&gt;&lt;/a&gt;日期文本转换&lt;/h2&gt;&lt;h3 id=&quot;文本-Rightarrow-日期&quot;&gt;&lt;a href=&quot;#文本-Rightarrow-日期&quot; class=&quot;headerlink&quot; title=&quot;文本 $\Rightarrow$ 日期&quot;&gt;&lt;/a&gt;文本 $\Rightarrow$ 日期&lt;/h3&gt;&lt;p&gt;可以使用&lt;code&gt;DATE(year, month, day)&lt;/code&gt;函数来实现，具体如下：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;A&lt;/th&gt;
&lt;th&gt;B&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;20170102&lt;/td&gt;
&lt;td&gt;=DATE(LEFT(A1, 4), MID(A1, 5, 2), RIGHT(A1, 2))&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;结果为：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;A&lt;/th&gt;
&lt;th&gt;B&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;20170102&lt;/td&gt;
&lt;td&gt;2017/01/02&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;备注：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;LEFT(text, [num_chars])&lt;/code&gt;表示从text中选取最__左侧__的num_chars个字符。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RIGHT(text, [num_chars])&lt;/code&gt;表示从text中选取最__右侧__的num_chars个字符。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MID(text, start_num, num_chars)&lt;/code&gt;表示从start_num开始返回num_chars个字符。&lt;/li&gt;
&lt;li&gt;可以点击&lt;a href=&quot;https://support.office.com/zh-cn/article/DATE-%E5%87%BD%E6%95%B0-E36C0C8C-4104-49DA-AB83-82328B832349&quot;&gt;这里&lt;/a&gt;查看关于__DATE函数__的更多用法。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;日期-Rightarrow-文本&quot;&gt;&lt;a href=&quot;#日期-Rightarrow-文本&quot; class=&quot;headerlink&quot; title=&quot;日期 $\Rightarrow$ 文本&quot;&gt;&lt;/a&gt;日期 $\Rightarrow$ 文本&lt;/h3&gt;&lt;p&gt;可以使用&lt;code&gt;TEXT(text, format)&lt;/code&gt;函数来实现，具体如下：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;A&lt;/th&gt;
&lt;th&gt;B&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2017-01-02&lt;/td&gt;
&lt;td&gt;=TEXT(A1, “yyyymmdd”)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;结果为：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;A&lt;/th&gt;
&lt;th&gt;B&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2017-01-02&lt;/td&gt;
&lt;td&gt;20170102&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;备注：可以点击&lt;a href=&quot;https://support.office.com/zh-cn/article/TEXT-%E5%87%BD%E6%95%B0-20D5AC4D-7B94-49FD-BB38-93D29371225C&quot;&gt;这里&lt;/a&gt;查看关于&lt;strong&gt;TEXT函数&lt;/strong&gt;的更多用法。&lt;/p&gt;</summary>
    
    
    
    <category term="斐" scheme="https://blog.wanghaoyu.cn/categories/%E6%96%90/"/>
    
    
    <category term="Office" scheme="https://blog.wanghaoyu.cn/tags/Office/"/>
    
  </entry>
  
  <entry>
    <title>容量缺失(Capacity Miss)与冲突缺失(Conflict Miss)的区别</title>
    <link href="https://blog.wanghaoyu.cn/2017/12/18/cache-miss-type/"/>
    <id>https://blog.wanghaoyu.cn/2017/12/18/cache-miss-type/</id>
    <published>2017-12-18T08:07:12.000Z</published>
    <updated>2024-02-27T06:16:02.110Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Cache缺失分类"><a href="#Cache缺失分类" class="headerlink" title="Cache缺失分类"></a>Cache缺失分类</h2><p>Cache的缺失主要分为<strong>强制缺失(Compulsory Miss)**、</strong>容量缺失(Capacity Miss)<strong>以及</strong>冲突缺失(Conflict Miss)<strong>，其中，强制缺失又称为</strong>冷缺失(Cold Miss)**。</p><h2 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h2><ul><li>强制缺失：CPU第一次访问相应Cache块，Cache中肯定没有该Cache块，引起的缺失叫做强制缺失。这是<strong>不可避免的</strong>。</li><li>容量缺失：有限的Cache容量导致Cache放不下而替换出Cache块，被替换出去的Cache块<strong>再被访问</strong>，引起的失效叫做容量失效。</li><li>冲突缺失：在直接相联或组相联的Cache中，不同的Cache块由于Index相同相互替换，引起的失效叫做冲突失效。</li></ul><h2 id="强制缺失"><a href="#强制缺失" class="headerlink" title="强制缺失"></a>强制缺失</h2><p>在Cache的三种缺失中，强制缺失非常好区分，只要是CPU<strong>第一次</strong>访问相应的Cache块引起的缺失，就称作强制缺失。</p><h2 id="容量缺失"><a href="#容量缺失" class="headerlink" title="容量缺失"></a>容量缺失</h2><p>在直接映射或组相联Cache的情况下，若该Cache行被替换出去时，<strong>Cache已经满了</strong>，就是容量缺失。</p><p>举一个例子说明一下：<br>    如果有一个64KB大小的数组需要重复访问，而Cache大小只有16KB，这时数组的大小远远大于Cache大小，没办法全部放入Cache。第一次访问数组发生的失效是强制缺失。而之后再访问数组，再发生的缺失则全都是容量缺失，这时Cache已经<strong>存满</strong>，容量不足以存储全部数据。</p><h2 id="冲突缺失"><a href="#冲突缺失" class="headerlink" title="冲突缺失"></a>冲突缺失</h2><p>在直接映射或组相联Cache的情况下，若该Cache行被替换出去时，<strong>还有其他空闲的Cache行</strong>，则是冲突缺失。</p><p>同样也举一个例子说明：<br>    如果有两个8KB大小的数据需要来回访问，但是这两个数组都映射到相同的地址，Cache的大小为16KB，足够存储全部的数据，但是因为相同地址发生了冲突需要来回替换，发生的缺失则全都是冲突缺失（第一次访问失效依旧是强制缺失），这时Cache并<strong>没有存满</strong>。</p><h2 id="降低Cache缺失的方法"><a href="#降低Cache缺失的方法" class="headerlink" title="降低Cache缺失的方法"></a>降低Cache缺失的方法</h2><ul><li>强制缺失：无法避免。</li><li>容量缺失：增加Cache大小。</li><li>冲突缺失：增加Cache相联度、优化替换策略、增大Cache大小等等。</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;Cache缺失分类&quot;&gt;&lt;a href=&quot;#Cache缺失分类&quot; class=&quot;headerlink&quot; title=&quot;Cache缺失分类&quot;&gt;&lt;/a&gt;Cache缺失分类&lt;/h2&gt;&lt;p&gt;Cache的缺失主要分为&lt;strong&gt;强制缺失(Compulsory Miss)*</summary>
      
    
    
    
    <category term="知识点" scheme="https://blog.wanghaoyu.cn/categories/%E7%9F%A5%E8%AF%86%E7%82%B9/"/>
    
    
    <category term="Cache" scheme="https://blog.wanghaoyu.cn/tags/Cache/"/>
    
  </entry>
  
  <entry>
    <title>并发程序设计思路</title>
    <link href="https://blog.wanghaoyu.cn/2017/12/16/ticketing-system/"/>
    <id>https://blog.wanghaoyu.cn/2017/12/16/ticketing-system/</id>
    <published>2017-12-16T15:35:40.000Z</published>
    <updated>2024-02-27T06:16:02.150Z</updated>
    
    <content type="html"><![CDATA[<h2 id="实验目的"><a href="#实验目的" class="headerlink" title="实验目的"></a>实验目的</h2><p>设计并实现用于列车售票的可线性化并发数据结构。</p><h2 id="实验说明"><a href="#实验说明" class="headerlink" title="实验说明"></a>实验说明</h2><p>给定Ticket类：</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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Ticket</span> </span>&#123;</span><br><span class="line">    <span class="keyword">long</span> tid;           <span class="comment">// 车票编号</span></span><br><span class="line">    String passenger;   <span class="comment">// 乘客名字</span></span><br><span class="line">    <span class="keyword">int</span> route;          <span class="comment">// 列车车次</span></span><br><span class="line">    <span class="keyword">int</span> coach;          <span class="comment">// 车厢号</span></span><br><span class="line">    <span class="keyword">int</span> seat;           <span class="comment">// 座位号</span></span><br><span class="line">    <span class="keyword">int</span> departure;      <span class="comment">// 出发站编号</span></span><br><span class="line">    <span class="keyword">int</span> arrival;        <span class="comment">// 到达站编号</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>给定TicketingSystem接口：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">TicketingSystem</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 买票，即乘客passenger购买route车次从departure站到arrival站的车票一张。若购买成功，返回有效的Ticket对象；若失败(无余票)，则返回null</span></span><br><span class="line">    <span class="function">Ticket <span class="title">buyTicket</span><span class="params">(String passenger, <span class="keyword">int</span> route, <span class="keyword">int</span> departure, <span class="keyword">int</span> arrival)</span></span>;</span><br><span class="line">    <span class="comment">// 查询余票，即查询route车次从departure站到arrival站的余票数</span></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">inquiry</span><span class="params">(<span class="keyword">int</span> route, <span class="keyword">int</span> departure, <span class="keyword">int</span> arrival)</span></span>;</span><br><span class="line">    <span class="comment">// 退票，对有效的Ticket对象返回true，对无效的Ticket返回false</span></span><br><span class="line">    <span class="function"><span class="keyword">boolean</span> <span class="title">refundTicket</span><span class="params">(Ticket ticket)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>给定TicketingDS类的构造器接口：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">TicketingDS(routenum, coachnum, seatnum, stationnum, threadnum)</span><br></pre></td></tr></table></figure><p>其中：route是车次总数（缺省为5），coachnum是列车车厢数目（缺省为8），seatnum是每节车厢的座位数（缺省为100），stationnum是每个车次经停站数量（缺省为10，含始发站和终点站），threadnum是并发购票的线程数（缺省为16）。</p><a id="more"></a><p><strong>说明：</strong></p><p>为简单起见，假设每个车次的coachnum、seatnum和stationnum都相同。车票涉及的各项参数均从1开始计数，例如车厢从1到8编号，车站从1到10编号等。</p><p>系统中同时存在threadnum个线程（缺省为16），每个线程是一个票务代理，按照<strong>60%**查询余票，</strong>30%<strong>购票和</strong>10%**退票的比率反复调用TicketingDS类的三种方法若干次（缺省为每个线程10000次）。按照线程数为4、8、16、32、64个的情况，分别给出每种方法调用的平均执行时间，同时计算系统的总吞吐率（单位时间内完成的方法调用总数）。</p><h2 id="正确性要求"><a href="#正确性要求" class="headerlink" title="正确性要求"></a>正确性要求</h2><ul><li>每张车票都有一个唯一的你编号tid，不能重复。</li><li>每一个tid的车票只能出售一次。退票后，原车票的tid作废。</li><li>每个区段有余票时，系统必须满足该区段的购票请求。</li><li>车票不能超卖，系统不能卖无座车票。</li><li>查询结果允许不精确，但是在某个车次查票过程中没有其他购票和退票的<strong>“静止”</strong>状态下，该车次查询余票的结果必须准确。</li></ul><h2 id="设计思路"><a href="#设计思路" class="headerlink" title="设计思路"></a>设计思路</h2><p>设计思路按照并发程序的基本设计思路来逐步实现。</p><p>设计并发程序的基本思路分别是<strong>粗粒度锁</strong>、<strong>细粒度锁</strong>、<strong>乐观锁</strong>和**无锁(lock-free)**四个阶段。这四个阶段的难度也逐步递增，基本上来说，达到乐观锁的性能就已经不错了。</p><p>特别感谢<strong>王鹿鸣</strong>同学提供的部分优化思路。</p><h2 id="粗粒度锁-Coarse-lock"><a href="#粗粒度锁-Coarse-lock" class="headerlink" title="粗粒度锁(Coarse-lock)"></a>粗粒度锁(Coarse-lock)</h2><h3 id="主要数据结构"><a href="#主要数据结构" class="headerlink" title="主要数据结构"></a>主要数据结构</h3><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Train</span> </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">boolean</span>[][] coachs;  <span class="comment">// 表示车座状态</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">int</span> seatNum;         <span class="comment">// 表示列车座位的总数</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">int</span> coachNum;        <span class="comment">// 表示车厢数</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">int</span> stationNum;      <span class="comment">// 表示车站数目</span></span><br><span class="line">    <span class="keyword">private</span> ReentrantLock lock; <span class="comment">// 列车锁，可重入锁</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>说明：用一个二维数组表示列车每个座位以及座位在每站之间的使用情况。<code>coachs[i][j] == false</code>表示火车的第i个座位在第j到j+1站之间，没有卖出。</p><h3 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h3><p>由于列车之间没有相关性，因此，可以给每个火车一把锁。<br>当购票和查询时，先获得该列车的锁，然后再遍历数组，修改某个座位的状态，最后释放锁。<br>由于退票不需要遍历数组，但是，依然要先获得锁，再修改座位的状态，最后释放锁。</p><p>需要注意的是，在修改座位状态时，<strong>一定要确认，修改前的状态是否符合你的预期，</strong>这样才能保证程序的<strong>正确性</strong>；若不符合，应返回false。如果没有验证座位的状态，会出现车票超售或者退票失败的情况。</p><h2 id="细粒度锁-Fine-lock"><a href="#细粒度锁-Fine-lock" class="headerlink" title="细粒度锁(Fine-lock)"></a>细粒度锁(Fine-lock)</h2><h3 id="主要数据结构-1"><a href="#主要数据结构-1" class="headerlink" title="主要数据结构"></a>主要数据结构</h3><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Train</span> </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">boolean</span>[][] coachs;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">int</span> seatNum;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">int</span> coachNum;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">int</span> stationNum;</span><br><span class="line">    <span class="keyword">private</span> ReentrantLock[] locks;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>与粗粒度锁相比，增加了锁的数量，每个座位有自己的一把锁。</p><h3 id="思路-1"><a href="#思路-1" class="headerlink" title="思路"></a>思路</h3><p>将锁进一步细化，给火车的每一个座位设置一把锁。<br>当遍历coachs数组时，要先获得该座位的锁，然后再去访问该座位的内容。<br>由于使用的是数组存储，因此不需要交叉手这样的方式加锁。</p><p>与粗粒度锁一样，在修改状态前，也要检查座位的状态是否符合预期。</p><h2 id="乐观锁-Optimistic-lock"><a href="#乐观锁-Optimistic-lock" class="headerlink" title="乐观锁(Optimistic-lock)"></a>乐观锁(Optimistic-lock)</h2><h3 id="主要数据结构-2"><a href="#主要数据结构-2" class="headerlink" title="主要数据结构"></a>主要数据结构</h3><p>乐观锁的数据结构与细粒度锁一致。</p><h3 id="思路-2"><a href="#思路-2" class="headerlink" title="思路"></a>思路</h3><p>乐观锁认为，在对共享变量操作时，乐观的认为，<strong>读操作与写操作同时发生的概率非常小</strong>，因此，在读取变量时不需要加锁。<br>因此，乐观锁的退票与细粒度锁一样，不需要修改。而在查询操作中，完全不加锁，直接遍历一遍。</p><p>在购票过程中，在遍历数组时，不需要获得锁，只有在找到合适的、符合要求的未出售的座位时，才尝试获得锁。<br>获得锁后，<strong><em>一定要再次确认座位的状态是否符合预期</em></strong>。因为，在加锁过程中原本符合预期的状态，可能被其他线程修改了。因此要<strong>再次确认</strong>！</p><h3 id="改进思路"><a href="#改进思路" class="headerlink" title="改进思路"></a>改进思路</h3><p>由于座位在每个站的状态使用boolean数组表示的，因此，可以尝试改用BitSet来表示。</p><h2 id="无锁-Lock-free"><a href="#无锁-Lock-free" class="headerlink" title="无锁(Lock-free)"></a>无锁(Lock-free)</h2><h3 id="主要数据结构-3"><a href="#主要数据结构-3" class="headerlink" title="主要数据结构"></a>主要数据结构</h3><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Train</span> </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> AtomicBitSet[] coachs;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">int</span> seatNum;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">int</span> coachNum;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">int</span> stationNum;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="思路-3"><a href="#思路-3" class="headerlink" title="思路"></a>思路</h3><p>由乐观锁的改进思路演化而来，将座位设置成原子对象(Atomic)，在修改车座状态时，通过CAS(CompareAndSet)方法操作。</p><p>由于车站数量不会过多，即<code>stationnum &lt; 64</code>，所以，可以使用一个Long代替BitSet。<br>通过自己编写AtomicBitSet，减轻额外负担，可以提高性能。</p><h3 id="改进思路-1"><a href="#改进思路-1" class="headerlink" title="改进思路"></a>改进思路</h3><p>为了防止伪共享，可以在AtomicBitSet后面添加padding，使得一个Cache Line中只有一个AtomicLong对象，降低Cache的乒乓效应。</p><h2 id="对已售车票的保存"><a href="#对已售车票的保存" class="headerlink" title="对已售车票的保存"></a>对已售车票的保存</h2><ul><li><p>ConcurrentLinkedDeque链表存储已售车票。但是发现在无锁结构下，该链表成为瓶颈。</p></li><li><p>ConcurrentHashMap存储已售车票，由于Hash表结构非常适合并发进程，因此，性能得到提升。<br>  PS: ConcurrentHashMap的contains(key)方法的效率远高于containsValue(value)方法。</p></li></ul><h2 id="实验代码"><a href="#实验代码" class="headerlink" title="实验代码"></a>实验代码</h2><p>代码地址：<a href="https://gitee.com/873314461/ticketing-system">https://gitee.com/873314461/ticketing-system</a></p><script src="//gitee.com/873314461/ticketing-system/widget_preview"></script><style>.pro_name a{color: #4183c4;}.osc_git_title{background-color: #fff;}.osc_git_box{background-color: #fff;}.osc_git_box{border-color: #E3E9ED;}.osc_git_info{color: #666;}.osc_git_main a{color: #9B9B9B;}</style><h2 id="正确性分析"><a href="#正确性分析" class="headerlink" title="正确性分析"></a>正确性分析</h2><h3 id="车票编号tid"><a href="#车票编号tid" class="headerlink" title="车票编号tid"></a>车票编号tid</h3><p>车票的id使用一个AtomicInteger实现，保证了多个线程同时调用getAndIncrement()时，tid不会重复。</p><h3 id="买票的正确性"><a href="#买票的正确性" class="headerlink" title="买票的正确性"></a>买票的正确性</h3><p>在买票时，遍历车座，比较每一个座位是否符合购票条件，若符合条件，则锁定座位，并出票；否则返回null。</p><ul><li>粗粒度锁：保证每列<strong>火车</strong>只有<strong>一个</strong>线程在<strong>买票</strong>、<strong>退票</strong>或者<strong>查询</strong>，因此，不会出现超售或者有座位但不出票的情况。</li><li>细粒度锁：保证每个<strong>座位</strong>只有<strong>一个</strong>线程在<strong>买票</strong>、<strong>退票</strong>或者<strong>查询</strong>，每个座位的状态在某个时刻是唯一的，保证正确性。</li><li>乐观锁：保证每个<strong>座位</strong>只有<strong>一个</strong>线程在<strong>买票</strong>或者<strong>退票</strong>，由于读操作不会修改座位状态，并且，加锁后会再次确认状态，因此，在修改时，每个座位的状态是确定的，从而保证正确性。</li><li>无锁：通过<strong>CAS</strong>方式，保证每个座位状态修改时，都是符合期望的，从而保证正确性。</li></ul><h3 id="查询车票的静态一致性"><a href="#查询车票的静态一致性" class="headerlink" title="查询车票的静态一致性"></a>查询车票的静态一致性</h3><p>在车票查询过程中，只需要保证静态一致性即可。</p><p>静态一致性要求：由一系列静止状态分隔开的方法调用应呈现出与按照它们的实时调用次序相同的执行效果。</p><p>因此只要保证处于静止状态时，统计的车票数是准确的即可。因此，可以直接遍历数组，判断每一个座位的状态即可保证静态一致性。</p><h2 id="性能分析"><a href="#性能分析" class="headerlink" title="性能分析"></a>性能分析</h2><h3 id="测试环境"><a href="#测试环境" class="headerlink" title="测试环境"></a>测试环境</h3><p>使用老师提供的服务器：80核、160线程、256GB内存，单核1.0GHz，jdk 1.7。</p><p>共有5个车次，每个车次10个站点（包括始发站和终点站），每趟列车8个车厢，每节车厢100个座位。</p><p>买票、退票和查询的比例为：30%、10%和60%。</p><p>方法调用总次数为：线程数(threadnum) * 1000000。</p><h3 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h3><p>整体来看，无锁(Lock-Free) &gt; 乐观锁(Optimistic-Lock) &gt; 粗粒度锁(Coarse-Lock) &gt; 细粒度锁(Fine-Lock)。<br>吞吐量如图所示：</p><p><img src="/.cn//01.png"></p><p>在通常情况下，细粒度锁的吞吐量效果应该优于粗粒度锁的，但是因为本题目数据结构的特殊性（数组），使得粗粒度锁的效果优于细粒度锁。</p><p>这种现象与链表相似，由于在遍历数组时，每一步都要加锁，会阻塞后面的线程，无法超车，所有线程排队遍历数组，导致效率降低；又因为加锁的额外开销，导致性能低于粗粒度锁。</p><p>对于买票、退票和查询的时间对比如下：</p><p><img src="/.cn//02.png"></p><p><img src="/.cn//03.png"></p><p><img src="/.cn//04.png"></p><p>我们发现，对于粗粒度锁和细粒度锁(退票方法除外)来说，方法调用的耗时基本上与线程数成线性关系。</p><h3 id="粗粒度锁"><a href="#粗粒度锁" class="headerlink" title="粗粒度锁"></a>粗粒度锁</h3><table><thead><tr><th align="center">ThreadNum</th><th align="left">Total Execution Time(ms)</th><th align="left">Throughput Rate(kop/s)</th><th align="left">Query AVE Time(ns)</th><th align="left">Buy AVE Time(ns)</th><th align="left">Refund AVE Time(ns)</th></tr></thead><tbody><tr><td align="center">1</td><td align="left">6469</td><td align="left">155</td><td align="left">7388</td><td align="left">4965</td><td align="left">767</td></tr><tr><td align="center">2</td><td align="left">9925</td><td align="left">202</td><td align="left">10964</td><td align="left">8434</td><td align="left">4087</td></tr><tr><td align="center">4</td><td align="left">28808</td><td align="left">139</td><td align="left">30037</td><td align="left">27598</td><td align="left">15647</td></tr><tr><td align="center">8</td><td align="left">47171</td><td align="left">170</td><td align="left">48256</td><td align="left">46116</td><td align="left">34296</td></tr><tr><td align="center">16</td><td align="left">86127</td><td align="left">186</td><td align="left">86554</td><td align="left">85520</td><td align="left">73857</td></tr><tr><td align="center">32</td><td align="left">147005</td><td align="left">218</td><td align="left">146173</td><td align="left">147348</td><td align="left">137933</td></tr><tr><td align="center">64</td><td align="left">353209</td><td align="left">181</td><td align="left">350689</td><td align="left">355260</td><td align="left">348310</td></tr></tbody></table><h3 id="细粒度锁"><a href="#细粒度锁" class="headerlink" title="细粒度锁"></a>细粒度锁</h3><table><thead><tr><th align="center">ThreadNum</th><th align="left">Total Execution Time(ms)</th><th align="left">Throughput Rate(kop/s)</th><th align="left">Query AVE Time(ns)</th><th align="left">Buy AVE Time(ns)</th><th align="left">Refund AVE Time(ns)</th></tr></thead><tbody><tr><td align="center">1</td><td align="left">17963</td><td align="left">56</td><td align="left">21619</td><td align="left">14618</td><td align="left">953</td></tr><tr><td align="center">2</td><td align="left">25675</td><td align="left">78</td><td align="left">31244</td><td align="left">21151</td><td align="left">1412</td></tr><tr><td align="center">4</td><td align="left">43890</td><td align="left">91</td><td align="left">52795</td><td align="left">36645</td><td align="left">2318</td></tr><tr><td align="center">8</td><td align="left">71095</td><td align="left">113</td><td align="left">85635</td><td align="left">60442</td><td align="left">2811</td></tr><tr><td align="center">16</td><td align="left">99502</td><td align="left">161</td><td align="left">119038</td><td align="left">85477</td><td align="left">4217</td></tr><tr><td align="center">32</td><td align="left">197168</td><td align="left">162</td><td align="left">229679</td><td align="left">178344</td><td align="left">10098</td></tr><tr><td align="center">64</td><td align="left">587544</td><td align="left">109</td><td align="left">686789</td><td align="left">553489</td><td align="left">17677</td></tr></tbody></table><h3 id="乐观锁"><a href="#乐观锁" class="headerlink" title="乐观锁"></a>乐观锁</h3><table><thead><tr><th align="center">ThreadNum</th><th align="left">Total Execution Time(ms)</th><th align="left">Throughput Rate(kop/s)</th><th align="left">Query AVE Time(ns)</th><th align="left">Buy AVE Time(ns)</th><th align="left">Refund AVE Time(ns)</th></tr></thead><tbody><tr><td align="center">1</td><td align="left">5862</td><td align="left">171</td><td align="left">6603</td><td align="left">4459</td><td align="left">861</td></tr><tr><td align="center">2</td><td align="left">6045</td><td align="left">331</td><td align="left">6718</td><td align="left">4929</td><td align="left">1501</td></tr><tr><td align="center">4</td><td align="left">6219</td><td align="left">643</td><td align="left">6875</td><td align="left">5251</td><td align="left">1897</td></tr><tr><td align="center">8</td><td align="left">6506</td><td align="left">1230</td><td align="left">6929</td><td align="left">5368</td><td align="left">3070</td></tr><tr><td align="center">16</td><td align="left">7942</td><td align="left">2015</td><td align="left">7928</td><td align="left">6699</td><td align="left">5125</td></tr><tr><td align="center">32</td><td align="left">11720</td><td align="left">2730</td><td align="left">9865</td><td align="left">9476</td><td align="left">8218</td></tr><tr><td align="center">64</td><td align="left">22591</td><td align="left">2833</td><td align="left">15138</td><td align="left">15422</td><td align="left">13319</td></tr></tbody></table><h3 id="无锁"><a href="#无锁" class="headerlink" title="无锁"></a>无锁</h3><table><thead><tr><th align="center">ThreadNum</th><th align="left">Total Execution Time(ms)</th><th align="left">Throughput Rate(kop/s)</th><th align="left">Query AVE Time(ns)</th><th align="left">Buy AVE Time(ns)</th><th align="left">Refund AVE Time(ns)</th></tr></thead><tbody><tr><td align="center">1</td><td align="left">3250</td><td align="left">308</td><td align="left">3436</td><td align="left">2039</td><td align="left">774</td></tr><tr><td align="center">2</td><td align="left">3230</td><td align="left">619</td><td align="left">3518</td><td align="left">2247</td><td align="left">934</td></tr><tr><td align="center">4</td><td align="left">3582</td><td align="left">1117</td><td align="left">3822</td><td align="left">2619</td><td align="left">1180</td></tr><tr><td align="center">8</td><td align="left">3590</td><td align="left">2228</td><td align="left">3714</td><td align="left">2468</td><td align="left">1010</td></tr><tr><td align="center">16</td><td align="left">5407</td><td align="left">2959</td><td align="left">5038</td><td align="left">3883</td><td align="left">1483</td></tr><tr><td align="center">32</td><td align="left">10566</td><td align="left">3029</td><td align="left">6906</td><td align="left">5979</td><td align="left">3585</td></tr><tr><td align="center">64</td><td align="left">18777</td><td align="left">3408</td><td align="left">11817</td><td align="left">10041</td><td align="left">5318</td></tr></tbody></table><h2 id="其他分析"><a href="#其他分析" class="headerlink" title="其他分析"></a>其他分析</h2><h3 id="结论-1"><a href="#结论-1" class="headerlink" title="结论"></a>结论</h3><p>在买票和退票中，都是Linearizability、Lock-Free的。但是在LockFree实现中的买票和退票方法，因为使用了CAS操作，可能存在某个倒霉的线程，每次CAS操作都被别的打断，因此不是Starvation-Free和Wait-Free。</p><p>在查票方法中，只有粗粒度锁的实现中是Linearizability，其他的方法中，都只符合静态一致性。<br>但是，查询方法是可以达到线性化的，只需要在每次查询前，对数组做快照，就可以实现线性化的查询方法。<br>该方法会导致查询耗时和内存急剧增长，在没有严格要求查询满足线性化时，尽量不要使用这种方法。</p><h3 id="买票"><a href="#买票" class="headerlink" title="买票"></a>买票</h3><table><thead><tr><th align="center">Lock Method</th><th align="center">Linearizability</th><th align="center">DeadLock-Free</th><th align="center">Starvation-Free</th><th align="center">Lock-Free</th><th align="center">Wait-Free</th></tr></thead><tbody><tr><td align="center">CoarseLock</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td></tr><tr><td align="center">FineLock</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td></tr><tr><td align="center">OptimisticLock with BitSet</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td></tr><tr><td align="center">OptimisticLock without BitSet</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td></tr><tr><td align="center">LockFree with AtomicBitSet</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">No</td><td align="center">Yes</td><td align="center">No</td></tr><tr><td align="center">LockFree with AtomicLong</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">No</td><td align="center">Yes</td><td align="center">No</td></tr></tbody></table><h3 id="退票"><a href="#退票" class="headerlink" title="退票"></a>退票</h3><table><thead><tr><th align="center">Lock Method</th><th align="center">Linearizability</th><th align="center">DeadLock-Free</th><th align="center">Starvation-Free</th><th align="center">Lock-Free</th><th align="center">Wait-Free</th></tr></thead><tbody><tr><td align="center">CoarseLock</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td></tr><tr><td align="center">FineLock</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td></tr><tr><td align="center">OptimisticLock with BitSet</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td></tr><tr><td align="center">OptimisticLock without BitSet</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td></tr><tr><td align="center">LockFree with AtomicBitSet</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">No</td><td align="center">Yes</td><td align="center">No</td></tr><tr><td align="center">LockFree with AtomicLong</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">No</td><td align="center">Yes</td><td align="center">No</td></tr></tbody></table><h3 id="查询"><a href="#查询" class="headerlink" title="查询"></a>查询</h3><table><thead><tr><th align="center">Lock Method</th><th align="center">Linearizability</th><th align="center">DeadLock-Free</th><th align="center">Starvation-Free</th><th align="center">Lock-Free</th><th align="center">Wait-Free</th></tr></thead><tbody><tr><td align="center">CoarseLock</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td></tr><tr><td align="center">FineLock</td><td align="center">No</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td></tr><tr><td align="center">OptimisticLock with BitSet</td><td align="center">No</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td></tr><tr><td align="center">OptimisticLock without BitSet</td><td align="center">No</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td></tr><tr><td align="center">LockFree with AtomicBitSet</td><td align="center">No</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td></tr><tr><td align="center">LockFree with AtomicLong</td><td align="center">No</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td><td align="center">Yes</td></tr></tbody></table><h2 id="修改记录"><a href="#修改记录" class="headerlink" title="修改记录"></a>修改记录</h2><p>2017.12.20 增加改进思路，提高对Cache Line的亲和性，并降低函数调用层次。<br>2018.01.05 增加正确性分析和性能分析。</p>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;实验目的&quot;&gt;&lt;a href=&quot;#实验目的&quot; class=&quot;headerlink&quot; title=&quot;实验目的&quot;&gt;&lt;/a&gt;实验目的&lt;/h2&gt;&lt;p&gt;设计并实现用于列车售票的可线性化并发数据结构。&lt;/p&gt;
&lt;h2 id=&quot;实验说明&quot;&gt;&lt;a href=&quot;#实验说明&quot; class=&quot;headerlink&quot; title=&quot;实验说明&quot;&gt;&lt;/a&gt;实验说明&lt;/h2&gt;&lt;p&gt;给定Ticket类：&lt;/p&gt;
&lt;figure class=&quot;highlight java&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;class&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;Ticket&lt;/span&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;long&lt;/span&gt; tid;           &lt;span class=&quot;comment&quot;&gt;// 车票编号&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    String passenger;   &lt;span class=&quot;comment&quot;&gt;// 乘客名字&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;int&lt;/span&gt; route;          &lt;span class=&quot;comment&quot;&gt;// 列车车次&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;int&lt;/span&gt; coach;          &lt;span class=&quot;comment&quot;&gt;// 车厢号&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;int&lt;/span&gt; seat;           &lt;span class=&quot;comment&quot;&gt;// 座位号&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;int&lt;/span&gt; departure;      &lt;span class=&quot;comment&quot;&gt;// 出发站编号&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;int&lt;/span&gt; arrival;        &lt;span class=&quot;comment&quot;&gt;// 到达站编号&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;给定TicketingSystem接口：&lt;/p&gt;
&lt;figure class=&quot;highlight java&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;class&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;TicketingSystem&lt;/span&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;// 买票，即乘客passenger购买route车次从departure站到arrival站的车票一张。若购买成功，返回有效的Ticket对象；若失败(无余票)，则返回null&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;function&quot;&gt;Ticket &lt;span class=&quot;title&quot;&gt;buyTicket&lt;/span&gt;&lt;span class=&quot;params&quot;&gt;(String passenger, &lt;span class=&quot;keyword&quot;&gt;int&lt;/span&gt; route, &lt;span class=&quot;keyword&quot;&gt;int&lt;/span&gt; departure, &lt;span class=&quot;keyword&quot;&gt;int&lt;/span&gt; arrival)&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;// 查询余票，即查询route车次从departure站到arrival站的余票数&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;function&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;inquiry&lt;/span&gt;&lt;span class=&quot;params&quot;&gt;(&lt;span class=&quot;keyword&quot;&gt;int&lt;/span&gt; route, &lt;span class=&quot;keyword&quot;&gt;int&lt;/span&gt; departure, &lt;span class=&quot;keyword&quot;&gt;int&lt;/span&gt; arrival)&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;// 退票，对有效的Ticket对象返回true，对无效的Ticket返回false&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;function&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;refundTicket&lt;/span&gt;&lt;span class=&quot;params&quot;&gt;(Ticket ticket)&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;给定TicketingDS类的构造器接口：&lt;/p&gt;
&lt;figure class=&quot;highlight java&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;TicketingDS(routenum, coachnum, seatnum, stationnum, threadnum)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;其中：route是车次总数（缺省为5），coachnum是列车车厢数目（缺省为8），seatnum是每节车厢的座位数（缺省为100），stationnum是每个车次经停站数量（缺省为10，含始发站和终点站），threadnum是并发购票的线程数（缺省为16）。&lt;/p&gt;</summary>
    
    
    
    <category term="课程设计" scheme="https://blog.wanghaoyu.cn/categories/%E8%AF%BE%E7%A8%8B%E8%AE%BE%E8%AE%A1/"/>
    
    
    <category term="Java" scheme="https://blog.wanghaoyu.cn/tags/Java/"/>
    
    <category term="Multi-threaded" scheme="https://blog.wanghaoyu.cn/tags/Multi-threaded/"/>
    
    <category term="Concurrent" scheme="https://blog.wanghaoyu.cn/tags/Concurrent/"/>
    
  </entry>
  
  <entry>
    <title>NAT实验报告</title>
    <link href="https://blog.wanghaoyu.cn/2017/11/30/nat-stack/"/>
    <id>https://blog.wanghaoyu.cn/2017/11/30/nat-stack/</id>
    <published>2017-11-30T13:57:45.000Z</published>
    <updated>2024-02-27T06:16:02.140Z</updated>
    
    <content type="html"><![CDATA[<h2 id="实验目的"><a href="#实验目的" class="headerlink" title="实验目的"></a>实验目的</h2><p>给定网络拓扑以及节点的网络地址配置（包含私网地址），实现NAT地址转换功能，使得左侧的私网H1节点能够连接H2节点并传输数据</p><h2 id="实验拓扑"><a href="#实验拓扑" class="headerlink" title="实验拓扑"></a>实验拓扑</h2><p><img src="/.cn//01.png"></p><h2 id="实验内容"><a href="#实验内容" class="headerlink" title="实验内容"></a>实验内容</h2><ol><li>运行给定网络拓扑(nat_topo.py)</li></ol><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">sudo python2 topo/nat_topo.py</span><br></pre></td></tr></table></figure><ol start="2"><li>在n1中启动NAT服务</li></ol><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">./scripts/disable_arp.sh</span><br><span class="line">./scripts/disable_icmp.sh</span><br><span class="line">./scripts/disable_ip_forward.sh # Archlinux中需要将-p ip改成-p ipv4</span><br><span class="line">./scripts/disable_tcp_rst.sh</span><br><span class="line">export LD_LIBRARY_PATH=.;       # 设置lib路径</span><br><span class="line">./nat                           # 启动NAT服务</span><br></pre></td></tr></table></figure><ol start="3"><li>在h2节点启动简易HTTP服务器</li></ol><figure class="highlight shell"><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">./scripts/disable_offloading.sh # 防止协议栈生成错误的checksum</span><br><span class="line">python2 -m SimpleHTTPServer</span><br></pre></td></tr></table></figure><ol start="4"><li>在h1节点启动wget请求h2</li></ol><figure class="highlight shell"><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">./scripts/disable_offloading.sh # 防止协议栈生成错误的checksum</span><br><span class="line">wget http://159.226.39.123:8000</span><br></pre></td></tr></table></figure><a id="more"></a><h2 id="主要数据结构"><a href="#主要数据结构" class="headerlink" title="主要数据结构"></a>主要数据结构</h2><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><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">nat_connection</span> &#123;</span></span><br><span class="line">u8 internal_fin;<span class="comment">// indicates whether received fin packet from inner node</span></span><br><span class="line">u8 external_fin;<span class="comment">// indicates whether received fin packet from outer node</span></span><br><span class="line"></span><br><span class="line">u32internal_seq_end;<span class="comment">// the highest sequence number transmitted by inner node</span></span><br><span class="line">u32external_seq_end;<span class="comment">// the highest sequence number transmitted by outer node</span></span><br><span class="line"></span><br><span class="line">u32 internal_ack;<span class="comment">// the highest sequence number acked by inner node</span></span><br><span class="line">u32 external_ack;<span class="comment">// the highest sequence number acked by outer node</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// the mapping entry used for address translation</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">nat_mapping</span> &#123;</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">list_head</span> <span class="title">list</span>;</span></span><br><span class="line"></span><br><span class="line">u32 internal_ip;<span class="comment">// ip address seen in private network</span></span><br><span class="line">u32 external_ip;<span class="comment">// ip address seen in public network (the ip address of external interface)</span></span><br><span class="line">u16 internal_port;<span class="comment">// port seen in private network</span></span><br><span class="line">u16 external_port;<span class="comment">// port seen in public network (assigned by nat)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">time_t</span> update_time;<span class="comment">// when receiving the latest packet</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">nat_connection</span> <span class="title">conn</span>;</span><span class="comment">// statistics of the tcp connection</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h2 id="实验过程"><a href="#实验过程" class="headerlink" title="实验过程"></a>实验过程</h2><p>本实验老师已经给了框架，只需要将几个函数的功能实现就可以了。所以这里只介绍每个函数的功能，具体实现见源代码。</p><p>代码地址：<a href="https://gitee.com/873314461/nat_stack">https://gitee.com/873314461/nat_stack</a></p><script src="//gitee.com/873314461/nat_stack/widget_preview"></script><style>.pro_name a{color: #4183c4;}.osc_git_title{background-color: #fff;}.osc_git_box{background-color: #fff;}.osc_git_box{border-color: #E3E9ED;}.osc_git_info{color: #666;}.osc_git_main a{color: #9B9B9B;}</style><h3 id="is-flow-finished"><a href="#is-flow-finished" class="headerlink" title="is_flow_finished"></a>is_flow_finished</h3><p>这个函数主要是判断TCP流有没有结束，如果TCP两端<strong>均</strong>发送了<strong>FIN</strong>报文且seq_end与ack相等，则证明TCP流已经结束了。</p><p>即<strong>同时</strong>满足以下4个条件时，TCP流已经结束。</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">internal_fin == <span class="literal">true</span></span><br><span class="line">external_fin == <span class="literal">true</span></span><br><span class="line">internal_seq_end == external_ack</span><br><span class="line">external_seq_end == internal_ack</span><br></pre></td></tr></table></figure><h3 id="nat-lookup-external"><a href="#nat-lookup-external" class="headerlink" title="nat_lookup_external"></a>nat_lookup_external</h3><p>这个函数是通过external_port在mapping_list中查找对应的nat_mapping对象。</p><p>只需要遍历整个链表，找到external_port相等的节点返回即可。</p><h3 id="nat-lookup-internal"><a href="#nat-lookup-internal" class="headerlink" title="nat_lookup_internal"></a>nat_lookup_internal</h3><p>与上个函数作用类似，只是通过internal_ip和internal_port在mapping_list中查找对应的nat_mapping对象。</p><h3 id="assign-external-port"><a href="#assign-external-port" class="headerlink" title="assign_external_port"></a>assign_external_port</h3><p>分配一个未使用的external_port。</p><p>只需要遍历端口池(nat.assigned_ports)，若nat.assigned_ports[port]为0，则将其置为1，则返回port。</p><h3 id="free-port"><a href="#free-port" class="headerlink" title="free_port"></a>free_port</h3><p>释放一个端口。</p><p>将nat.assigned_ports[port]置为0。</p><h3 id="nat-insert-mapping"><a href="#nat-insert-mapping" class="headerlink" title="nat_insert_mapping"></a>nat_insert_mapping</h3><p>创建一个nat_mapping对象，并将其插入到hash表中。</p><p>需要：</p><ol><li>申请内存。</li><li>初始化相应信息。</li><li>插入到list中。</li><li>并将nat_mapping指针返回。</li></ol><h3 id="get-packet-direction"><a href="#get-packet-direction" class="headerlink" title="get_packet_direction"></a>get_packet_direction</h3><p>这个函数主要是通过packet的内容，判断这个报文的传送方向是DIR_IN还是DIR_OUT。</p><p>判断方法是，用packet的目的IP查找路由表，得到相应的iface。若iface与internal_iface相同，则方向为DIR_IN;若iface与external_iface相同，则方向为DIR_OUT。</p><p>具体方法如下：</p><ol><li>将packet解析。</li><li>根据目的IP查找路由表。</li><li>比较iface，并返回结果。</li></ol><h3 id="nat-update-tcp-connection"><a href="#nat-update-tcp-connection" class="headerlink" title="nat_update_tcp_connection"></a>nat_update_tcp_connection</h3><p>这个函数用来更新nat_mapping对象中的tcp信息。包括：最后连接时间(update_time)和nat_connection对象。</p><ol><li>解析packet。</li><li>计算TCP报文的seq_end。</li><li>更新update_time。</li><li>更新nat_connection对象。</li></ol><p>PS: 注意<strong>网络序</strong>与<strong>主机序</strong>的转换！</p><h3 id="nat-get-mapping-from-packet"><a href="#nat-get-mapping-from-packet" class="headerlink" title="nat_get_mapping_from_packet"></a>nat_get_mapping_from_packet</h3><p>通过packet在nat_mapping_list中查找对应的nat_mapping对象。</p><p>若报文方向为DIR_IN，则通过external查找。</p><p>若报文方向为DIR_OUT，则通过internal查找。</p><h3 id="do-translation"><a href="#do-translation" class="headerlink" title="do_translation"></a>do_translation</h3><p>这个函数是NAT实验的核心函数。实现真正的包转发。</p><ol><li>根据(server_ip, server_port)计算hash</li><li>查找packet所对应的nat_mapping对象。</li><li>若未找到nat_mapping对象，且传输方向为DIR_OUT，则插入到nat_mapping_list中。</li><li>修改packet的源、目的IP地址和端口。</li><li>重新计算TCP和IP的checksum。</li><li>更新nat_mapping对象中的TCP信息。</li><li>将报文发送出去。</li></ol><h3 id="nat-timeout"><a href="#nat-timeout" class="headerlink" title="nat_timeout"></a>nat_timeout</h3><p>这是一个单独运行的线程，用来关闭并回收已经结束的或者超时的TCP流的nat_mapping对象。</p><p>每隔一定时间，遍历整个nat_mapping_list，将超时的或者已经结束的nat_mapping对象移除，并释放资源。</p><p>PS：遍历list时，一定要使用list_for_each_entry_<strong>safe</strong>这个循环，只有在这个循环中，才可以对list进行插入或者删除节点的操作。</p><h2 id="实验感悟"><a href="#实验感悟" class="headerlink" title="实验感悟"></a>实验感悟</h2><ul><li>了解了NAT的工作原理。</li></ul>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;实验目的&quot;&gt;&lt;a href=&quot;#实验目的&quot; class=&quot;headerlink&quot; title=&quot;实验目的&quot;&gt;&lt;/a&gt;实验目的&lt;/h2&gt;&lt;p&gt;给定网络拓扑以及节点的网络地址配置（包含私网地址），实现NAT地址转换功能，使得左侧的私网H1节点能够连接H2节点并传输数据&lt;/p&gt;
&lt;h2 id=&quot;实验拓扑&quot;&gt;&lt;a href=&quot;#实验拓扑&quot; class=&quot;headerlink&quot; title=&quot;实验拓扑&quot;&gt;&lt;/a&gt;实验拓扑&lt;/h2&gt;&lt;p&gt;&lt;img src=&quot;/.cn//01.png&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;实验内容&quot;&gt;&lt;a href=&quot;#实验内容&quot; class=&quot;headerlink&quot; title=&quot;实验内容&quot;&gt;&lt;/a&gt;实验内容&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;运行给定网络拓扑(nat_topo.py)&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&quot;highlight shell&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;sudo python2 topo/nat_topo.py&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;在n1中启动NAT服务&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&quot;highlight shell&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;./scripts/disable_arp.sh&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;./scripts/disable_icmp.sh&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;./scripts/disable_ip_forward.sh # Archlinux中需要将-p ip改成-p ipv4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;./scripts/disable_tcp_rst.sh&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;export LD_LIBRARY_PATH=.;       # 设置lib路径&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;./nat                           # 启动NAT服务&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;在h2节点启动简易HTTP服务器&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&quot;highlight shell&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;./scripts/disable_offloading.sh # 防止协议栈生成错误的checksum&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;python2 -m SimpleHTTPServer&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;在h1节点启动wget请求h2&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&quot;highlight shell&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;./scripts/disable_offloading.sh # 防止协议栈生成错误的checksum&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;wget http://159.226.39.123:8000&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;</summary>
    
    
    
    <category term="课程设计" scheme="https://blog.wanghaoyu.cn/categories/%E8%AF%BE%E7%A8%8B%E8%AE%BE%E8%AE%A1/"/>
    
    
    <category term="C" scheme="https://blog.wanghaoyu.cn/tags/C/"/>
    
    <category term="NAT" scheme="https://blog.wanghaoyu.cn/tags/NAT/"/>
    
  </entry>
  
  <entry>
    <title>TCP实验报告</title>
    <link href="https://blog.wanghaoyu.cn/2017/11/25/tcp-stack/"/>
    <id>https://blog.wanghaoyu.cn/2017/11/25/tcp-stack/</id>
    <published>2017-11-25T09:47:50.000Z</published>
    <updated>2024-02-27T06:16:02.150Z</updated>
    
    <content type="html"><![CDATA[<h2 id="实验目的"><a href="#实验目的" class="headerlink" title="实验目的"></a>实验目的</h2><p>给定网络拓扑和节点配置，节点实现TCP最基本的连接管理和数据传输功能，使得节点之间能够在<strong>无丢包</strong>网络环境中传输数据。</p><h2 id="实验拓扑"><a href="#实验拓扑" class="headerlink" title="实验拓扑"></a>实验拓扑</h2><p><img src="/.cn//01.png"></p><h2 id="实验内容"><a href="#实验内容" class="headerlink" title="实验内容"></a>实验内容</h2><ol><li><p>运行给定网络拓扑(tcp_topo.py)</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo python2 topo/tcp_topo.py</span><br></pre></td></tr></table></figure></li><li><p>在节点h1上执行TCP程序</p><p> 执行脚本，禁止协议栈的相应功能，并在h1上运行TCP协议栈的服务器模式。</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></pre></td><td class="code"><pre><span class="line">./scripts/disable_arp.sh</span><br><span class="line">./scripts/disable_icmp.sh</span><br><span class="line">./scripts/disable_ip_forward.sh</span><br><span class="line">./scripts/disable_tcp_rst.sh</span><br><span class="line"><span class="built_in">export</span> LD_LIBRARY_PATH=.;</span><br><span class="line">./tcp_stack server 10001</span><br></pre></td></tr></table></figure></li><li><p>在节点h2上执行TCP程序</p><p> 执行脚本，禁止协议栈的相应功能，并在h2上运行TCP协议栈的客户端模式，连接h1并从h1正确获得数据。</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></pre></td><td class="code"><pre><span class="line">./scripts/disable_arp.sh</span><br><span class="line">./scripts/disable_icmp.sh</span><br><span class="line">./scripts/disable_ip_forward.sh</span><br><span class="line">./scripts/disable_tcp_rst.sh</span><br><span class="line"><span class="built_in">export</span> LD_LIBRARY_PATH=.;</span><br><span class="line">./tcp_stack client 10.0.0.1 10001</span><br></pre></td></tr></table></figure><a id="more"></a><h2 id="主要数据结构"><a href="#主要数据结构" class="headerlink" title="主要数据结构"></a>主要数据结构</h2><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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//socket 对象</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">tcp_sock</span> &#123;</span></span><br><span class="line">    <span class="comment">// sk_ip, sk_sport, sk_sip, sk_dport are the 4-tuple that represents a </span></span><br><span class="line">    <span class="comment">// connection</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">sock_addr</span> <span class="title">local</span>;</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">sock_addr</span> <span class="title">peer</span>;</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> sk_sip local.ip</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> sk_sport local.port</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> sk_dip peer.ip</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> sk_dport peer.port</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// pointer to parent tcp sock, a tcp sock which bind and listen to a port </span></span><br><span class="line">    <span class="comment">// is the parent of tcp socks when *accept* a connection request</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">tcp_sock</span> *<span class="title">parent</span>;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// represents the number that the tcp sock is referred, if this number </span></span><br><span class="line">    <span class="comment">// decreased to zero, the tcp sock should be released</span></span><br><span class="line">    <span class="keyword">int</span> ref_cnt;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// hash_list is used to hash tcp sock into listen_table or established_table, </span></span><br><span class="line">    <span class="comment">// bind_hash_list is used to hash into bind_table</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_head</span> <span class="title">hash_list</span>;</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_head</span> <span class="title">bind_hash_list</span>;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// when a passively opened tcp sock receives a SYN packet, it mallocs a child </span></span><br><span class="line">    <span class="comment">// tcp sock to serve the incoming connection, which is pending in the </span></span><br><span class="line">    <span class="comment">// listen_queue of parent tcp sock</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_head</span> <span class="title">listen_queue</span>;</span></span><br><span class="line">    <span class="comment">// when receiving the last packet (ACK) of the 3-way handshake, the tcp sock </span></span><br><span class="line">    <span class="comment">// in listen_queue will be moved into accept_queue, waiting for *accept* by </span></span><br><span class="line">    <span class="comment">// parent tcp sock</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_head</span> <span class="title">accept_queue</span>;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> TCP_MAX_BACKLOG 128</span></span><br><span class="line">    <span class="comment">// the number of pending tcp sock in accept_queue</span></span><br><span class="line">    <span class="keyword">int</span> accept_backlog;</span><br><span class="line">    <span class="comment">// the maximum number of pending tcp sock in accept_queue</span></span><br><span class="line">    <span class="keyword">int</span> backlog;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// the list node used to link listen_queue or accept_queue of parent tcp sock</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">list_head</span> <span class="title">list</span>;</span></span><br><span class="line">    <span class="comment">// tcp timer used during TCP_TIME_WAIT state</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">tcp_timer</span> <span class="title">timewait</span>;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// async waiting structure of *connect*, *accept*, *recv*, and *send*</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">synch_wait</span> *<span class="title">wait_connect</span>;</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">synch_wait</span> *<span class="title">wait_accept</span>;</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">synch_wait</span> *<span class="title">wait_recv</span>;</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">synch_wait</span> *<span class="title">wait_send</span>;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// receiving buffer</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">ring_buffer</span> *<span class="title">rcv_buf</span>;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// tcp state, see enum tcp_state in tcp.h</span></span><br><span class="line">    <span class="keyword">int</span> state;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// initial sending sequence number</span></span><br><span class="line">    u32 iss;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// the highest byte that is ACKed by peer</span></span><br><span class="line">    u32 snd_una;</span><br><span class="line">    <span class="comment">// the highest byte sent</span></span><br><span class="line">    u32 snd_nxt;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// the highest byte ACKed by itself (i.e. the byte expected to receive next)</span></span><br><span class="line">    u32 rcv_nxt;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// the size of sending window (i.e. the receiving window advertised by peer)</span></span><br><span class="line">    u16 snd_wnd;</span><br><span class="line">    <span class="comment">// the size of receiving window (advertised by tcp sock itself)</span></span><br><span class="line">    u16 rcv_wnd;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></li></ol><h2 id="TCP状态转移图"><a href="#TCP状态转移图" class="headerlink" title="TCP状态转移图"></a>TCP状态转移图</h2><p><img src="/.cn//02.png"></p><p>TCP建立连接状态转移图<br><img src="/.cn//03.png"></p><p>TCP断开连接状态转移图<br><img src="/.cn//04.png"></p><h2 id="实验过程"><a href="#实验过程" class="headerlink" title="实验过程"></a>实验过程</h2><p>本实验老师已经给了框架，只需要将几个函数的功能实现就可以了。所以这里只介绍每个函数的功能，具体实现见源代码。</p><p>代码地址：<a href="https://gitee.com/873314461/tcp_stack">https://gitee.com/873314461/tcp_stack</a></p><script src="//gitee.com/873314461/tcp_stack/widget_preview"></script><style>.pro_name a{color: #4183c4;}.osc_git_title{background-color: #fff;}.osc_git_box{background-color: #fff;}.osc_git_box{border-color: #E3E9ED;}.osc_git_info{color: #666;}.osc_git_main a{color: #9B9B9B;}</style><h3 id="tcp-state-listen"><a href="#tcp-state-listen" class="headerlink" title="tcp_state_listen"></a>tcp_state_listen</h3><p>这个函数是用来处理处于<strong>TCP_LISTEN</strong>状态的socket。</p><p>处于监听状态的ServerSocket，在收到第一个SYN报文时，将会：</p><ol><li>生成一个<strong>新的</strong>子socket，并将子socket的<strong>parent</strong>指向原来的ServerSocket。</li><li>将子socket插入到原ServerSocket的listen_queue队列中。</li><li>用子socket发送<strong>SYN|ACK</strong>报文。(TCP连接的第二次握手)</li><li>将子socket的状态置为<strong>TCP_SYN_RECV</strong>，并将子socket插入到established_table中——因为这时区分TCP连接的四元组(saddr, daddr, sport, dport)已经确定了。</li></ol><h3 id="tcp-state-syn-sent"><a href="#tcp-state-syn-sent" class="headerlink" title="tcp_state_syn_sent"></a>tcp_state_syn_sent</h3><p>这个函数是用来处理处于TCP_SYN_SENT状态的socket。当客户端发出第一个SYN报文之后，就会处于这个状态。</p><p>在这个状态，需要处理一下内容：</p><ol><li>判断这个收到的报文，是否是SYN|ACK报文，如果不是，回复RST报文并结束。</li><li>如果收到的报文正常，则需要回复ACK报文。(TCP第三次握手)</li><li>将socket转换为TCP_ESTABLISHED状态，并加入到established_table中。</li><li>唤醒阻塞于tcp_sock_connect的进程。</li></ol><h3 id="tcp-state-syn-recv"><a href="#tcp-state-syn-recv" class="headerlink" title="tcp_state_syn_recv"></a>tcp_state_syn_recv</h3><p>这个函数是用来处理处于TCP_SYN_RECV状态的socket。当服务器收到客户端发来的第一个SYN报文，并且回复了SYN|ACK报文后，处于这个状态。</p><p>在这个状态将要：</p><ol><li>将socket从原ServerSocket的listen_queue队列中移除。</li><li>将socket插入到原ServerSocket的accept_queue队列中。</li><li>将socket的状态置为TCP_ESTABLISHED。</li><li>唤醒处于tcp_accpet的进程，即：唤醒原ServerSocket的wait_accept对象。</li></ol><h3 id="tcp-recv-data"><a href="#tcp-recv-data" class="headerlink" title="tcp_recv_data"></a>tcp_recv_data</h3><p>这个函数用来接收报文发送的数据，并将数据送到socket的环形缓冲区(rcv_buf)中。</p><ol><li>将报文的负载写入到环形缓冲区中。</li><li>唤醒正在等待接收消息的进程，即：socket的wait_recv对象。</li></ol><h3 id="tcp-process"><a href="#tcp-process" class="headerlink" title="tcp_process"></a>tcp_process</h3><p>这个函数是处理接收到的TCP报文的主函数。根据各种不同的状态，选择合适的策略，处理报文。在理解这个函数过程中，请全程参考TCP状态转移图。</p><ol><li>如果socket处于TCP_CLOSED状态，则交给tcp_state_closed函数处理。</li><li>如果socket处于TCP_LISTEN状态，则交给tcp_state_listen函数处理。</li><li>如果socket处于TCP_SYN_SENT状态，则交给tcp_state_syn_sent函数处理。</li><li>调用is_tcp_seq_valid函数验证报文的序列号是否正确，若不正确，丢弃这个包</li><li>若RST为1，则关闭这个连接，并释放这个连接的资源。因为RST表示连接重置，这个连接已经不再使用了。</li><li>若SYN为1，则回复RST，并关闭这个连接。因为正常的SYN连接，已经在第2、3步处理过了。</li><li>若ACK为0，则丢弃这个包。因为除了第一个SYN报文之外，所有的报文的ACK都应该为1。(需要实验验证一下)</li><li>如果socket处于TCP_SYN_RECV状态，调用tcp_state_syn_recv函数处理。</li><li>如果socket处于TCP_FIN_WAIT_1，则将socket状态转换到TCP_FIN_WAIT_2状态。</li><li>如果socket处于TCP_LAST_ACK，则将socket状态转换到TCP_CLOSED状态，并从established_table中移除，并释放资源。</li><li>如果socket处于TCP_FIN_WAIT_2状态，则需要判断收到的报文是否是FIN|ACK报文：若是，则回复ACK报文，并启动定时器；否则，丢弃这个包。</li><li>如果接收的报文确认了发送的报文，则更新窗口大小。</li><li>若报文有负载数据，则接收数据。</li><li>若FIN为1，则将socket状态转变到TCP_CLOSE_WAIT状态，并回复ACK报文，表名接收到FIN报文。发送FIN|ACK报文，请求关闭这个连接，并将socket状态置为TCP_LAST_ACK。</li><li>若前面没有返回，则应当发送ACK报文，确认当前接收的报文。</li></ol><p>PS: 本函数的逻辑可能存在问题，需要再考虑一下。</p><h3 id="alloc-tcp-sock"><a href="#alloc-tcp-sock" class="headerlink" title="alloc_tcp_sock"></a>alloc_tcp_sock</h3><p>这个函数是创建一个tcp_sock对象，但是根据函数名，我认为这个函数应当只申请内存，并不进行初始化，但是，我在头文件中没有找到初始化函数，因此只能在这个函数中，对tcp_sock对象进行初始化。</p><ol><li>申请内存。</li><li>初始化两个队列：listen_queue和accept_queue。</li><li>初始化四个等待对象：wait_accept、wait_connect、wait_recv和wait_send</li><li>初始化发送序列号以及发送、接收窗口大小。</li><li>初始化环形缓冲区。</li><li>设置socket状态为TCP_CLOSED。</li></ol><h3 id="tcp-sock-lookup-established"><a href="#tcp-sock-lookup-established" class="headerlink" title="tcp_sock_lookup_established"></a>tcp_sock_lookup_established</h3><p>这个函数从从established_table中查询符合要求的socket，并返回。</p><p>查询过程如下：</p><ol><li>用四元组(saddr, daddr, sport, dport)计算hash。</li><li>遍历相应的队列，将socket取出，并返回。</li><li>若没有找到，则返回NULL。</li></ol><h3 id="tcp-sock-lookup-listen"><a href="#tcp-sock-lookup-listen" class="headerlink" title="tcp_sock_lookup_listen"></a>tcp_sock_lookup_listen</h3><p>这个函数跟tcp_sock_lookup_established作用类似，是在listen_table中查询。计算hash时，只使用sport，其他位置用0代替。</p><h3 id="tcp-sock-connect"><a href="#tcp-sock-connect" class="headerlink" title="tcp_sock_connect"></a>tcp_sock_connect</h3><p>这个函数是客户端用来发起连接的函数。需要：</p><ol><li>设置源地址、目的地址、源端口、目的端口。</li><li>将socket加入到bind_table中。</li><li>将socket置为TCP_SYN_SENT状态，并加入到established_table中。</li><li>发送SYN报文。</li><li>阻塞在wait_connect，等待被唤醒。</li></ol><p>PS：一定要注意<strong>2和3的顺序</strong>，如果先发送SYN报文，在网速快并且CPU特别慢的时候，会出现收到SYN|ACK报文时，socket还没有插入到established_table中，这就会造成在tcp_sock_lookup_established函数中找不到相应的socket，造成程序错误。</p><h3 id="tcp-sock-listen"><a href="#tcp-sock-listen" class="headerlink" title="tcp_sock_listen"></a>tcp_sock_listen</h3><p>这个函数使socket进入TCP_LISTEN状态，并插入到listen_table中。</p><h3 id="tcp-sock-accept"><a href="#tcp-sock-accept" class="headerlink" title="tcp_sock_accept"></a>tcp_sock_accept</h3><p>ServerSocket使用这个函数，获取一个新建立的连接，进行处理。</p><ol><li>若ServerSocket的accept_queue不为空，则直接弹出队列的第一个元素，并返回。</li><li>若accept_queue为空，则等待wait_accept。被唤醒后，弹出队列的第一个元素，并返回。</li></ol><h3 id="tcp-sock-read"><a href="#tcp-sock-read" class="headerlink" title="tcp_sock_read"></a>tcp_sock_read</h3><p>使用该函数接收socket发送过来的数据。</p><ol><li>若环形缓冲区为空，则等待wait_recv，唤醒后读取数据、调整窗口并返回。</li><li>若环形缓冲区不为空，则直接调整窗口并返回数据。</li></ol><h3 id="tcp-sock-write"><a href="#tcp-sock-write" class="headerlink" title="tcp_sock_write"></a>tcp_sock_write</h3><p>使用该函数发送数据。直接调用tcp_send_data函数发送数据。<br>PS：获取需要验证窗口大小。</p><h2 id="实验收获"><a href="#实验收获" class="headerlink" title="实验收获"></a>实验收获</h2><ul><li>通过这次实验，真正清晰的了解了TCP连接建立与断开的详细过程。</li><li>通过阅读老师给的代码，学习到了一种新的链表结构，与之前学习的链表有很大的不同，非常高效且不浪费空间。</li><li>通过阅读代码，并查阅资料，了解了如何禁用系统的协议栈，实现用户态的TCP/IP栈。</li></ul>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;实验目的&quot;&gt;&lt;a href=&quot;#实验目的&quot; class=&quot;headerlink&quot; title=&quot;实验目的&quot;&gt;&lt;/a&gt;实验目的&lt;/h2&gt;&lt;p&gt;给定网络拓扑和节点配置，节点实现TCP最基本的连接管理和数据传输功能，使得节点之间能够在&lt;strong&gt;无丢包&lt;/strong&gt;网络环境中传输数据。&lt;/p&gt;
&lt;h2 id=&quot;实验拓扑&quot;&gt;&lt;a href=&quot;#实验拓扑&quot; class=&quot;headerlink&quot; title=&quot;实验拓扑&quot;&gt;&lt;/a&gt;实验拓扑&lt;/h2&gt;&lt;p&gt;&lt;img src=&quot;/.cn//01.png&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;实验内容&quot;&gt;&lt;a href=&quot;#实验内容&quot; class=&quot;headerlink&quot; title=&quot;实验内容&quot;&gt;&lt;/a&gt;实验内容&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;运行给定网络拓扑(tcp_topo.py)&lt;/p&gt;
&lt;figure class=&quot;highlight sh&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;sudo python2 topo/tcp_topo.py&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;在节点h1上执行TCP程序&lt;/p&gt;
&lt;p&gt; 执行脚本，禁止协议栈的相应功能，并在h1上运行TCP协议栈的服务器模式。&lt;/p&gt;
&lt;figure class=&quot;highlight sh&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;./scripts/disable_arp.sh&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;./scripts/disable_icmp.sh&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;./scripts/disable_ip_forward.sh&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;./scripts/disable_tcp_rst.sh&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;built_in&quot;&gt;export&lt;/span&gt; LD_LIBRARY_PATH=.;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;./tcp_stack server 10001&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;在节点h2上执行TCP程序&lt;/p&gt;
&lt;p&gt; 执行脚本，禁止协议栈的相应功能，并在h2上运行TCP协议栈的客户端模式，连接h1并从h1正确获得数据。&lt;/p&gt;
&lt;figure class=&quot;highlight sh&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;./scripts/disable_arp.sh&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;./scripts/disable_icmp.sh&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;./scripts/disable_ip_forward.sh&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;./scripts/disable_tcp_rst.sh&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;built_in&quot;&gt;export&lt;/span&gt; LD_LIBRARY_PATH=.;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;./tcp_stack client 10.0.0.1 10001&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/li&gt;&lt;/ol&gt;</summary>
    
    
    
    <category term="课程设计" scheme="https://blog.wanghaoyu.cn/categories/%E8%AF%BE%E7%A8%8B%E8%AE%BE%E8%AE%A1/"/>
    
    
    <category term="C" scheme="https://blog.wanghaoyu.cn/tags/C/"/>
    
    <category term="Socket" scheme="https://blog.wanghaoyu.cn/tags/Socket/"/>
    
    <category term="TCP" scheme="https://blog.wanghaoyu.cn/tags/TCP/"/>
    
  </entry>
  
  <entry>
    <title>编译OpenWRT(官方版)</title>
    <link href="https://blog.wanghaoyu.cn/2017/11/16/make-openwrt-new/"/>
    <id>https://blog.wanghaoyu.cn/2017/11/16/make-openwrt-new/</id>
    <published>2017-11-16T09:44:33.000Z</published>
    <updated>2024-02-27T06:16:02.120Z</updated>
    
    <content type="html"><![CDATA[<h2 id="本文目的"><a href="#本文目的" class="headerlink" title="本文目的"></a>本文目的</h2><p>虽然之前也写过编译OpenWRT的文章，但是由于版本等原因，此次遇到的问题与之前不太一样，特地记录一下。</p><h2 id="编译环境"><a href="#编译环境" class="headerlink" title="编译环境"></a>编译环境</h2><ul><li>Archlinux 64bit</li><li>GCC v7.2.0</li><li>Perl v5.26.1</li></ul><h2 id="项目源码"><a href="#项目源码" class="headerlink" title="项目源码"></a>项目源码</h2><p>项目源码托管在GitHub上，地址如下：**<a href="https://github.com/openwrt/openwrt">openwrt</a>**。</p><h2 id="前提工作"><a href="#前提工作" class="headerlink" title="前提工作"></a>前提工作</h2><ol><li><p>必装工具<br> 可以在<a href="http://wiki.openwrt.org/doc/howto/buildroot.exigence">这里</a>找到目前支持系统的编译工具。</p><p> Archlinux的安装命令如下：</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">sudo pacman -S --needed subversion asciidoc bash bc binutils bzip2 fastjar flex git gcc util-linux gawk intltool zlib mercurial make cdrkit ncurses openssl patch perl-extutils-makemaker rsync sdcc unzip wget gettext libxslt boost libusb bin86 sharutils b43-fwcutter findutils</span><br></pre></td></tr></table></figure></li><li><p>Checkout项目代码</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">git clone git@github.com:openwrt/openwrt.git</span><br></pre></td></tr></table></figure></li></ol><p>取得源代码后，需要执行下面的命令，将package全部安装。</p><figure class="highlight shell"><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">./scripts/feeds update -a</span><br><span class="line">./scripts/feeds install -a</span><br></pre></td></tr></table></figure><a id="more"></a><h2 id="编译"><a href="#编译" class="headerlink" title="编译"></a>编译</h2><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></pre></td><td class="code"><pre><span class="line">cd openwrt</span><br><span class="line">make menuconfig</span><br><span class="line">make V=s -j1</span><br></pre></td></tr></table></figure><h2 id="遇到的问题"><a href="#遇到的问题" class="headerlink" title="遇到的问题"></a>遇到的问题</h2><p>出现问题的部分主要是host工具编译出错。主要是因为这些工具之间有相互依赖，某个部分修改后，会引起不兼容的问题。</p><h3 id="automake编译报错"><a href="#automake编译报错" class="headerlink" title="automake编译报错"></a>automake编译报错</h3><p><img src="/.cn//01.jpg"></p><p>Perl版本过高会导致Perl脚本的正则匹配出现问题，在Perl 5中取消了正则匹配的大括号（<code>&#123;</code>、<code>&#125;</code>），因此，只需要在相应的文件中，把大括号删掉就行了。</p><p>注意：编译报错是由一个<code>tmp</code>文件引起的，因此，需要修改生成tmp文件的源文件，才可以修复错误。</p><h3 id="u-boot编译报错"><a href="#u-boot编译报错" class="headerlink" title="u-boot编译报错"></a>u-boot编译报错</h3><p><img src="/.cn//02.jpg"></p><p>这个错误是由于rsa库版本更新导致的，需要使用这个**<a href="https://gitee.com/873314461/codes/kp51ugchbq7ejnti6dfv846">补丁文件</a>**进行修复。</p><p>修复时，需要将不定文件放在与rsa-sign.c相同目录下执行。<br><img src="/.cn//03.jpg"></p><h3 id="cmake编译错误"><a href="#cmake编译错误" class="headerlink" title="cmake编译错误"></a>cmake编译错误</h3><p><img src="/.cn//04.jpg"></p><p>这个问题是由于openssl版本引起的，可以使用系统自带的openssl回避这个问题。</p><p>在cmake根目录下，手动编译安装，在配置时，需要指明使用系统的库文件。记得修改<strong>PATH</strong></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></pre></td><td class="code"><pre><span class="line">./configure --system-libs --prefix=&lt;PATH&gt;/openwrt/staging_dir/host</span><br><span class="line">make</span><br><span class="line">make install</span><br></pre></td></tr></table></figure><h3 id="cmake编译错误-1"><a href="#cmake编译错误-1" class="headerlink" title="cmake编译错误"></a>cmake编译错误</h3><p><img src="/.cn//05.jpg"></p><p>这个问题是由于系统缺少JsonCpp库引起的，在系统中安装一下就行了。</p>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;本文目的&quot;&gt;&lt;a href=&quot;#本文目的&quot; class=&quot;headerlink&quot; title=&quot;本文目的&quot;&gt;&lt;/a&gt;本文目的&lt;/h2&gt;&lt;p&gt;虽然之前也写过编译OpenWRT的文章，但是由于版本等原因，此次遇到的问题与之前不太一样，特地记录一下。&lt;/p&gt;
&lt;h2 id=&quot;编译环境&quot;&gt;&lt;a href=&quot;#编译环境&quot; class=&quot;headerlink&quot; title=&quot;编译环境&quot;&gt;&lt;/a&gt;编译环境&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Archlinux 64bit&lt;/li&gt;
&lt;li&gt;GCC v7.2.0&lt;/li&gt;
&lt;li&gt;Perl v5.26.1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;项目源码&quot;&gt;&lt;a href=&quot;#项目源码&quot; class=&quot;headerlink&quot; title=&quot;项目源码&quot;&gt;&lt;/a&gt;项目源码&lt;/h2&gt;&lt;p&gt;项目源码托管在GitHub上，地址如下：**&lt;a href=&quot;https://github.com/openwrt/openwrt&quot;&gt;openwrt&lt;/a&gt;**。&lt;/p&gt;
&lt;h2 id=&quot;前提工作&quot;&gt;&lt;a href=&quot;#前提工作&quot; class=&quot;headerlink&quot; title=&quot;前提工作&quot;&gt;&lt;/a&gt;前提工作&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;必装工具&lt;br&gt; 可以在&lt;a href=&quot;http://wiki.openwrt.org/doc/howto/buildroot.exigence&quot;&gt;这里&lt;/a&gt;找到目前支持系统的编译工具。&lt;/p&gt;
&lt;p&gt; Archlinux的安装命令如下：&lt;/p&gt;
 &lt;figure class=&quot;highlight shell&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;sudo pacman -S --needed subversion asciidoc bash bc binutils bzip2 fastjar flex git gcc util-linux gawk intltool zlib mercurial make cdrkit ncurses openssl patch perl-extutils-makemaker rsync sdcc unzip wget gettext libxslt boost libusb bin86 sharutils b43-fwcutter findutils&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Checkout项目代码&lt;/p&gt;
&lt;figure class=&quot;highlight shell&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;git clone git@github.com:openwrt/openwrt.git&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;取得源代码后，需要执行下面的命令，将package全部安装。&lt;/p&gt;
&lt;figure class=&quot;highlight shell&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;./scripts/feeds update -a&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;./scripts/feeds install -a&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;</summary>
    
    
    
    <category term="OpenWRT" scheme="https://blog.wanghaoyu.cn/categories/OpenWRT/"/>
    
    
    <category term="OpenWRT" scheme="https://blog.wanghaoyu.cn/tags/OpenWRT/"/>
    
    <category term="Make" scheme="https://blog.wanghaoyu.cn/tags/Make/"/>
    
  </entry>
  
  <entry>
    <title>JDK环境变量配置</title>
    <link href="https://blog.wanghaoyu.cn/2017/11/09/jdk-environment/"/>
    <id>https://blog.wanghaoyu.cn/2017/11/09/jdk-environment/</id>
    <published>2017-11-09T09:01:42.000Z</published>
    <updated>2024-02-27T06:16:02.120Z</updated>
    
    <content type="html"><![CDATA[<p>之前配置JDK环境，总是忘记需要设置哪几个环境变量，所以在这里记录一下。</p><ul><li><p><strong>JAVA_HOME</strong> = <code>C:\Program Files\Java\jdk-9.0.1</code></p></li><li><p><strong>CLASSPATH</strong> = <code>.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar</code></p></li><li><p><strong>Path</strong> += <code>%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;</code></p></li></ul><p>PS:最新的Windows 10中的环境变量，需要在<strong>设置</strong>中搜索<strong>编辑系统环境变量</strong>打开。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;之前配置JDK环境，总是忘记需要设置哪几个环境变量，所以在这里记录一下。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;JAVA_HOME&lt;/strong&gt; = &lt;code&gt;C:\Program Files\Java\jdk-9.0.1&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
</summary>
      
    
    
    
    <category term="环境搭建" scheme="https://blog.wanghaoyu.cn/categories/%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/"/>
    
    
    <category term="Java" scheme="https://blog.wanghaoyu.cn/tags/Java/"/>
    
    <category term="JDK" scheme="https://blog.wanghaoyu.cn/tags/JDK/"/>
    
    <category term="Environment" scheme="https://blog.wanghaoyu.cn/tags/Environment/"/>
    
  </entry>
  
  <entry>
    <title>MySQL5.7+ 初始化密码</title>
    <link href="https://blog.wanghaoyu.cn/2017/04/01/mysql-init-password/"/>
    <id>https://blog.wanghaoyu.cn/2017/04/01/mysql-init-password/</id>
    <published>2017-04-01T06:26:33.000Z</published>
    <updated>2024-02-27T06:16:02.140Z</updated>
    
    <content type="html"><![CDATA[<p>今天服务器重装了系统CentOS 7，需要安装MySQL。</p><h2 id="获取repo"><a href="#获取repo" class="headerlink" title="获取repo"></a>获取repo</h2><p>访问：<a href="https://dev.mysql.com/downloads/repo/yum/">https://dev.mysql.com/downloads/repo/yum/</a><br>下载rpm文件</p><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><figure class="highlight sh"><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">sudo rpm -ivh mysql57-community*.rpm</span><br><span class="line">yum install -y mysql-server</span><br></pre></td></tr></table></figure><h2 id="运行"><a href="#运行" class="headerlink" title="运行"></a>运行</h2><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo systemctl start mysqld</span><br></pre></td></tr></table></figure><h2 id="重置密码"><a href="#重置密码" class="headerlink" title="重置密码"></a>重置密码</h2><p>MySQL 5.7以上，会在安装时会默认生成一个临时密码。MySQL 5.6以下则默认密码为空。</p><figure class="highlight sh"><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="comment"># 获取临时密码</span></span><br><span class="line">sudo grep <span class="string">&#x27;temporary password&#x27;</span> /var/<span class="built_in">log</span>/mysqld.log</span><br></pre></td></tr></table></figure><p>显示如下信息：</p><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">2016-12-01T00</span>:<span class="number">22</span>:<span class="number">31.416107</span>Z <span class="number">1</span> [Note] A temporary password is generated for root<span class="variable">@localhost</span>: mqRfBU_3Xk&gt;r</span><br></pre></td></tr></table></figure><p>重置密码：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo mysql_secure_installation</span><br></pre></td></tr></table></figure><p>输入临时密码，然后再输入新密码就可以了。</p><p>新密码要求是强密码，需要包含字母、数字、特殊符号。</p><h2 id="允许远程访问"><a href="#允许远程访问" class="headerlink" title="允许远程访问"></a>允许远程访问</h2><p>授权用户远程访问</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">grant</span> <span class="keyword">all</span> <span class="keyword">privileges</span>  <span class="keyword">on</span> *.* <span class="keyword">to</span> <span class="keyword">user</span>@<span class="string">&#x27;ip&#x27;</span> <span class="keyword">identified</span> <span class="keyword">by</span> <span class="string">&quot;password&quot;</span>;</span><br></pre></td></tr></table></figure><p>如果允许任意地址，则将<code>@&#39;ip&#39;</code>换成<code>@&#39;%&#39;</code>。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;今天服务器重装了系统CentOS 7，需要安装MySQL。&lt;/p&gt;
&lt;h2 id=&quot;获取repo&quot;&gt;&lt;a href=&quot;#获取repo&quot; class=&quot;headerlink&quot; title=&quot;获取repo&quot;&gt;&lt;/a&gt;获取repo&lt;/h2&gt;&lt;p&gt;访问：&lt;a href=&quot;https</summary>
      
    
    
    
    <category term="知识点" scheme="https://blog.wanghaoyu.cn/categories/%E7%9F%A5%E8%AF%86%E7%82%B9/"/>
    
    
    <category term="MySQL" scheme="https://blog.wanghaoyu.cn/tags/MySQL/"/>
    
  </entry>
  
  <entry>
    <title>Linux安装libsodium</title>
    <link href="https://blog.wanghaoyu.cn/2017/04/01/libsodium/"/>
    <id>https://blog.wanghaoyu.cn/2017/04/01/libsodium/</id>
    <published>2017-04-01T06:20:26.000Z</published>
    <updated>2024-02-27T06:16:02.120Z</updated>
    
    <content type="html"><![CDATA[<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></pre></td><td class="code"><pre><span class="line">wget https://download.libsodium.org/libsodium/releases/LATEST.tar.gz</span><br><span class="line">tar zxf LATEST.tar.gz</span><br><span class="line"><span class="built_in">cd</span> libsodium*</span><br><span class="line">./configure</span><br><span class="line">make &amp;&amp; make install</span><br><span class="line"></span><br><span class="line"><span class="comment"># 修复关联</span></span><br><span class="line"><span class="comment"># Ubuntu 只运行 ldconfig 就可以</span></span><br><span class="line"><span class="built_in">echo</span> /usr/<span class="built_in">local</span>/lib &gt; /etc/ld.so.conf.d/usr_local_lib.conf</span><br><span class="line">ldconfig</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;figure class=&quot;highlight sh&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;l</summary>
      
    
    
    
    <category term="知识点" scheme="https://blog.wanghaoyu.cn/categories/%E7%9F%A5%E8%AF%86%E7%82%B9/"/>
    
    
    <category term="Linux" scheme="https://blog.wanghaoyu.cn/tags/Linux/"/>
    
    <category term="Libsodium" scheme="https://blog.wanghaoyu.cn/tags/Libsodium/"/>
    
    <category term="Shadowsocks" scheme="https://blog.wanghaoyu.cn/tags/Shadowsocks/"/>
    
  </entry>
  
  <entry>
    <title>Windows命令计算MD5与SHA1/256值</title>
    <link href="https://blog.wanghaoyu.cn/2017/04/01/windows-hash/"/>
    <id>https://blog.wanghaoyu.cn/2017/04/01/windows-hash/</id>
    <published>2017-04-01T06:13:07.000Z</published>
    <updated>2024-02-27T06:16:02.150Z</updated>
    
    <content type="html"><![CDATA[<figure class="highlight mipsasm"><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="comment"># MD5</span></span><br><span class="line">certutil -hashfile yourfilename.<span class="keyword">ext </span>MD5</span><br><span class="line"><span class="comment"># SHA1</span></span><br><span class="line">certutil -hashfile yourfilename.<span class="keyword">ext </span><span class="keyword">SHA1</span></span><br><span class="line"><span class="keyword"># </span><span class="keyword">SHA256</span></span><br><span class="line"><span class="keyword">certutil </span>-hashfile yourfilename.<span class="keyword">ext </span><span class="keyword">SHA256</span></span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;figure class=&quot;highlight mipsasm&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span cla</summary>
      
    
    
    
    <category term="知识点" scheme="https://blog.wanghaoyu.cn/categories/%E7%9F%A5%E8%AF%86%E7%82%B9/"/>
    
    
    <category term="Windows" scheme="https://blog.wanghaoyu.cn/tags/Windows/"/>
    
    <category term="Hash" scheme="https://blog.wanghaoyu.cn/tags/Hash/"/>
    
  </entry>
  
  <entry>
    <title>Linux下将整型IPv4地址转换成点分十进制</title>
    <link href="https://blog.wanghaoyu.cn/2017/04/01/ip-addr/"/>
    <id>https://blog.wanghaoyu.cn/2017/04/01/ip-addr/</id>
    <published>2017-04-01T05:58:34.000Z</published>
    <updated>2024-02-27T06:16:02.120Z</updated>
    
    <content type="html"><![CDATA[<h2 id="用户态"><a href="#用户态" class="headerlink" title="用户态"></a>用户态</h2><p>今天看netfilter的源码，发现一个打印点分十进制的好方法：使用NIPQUAD和NIPQUAD_FMT宏：</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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> NIPQUAD_FMT <span class="meta-string">&quot;%u.%u.%u.%u&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> NIPQUAD(addr) \</span></span><br><span class="line"> ((<span class="keyword">unsigned</span> <span class="keyword">char</span> *)&amp;addr)[<span class="number">0</span>], \</span><br><span class="line"> ((<span class="keyword">unsigned</span> <span class="keyword">char</span> *)&amp;addr)[<span class="number">1</span>], \</span><br><span class="line"> ((<span class="keyword">unsigned</span> <span class="keyword">char</span> *)&amp;addr)[<span class="number">2</span>], \</span><br><span class="line"> ((<span class="keyword">unsigned</span> <span class="keyword">char</span> *)&amp;addr)[<span class="number">3</span>]</span><br></pre></td></tr></table></figure> <a id="more"></a><p> 写个测试代码:</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><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"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> NIPQUAD_FMT <span class="meta-string">&quot;%u.%u.%u.%u&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> NIPQUAD(addr) \</span></span><br><span class="line"> ((<span class="keyword">unsigned</span> <span class="keyword">char</span> *)&amp;addr)[<span class="number">0</span>], \</span><br><span class="line"> ((<span class="keyword">unsigned</span> <span class="keyword">char</span> *)&amp;addr)[<span class="number">1</span>], \</span><br><span class="line"> ((<span class="keyword">unsigned</span> <span class="keyword">char</span> *)&amp;addr)[<span class="number">2</span>], \</span><br><span class="line"> ((<span class="keyword">unsigned</span> <span class="keyword">char</span> *)&amp;addr)[<span class="number">3</span>]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">unsigned</span> <span class="keyword">int</span> ip;</span><br><span class="line">    ip = htonl(<span class="number">3232235777</span>);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;ip = &quot;</span> NIPQUAD_FMT <span class="string">&quot;\n&quot;</span>, NIPQUAD(ip));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>输出结果:</p><figure class="highlight routeros"><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">tony@ubuntu-a:~/code$ ./a.out </span><br><span class="line">ip = 192.168.1.1</span><br></pre></td></tr></table></figure><p>这个用法要注意的就是，NIPQUAD的参数需要是网络序的，所以事先用htonl转一下就可以了。</p><h2 id="核心态"><a href="#核心态" class="headerlink" title="核心态"></a>核心态</h2><p>另外还看到一种用法，就是使用%pI4打印地址：</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="built_in">sprintf</span>(buffer, <span class="string">&quot;%pI4&quot;</span>, &amp;ip);</span><br></pre></td></tr></table></figure><p>网上是这样说的：</p><blockquote><p>The kernel’s family of printf() functions has a special format specifier for IP-addresses (%pI4 for IPv4-addresses, %pI6 for IPv6).</p></blockquote><p>也就是说这种格式仅供内核来使用，一般用户程序是不能用的。</p>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;用户态&quot;&gt;&lt;a href=&quot;#用户态&quot; class=&quot;headerlink&quot; title=&quot;用户态&quot;&gt;&lt;/a&gt;用户态&lt;/h2&gt;&lt;p&gt;今天看netfilter的源码，发现一个打印点分十进制的好方法：使用NIPQUAD和NIPQUAD_FMT宏：&lt;/p&gt;
&lt;figure class=&quot;highlight c&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;meta&quot;&gt;#&lt;span class=&quot;meta-keyword&quot;&gt;define&lt;/span&gt; NIPQUAD_FMT &lt;span class=&quot;meta-string&quot;&gt;&amp;quot;%u.%u.%u.%u&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;meta&quot;&gt;#&lt;span class=&quot;meta-keyword&quot;&gt;define&lt;/span&gt; NIPQUAD(addr) \&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; ((&lt;span class=&quot;keyword&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;char&lt;/span&gt; *)&amp;amp;addr)[&lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;], \&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; ((&lt;span class=&quot;keyword&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;char&lt;/span&gt; *)&amp;amp;addr)[&lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;], \&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; ((&lt;span class=&quot;keyword&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;char&lt;/span&gt; *)&amp;amp;addr)[&lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;], \&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; ((&lt;span class=&quot;keyword&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;char&lt;/span&gt; *)&amp;amp;addr)[&lt;span class=&quot;number&quot;&gt;3&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;</summary>
    
    
    
    <category term="知识点" scheme="https://blog.wanghaoyu.cn/categories/%E7%9F%A5%E8%AF%86%E7%82%B9/"/>
    
    
    <category term="Linux" scheme="https://blog.wanghaoyu.cn/tags/Linux/"/>
    
    <category term="IPv4" scheme="https://blog.wanghaoyu.cn/tags/IPv4/"/>
    
  </entry>
  
  <entry>
    <title>编译OpenWRT(非官方版)</title>
    <link href="https://blog.wanghaoyu.cn/2017/01/19/make-openwrt/"/>
    <id>https://blog.wanghaoyu.cn/2017/01/19/make-openwrt/</id>
    <published>2017-01-19T08:22:02.000Z</published>
    <updated>2024-02-27T06:16:02.140Z</updated>
    
    <content type="html"><![CDATA[<h2 id="编译环境"><a href="#编译环境" class="headerlink" title="编译环境"></a>编译环境</h2><ul><li>Ubuntu 12.04 32bit</li><li>Windows Subsystem for Linux</li></ul><h2 id="项目源码"><a href="#项目源码" class="headerlink" title="项目源码"></a>项目源码</h2><p>项目源码托管在GitHub上，地址如下：**<a href="https://github.com/rssnsj/openwrt-hc5x61">openwrt-hc5x61</a>**。</p><p>本项目不是官方支持的OpenWRT，因此OpenWRT版本比较旧，有较大的差别。</p><p>本项目使用的是<a href="http://www.hiwifi.com/">极路由</a>的极路由2路由器。</p><h2 id="前提工作"><a href="#前提工作" class="headerlink" title="前提工作"></a>前提工作</h2><ol><li>必装工具(仅限Debian和Ubuntu)<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">sudo apt-get install build-essential git subversion wget flex gettext libncurses5-dev unzip gawk liblzma-dev zlib1g-dev ccache u-boot-tools</span><br></pre></td></tr></table></figure></li><li>Checkout项目代码<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">git clone https://github.com/rssnsj/openwrt-hc5x61.git</span><br></pre></td></tr></table></figure><a id="more"></a><h2 id="编译"><a href="#编译" class="headerlink" title="编译"></a>编译</h2>注意：建议国内用户在编译过程中全程翻墙，因为会从国外下载一些额外的软件包。推荐使用<strong>proxychains</strong>，具体使用方法，请自行Google<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></pre></td><td class="code"><pre><span class="line">cd openwrt-hc5x61</span><br><span class="line"><span class="meta">#</span><span class="bash"> 将hc5761(极2)设置为编译目标对象</span></span><br><span class="line">ln -s ./config-hiwifi-hc5761 ./config-hiwifi</span><br><span class="line"><span class="meta">#</span><span class="bash"> 可自定义自己需要的额外功能</span></span><br><span class="line">make menuconfig</span><br><span class="line"><span class="meta">#</span><span class="bash"> 编译</span></span><br><span class="line">make</span><br></pre></td></tr></table></figure>编译成功后，目标文件在<code>openwrt-ramips/bin/ramips/openwrt-ramips-mt7620a-hc5761-squashfs-sysupgrade.bin</code></li></ol><p><strong>注意1：</strong><br>编译过程中，会有一些组件下载失败，手动下载同名的文件放到<code>openwrt-ramips/dl</code>下面，再执行<code>make</code>继续编译。<br><strong>注意2：</strong><br>pdnsd组件原项目地址已经失效，请单独下载，注意切换分支。<br>文件名为：pdnsd-1.2.9a-par-a8e46ccba7b0fa2230d6c42ab6dcd92926f6c21d.tar.gz<br>commit Hash:a8e46ccba7b0fa2230d6c42ab6dcd92926f6c21d</p><h2 id="刷机"><a href="#刷机" class="headerlink" title="刷机"></a>刷机</h2><h3 id="FTP"><a href="#FTP" class="headerlink" title="FTP"></a>FTP</h3><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 登录路由器(IP地址是路由器地址)</span></span><br><span class="line">ssh root@192.168.199.1</span><br><span class="line">cd /tmp</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 替换U-boot以防止万一刷砖无法直接tftp刷root固件</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 已经替换的可以忽略，以极2为例，其他请参考项目文档</span></span><br><span class="line">wget http://rssn.cn/roms/uboot/HC5761-uboot.bin</span><br><span class="line">mtd write HC5761-uboot.bin u-boot</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 下载固件</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 可以通过FTP等方式，将刚刚生成的文件放到/tmp文件夹下</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 刷机</span></span><br><span class="line">sysupgrade -F -n openwrt-ramips-mt7620a-hc5761-squashfs-sysupgrade.bin</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;编译环境&quot;&gt;&lt;a href=&quot;#编译环境&quot; class=&quot;headerlink&quot; title=&quot;编译环境&quot;&gt;&lt;/a&gt;编译环境&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Ubuntu 12.04 32bit&lt;/li&gt;
&lt;li&gt;Windows Subsystem for Linux&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;项目源码&quot;&gt;&lt;a href=&quot;#项目源码&quot; class=&quot;headerlink&quot; title=&quot;项目源码&quot;&gt;&lt;/a&gt;项目源码&lt;/h2&gt;&lt;p&gt;项目源码托管在GitHub上，地址如下：**&lt;a href=&quot;https://github.com/rssnsj/openwrt-hc5x61&quot;&gt;openwrt-hc5x61&lt;/a&gt;**。&lt;/p&gt;
&lt;p&gt;本项目不是官方支持的OpenWRT，因此OpenWRT版本比较旧，有较大的差别。&lt;/p&gt;
&lt;p&gt;本项目使用的是&lt;a href=&quot;http://www.hiwifi.com/&quot;&gt;极路由&lt;/a&gt;的极路由2路由器。&lt;/p&gt;
&lt;h2 id=&quot;前提工作&quot;&gt;&lt;a href=&quot;#前提工作&quot; class=&quot;headerlink&quot; title=&quot;前提工作&quot;&gt;&lt;/a&gt;前提工作&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;必装工具(仅限Debian和Ubuntu)&lt;figure class=&quot;highlight shell&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;sudo apt-get install build-essential git subversion wget flex gettext libncurses5-dev unzip gawk liblzma-dev zlib1g-dev ccache u-boot-tools&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/li&gt;
&lt;li&gt;Checkout项目代码&lt;figure class=&quot;highlight shell&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;git clone https://github.com/rssnsj/openwrt-hc5x61.git&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/li&gt;&lt;/ol&gt;</summary>
    
    
    
    <category term="OpenWRT" scheme="https://blog.wanghaoyu.cn/categories/OpenWRT/"/>
    
    
    <category term="OpenWRT" scheme="https://blog.wanghaoyu.cn/tags/OpenWRT/"/>
    
    <category term="Make" scheme="https://blog.wanghaoyu.cn/tags/Make/"/>
    
  </entry>
  
  <entry>
    <title>Java NIO Selector解析</title>
    <link href="https://blog.wanghaoyu.cn/2017/01/09/java-nio-selector/"/>
    <id>https://blog.wanghaoyu.cn/2017/01/09/java-nio-selector/</id>
    <published>2017-01-09T10:41:57.000Z</published>
    <updated>2024-02-27T06:16:02.120Z</updated>
    
    <content type="html"><![CDATA[<h3 id="用法"><a href="#用法" class="headerlink" title="用法"></a>用法</h3><p>Selector的主要方法有三个：</p><ul><li>Selector.open():Selector<br>主要用于创建一个selector</li><li>select():int<br>该方法有两种模式：在阻塞模式下，该方法会一直阻塞，直到超时或者有注册的事件发生，返回发生事件的数量；在非阻塞模式下，会立即返回，如果没有事件发生，则返回0。</li><li>selectedKeys():Set<SelectionKey><br>获取已经就绪的事件的key的集合。</SelectionKey></li></ul><h3 id="Selector-open-原理"><a href="#Selector-open-原理" class="headerlink" title="Selector.open()原理"></a>Selector.open()原理</h3><p>通过阅读<a href="http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/nio/channels/Selector.java?av=f"><strong>java.nio.channels.Selector</strong></a>的源码（如下），可以发现，其调用了<code>SelectorProvider.provider()</code>的<code>openSelector()</code>函数。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> Selector More ...open() <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">    <span class="keyword">return</span> SelectorProvider.provider().openSelector();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>先看<code>SelectorProvider.provider()</code>，在这个函数中，保证了provider有且仅有一个。</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> SelectorProvider <span class="title">provider</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">synchronized</span> (lock) &#123;</span><br><span class="line">        <span class="keyword">if</span> (provider != <span class="keyword">null</span>)   <span class="comment">//保证只有一个</span></span><br><span class="line">            <span class="keyword">return</span> provider;</span><br><span class="line">        <span class="keyword">return</span> AccessController.doPrivileged(</span><br><span class="line">            <span class="keyword">new</span> PrivilegedAction&lt;SelectorProvider&gt;() &#123;</span><br><span class="line">                <span class="function"><span class="keyword">public</span> SelectorProvider <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                    <span class="keyword">if</span> (loadProviderFromProperty())</span><br><span class="line">                        <span class="keyword">return</span> provider;</span><br><span class="line">                    <span class="keyword">if</span> (loadProviderAsService())</span><br><span class="line">                        <span class="keyword">return</span> provider;</span><br><span class="line">                    provider = sun.nio.ch.DefaultSelectorProvider.create();</span><br><span class="line">                    <span class="keyword">return</span> provider;</span><br><span class="line">                &#125;</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><a id="more"></a><p>再看<code>create()</code>函数，在该函数中，区分了操作系统，在Windows下，使用的是<code>sun.nio.ch.PollSelectorProvider</code>类，作为Selector。也就是说，<strong>SelectorProvider.provider()返回了一个PollSelectorProvider对象。</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> SelectorProvider <span class="title">create</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    String osname = AccessController</span><br><span class="line">        .doPrivileged(<span class="keyword">new</span> GetPropertyAction(<span class="string">&quot;os.name&quot;</span>));</span><br><span class="line">    <span class="keyword">if</span> (osname.equals(<span class="string">&quot;SunOS&quot;</span>))</span><br><span class="line">        <span class="keyword">return</span> createProvider(<span class="string">&quot;sun.nio.ch.DevPollSelectorProvider&quot;</span>);</span><br><span class="line">    <span class="keyword">if</span> (osname.equals(<span class="string">&quot;Linux&quot;</span>))</span><br><span class="line">        <span class="keyword">return</span> createProvider(<span class="string">&quot;sun.nio.ch.EPollSelectorProvider&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> sun.nio.ch.PollSelectorProvider();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在<a href="http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/sun/nio/ch/PollSelectorProvider.java"><strong>sun.nio.ch.PollSelectorProvider</strong></a>的<code>openSelector()</code>函数（如下）中，返回了一个PollSelectorImpl的对象。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">PollSelectorProvider</span></span></span><br><span class="line"><span class="class">    <span class="keyword">extends</span> <span class="title">SelectorProviderImpl</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> AbstractSelector <span class="title">openSelector</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> PollSelectorImpl(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Channel <span class="title">inheritedChannel</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> InheritedChannel.getChannel();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在PollSelectorImpl的构造器（如下）中，我发现，其创建了一个pipe，创建函数是<code>IOUtil.makePipe()</code>，但是这个函数是native的，我估计应该是OS提供的一个函数。这个pipe的主要作用是，在select()函数被阻塞的时候，用于唤醒线程使用。</p><p>需要注意的是，虽然函数名称叫做makePipe，但是在Windows平台下实现中，并没有使用pipe，而是使用的TCP连接。</p><p>同时还要注意<code>super</code>函数的参数，第一个<strong>1</strong>是channels总数，第二个<strong>1</strong>是偏移量，说明，在初始化过程中，就已经有一个channel在这个数组中了，channels[0]就是刚刚创建的pipe。这个channel就是用来唤醒select()的。</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></pre></td><td class="code"><pre><span class="line">PollSelectorImpl(SelectorProvider sp) &#123;</span><br><span class="line">    <span class="keyword">super</span>(sp, <span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line">    <span class="keyword">long</span> pipeFds = IOUtil.makePipe(<span class="keyword">false</span>);  <span class="comment">//创建pipe</span></span><br><span class="line">    fd0 = (<span class="keyword">int</span>) (pipeFds &gt;&gt;&gt; <span class="number">32</span>);</span><br><span class="line">    fd1 = (<span class="keyword">int</span>) pipeFds;</span><br><span class="line">    pollWrapper = <span class="keyword">new</span> PollArrayWrapper(INIT_CAP);</span><br><span class="line">    pollWrapper.initInterrupt(fd0, fd1);</span><br><span class="line">    channelArray = <span class="keyword">new</span> SelectionKeyImpl[INIT_CAP];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>综上所述，<code>Selector.open()</code>函数的作用是：<strong>保证了provider有且仅有一个，并返回了一个PollSelectorImpl对象，同时创建了一条pipe</strong>。</p><h3 id="select-原理"><a href="#select-原理" class="headerlink" title="select()原理"></a>select()原理</h3><p>select的执行体是在<a href="http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/sun/nio/ch/PollSelectorImpl.java"><strong><code>sun.nio.ch.PollSelectorImpl</code></strong></a>中的<code>doSelect</code>函数（如下）。</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">int</span> <span class="title">doSelect</span><span class="params">(<span class="keyword">long</span> timeout)</span></span></span><br><span class="line"><span class="function">    <span class="keyword">throws</span> IOException</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (channelArray == <span class="keyword">null</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> ClosedSelectorException();</span><br><span class="line">    processDeregisterQueue();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        begin();</span><br><span class="line">        pollWrapper.poll(totalChannels, <span class="number">0</span>, timeout);    <span class="comment">//在这里轮询</span></span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        end();</span><br><span class="line">    &#125;</span><br><span class="line">    processDeregisterQueue();</span><br><span class="line">    <span class="keyword">int</span> numKeysUpdated = updateSelectedKeys();</span><br><span class="line">    <span class="keyword">if</span> (pollWrapper.getReventOps(<span class="number">0</span>) != <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="comment">// Clear the wakeup pipe</span></span><br><span class="line">        pollWrapper.putReventOps(<span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">        <span class="keyword">synchronized</span> (interruptLock) &#123;</span><br><span class="line">            IOUtil.drain(fd0);</span><br><span class="line">            interruptTriggered = <span class="keyword">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> numKeysUpdated;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>updateSelectedKeys()</code>函数（如下）的实现是在<a href="http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/sun/nio/ch/AbstractPollSelectorImpl.java"><strong>sun.nio.ch.AbstractPollSelectorImpl</strong></a>中。<br>通过<code>getReventOps()</code>函数来判断通道是否有事件发生，若发生，将其加入到<code>selectedKeys</code>中。</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><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Copy the information in the pollfd structs into the opss</span></span><br><span class="line"><span class="comment"> * of the corresponding Channels. Add the ready keys to the</span></span><br><span class="line"><span class="comment"> * ready queue.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">int</span> <span class="title">updateSelectedKeys</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> numKeysUpdated = <span class="number">0</span>;</span><br><span class="line">    <span class="comment">// Skip zeroth entry; it is for interrupts only</span></span><br><span class="line">    <span class="comment">// 第0个是自带的，用于唤醒select()的，因此从1开始循环。</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i=channelOffset; i&lt;totalChannels; i++) &#123;</span><br><span class="line">        <span class="keyword">int</span> rOps = pollWrapper.getReventOps(i);</span><br><span class="line">        <span class="keyword">if</span> (rOps != <span class="number">0</span>) &#123;</span><br><span class="line">            SelectionKeyImpl sk = channelArray[i];</span><br><span class="line">            pollWrapper.putReventOps(i, <span class="number">0</span>);</span><br><span class="line">            <span class="keyword">if</span> (selectedKeys.contains(sk)) &#123;</span><br><span class="line">                <span class="keyword">if</span> (sk.channel.translateAndSetReadyOps(rOps, sk)) &#123;</span><br><span class="line">                    numKeysUpdated++;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                sk.channel.translateAndSetReadyOps(rOps, sk);</span><br><span class="line">                <span class="keyword">if</span> ((sk.nioReadyOps() &amp; sk.nioInterestOps()) != <span class="number">0</span>) &#123;</span><br><span class="line">                    selectedKeys.add(sk);</span><br><span class="line">                    numKeysUpdated++;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> numKeysUpdated;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="selectedKeys-原理"><a href="#selectedKeys-原理" class="headerlink" title="selectedKeys()原理"></a>selectedKeys()原理</h3><p>该方法直接返回publicSelectedKeys这个Set。</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Set&lt;SelectionKey&gt; <span class="title">selectedKeys</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (!isOpen() &amp;&amp; !Util.atBugLevel(<span class="string">&quot;1.4&quot;</span>))</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> ClosedSelectorException();</span><br><span class="line">    <span class="keyword">return</span> publicSelectedKeys;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>publicSelectedKeys在<a href="http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/sun/nio/ch/SelectorImpl.java"><strong>SelectorImpl</strong></a>中的初始化中，与<code>selectedKeys</code>做了绑定，并在<code>updateSelectedKeys()</code>函数中，进行更新。</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> <span class="title">SelectorImpl</span><span class="params">(SelectorProvider sp)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">super</span>(sp);</span><br><span class="line">    keys = <span class="keyword">new</span> HashSet&lt;SelectionKey&gt;();</span><br><span class="line">    selectedKeys = <span class="keyword">new</span> HashSet&lt;SelectionKey&gt;();</span><br><span class="line">    <span class="keyword">if</span> (Util.atBugLevel(<span class="string">&quot;1.4&quot;</span>)) &#123;</span><br><span class="line">        publicKeys = keys;</span><br><span class="line">        publicSelectedKeys = selectedKeys;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        publicKeys = Collections.unmodifiableSet(keys);</span><br><span class="line">        publicSelectedKeys = Util.ungrowableSet(selectedKeys);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h3><ol><li><a href="http://blog.csdn.net/u010412719/article/details/52760004">Java NIO</a></li><li><a href="http://ifeve.com/java-nio-all/">Java NIO 系列教程</a></li><li><a href="http://grepcode.com/snapshot/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/">Java源码</a></li></ol>]]></content>
    
    
    <summary type="html">&lt;h3 id=&quot;用法&quot;&gt;&lt;a href=&quot;#用法&quot; class=&quot;headerlink&quot; title=&quot;用法&quot;&gt;&lt;/a&gt;用法&lt;/h3&gt;&lt;p&gt;Selector的主要方法有三个：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Selector.open():Selector&lt;br&gt;主要用于创建一个selector&lt;/li&gt;
&lt;li&gt;select():int&lt;br&gt;该方法有两种模式：在阻塞模式下，该方法会一直阻塞，直到超时或者有注册的事件发生，返回发生事件的数量；在非阻塞模式下，会立即返回，如果没有事件发生，则返回0。&lt;/li&gt;
&lt;li&gt;selectedKeys():Set&lt;SelectionKey&gt;&lt;br&gt;获取已经就绪的事件的key的集合。&lt;/SelectionKey&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;Selector-open-原理&quot;&gt;&lt;a href=&quot;#Selector-open-原理&quot; class=&quot;headerlink&quot; title=&quot;Selector.open()原理&quot;&gt;&lt;/a&gt;Selector.open()原理&lt;/h3&gt;&lt;p&gt;通过阅读&lt;a href=&quot;http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/nio/channels/Selector.java?av=f&quot;&gt;&lt;strong&gt;java.nio.channels.Selector&lt;/strong&gt;&lt;/a&gt;的源码（如下），可以发现，其调用了&lt;code&gt;SelectorProvider.provider()&lt;/code&gt;的&lt;code&gt;openSelector()&lt;/code&gt;函数。&lt;/p&gt;
&lt;figure class=&quot;highlight java&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;static&lt;/span&gt; Selector More ...open() &lt;span class=&quot;keyword&quot;&gt;throws&lt;/span&gt; IOException &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; SelectorProvider.provider().openSelector();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;先看&lt;code&gt;SelectorProvider.provider()&lt;/code&gt;，在这个函数中，保证了provider有且仅有一个。&lt;/p&gt;
&lt;figure class=&quot;highlight java&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;17&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;function&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;static&lt;/span&gt; SelectorProvider &lt;span class=&quot;title&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;params&quot;&gt;()&lt;/span&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;synchronized&lt;/span&gt; (lock) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (provider != &lt;span class=&quot;keyword&quot;&gt;null&lt;/span&gt;)   &lt;span class=&quot;comment&quot;&gt;//保证只有一个&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; provider;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; AccessController.doPrivileged(&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &lt;span class=&quot;keyword&quot;&gt;new&lt;/span&gt; PrivilegedAction&amp;lt;SelectorProvider&amp;gt;() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                &lt;span class=&quot;function&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;public&lt;/span&gt; SelectorProvider &lt;span class=&quot;title&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;params&quot;&gt;()&lt;/span&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (loadProviderFromProperty())&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                        &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; provider;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (loadProviderAsService())&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                        &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; provider;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    provider = sun.nio.ch.DefaultSelectorProvider.create();&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                    &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; provider;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;</summary>
    
    
    
    <category term="知识点" scheme="https://blog.wanghaoyu.cn/categories/%E7%9F%A5%E8%AF%86%E7%82%B9/"/>
    
    
    <category term="Java" scheme="https://blog.wanghaoyu.cn/tags/Java/"/>
    
    <category term="NIO" scheme="https://blog.wanghaoyu.cn/tags/NIO/"/>
    
    <category term="Selector" scheme="https://blog.wanghaoyu.cn/tags/Selector/"/>
    
  </entry>
  
  <entry>
    <title>Java NIO Buffer解析</title>
    <link href="https://blog.wanghaoyu.cn/2017/01/09/java-nio-buffer/"/>
    <id>https://blog.wanghaoyu.cn/2017/01/09/java-nio-buffer/</id>
    <published>2017-01-09T10:36:02.000Z</published>
    <updated>2024-02-27T06:16:02.120Z</updated>
    
    <content type="html"><![CDATA[<h3 id="主要属性"><a href="#主要属性" class="headerlink" title="主要属性"></a>主要属性</h3><ul><li><p>capacity<br>作为一个内存块，Buffer有一个固定的大小值，也叫<code>capacity</code>.你只能往里写capacity个byte、long、char等类型。一旦Buffer满了，需要将其清空(通过读数据或者清除数据)才能继续写数据往里写数据。</p></li><li><p>position<br>当你写数据到Buffer中时，position表示当前的位置。初始的position值为0。position最大可为capacity–1。当读取数据时，也是从某个特定位置读。当将Buffer从写模式切换到读模式，position会被重置为0。当从Buffer的position处读取数据时，position向前移动到下一个可读的位置。</p></li><li><p>limit<br>在写模式下，Buffer的limit表示你最多能往Buffer里写多少数据。写模式下，limit等于Buffer的capacity。当切换Buffer到读模式时，limit表示你最多能读到多少数据。因此，当切换Buffer到读模式时，limit会被设置成写模式下的position值。换句话说，你能读到之前写入的所有数据。</p></li><li><p>三者关系如下：</p></li></ul><p><img src="/.cn//1.png"></p><h3 id="Buffer构造器"><a href="#Buffer构造器" class="headerlink" title="Buffer构造器"></a>Buffer构造器</h3><p>尽管Buffer是一个抽象类，但是依然有自己的构造器。<br>其主要作用是初始化抽象类的成员和子类的初始化。</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></pre></td><td class="code"><pre><span class="line">Buffer(<span class="keyword">int</span> mark, <span class="keyword">int</span> pos, <span class="keyword">int</span> lim, <span class="keyword">int</span> cap) &#123;       <span class="comment">// package-private</span></span><br><span class="line">    <span class="keyword">if</span> (cap &lt; <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;Negative capacity: &quot;</span> + cap);</span><br><span class="line">    <span class="keyword">this</span>.capacity = cap;</span><br><span class="line">    limit(lim);</span><br><span class="line">    position(pos);</span><br><span class="line">    <span class="keyword">if</span> (mark &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (mark &gt; pos)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;mark &gt; position: (&quot;</span></span><br><span class="line">                                               + mark + <span class="string">&quot; &gt; &quot;</span> + pos + <span class="string">&quot;)&quot;</span>);</span><br><span class="line">        <span class="keyword">this</span>.mark = mark;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><a id="more"></a><h3 id="Buffer常用方法"><a href="#Buffer常用方法" class="headerlink" title="Buffer常用方法"></a>Buffer常用方法</h3><ul><li>allocate()        分配一段空间的Buffer对象</li><li>put()             用于往Buffer中添加元素</li><li>flip()            用于将写模式转化为读模式</li><li>hasRemaining()    判断Buffer中是否还有元素可读</li><li>get()             读取Buffer中position位置的元素</li><li>clear()           清空Buffer </li><li>compact()         清除已读取的部分</li></ul><h3 id="关于Buffer的数据结构"><a href="#关于Buffer的数据结构" class="headerlink" title="关于Buffer的数据结构"></a>关于Buffer的数据结构</h3><p>通过查看<a href="http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/nio/IntBuffer.java?av=f"><strong>java.nio.IntBuffer</strong></a>这个类的初始化函数及成员（如下），可以确定，<strong>Buffer的基本数据结构是数组！</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> <span class="keyword">int</span>[] hb;                  <span class="comment">// Non-null only for heap buffers</span></span><br><span class="line"><span class="keyword">final</span> <span class="keyword">int</span> offset;</span><br><span class="line"><span class="keyword">boolean</span> isReadOnly;                 <span class="comment">// Valid only for heap buffers</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Creates a new buffer with the given mark, position, limit, capacity,</span></span><br><span class="line"><span class="comment">// backing array, and array offset</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line">IntBuffer(<span class="keyword">int</span> mark, <span class="keyword">int</span> pos, <span class="keyword">int</span> lim, <span class="keyword">int</span> cap,   <span class="comment">// package-private</span></span><br><span class="line">          <span class="keyword">int</span>[] hb, <span class="keyword">int</span> offset)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">super</span>(mark, pos, lim, cap);</span><br><span class="line">    <span class="keyword">this</span>.hb = hb;</span><br><span class="line">    <span class="keyword">this</span>.offset = offset;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="关于compact函数的实现"><a href="#关于compact函数的实现" class="headerlink" title="关于compact函数的实现"></a>关于compact函数的实现</h3><p>这个函数我是比较感兴趣的，他与<code>clear()</code>的区别是只删除已读的部分。</p><p>通过查看<a href="http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/nio/HeapIntBuffer.java?av=f"><strong>java.nio.HeapIntBuffer</strong></a>，该类是<strong>java.nio.IntBuffer</strong>的子类，可以得知compact函数的实现。</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> IntBuffer <span class="title">compact</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 将剩余的没有访问的元素拷贝到Buffer从零开始的位置</span></span><br><span class="line">    System.arraycopy(hb, ix(position()), hb, ix(<span class="number">0</span>), remaining());</span><br><span class="line">    <span class="comment">// 重置position</span></span><br><span class="line">    position(remaining());</span><br><span class="line">    <span class="comment">// 重置limit</span></span><br><span class="line">    limit(capacity());</span><br><span class="line">    <span class="comment">// 删除mark</span></span><br><span class="line">    discardMark();</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h3><ol><li><a href="http://blog.csdn.net/u010412719/article/details/52760004">Java NIO</a></li><li><a href="http://ifeve.com/java-nio-all/">Java NIO 系列教程</a></li><li><a href="http://grepcode.com/snapshot/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/">Java源码</a></li></ol>]]></content>
    
    
    <summary type="html">&lt;h3 id=&quot;主要属性&quot;&gt;&lt;a href=&quot;#主要属性&quot; class=&quot;headerlink&quot; title=&quot;主要属性&quot;&gt;&lt;/a&gt;主要属性&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;capacity&lt;br&gt;作为一个内存块，Buffer有一个固定的大小值，也叫&lt;code&gt;capacity&lt;/code&gt;.你只能往里写capacity个byte、long、char等类型。一旦Buffer满了，需要将其清空(通过读数据或者清除数据)才能继续写数据往里写数据。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;position&lt;br&gt;当你写数据到Buffer中时，position表示当前的位置。初始的position值为0。position最大可为capacity–1。当读取数据时，也是从某个特定位置读。当将Buffer从写模式切换到读模式，position会被重置为0。当从Buffer的position处读取数据时，position向前移动到下一个可读的位置。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;limit&lt;br&gt;在写模式下，Buffer的limit表示你最多能往Buffer里写多少数据。写模式下，limit等于Buffer的capacity。当切换Buffer到读模式时，limit表示你最多能读到多少数据。因此，当切换Buffer到读模式时，limit会被设置成写模式下的position值。换句话说，你能读到之前写入的所有数据。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;三者关系如下：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;/.cn//1.png&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;Buffer构造器&quot;&gt;&lt;a href=&quot;#Buffer构造器&quot; class=&quot;headerlink&quot; title=&quot;Buffer构造器&quot;&gt;&lt;/a&gt;Buffer构造器&lt;/h3&gt;&lt;p&gt;尽管Buffer是一个抽象类，但是依然有自己的构造器。&lt;br&gt;其主要作用是初始化抽象类的成员和子类的初始化。&lt;/p&gt;
&lt;figure class=&quot;highlight java&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;13&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;Buffer(&lt;span class=&quot;keyword&quot;&gt;int&lt;/span&gt; mark, &lt;span class=&quot;keyword&quot;&gt;int&lt;/span&gt; pos, &lt;span class=&quot;keyword&quot;&gt;int&lt;/span&gt; lim, &lt;span class=&quot;keyword&quot;&gt;int&lt;/span&gt; cap) &amp;#123;       &lt;span class=&quot;comment&quot;&gt;// package-private&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (cap &amp;lt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &lt;span class=&quot;keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;new&lt;/span&gt; IllegalArgumentException(&lt;span class=&quot;string&quot;&gt;&amp;quot;Negative capacity: &amp;quot;&lt;/span&gt; + cap);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;this&lt;/span&gt;.capacity = cap;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    limit(lim);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    position(pos);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (mark &amp;gt;= &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; (mark &amp;gt; pos)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;            &lt;span class=&quot;keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;new&lt;/span&gt; IllegalArgumentException(&lt;span class=&quot;string&quot;&gt;&amp;quot;mark &amp;gt; position: (&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;                                               + mark + &lt;span class=&quot;string&quot;&gt;&amp;quot; &amp;gt; &amp;quot;&lt;/span&gt; + pos + &lt;span class=&quot;string&quot;&gt;&amp;quot;)&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;        &lt;span class=&quot;keyword&quot;&gt;this&lt;/span&gt;.mark = mark;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;</summary>
    
    
    
    <category term="知识点" scheme="https://blog.wanghaoyu.cn/categories/%E7%9F%A5%E8%AF%86%E7%82%B9/"/>
    
    
    <category term="Java" scheme="https://blog.wanghaoyu.cn/tags/Java/"/>
    
    <category term="NIO" scheme="https://blog.wanghaoyu.cn/tags/NIO/"/>
    
    <category term="Buffer" scheme="https://blog.wanghaoyu.cn/tags/Buffer/"/>
    
  </entry>
  
</feed>
