<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>一隅</title>
  
  <subtitle>记录生活</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://sqduan.github.io/"/>
  <updated>2025-01-15T14:57:03.324Z</updated>
  <id>http://sqduan.github.io/</id>
  
  <author>
    <name>Shiqi Duan</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>PS抠图操作</title>
    <link href="http://sqduan.github.io/2025/01/15/%E7%94%9F%E6%B4%BB/%E7%BE%8E%E6%9C%AF/PS%EF%BC%9A%E6%8A%A0%E5%9B%BE%E6%93%8D%E4%BD%9C/"/>
    <id>http://sqduan.github.io/2025/01/15/生活/美术/PS：抠图操作/</id>
    <published>2025-01-15T14:25:01.594Z</published>
    <updated>2025-01-15T14:57:03.324Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p><img src="https://fiverr-res.cloudinary.com/images/q_auto,f_auto/gigs/41694069/original/66a90c41f4dbbe1fcbfe6c4a4a4f03dc5ebea34b/create-a-pixel-art-background-and-scenery.png" alt="" align=center /></p><p>本文将总结一些常用PS抠图操作。</p><a id="more"></a><h2 id="扣毛发"><a href="#扣毛发" class="headerlink" title="扣毛发"></a>扣毛发</h2><p>第一步：创建一个红色的纯色图层作为背景</p><p>第二步：使用普通套索工具（L），框选头发所在部位，然后点击上方“选择并遮住”</p><p>第三步：右侧视图模式视图选择“图层”，输出到选择“新建带有图层蒙版的图层”</p><p>第四步：使用左侧第二个调整边缘画笔，涂抹直至所有的头发都被覆盖</p><p>第五步：勾选右侧智能半径，移动边缘视情况调节，此处调节为-5，最后点击确定</p><h3 id="字符字体"><a href="#字符字体" class="headerlink" title="字符字体"></a>字符字体</h3><p><a href="https://www.dafont.com/bitmap.php">像素字符网站</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;https://fiverr-res.cloudinary.com/images/q_auto,f_auto/gigs/41694069/original/66a90c41f4dbbe1fcbfe6c4a4a4f03dc5ebea34b/create-a-pixel-art-background-and-scenery.png&quot; alt=&quot;&quot; align=center /&gt;&lt;/p&gt;
&lt;p&gt;本文将总结一些常用PS抠图操作。&lt;/p&gt;
    
    </summary>
    
      <category term="生活" scheme="http://sqduan.github.io/categories/%E7%94%9F%E6%B4%BB/"/>
    
      <category term="美术" scheme="http://sqduan.github.io/categories/%E7%94%9F%E6%B4%BB/%E7%BE%8E%E6%9C%AF/"/>
    
      <category term="PS" scheme="http://sqduan.github.io/categories/%E7%94%9F%E6%B4%BB/%E7%BE%8E%E6%9C%AF/PS/"/>
    
    
  </entry>
  
  <entry>
    <title>Blender渲染</title>
    <link href="http://sqduan.github.io/2025/01/03/%E7%94%9F%E6%B4%BB/Blender/Blender%E6%B8%B2%E6%9F%93/"/>
    <id>http://sqduan.github.io/2025/01/03/生活/Blender/Blender渲染/</id>
    <published>2025-01-03T14:13:21.009Z</published>
    <updated>2025-01-03T14:52:42.430Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>记录一下blender中的渲染过程</p><a id="more"></a>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;记录一下blender中的渲染过程&lt;/p&gt;
    
    </summary>
    
      <category term="生活" scheme="http://sqduan.github.io/categories/%E7%94%9F%E6%B4%BB/"/>
    
      <category term="Blender" scheme="http://sqduan.github.io/categories/%E7%94%9F%E6%B4%BB/Blender/"/>
    
    
  </entry>
  
  <entry>
    <title>AE效果大全</title>
    <link href="http://sqduan.github.io/2025/01/01/%E7%94%9F%E6%B4%BB/%E7%BE%8E%E6%9C%AF/AE%EF%BC%9A%E6%95%88%E6%9E%9C%E5%A4%A7%E5%85%A8/"/>
    <id>http://sqduan.github.io/2025/01/01/生活/美术/AE：效果大全/</id>
    <published>2025-01-01T12:45:26.275Z</published>
    <updated>2025-01-01T14:31:46.475Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p><img src="https://github.com/sqduan/hexoimg/blob/master/65c256a3ef12d23439db6f0816b92e60.gif?raw=true" alt="" align=center /></p><p>本文将整理AE常用到的一些效果</p><a id="more"></a><h2 id="扭曲效果"><a href="#扭曲效果" class="headerlink" title="扭曲效果"></a>扭曲效果</h2><h3 id="湍流置换"><a href="#湍流置换" class="headerlink" title="湍流置换"></a>湍流置换</h3><div class="table-container"><table><thead><tr><th>参数</th><th>作用</th><th></th></tr></thead><tbody><tr><td>数量</td><td></td><td></td></tr><tr><td>大小</td><td></td><td></td></tr><tr><td>复杂度</td><td></td><td></td></tr><tr><td>演化</td><td></td></tr></tbody></table></div><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><div id="footnotes"><hr><div id="footnotelist"><ol><li id="fn:1"><a href="https://www.youtube.com/watch?v=b0JesMPdeIk&amp;ab_channel=ChrisOVEMERY">Wiggly/Jittery/shaking animation, how and why - AFTER EFFECTS TUTORIAL</a><a href="#fnref:1" rev="footnote"> ↩</a></li><li id="fn:2"><a href="https://www.bilibili.com/video/BV11v411v7ud/?vd_source=0ac0f268155ca9fe0a7df16b11051b29">一个简单的扭曲效果居然还能这么玩？举一反三一下看看ae湍流置换还有哪些玩法。_哔哩哔哩_bilibili</a><a href="#fnref:2" rev="footnote"> ↩</a></li></ol></div></div>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;https://github.com/sqduan/hexoimg/blob/master/65c256a3ef12d23439db6f0816b92e60.gif?raw=true&quot; alt=&quot;&quot; align=center /&gt;&lt;/p&gt;
&lt;p&gt;本文将整理AE常用到的一些效果&lt;/p&gt;
    
    </summary>
    
      <category term="生活" scheme="http://sqduan.github.io/categories/%E7%94%9F%E6%B4%BB/"/>
    
      <category term="AE" scheme="http://sqduan.github.io/categories/%E7%94%9F%E6%B4%BB/AE/"/>
    
    
  </entry>
  
  <entry>
    <title>Blender 光</title>
    <link href="http://sqduan.github.io/2024/12/03/%E7%94%9F%E6%B4%BB/Blender/Blender%20%E5%85%89/"/>
    <id>http://sqduan.github.io/2024/12/03/生活/Blender/Blender 光/</id>
    <published>2024-12-03T13:15:28.747Z</published>
    <updated>2024-12-03T14:25:57.700Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><blockquote><p>上帝说：要有光</p></blockquote><a id="more"></a>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;上帝说：要有光&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="生活" scheme="http://sqduan.github.io/categories/%E7%94%9F%E6%B4%BB/"/>
    
      <category term="Blender" scheme="http://sqduan.github.io/categories/%E7%94%9F%E6%B4%BB/Blender/"/>
    
    
  </entry>
  
  <entry>
    <title>Blender材质</title>
    <link href="http://sqduan.github.io/2024/10/17/%E7%94%9F%E6%B4%BB/Blender/Blender%E6%9D%90%E8%B4%A8/"/>
    <id>http://sqduan.github.io/2024/10/17/生活/Blender/Blender材质/</id>
    <published>2024-10-17T13:45:41.847Z</published>
    <updated>2024-10-17T14:35:22.488Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>本文将介绍如何在Blender中创建并添加材质</p><a id="more"></a><p>材质有三类关键的参数：</p><div class="table-container"><table><thead><tr><th>参数</th><th>解释</th><th>子参数</th><th>功能</th></tr></thead><tbody><tr><td>Surface</td><td>使用最多的一类参数，决定物体表面材质，例如纸张、木头或金属</td><td>Shader</td><td>选择shader，默认是Principled BSDF</td></tr><tr><td>Volume</td><td>决定物体内填充的材质，通常用于创造烟雾、云、液体等</td><td></td><td></td></tr><tr><td>Displacement</td><td>网格的偏移，通常在创建一些具有凹凸纹理（例如砖墙）时使用</td><td></td></tr></tbody></table></div>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本文将介绍如何在Blender中创建并添加材质&lt;/p&gt;
    
    </summary>
    
      <category term="生活" scheme="http://sqduan.github.io/categories/%E7%94%9F%E6%B4%BB/"/>
    
      <category term="Blender" scheme="http://sqduan.github.io/categories/%E7%94%9F%E6%B4%BB/Blender/"/>
    
    
  </entry>
  
  <entry>
    <title>Blender Modifier总结</title>
    <link href="http://sqduan.github.io/2024/10/13/%E7%94%9F%E6%B4%BB/Blender/Blender%20Modifier/"/>
    <id>http://sqduan.github.io/2024/10/13/生活/Blender/Blender Modifier/</id>
    <published>2024-10-13T14:16:46.009Z</published>
    <updated>2024-10-20T14:49:38.259Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>在Blender中，我们通过modifier为物体添加一些特定的属性，本文将介绍最常用的一些modifier</p><a id="more"></a><h2 id="生成"><a href="#生成" class="headerlink" title="生成"></a>生成</h2><p>生成类的modifier一般与物体的生成及形变有关</p><h3 id="Subdivision-Surface-细分曲面"><a href="#Subdivision-Surface-细分曲面" class="headerlink" title="Subdivision Surface (细分曲面)"></a>Subdivision Surface (细分曲面)</h3><p>将一个物体的表面使用插值进行光滑化处理，从而得到一个高模</p><h3 id="Solidify（固体化）"><a href="#Solidify（固体化）" class="headerlink" title="Solidify（固体化）"></a>Solidify（固体化）</h3><p>给予物体厚度</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;在Blender中，我们通过modifier为物体添加一些特定的属性，本文将介绍最常用的一些modifier&lt;/p&gt;
    
    </summary>
    
      <category term="生活" scheme="http://sqduan.github.io/categories/%E7%94%9F%E6%B4%BB/"/>
    
      <category term="Blender" scheme="http://sqduan.github.io/categories/%E7%94%9F%E6%B4%BB/Blender/"/>
    
    
  </entry>
  
  <entry>
    <title>AE常用快捷键</title>
    <link href="http://sqduan.github.io/2024/10/13/%E7%94%9F%E6%B4%BB/%E7%BE%8E%E6%9C%AF/AE%EF%BC%9A%E5%B8%B8%E7%94%A8%E5%BF%AB%E6%8D%B7%E9%94%AE/"/>
    <id>http://sqduan.github.io/2024/10/13/生活/美术/AE：常用快捷键/</id>
    <published>2024-10-13T04:15:54.853Z</published>
    <updated>2025-01-05T14:45:20.671Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p><img src="https://github.com/sqduan/hexoimg/blob/master/65c256a3ef12d23439db6f0816b92e60.gif?raw=true" alt="" align=center /></p><p>总结AE中常用的快捷键</p><a id="more"></a><h2 id="基本快捷键"><a href="#基本快捷键" class="headerlink" title="基本快捷键"></a>基本快捷键</h2><div class="table-container"><table><thead><tr><th>快捷键</th><th>功能</th></tr></thead><tbody><tr><td>V</td><td>选取工具</td></tr><tr><td></td><td></td></tr><tr><td></td></tr></tbody></table></div><h2 id="合成属性"><a href="#合成属性" class="headerlink" title="合成属性"></a>合成属性</h2><div class="table-container"><table><thead><tr><th>快捷键</th><th>功能</th></tr></thead><tbody><tr><td>P</td><td>位置</td></tr><tr><td>T</td><td>透明度</td></tr></tbody></table></div><h2 id="时间轴与合成"><a href="#时间轴与合成" class="headerlink" title="时间轴与合成"></a>时间轴与合成</h2><div class="table-container"><table><thead><tr><th>快捷键</th><th>功能</th></tr></thead><tbody><tr><td>Ctrl Shift D</td><td>将合成拆分成两个</td></tr></tbody></table></div>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;https://github.com/sqduan/hexoimg/blob/master/65c256a3ef12d23439db6f0816b92e60.gif?raw=true&quot; alt=&quot;&quot; align=center /&gt;&lt;/p&gt;
&lt;p&gt;总结AE中常用的快捷键&lt;/p&gt;
    
    </summary>
    
      <category term="生活" scheme="http://sqduan.github.io/categories/%E7%94%9F%E6%B4%BB/"/>
    
      <category term="AE" scheme="http://sqduan.github.io/categories/%E7%94%9F%E6%B4%BB/AE/"/>
    
    
  </entry>
  
  <entry>
    <title>Blender常用快捷键</title>
    <link href="http://sqduan.github.io/2024/10/13/%E7%94%9F%E6%B4%BB/Blender/Blender%E5%B8%B8%E7%94%A8%E5%BF%AB%E6%8D%B7%E9%94%AE/"/>
    <id>http://sqduan.github.io/2024/10/13/生活/Blender/Blender常用快捷键/</id>
    <published>2024-10-13T02:04:49.706Z</published>
    <updated>2025-03-09T15:15:10.757Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>记录Blender中自己常用的快捷键</p><a id="more"></a><h2 id="对象选择"><a href="#对象选择" class="headerlink" title="对象选择"></a>对象选择</h2><div class="table-container"><table><thead><tr><th>快捷键</th><th>功能</th><th>图片</th></tr></thead><tbody><tr><td>鼠标左键/<code>shift</code></td><td>选择</td><td></td></tr><tr><td>1/2/3</td><td>选择模式：点/线/面</td><td></td></tr><tr><td><code>ctrl + i</code></td><td>反向选择</td><td></td></tr><tr><td><code>H</code></td><td>隐藏对象</td></tr></tbody></table></div><h2 id="对象关系"><a href="#对象关系" class="headerlink" title="对象关系"></a>对象关系</h2><div class="table-container"><table><thead><tr><th>快捷键</th><th>功能</th><th>图片</th></tr></thead><tbody><tr><td><code>p</code></td><td>拆分对象</td><td></td></tr><tr><td><code>ctrl + j</code></td><td>合并对象</td></tr></tbody></table></div><h2 id="对象创建"><a href="#对象创建" class="headerlink" title="对象创建"></a>对象创建</h2><div class="table-container"><table><thead><tr><th>快捷键</th><th>功能</th><th>图片</th></tr></thead><tbody><tr><td><code>shift + a</code></td><td>创建新对象</td><td></td></tr><tr><td><code>shift + d</code></td><td>复制新对象</td></tr></tbody></table></div><h2 id="对象位置"><a href="#对象位置" class="headerlink" title="对象位置"></a>对象位置</h2><div class="table-container"><table><thead><tr><th>快捷键</th><th>功能</th><th>备注</th></tr></thead><tbody><tr><td><code>g + xyz</code></td><td>将物体沿着x、y、z轴移动</td><td>如果按住ctrl，是网格捕捉移动</td></tr><tr><td><code>shift + TAB</code></td><td>移动时吸附</td></tr></tbody></table></div><h2 id="对象查看"><a href="#对象查看" class="headerlink" title="对象查看"></a>对象查看</h2><div class="table-container"><table><thead><tr><th>快捷键</th><th>功能</th><th>图片</th></tr></thead><tbody><tr><td><code>alt + z</code></td><td>打开x-ray</td></tr></tbody></table></div><h2 id="轮盘"><a href="#轮盘" class="headerlink" title="轮盘"></a>轮盘</h2><div class="table-container"><table><thead><tr><th>快捷键</th><th>功能</th><th>图片</th></tr></thead><tbody><tr><td><code>shift + s</code></td><td>打开光标轮盘（可以设置光标的位置到指定对象上）</td><td></td></tr><tr><td><code>z</code></td><td>着色轮盘</td></tr></tbody></table></div><h2 id="编辑"><a href="#编辑" class="headerlink" title="编辑"></a>编辑</h2><div class="table-container"><table><thead><tr><th>快捷键</th><th>功能</th><th>备注</th></tr></thead><tbody><tr><td>Tab</td><td>切换编辑模式</td><td>如果按1/2/3可以切换点线面选择</td></tr><tr><td>X</td><td>删除</td><td></td></tr><tr><td>shift-s</td><td>打开锚点设置轮盘</td><td></td></tr><tr><td>ctrl-r</td><td>Loop cut</td><td>在某个面上切分一条线，细分模型</td></tr><tr><td>ctrl-b</td><td></td><td>倒角，和ctrl-r结合使用可以创建细分面</td></tr><tr><td>i</td><td></td><td>选中某个面之后缩放</td></tr><tr><td>e</td><td></td><td>将某个面挤出</td></tr></tbody></table></div><h3 id="编辑模式下操作"><a href="#编辑模式下操作" class="headerlink" title="编辑模式下操作"></a>编辑模式下操作</h3><div class="table-container"><table><thead><tr><th>键位</th><th>功能</th><th>图片</th></tr></thead><tbody><tr><td>Tab<br />选择Select mode为face select<br />X delete faces</td><td>删除某个面</td><td></td></tr><tr><td>shift + tab</td><td>开启/关闭吸附功能</td><td></td></tr><tr><td>Alt</td><td>选择某行线或者面</td><td></td></tr><tr><td>o</td><td>Proportional Editing，比例编辑，会同时修改比例圈中所有的点/边/面</td><td></td></tr><tr><td>F</td><td>选择好某些点之后，可以按F进行填充</td></tr></tbody></table></div><h2 id="运动控制"><a href="#运动控制" class="headerlink" title="运动控制"></a>运动控制</h2><div class="table-container"><table><thead><tr><th>快捷键</th><th>功能</th><th>图片</th></tr></thead><tbody><tr><td>shift + F12</td><td>打开time line</td></tr></tbody></table></div><h2 id="菜单"><a href="#菜单" class="headerlink" title="菜单"></a>菜单</h2><div class="table-container"><table><thead><tr><th>快捷键</th><th>功能</th><th>图片</th></tr></thead><tbody><tr><td>N</td><td>切换显示侧栏</td></tr></tbody></table></div><h2 id="视角切换"><a href="#视角切换" class="headerlink" title="视角切换"></a>视角切换</h2><div class="table-container"><table><thead><tr><th>快捷键</th><th>功能</th></tr></thead><tbody><tr><td><code>shift + 左键</code></td><td>平移拖动</td></tr><tr><td></td><td></td></tr><tr><td>~（数字键1旁边的）</td><td>视角转盘</td></tr><tr><td></td><td></td></tr><tr><td>小键盘0</td><td>切换至主摄像机</td></tr><tr><td>Ctrl 0</td><td>切换至选择的摄像机</td></tr><tr><td>Ctrl Alt 0</td><td>将摄像机切换至当前视角</td></tr><tr><td>Shift <code>~</code></td><td>摄像机飞行（wasd控制前后左右，qe控制上下）</td></tr><tr><td>小键盘<code>.</code></td><td>切换至选择的物体</td></tr></tbody></table></div><h2 id="渲染"><a href="#渲染" class="headerlink" title="渲染"></a>渲染</h2><div class="table-container"><table><thead><tr><th>快捷键</th><th>功能</th></tr></thead><tbody><tr><td>F12</td><td>开始渲染</td></tr></tbody></table></div>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;记录Blender中自己常用的快捷键&lt;/p&gt;
    
    </summary>
    
      <category term="生活" scheme="http://sqduan.github.io/categories/%E7%94%9F%E6%B4%BB/"/>
    
      <category term="Blender" scheme="http://sqduan.github.io/categories/%E7%94%9F%E6%B4%BB/Blender/"/>
    
    
  </entry>
  
  <entry>
    <title>构建安全代码</title>
    <link href="http://sqduan.github.io/2024/10/13/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B/%E6%9E%84%E5%BB%BA%E5%AE%89%E5%85%A8%E4%BB%A3%E7%A0%81/"/>
    <id>http://sqduan.github.io/2024/10/13/计算机/软件工程/构建安全代码/</id>
    <published>2024-10-13T01:59:46.522Z</published>
    <updated>2024-10-13T01:59:46.522Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p> 本文将针对编码过程中的一些漏洞进行总结，并给出相关示例</p><a id="more"></a><h2 id="内存操作漏洞"><a href="#内存操作漏洞" class="headerlink" title="内存操作漏洞"></a>内存操作漏洞</h2><h3 id="字符串与数组操作"><a href="#字符串与数组操作" class="headerlink" title="字符串与数组操作"></a>字符串与数组操作</h3><h4 id="缓冲区溢出"><a href="#缓冲区溢出" class="headerlink" title="缓冲区溢出"></a>缓冲区溢出</h4><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></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 class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</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">char</span> name[] = <span class="string">"duanshiqi"</span>;</span><br><span class="line">    <span class="keyword">uint8_t</span> id = <span class="number">0</span>;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"please input the student id: \n"</span>);</span><br><span class="line">    <span class="built_in">scanf</span>(<span class="string">"%d, &amp;id"</span>);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"student is %d, name is %s\n"</span>, id, name);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这是一个典型的缓冲区溢出漏洞，id的类型是unsigned char，但是在<code>scanf</code>中格式却是<code>%d</code>，这将导致当输入较大的数字时，会对<code>id</code>后面的空间进行覆盖，导致输出错误。</p><h4 id="差一错误"><a href="#差一错误" class="headerlink" title="差一错误"></a>差一错误</h4><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">constexpr</span> <span class="keyword">char</span> buf[] = <span class="string">"hello world"</span>;</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">char</span> name[] = <span class="string">"duanshiqi"</span>;</span><br><span class="line">    <span class="keyword">char</span> str[<span class="number">11</span>];</span><br><span class="line">    <span class="built_in">memcpy</span>(str, buf, <span class="number">11</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面是典型的差一错误，HELLO_WORLD后面是有<code>\0</code>的，所以长度实际为12，比11大。</p><h4 id="外部可控的format"><a href="#外部可控的format" class="headerlink" title="外部可控的format"></a>外部可控的format</h4><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">OutputLog</span><span class="params">(<span class="keyword">char</span>* loginfo)</span></span>&#123;</span><br><span class="line">    <span class="keyword">char</span> password[] =<span class="string">"password"</span>;</span><br><span class="line">    <span class="built_in">printf</span>(loginfo);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面直接用<code>printf()</code>未经格式化输出loginfo，那么如果外部精心构造了一个<code>format</code>，那么可能导致password泄漏。</p><h2 id="函数漏洞"><a href="#函数漏洞" class="headerlink" title="函数漏洞"></a>函数漏洞</h2><h3 id="使用安全的函数"><a href="#使用安全的函数" class="headerlink" title="使用安全的函数"></a>使用安全的函数</h3><h4 id="一些老旧的函数不甚安全"><a href="#一些老旧的函数不甚安全" class="headerlink" title="一些老旧的函数不甚安全"></a>一些老旧的函数不甚安全</h4><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">GetScore</span><span class="params">(<span class="keyword">int</span>* scoreList)</span></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> score[] = &#123;<span class="number">0</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>,<span class="number">7</span>,<span class="number">8</span>,<span class="number">89</span>,<span class="number">123</span>,<span class="number">111</span>&#125;;</span><br><span class="line">    <span class="built_in">memcpy</span>(scoreList, score, <span class="keyword">sizeof</span>(score)/<span class="keyword">sizeof</span>(score[<span class="number">0</span>]));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面scoreList并没有指定拷贝长度，如果scoreList长度小于score，那么会造成缓冲区溢出，所以在传入指针指向的空间时，要同时传入空间大小。在这里，<code>memcpy</code>就是一个典型的不安全函数。</p><p>C11标准对于不安全的函数指定了安全版本，请使用安全版本替代不安全函数。安全函数的安全特性如下：</p><ul><li>边界检查及入参检查</li><li>字符串强制0结尾</li><li>增加错误码</li><li>内存重叠检查</li><li>限定操作内存的最大长度</li></ul><p>对于<code>memcpy</code>，其安全函数版本如下：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">errno_t</span> <span class="title">memcpy_s</span><span class="params">(<span class="keyword">void</span> *dest, <span class="keyword">size_t</span> dsetMax, <span class="keyword">const</span> <span class="keyword">void</span>* src, <span class="keyword">size_t</span> count)</span></span>;</span><br></pre></td></tr></table></figure><h4 id="使用安全函数要谨慎"><a href="#使用安全函数要谨慎" class="headerlink" title="使用安全函数要谨慎"></a>使用安全函数要谨慎</h4><p>即使有了安全函数，也可能出现不正确使用的情况，例如将源长度直接作为目的长度，属于典型的掩耳盗铃的做法：</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">memcpy_s(dest, <span class="keyword">sizeof</span>(src), src, <span class="keyword">sizeof</span>(src));  <span class="comment">// 禁止掩耳盗铃</span></span><br></pre></td></tr></table></figure><p>同时，确保目的长度确实地等于传入的目的地址，有时候定义了一个宏作为目的长度，结果输入的目的内存空间长度和宏不相等，也是要禁止的。</p><h4 id="必须检查安全函数返回值"><a href="#必须检查安全函数返回值" class="headerlink" title="必须检查安全函数返回值"></a>必须检查安全函数返回值</h4><p>安全函数为我们设置了返回值，我们不能忽略，使用时一定要检查安全函数的返回值。</p><h2 id="整数"><a href="#整数" class="headerlink" title="整数"></a>整数</h2><h3 id="谨慎地进行整数间比较操作"><a href="#谨慎地进行整数间比较操作" class="headerlink" title="谨慎地进行整数间比较操作"></a>谨慎地进行整数间比较操作</h3><p><code>char</code>类型最大值比<code>int</code>类型最大值小，如果使用<code>char</code>的类型和<code>int</code>类型比较，很有可能出现无效比较的情况，需要特别注意。</p><h3 id="注意整数越界问题"><a href="#注意整数越界问题" class="headerlink" title="注意整数越界问题"></a>注意整数越界问题</h3><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="keyword">char</span> g_studentScore[<span class="number">100</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line"><span class="keyword">unsigned</span> short g_curOffset = <span class="number">12</span>;   <span class="comment">// 已经保存的学生成绩个数</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">AddStudentScore</span><span class="params">(<span class="keyword">char</span>* score, <span class="keyword">unsigned</span> short num)</span></span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(score == <span class="literal">nullptr</span>)&#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">unsigned</span> short <span class="keyword">int</span> length = g_curOffset + num;</span><br><span class="line">    <span class="keyword">if</span>(length &gt; (<span class="keyword">sizeof</span>(g_studentScore) - <span class="number">1</span>))&#123;</span><br><span class="line">        err;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">unsigned</span> short i = <span class="number">0</span>; i &lt; num; i++)&#123;</span><br><span class="line">        g_studentScore[g_curOffset] = score[i];</span><br><span class="line">        g_curOffset++;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面的代码看似会对<code>length</code>进行检查，保证<code>length</code>不超过100，但是问题就出现在相加的语句上：</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">length = g_curOffset + num;</span><br></pre></td></tr></table></figure><p>这一句并没有进行整数越界检查，如果<code>num + g_curOffset</code>超过了65535，那么<code>length</code>会溢出，导致实际相加的结果很小，但是<code>num</code>又很大，后面的循环会导致缓冲区溢出。可以在循环的时候添加检查，如果越界，就退出。</p><h2 id="内存"><a href="#内存" class="headerlink" title="内存"></a>内存</h2><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><div id="footnotes"><hr><div id="footnotelist"><ol><li id="fn:1">[输入验证](<a href="https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html">Input Validation - OWASP Cheat Sheet Series</a>)<a href="#fnref:1" rev="footnote"> ↩</a></li></ol></div></div>]]></content>
    
    <summary type="html">
    
      &lt;p&gt; 本文将针对编码过程中的一些漏洞进行总结，并给出相关示例&lt;/p&gt;
    
    </summary>
    
      <category term="计算机" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="软件工程" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B/"/>
    
    
  </entry>
  
  <entry>
    <title>编写高质量的代码</title>
    <link href="http://sqduan.github.io/2024/10/13/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B/%E7%BC%96%E5%86%99%E9%AB%98%E8%B4%A8%E9%87%8F%E7%9A%84%E4%BB%A3%E7%A0%81/"/>
    <id>http://sqduan.github.io/2024/10/13/计算机/软件工程/编写高质量的代码/</id>
    <published>2024-10-13T01:59:46.522Z</published>
    <updated>2024-10-13T01:59:46.522Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><blockquote><p>Designing a software is just like designing a jewelry</p></blockquote><a id="more"></a><p>本文属于软件工程系列笔记总结第一部分，如何编写高质量的代码。</p><h2 id="编程过程与规范"><a href="#编程过程与规范" class="headerlink" title="编程过程与规范"></a>编程过程与规范</h2><p>此处详见<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup>，关于注释多说几点，首先注释要解释为什么而不是怎么做，其次不要对明确知道功能的语句进行注释，应当以块作为划分，除非某一条语句特别复杂。应尽可能做到去文档化，使开发文档融合在注释中，最后利用一些工具自动生成开发文档。另外需要注意的是，<strong>对于不好的程序，不要试图修改，而是要直接重新写。</strong></p><h2 id="良好的编程实践"><a href="#良好的编程实践" class="headerlink" title="良好的编程实践"></a>良好的编程实践</h2><p>在编程实践中，我们要做到以下几点：</p><ul><li>看：阅读优秀代码</li><li>问：以专业的方式提出问题，通过问题提升技能，这里给出一个网页，描述了应当如何提问<sup id="fnref:2"><a href="#fn:2" rel="footnote">2</a></sup></li><li>练：大量的动手练习</li></ul><h3 id="开发软件的过程"><a href="#开发软件的过程" class="headerlink" title="开发软件的过程"></a>开发软件的过程</h3><p>开发软件的过程是一个自上而下然后自下而上的过程，我们首先根据指定的问题，将一个大的问题拆分为小的问题，然后针对小问题设计解决模块，最后将小模块进行组合，构成大型的软件系统。为了高质量的完成这个过程，我们需要三种编程实践</p><ul><li>模块化设计</li><li>面向抽象编程</li><li>错误与异常处理</li></ul><h3 id="模块化设计"><a href="#模块化设计" class="headerlink" title="模块化设计"></a>模块化设计</h3><p>将大程序按照功能拆分成一系列小的模块，可以有效降低设计复杂性、提高可靠性、缩短周期、易于维护与功能扩展。常见模块划分原则包括水平、垂直；易变、不易变；基于单一职责。下面是分别使用水平和垂直原则对WEB应用进行的划分</p><p><img src="https://raw.githubusercontent.com/sqduan/hexoimg/master/softwareStructure.png" width = "500" alt="图片名称" align=center /></p><p>所谓易变与不易变，是将代码中可能会经常改动的和不容易经常改动的分开，每次迭代只需改变易变部分；而单一职责原则即设计时一个模块只实现一个职责，注意单一职责不是指单一功能，而是说避免万能类和庞大函数。</p><h4 id="案例：生命游戏的模块划分"><a href="#案例：生命游戏的模块划分" class="headerlink" title="案例：生命游戏的模块划分"></a>案例：生命游戏的模块划分</h4><p>生命游戏是一个很经典的细胞自动机算法，对于一个生命游戏，它包括棋盘、逻辑规则和计时器，所以我们可以将上述三个部分设置为三个不同的模块，并通过一定的接口相互调用。</p><div class="table-container"><table><thead><tr><th>模块名称</th><th>功能</th><th>备注</th></tr></thead><tbody><tr><td>地图模块</td><td>管理地图相关的初始化、获取与更新</td><td></td></tr><tr><td>逻辑模块</td><td>控制完整游戏逻辑，根据地图模块的数据对地图模块进行更新</td><td></td></tr><tr><td>时间模块</td><td>在适当的时间对地图进行更新</td><td></td></tr><tr><td>UI模块</td><td>管理用户输入输出交互</td></tr></tbody></table></div><h3 id="面向抽象编程"><a href="#面向抽象编程" class="headerlink" title="面向抽象编程"></a>面向抽象编程</h3><p>在设计好模块的基础上，我们可以先设计出各个模块的骨架，或者说对各个模块进行抽象，定义它们之间的接口；模块间相互关联的接口在未来开发中应尽可能保持不变。</p><h4 id="案例：生命游戏的接口设计"><a href="#案例：生命游戏的接口设计" class="headerlink" title="案例：生命游戏的接口设计"></a>案例：生命游戏的接口设计</h4><h5 id="地图模块接口"><a href="#地图模块接口" class="headerlink" title="地图模块接口"></a>地图模块接口</h5><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">enum</span> LifeState&#123;</span><br><span class="line">    NOLIFE = <span class="number">0</span>,</span><br><span class="line">    ALIVE</span><br><span class="line">    DEAD   </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">GameMap</span>&#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 构造函数</span></span><br><span class="line">    GameMap(<span class="keyword">unsigned</span> <span class="keyword">int</span> _height = <span class="number">10</span>, <span class="keyword">unsigned</span> <span class="keyword">int</span> _width = <span class="number">10</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 重置地图并填充活细胞</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">reset</span><span class="params">(<span class="keyword">float</span> life_ratio)</span></span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 获得指定位置周围的细胞数目</span></span><br><span class="line">    <span class="function"><span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="title">getNeighborCount</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">int</span> row, <span class="keyword">unsigned</span> <span class="keyword">int</span> col)</span></span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 更新地图上某个方格的生命状态</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">setLifeState</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">int</span> row, <span class="keyword">unsigned</span> <span class="keyword">int</span> col, LifeState life_state)</span></span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 获得地图上某个方格的生命状态</span></span><br><span class="line">    <span class="function">LifeState <span class="title">getLifeState</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">int</span> row, <span class="keyword">unsigned</span> <span class="keyword">int</span> col)</span></span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 获得地图的长和宽</span></span><br><span class="line">    <span class="function"><span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="title">getHeight</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> m_height;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="title">getWidth</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> m_width;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 设置地图的长和宽</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">setHeight</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">int</span> _height)</span></span>&#123;</span><br><span class="line">        m_height = _height;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">setWidth</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">int</span> _width)</span></span>&#123;</span><br><span class="line">        m_width  = _width;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">unsigned</span> <span class="keyword">int</span> m_height;</span><br><span class="line">    <span class="keyword">unsigned</span> <span class="keyword">int</span> m_width;</span><br><span class="line">    <span class="built_in">vector</span>&lt;<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;&gt; m_board&#123;&#125;;</span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h5 id="逻辑模块接口"><a href="#逻辑模块接口" class="headerlink" title="逻辑模块接口"></a>逻辑模块接口</h5><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">GameControl</span>&#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    GameControl();</span><br><span class="line">    ~GameControl();</span><br><span class="line">    <span class="comment">// 进行一次游戏循环，将更新一次地图并打印</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">update</span><span class="params">()</span></span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 结束游戏</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">end</span><span class="params">()</span></span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 在控制台上打印地图</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">printMap</span><span class="params">()</span></span>;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    GameMap* <span class="built_in">map</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h5 id="时钟模块"><a href="#时钟模块" class="headerlink" title="时钟模块"></a>时钟模块</h5><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Timer</span>&#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 设置时钟频率</span></span><br><span class="line">    Timer(<span class="keyword">unsigned</span> <span class="keyword">int</span> _interval, <span class="keyword">void</span> fun);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 开始时钟</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">start</span><span class="params">()</span></span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h4 id="模块实现"><a href="#模块实现" class="headerlink" title="模块实现"></a>模块实现</h4><p>在模块化分解之后，开发人员可以分别实现各个模块，根据函数单一职责原则，各模块内部可以定义更多函数，与此同时，模块测试的设计工作也可以开始。需要注意的是，前面设计的接口一旦设计好了，<strong>尽可能不要变动，否则可能造成连带性的一系列修改</strong>。</p><h4 id="错误和异常处理"><a href="#错误和异常处理" class="headerlink" title="错误和异常处理"></a>错误和异常处理</h4><p>在实现过程中，我们还需要考虑常见的错误和异常应该如何处理，特别是涉及到用户输入的部分，同时，我们还需要针对是否使用异常机制进行一些斟酌。</p><h2 id="代码静态检查"><a href="#代码静态检查" class="headerlink" title="代码静态检查"></a>代码静态检查</h2><p>所谓代码静态检查，是指在代码未运行期间，通过对源代码的阅读和审查，保证软件的质量和规范性。为了规范化审查流程，本节给出一些静态检查工具以及静态检查Checklist，帮助我们更好地进行代码静态检查过程。</p><h3 id="缺陷检查表"><a href="#缺陷检查表" class="headerlink" title="缺陷检查表"></a>缺陷检查表</h3><div class="table-container"><table><thead><tr><th>类别</th><th>常见缺陷</th><th>类别</th><th>常见缺陷</th></tr></thead><tbody><tr><td>编程规范</td><td>命名规则、注释、排版、声明初始化、语言格式等</td><td>程序流程</td><td>循环结束条件是否准确</td></tr><tr><td>面向对象设计</td><td>类的设计与抽象是否合适</td><td></td><td>是否避免了死循环的产生</td></tr><tr><td></td><td>是否符合面向接口编程的思想</td><td></td><td>对循环处理是否合适，是否避免多层嵌套</td></tr><tr><td></td><td>是否使用合适的设计模式</td><td></td><td></td></tr><tr><td>性能方面</td><td>是否能够正确处理海量数据</td><td></td><td></td></tr><tr><td></td><td>是否选择了合适的数据结构并进行了设置</td><td></td><td></td></tr><tr><td></td><td>是否滥用数据结构</td><td></td><td></td></tr><tr><td></td><td>是否采用通用线程池或对象池等高速缓存技术</td><td></td><td></td></tr><tr><td></td><td>接口是否设计合理，内部是否尽可能没有类型转换</td><td></td><td></td></tr><tr><td></td><td>是否采用内存或硬盘缓冲机制提高效率</td><td></td><td></td></tr><tr><td></td><td>并发访问策略</td><td></td><td></td></tr><tr><td></td><td>IO方面是否使用合适的类或良好的方法提高性能，例如减少序列化，使用buffer类封装</td><td></td><td></td></tr><tr><td></td><td>同步方法是否正确使用而没有滥用</td><td></td><td></td></tr><tr><td></td><td>递归迭代深度是否合适</td><td></td><td></td></tr><tr><td></td><td>如果有阻塞，是否考虑了性能保证的措施</td><td></td><td></td></tr><tr><td></td><td>避免过度优化，对性能要求高的代码</td><td></td><td></td></tr><tr><td>资源管理</td><td>分配内存是否释放，错误发生时是否保证所有资源都释放掉，是否同一个对象多次释放</td><td></td><td></td></tr><tr><td></td><td>代码是否保存准确的引用计数</td><td></td></tr></tbody></table></div><h3 id="代码静态分析工具"><a href="#代码静态分析工具" class="headerlink" title="代码静态分析工具"></a>代码静态分析工具</h3><p>这里给出一些常用的代码分析工具，具体的应用方式请参考</p><h4 id="C-C"><a href="#C-C" class="headerlink" title="C/C++"></a>C/C++</h4><p><a href="http://cppcheck.net/">cppcheck：一个C++代码静态检查工具</a></p><h2 id="代码性能分析及优化"><a href="#代码性能分析及优化" class="headerlink" title="代码性能分析及优化"></a>代码性能分析及优化</h2><p>优化的目的：<strong>结果相同，效率更高</strong>，根据2/8原则，实现程序重构、优化、扩展及文档相关内容一般会消耗80%工作量。</p><h3 id="优化的要点及原则"><a href="#优化的要点及原则" class="headerlink" title="优化的要点及原则"></a>优化的要点及原则</h3><h4 id="要点"><a href="#要点" class="headerlink" title="要点"></a>要点</h4><ul><li>针对时间和空间两个方面进行优化</li><li>正确性、可靠性、健壮性、可读性是前提</li><li>全局效率为主，局部效率为辅</li><li>先找到代码中的效率瓶颈</li><li><strong>先优化数据结构和算法，后优化执行代码</strong></li><li>时间效率和空间效率可能是对立的，此时就需要进行权衡</li><li>从设计之初就开始考虑优化程序性能</li><li>测试数据很重要，要覆盖所有情况</li><li>永远不要在执行性能评估的情况下尝试对代码优化</li></ul><h4 id="原则"><a href="#原则" class="headerlink" title="原则"></a>原则</h4><p>一般来说，优化是一个追求性价比的问题，要优先对优化性价比最高的部分进行优化，所谓性价比，是指投入的优化精力和得到的优化效果达到平衡的一个时刻。并不是说程序最耗时的部分就是最难优化的，因为可能这部分优化起来很困难。记住一个原则：<strong>进行最有性价比的优化。</strong></p><h3 id="优化的一般步骤"><a href="#优化的一般步骤" class="headerlink" title="优化的一般步骤"></a>优化的一般步骤</h3><pre class="mermaid">graph LR    node["证明需要优化"]    node1["找出优化关键部分"]    node2["代码测试"]    node3["进行优化"]    node4["评测优化结果"]    node-->node1     node1-->node2    node2-->node3    node3-->node4</pre><h3 id="案例：词频统计代码"><a href="#案例：词频统计代码" class="headerlink" title="案例：词频统计代码"></a>案例：词频统计代码</h3><p>使用Python读入一个文本文件，然后统计该文本文件中英文单词出现的频率，并输出词频最高的100个单词，单词指的是被空格和标点符号进行分割的字符串。Python的代码如下：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">splitWords</span><span class="params">(InputFile)</span></span></span><br><span class="line"><span class="function"># 文件<span class="title">IO</span></span></span><br><span class="line"><span class="function"><span class="title">try</span>:</span></span><br><span class="line">        alltext = fileobject.read()    <span class="comment"># 文件IO通常比较慢</span></span><br><span class="line">    <span class="keyword">finally</span>:</span><br><span class="line">        fileobject.close()</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 分割单词</span></span><br><span class="line">    words = re.split(<span class="string">'[^a-zA-Z]+'</span>, alltext)  <span class="comment"># 使用正则表达式分词</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 统计单词词频</span></span><br><span class="line">    dic = &#123;&#125;</span><br><span class="line">    <span class="keyword">for</span> word <span class="keyword">in</span> words:</span><br><span class="line">        <span class="keyword">if</span> word <span class="keyword">in</span> dic.keys():         <span class="comment"># 词频统计</span></span><br><span class="line">            dic[word] += <span class="number">1</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            dic[word] = <span class="number">1</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 排序</span></span><br><span class="line">    result = sorted(dic.items(), key = <span class="keyword">lambda</span> dic:dic[<span class="number">1</span>], reverse = <span class="literal">True</span>) <span class="comment"># 排序，使用内置算法</span></span><br></pre></td></tr></table></figure><p>为了获得影响程序性能的部分，我们需要一定的工具对代码进行测试，确定性能瓶颈，python中提供了profile帮助我们自动确定软件的性能，一个简单的使用例子如下：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="keyword">import</span> profile</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">profileTest</span><span class="params">()</span>:</span></span><br><span class="line">    Total = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">10</span>):</span><br><span class="line">        Total = Total*(i+<span class="number">1</span>)</span><br><span class="line">        print(Total)</span><br><span class="line">    <span class="keyword">return</span> Total</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line">    profile.run(<span class="string">"profileTest()"</span>)</span><br></pre></td></tr></table></figure><p>运行结果如下：</p><figure class="highlight plain"><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">      15 function calls in 0.016 seconds</span><br><span class="line"></span><br><span class="line">Ordered by: standard name</span><br><span class="line">ncalls    tottime  percall  cumtime  percall filename:lineno(function)</span><br><span class="line">     1    0.000    0.000    0.000    0.000 :0(exec)</span><br><span class="line">    10    0.000    0.000    0.000    0.000 :0(print)</span><br><span class="line">     1    0.016    0.016    0.016    0.016 :0(setprofile)</span><br><span class="line">     1    0.000    0.000    0.000    0.000 &lt;string&gt;:1(&lt;module&gt;)</span><br><span class="line">     1    0.000    0.000    0.016    0.016 profile:0(profileTest())</span><br><span class="line">     0    0.000             0.000          profile:0(profiler)</span><br><span class="line">     1    0.000    0.000    0.000    0.000 profileTest.py:3(profileTest)</span><br></pre></td></tr></table></figure><p>可以看到函数的调用次数以及运行时间，具体参数解释如下：</p><div class="table-container"><table><thead><tr><th>参数名</th><th>含义</th></tr></thead><tbody><tr><td>ncalls</td><td>调用次数</td></tr><tr><td>tottime</td><td>函数运行总时间，除去函数中调用的其他函数</td></tr><tr><td>percall</td><td>一次时间</td></tr><tr><td>cumtime</td><td>函数运行总时间，包括函数中调用的其他函数</td></tr><tr><td>filename:lineno(function)</td><td>文件名，函数行号，函数名</td></tr></tbody></table></div><h2 id="结对编程"><a href="#结对编程" class="headerlink" title="结对编程"></a>结对编程</h2><p>所谓结对编程，就是两人合作，同时针对一个功能进行开发，类似于开车过程中的领航员和驾驶员，合作可能得到更优的结果</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><div id="footnotes"><hr><div id="footnotelist"><ol><li id="fn:1"><a href="https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/contents/">Google C++编程风格</a><a href="#fnref:1" rev="footnote"> ↩</a></li><li id="fn:2"><a href="https://github.com/seajs/seajs/issues/545">如何正确提问</a><a href="#fnref:2" rev="footnote"> ↩</a></li></ol></div></div>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;Designing a software is just like designing a jewelry&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="计算机" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="软件工程" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B/"/>
    
    
  </entry>
  
  <entry>
    <title>软件设计概要说明书</title>
    <link href="http://sqduan.github.io/2024/10/13/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B/%E8%BD%AF%E4%BB%B6%E8%AE%BE%E8%AE%A1%E6%A6%82%E8%A6%81%E8%AF%B4%E6%98%8E%E4%B9%A6/"/>
    <id>http://sqduan.github.io/2024/10/13/计算机/软件工程/软件设计概要说明书/</id>
    <published>2024-10-13T01:59:46.522Z</published>
    <updated>2024-10-13T01:59:46.522Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><blockquote><p>preparation may quicken the process</p></blockquote><a id="more"></a><h2 id="概要设计"><a href="#概要设计" class="headerlink" title="概要设计"></a>概要设计</h2><h3 id="什么是概要设计"><a href="#什么是概要设计" class="headerlink" title="什么是概要设计"></a>什么是概要设计</h3><p>确定软件系统的总体布局，各个子模块的功能和模块间的关系，与外部系统的关系，选择的技术路线。有一些研究与论证性的内容。并输出《软件概要设计说明书》。<strong>搞清楚“总体实现方案”</strong>。概要设计是用来评价软件整体设计可行性的重要支撑标准，由于每个模块这个阶段已经开始确定，可以很好的检查已有的模块是否已经足够完整，还可以用于评估工作量以及知道下一步的主要计划，它是一个纲领。</p><h3 id="注意要点"><a href="#注意要点" class="headerlink" title="注意要点"></a>注意要点</h3><ul><li>用来评价总体设计的可行性。</li><li>用来检查设计的模块是否完整，保证每一个功能都有对应的模块来实现。</li><li>用来评估开发工作量、指导开发计划（在不写详细设计的情况下）。</li><li><strong>概要设计阶段过于重视业务流程是个误区。</strong></li></ul><h2 id="文档结构"><a href="#文档结构" class="headerlink" title="文档结构"></a>文档结构</h2><h3 id="1-引言"><a href="#1-引言" class="headerlink" title="1 引言"></a>1 引言</h3><h4 id="1-1-编写目的"><a href="#1-1-编写目的" class="headerlink" title="1.1 编写目的"></a>1.1 编写目的</h4><p>本阶段完成系统的大致设计并明确系统的数据结构与软件结构。本概要设计说明书的目的就是进一步细化软件设计阶段得出的软件概貌，把它加工成在程序细节上非常接近与源程序开发的软件表示。</p><h4 id="1-2-预期读者"><a href="#1-2-预期读者" class="headerlink" title="1.2 预期读者"></a>1.2 预期读者</h4><p>开发人员、测试人员、分析人员</p><h4 id="1-3-项目背景"><a href="#1-3-项目背景" class="headerlink" title="1.3 项目背景"></a>1.3 项目背景</h4><h5 id="1-3-1-相关单位"><a href="#1-3-1-相关单位" class="headerlink" title="1.3.1 相关单位"></a>1.3.1 相关单位</h5><ul><li>任务提出者：xxx人员</li><li>开发者：xxx</li><li>使用者：</li></ul><h4 id="1-4-定义"><a href="#1-4-定义" class="headerlink" title="1.4 定义"></a>1.4 定义</h4><p>列出本文档中所用到的专门术语的定义和缩写词的原意</p><h4 id="1-5-参考资料"><a href="#1-5-参考资料" class="headerlink" title="1.5 参考资料"></a>1.5 参考资料</h4><h3 id="2-总体设计"><a href="#2-总体设计" class="headerlink" title="2 总体设计"></a>2 总体设计</h3><h4 id="2-1-设计前提和约束条件"><a href="#2-1-设计前提和约束条件" class="headerlink" title="2.1 设计前提和约束条件"></a>2.1 设计前提和约束条件</h4><ol><li>系统用户登陆通过与认证系统实名认证；</li><li>有合理的通知功能；</li><li>遵循项目开发文档所制定的规范；</li><li>符合系统权限要求。</li></ol><h4 id="2-2-基本设计思想"><a href="#2-2-基本设计思想" class="headerlink" title="2.2 基本设计思想"></a>2.2 基本设计思想</h4><p>写出软件的基本设计思路，突出软件的优点，一个例子如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">建设项目信息管理涉及业主方、设计单位、施工单位、运营管理单位、政府部门等众多参与方，信息量巨大，信息交换复杂。而传统的信息管理方式凌乱无序，信息利用率低。</span><br><span class="line"></span><br><span class="line">因此，基于BIM的信息管理框架的构建思路的核心就是要改变传统的信息传递和共享方式，通过BIM将不同阶段、不同参与方之间的信息有效地集成起来，真正实现建设项目全生命周期的信息管理。因此，基于BIM的建设项目全生命周期信息管理框架的构建主要从以下三点展开：</span><br><span class="line"></span><br><span class="line">（1）数据问题：</span><br><span class="line"></span><br><span class="line">建设项目信息管理过程中，产生的信息形式多样，各参与方所用的信息管理软件不尽相同，如何实现BIM数据和其他形式数据的共享和利用，保证不同阶段产生的信息能够持续应用，而避免重复输入，就需要建立可以保证不同BIM应用之间的信息提取、关联及扩展的数据库，该数据库也是基于BIM的信息管理框架的基础。</span><br><span class="line"></span><br><span class="line">（2）信息模型：</span><br><span class="line"></span><br><span class="line">数据库是存储信息的地方，而信息模型是承载信息的载体。随着建设项目的进展，信息数据不断增加，如何保证这些信息分门别类有效地存储，需要在全生命周期不同阶段，针对不同的BIM应用形成子信息模型，由各子信息模型来承载不同专业和类别的信息，以保证信息的有序。子信息模型通过提取上一阶段信息模型中的数据，然后再经过扩展和集成，如此继续反复，最终形成全生命周期信息模型。</span><br><span class="line"></span><br><span class="line">（3）功能实现：</span><br><span class="line"></span><br><span class="line">对信息进行存储和管理的最终目的就是有效地应用信息，进行建设项目管理，因此，在管理框架的最上层为功能模块层。不同的功能模块对应着不同的BIM应用，也即为一个功能子信息模型。使用多种研发技术、中间件、设备搭建基于互联网环境的轻量化BIM信息管理平台；采用B/S架构构建平台；</span><br><span class="line"></span><br><span class="line">具体通过以下技术实现：</span><br><span class="line"></span><br><span class="line">基于OpenGL的三维模型引擎，集成BIM+GIS数据；</span><br><span class="line">基于ActiveX的浏览器、移动端APP模型引擎部署；</span><br><span class="line">基于关系型数据库SQL Server的结构化构件数据存储、检索；</span><br><span class="line">基于React的H5前端研发技术；</span><br><span class="line">基于PHP的表单、流程处理功能开发；</span><br><span class="line">基于MySQL的业务表单、流程数据存储；</span><br><span class="line">基于萤石云的视频监控中间件集成。</span><br></pre></td></tr></table></figure><h4 id="2-3-总体结构及概要"><a href="#2-3-总体结构及概要" class="headerlink" title="2.3 总体结构及概要"></a>2.3 总体结构及概要</h4><p>概述软件总体结构，或者放一个业务流程图，并做简要介绍</p><h4 id="2-4-功能分配"><a href="#2-4-功能分配" class="headerlink" title="2.4 功能分配"></a>2.4 功能分配</h4><p>表明各项功能与程序结构及模块的关系</p><h3 id="3-模块概要设计"><a href="#3-模块概要设计" class="headerlink" title="3 模块概要设计"></a>3 模块概要设计</h3><p>模块设计,可以写以下内容：</p><ol><li>模块描述：说明哪些模块实现了哪些功能；</li><li>模块层次结构：可以使用某个视角的软件框架图来表达；</li><li>模块间的关系：模块间依赖关系的描述，通信机制描述；</li><li>处理方式设计：说一些满足功能和性能的算法；</li></ol><h4 id="3-1-模块描述"><a href="#3-1-模块描述" class="headerlink" title="3.1 模块描述"></a>3.1 模块描述</h4><div class="table-container"><table><thead><tr><th>模块名称</th><th>功能</th><th>备注</th></tr></thead><tbody><tr><td>xxx模块</td><td>实现自动路径规划算法</td><td></td></tr><tr><td></td><td></td><td></td></tr><tr><td></td><td></td></tr></tbody></table></div><h4 id="3-2-模块层次结构及关系"><a href="#3-2-模块层次结构及关系" class="headerlink" title="3.2 模块层次结构及关系"></a>3.2 模块层次结构及关系</h4><h3 id="4-接口设计"><a href="#4-接口设计" class="headerlink" title="4 接口设计"></a>4 接口设计</h3><h4 id="4-1-外部接口"><a href="#4-1-外部接口" class="headerlink" title="4.1 外部接口"></a>4.1 外部接口</h4><p>包括软件接口及硬件接口。其中用户界面应当给出总体设计框图及开发框架；软件接口应当给出一些输入输出及通信接口，例如采用Visual C#提供与SQL Server2000连接进行访问数据库的操作等；硬件接口应给出输入/输出所需的软硬件环境等。</p><h4 id="4-2-内部接口"><a href="#4-2-内部接口" class="headerlink" title="4.2 内部接口"></a>4.2 内部接口</h4><p>模块之间的接口，确定模块间的连接是怎样的，同时确定模块间信息传递方式，例如采用函数调用、参数传递、返回值的方式。具体参数的结构将在数据结构设计的内容中说明。接口传递的信息将是以数据结构封装了的数据，以参数传递或返回值的形式在各模块间传输。</p><h4 id="4-3-用户接口"><a href="#4-3-用户接口" class="headerlink" title="4.3 用户接口"></a>4.3 用户接口</h4><p>用户接口是系统与用户进行信息交换的媒介，一般指软件接口，包括命令接口、程序接口和图形接口</p><h3 id="5-运行设计"><a href="#5-运行设计" class="headerlink" title="5 运行设计"></a>5 运行设计</h3><p>在运行设计中，我们主要考虑运行时模块的组合、控制以及时间</p><h4 id="5-1-运行模块组合"><a href="#5-1-运行模块组合" class="headerlink" title="5.1 运行模块组合"></a>5.1 运行模块组合</h4><p>在用户选择</p><h2 id="范例——软件注册机程序"><a href="#范例——软件注册机程序" class="headerlink" title="范例——软件注册机程序"></a>范例——软件注册机程序</h2><h3 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h3><h4 id="编写目的"><a href="#编写目的" class="headerlink" title="编写目的"></a>编写目的</h4><p>本概要设计说明书的目的就是进一步细化软件注册机程序设计阶段得出的软件概貌，把它加工成在程序细节上非常接近与源程序开发的软件表示。</p><h4 id="预期读者"><a href="#预期读者" class="headerlink" title="预期读者"></a>预期读者</h4><p>开发人员、测试人员、分析人员</p><h4 id="项目背景"><a href="#项目背景" class="headerlink" title="项目背景"></a>项目背景</h4><h5 id="相关单位"><a href="#相关单位" class="headerlink" title="相关单位"></a>相关单位</h5><ul><li>任务提出者：xxx人员</li><li>开发者：xxx</li><li>使用者：</li></ul><h4 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h4><h4 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h4><h3 id="总体设计"><a href="#总体设计" class="headerlink" title="总体设计"></a>总体设计</h3><h4 id="总体结构及概要"><a href="#总体结构及概要" class="headerlink" title="总体结构及概要"></a>总体结构及概要</h4><p> <img src="https://raw.githubusercontent.com/sqduan/hexoimg/master/RegisterStructure.png" width = "300"  alt="图片名称" align=center /></p><p>本系统采用三层架构进行设计，层与层之间采用C++接口进行衔接，降低了模块耦合度，并使用工厂模式实现对不同需求对应接口的不同实现，并在每一层功能完成后进行相应层的单元测试，对开发过程中避免了很多不必要的麻烦，节约了开发时间。本系统在开发过程中实现了国际化框架，可以识别不同的浏览器语言相应的显示对应语言的内容，由于开发时间限制，语言包只提供了缺省的中文语言包。</p><p><strong>表示层</strong>：该系统具有产品注册和注册码生成两个会话界面，对用户输入的注册信息进行了验证，能够有效约束不符合条件的验证信息，及时在屏幕上反馈给用户，只对符合要求的注册信息提交给下层业务处理层，保证了业务处理层接收到的表单信息的纯净性。</p><p><strong>业务逻辑层</strong>：对本系统需要提供的功能进行封装和分类，分成RegisterService和RegisterCodeService两个字系统。</p><p><strong>数据访问层</strong>：数据访问层对PC的注册表进行访问，从而实现注册功能。</p><h3 id="模块设计"><a href="#模块设计" class="headerlink" title="模块设计"></a>模块设计</h3><h4 id="模块描述"><a href="#模块描述" class="headerlink" title="模块描述"></a>模块描述</h4><div class="table-container"><table><thead><tr><th>模块名称</th><th>功能</th><th>备注</th></tr></thead><tbody><tr><td>UI模块</td><td>负责与用户进行交互，根据用户输入的功能，调用不同的模块完成不同的业务逻辑，包括产品注册码生成、产品注册以及软件试用模块</td><td></td></tr><tr><td>产品注册模块</td><td>负责实现软件注册功能，通过用户提供的注册码，调用注册表管理模块，实现注册表注册</td><td></td></tr><tr><td>注册码生成模块</td><td>根据用户输入的用户名，生成有效的注册码</td><td></td></tr><tr><td>注册表管理模块</td><td>负责用户注册表的生成及管理功能</td><td></td></tr><tr><td>软件试用管理模块</td><td>负责软件试用的管理</td></tr></tbody></table></div><h3 id="接口设计"><a href="#接口设计" class="headerlink" title="接口设计"></a>接口设计</h3><h4 id="外部接口"><a href="#外部接口" class="headerlink" title="外部接口"></a>外部接口</h4><h5 id="软件接口"><a href="#软件接口" class="headerlink" title="软件接口"></a>软件接口</h5><p>系统使用Qt库中的QSetting实现对注册表的访问</p><h5 id="硬件接口"><a href="#硬件接口" class="headerlink" title="硬件接口"></a>硬件接口</h5><p>输入：采用键盘、鼠标及Qt/C++中标准输入进行输入处理。</p><p>输出：采用Qt/C++中标准输出或其他输出设备对输出进行处理</p><h4 id="内部接口"><a href="#内部接口" class="headerlink" title="内部接口"></a>内部接口</h4><h5 id="UI模块"><a href="#UI模块" class="headerlink" title="UI模块"></a>UI模块</h5><h5 id="产品注册模块"><a href="#产品注册模块" class="headerlink" title="产品注册模块"></a>产品注册模块</h5><div class="table-container"><table><thead><tr><th style="text-align:left">接口名称</th><th style="text-align:left">输入及输入格式</th><th style="text-align:left">输出及输出格式</th><th>功能</th><th>属性</th></tr></thead><tbody><tr><td style="text-align:left">GetCPUID()</td><td style="text-align:left">null</td><td style="text-align:left">CPU ID：string</td><td>获得CPU ID号码</td><td>public</td></tr><tr><td style="text-align:left">GetDiskID()</td><td style="text-align:left">null</td><td style="text-align:left">Disk ID：string</td><td>获得磁盘 ID号码</td><td>public</td></tr><tr><td style="text-align:left"></td><td style="text-align:left"></td><td style="text-align:left"></td><td></td></tr></tbody></table></div><h5 id="注册码生成模块"><a href="#注册码生成模块" class="headerlink" title="注册码生成模块"></a>注册码生成模块</h5><h4 id="用户接口"><a href="#用户接口" class="headerlink" title="用户接口"></a>用户接口</h4><h5 id="用户图形界面"><a href="#用户图形界面" class="headerlink" title="用户图形界面"></a>用户图形界面</h5><p>用户图形界面总体设计，用户图形界面使用Qt Quick实现UI设计</p><p><img src="https://raw.githubusercontent.com/sqduan/hexoimg/master/UIDesignStructure.png" width = "500"  alt="图片名称" align=center /></p><h3 id="运行设计"><a href="#运行设计" class="headerlink" title="运行设计"></a>运行设计</h3><p>客户在注册码生成器UI模块中输入用户名，由注册码生成模块产生相应的注册码，并写入注册码文件中。</p><p>用户在注册UI模块中选择注册或试用产品，如选择注册，则调用产品注册模块，通过用户提供的注册码，调用注册表管理模块，实现注册功能；如选择试用，则调用软件试用模块，软件试用模块调用注册表管理模块，根据注册表中的剩余使用次数，决定是否允许用户试用。</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;preparation may quicken the process&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="计算机" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="软件工程" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B/"/>
    
    
  </entry>
  
  <entry>
    <title>减少编程错误</title>
    <link href="http://sqduan.github.io/2024/10/13/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B/%E5%87%8F%E5%B0%91%E7%BC%96%E7%A8%8B%E9%94%99%E8%AF%AF/"/>
    <id>http://sqduan.github.io/2024/10/13/计算机/软件工程/减少编程错误/</id>
    <published>2024-10-13T01:59:46.521Z</published>
    <updated>2024-10-13T01:59:46.521Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><blockquote><p>if(connection == nullptr)</p><p>​    goto fail;</p><p>​    goto fail;</p></blockquote><a id="more"></a><h2 id="安全编码与未定义行为"><a href="#安全编码与未定义行为" class="headerlink" title="安全编码与未定义行为"></a>安全编码与未定义行为</h2><h3 id="未定义行为"><a href="#未定义行为" class="headerlink" title="未定义行为"></a>未定义行为</h3><h4 id="缓冲区溢出"><a href="#缓冲区溢出" class="headerlink" title="缓冲区溢出"></a>缓冲区溢出</h4><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></pre></td><td class="code"><pre><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">int</span> i;</span><br><span class="line">    <span class="keyword">int</span> a[<span class="number">4</span>] = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span>(i = <span class="number">0</span>; i &lt;= <span class="number">4</span>; i++)&#123;</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>产生了越界访问，导致了未定义行为。建议：</p><ul><li>不要使用超出边界的指针或数组下标（这不是废话吗）</li><li>缓冲区溢出问题在CWE的数据库中排名第二</li></ul><h4 id="访问已经销毁的局部空间（悬空指针）"><a href="#访问已经销毁的局部空间（悬空指针）" class="headerlink" title="访问已经销毁的局部空间（悬空指针）"></a>访问已经销毁的局部空间（悬空指针）</h4><p>这种最常见的就是在一个函数中定义一段空间，然后返回这段空间的首地址。由于这段空间在函数返回后就被销毁，因此这个地址也成为了悬空指针。</p><h4 id="超出类型能容纳的范围"><a href="#超出类型能容纳的范围" class="headerlink" title="超出类型能容纳的范围"></a>超出类型能容纳的范围</h4><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int32_t</span> <span class="title">Average</span><span class="params">(<span class="keyword">int32_t</span> x, <span class="keyword">int32_t</span> y)</span></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> (x+y)/<span class="number">2</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>两个整型相加，可能会溢出。建议：</p><ul><li>确保有符号整数运算不溢出，避免无符号整数运算产生的回绕</li></ul><h4 id="有符号和无符号混用"><a href="#有符号和无符号混用" class="headerlink" title="有符号和无符号混用"></a>有符号和无符号混用</h4><p>建议：</p><ul><li>用来表示数值时，不要用char</li><li>避免混用有符号和无符号数，如果不得不一起用，显式指定转换类型</li><li>类型转换导致的溢出问题在CWE中排11位，有超过800条漏洞</li></ul><h3 id="未定义行为后果"><a href="#未定义行为后果" class="headerlink" title="未定义行为后果"></a>未定义行为后果</h3><p>未定义行为的一个典型后果是，由于访问了非法内存，所以可以通过精心设计构造的输入，修改栈中保存的返回地址，从而跳转到黑客想要跳转的区域。</p><h2 id="表达式与变量"><a href="#表达式与变量" class="headerlink" title="表达式与变量"></a>表达式与变量</h2><h3 id="全局变量初始化"><a href="#全局变量初始化" class="headerlink" title="全局变量初始化"></a>全局变量初始化</h3><p>假设我们在<code>a.cpp</code>中有一个全局变量</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">int g_x = 1;</span><br></pre></td></tr></table></figure><p>而在<code>b.cpp</code>中，也有一个全局变量，且使用<code>g_x</code>进行定义</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="keyword">extern</span> <span class="keyword">int</span> g_x;</span><br><span class="line"><span class="keyword">int</span> g_y = <span class="number">10</span> + g_x;</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="built_in">cout</span> &lt;&lt; g_y &lt;&lt; <span class="built_in">endl</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里就有了问题，两个不同文件定义的全局变量，其初始化顺序是不确定的。建议：</p><ul><li>避免全局变量出现初始化顺序依赖的情况</li></ul><h2 id="代码度量"><a href="#代码度量" class="headerlink" title="代码度量"></a>代码度量</h2><h3 id="代码的组织"><a href="#代码的组织" class="headerlink" title="代码的组织"></a>代码的组织</h3><ul><li>程序必须为阅读者编写，只是顺便用于机器执行</li><li>短小的函数总是更简洁、更容易阅读</li><li>功能单一的函数更容易复用，简洁明了的代码更容易维护</li></ul><h2 id="业界指南、规范与工具"><a href="#业界指南、规范与工具" class="headerlink" title="业界指南、规范与工具"></a>业界指南、规范与工具</h2><h3 id="业界指南"><a href="#业界指南" class="headerlink" title="业界指南"></a>业界指南</h3><ul><li>谷歌C++风格指南<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup>，以风格约定与习惯引导为主，不涉及消除安全问题</li><li>SEI CERT C++ Coding Standard<sup id="fnref:2"><a href="#fn:2" rel="footnote">2</a></sup>，以消除安全隐患为目的，不涉及风格约定，内容太多，建议作为字典</li><li>C++ Core Guideline<sup id="fnref:3"><a href="#fn:3" rel="footnote">3</a></sup>，重点为如何合理应用现代C++特性，这个非常值得一读</li></ul><h3 id="工具"><a href="#工具" class="headerlink" title="工具"></a>工具</h3><h4 id="编译器"><a href="#编译器" class="headerlink" title="编译器"></a>编译器</h4><p>gcc等编译器本身就能检查很多问题，且几乎不会误报，我们应当做到保持编译器警告清零</p><h4 id="辅助检查工具"><a href="#辅助检查工具" class="headerlink" title="辅助检查工具"></a>辅助检查工具</h4><ul><li>PC-link Plus：以CERT、MISRA等业界规范作为准则，能检查上千条规则，但是有误报和漏报</li><li>CPPCheck：开源静态检查工具，主要检查未定义行为，以减少误报为设计目标</li><li>华为云代码检查：<a href="https://www.huaweicloud.com/product/codecheck.html">代码检查CodeCheck<em>精准定位</em>代码缺陷<em>安全检查</em>华为云 (huaweicloud.com)</a></li></ul><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><div id="footnotes"><hr><div id="footnotelist"><ol><li id="fn:1"><a href="https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/contents/">C++ 风格指南 - 内容目录 — Google 开源项目风格指南 (zh-google-styleguide.readthedocs.io)</a><a href="#fnref:1" rev="footnote"> ↩</a></li><li id="fn:2"><a href="https://resources.sei.cmu.edu/downloads/secure-coding/assets/sei-cert-cpp-coding-standard-2016-v01.pdf">SEI CERT C++ Coding Standard (2016 Edition) (cmu.edu)</a><a href="#fnref:2" rev="footnote"> ↩</a></li><li id="fn:3"><a href="https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines">C++ Core Guidelines (isocpp.github.io)</a><a href="#fnref:3" rev="footnote"> ↩</a></li></ol></div></div>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;if(connection == nullptr)&lt;/p&gt;
&lt;p&gt;​    goto fail;&lt;/p&gt;
&lt;p&gt;​    goto fail;&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="计算机" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="软件工程" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B/"/>
    
    
  </entry>
  
  <entry>
    <title>单例模式</title>
    <link href="http://sqduan.github.io/2024/10/13/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/"/>
    <id>http://sqduan.github.io/2024/10/13/计算机/设计模式/单例模式/</id>
    <published>2024-10-13T01:59:46.521Z</published>
    <updated>2024-10-13T01:59:46.521Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><blockquote><p>对我来说，你只是一个小男孩，就像其他成千上万个小男孩一样没有什么两样。我不需要你。你也不需要我。对你来说，我也只是一只狐狸，和其他成千上万的狐狸没有什么不同。但是，如果你驯养了我，我们就会彼此需要。对我来说，你就是我的世界里独一无二的了；我对你来说，也是你的世界里的唯一。</p></blockquote><a id="more"></a><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><h3 id="什么是单例模式"><a href="#什么是单例模式" class="headerlink" title="什么是单例模式"></a>什么是单例模式</h3><p>单例模式确保一个类仅有一个实例，并提供一个全局访问点，该实例被所有程序模块共享。</p><h3 id="单例模式应用场景"><a href="#单例模式应用场景" class="headerlink" title="单例模式应用场景"></a>单例模式应用场景</h3><p>系统日志输出、GUI应用必须单鼠标，操作系统只能有一个窗口管理器、一台电脑只能连接一个键盘等。</p><h2 id="单例模式的C-实现"><a href="#单例模式的C-实现" class="headerlink" title="单例模式的C++实现"></a>单例模式的C++实现</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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Singleton</span>&#123;</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    Singleton()&#123;</span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">static</span> Singleton* m_pInstance;</span><br><span class="line">    </span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">static</span> Sinleton* <span class="title">getInstance</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(m_pInstance == <span class="literal">nullptr</span>) </span><br><span class="line">            m_pInstance = <span class="keyword">new</span> Singleton();</span><br><span class="line">        <span class="keyword">return</span> m_pInstance;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><div id="footnotes"><hr><div id="footnotelist"><ol><li id="fn:1"><a href="http://asi.insa-rouen.fr/enseignement/siteUV/genie_logiciel/supports/ressources/exemples_de_la_vie_reelle_pour_illustrer_pattern.pdf">Gamma E. Design patterns: elements of reusable object-oriented software[M]. Pearson Education India, 1995.</a><a href="#fnref:1" rev="footnote"> ↩</a></li><li id="fn:2"><a href="https://www.runoob.com/design-pattern/design-pattern-intro.html">设计模式简介</a><a href="#fnref:2" rev="footnote"> ↩</a></li></ol></div></div>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;对我来说，你只是一个小男孩，就像其他成千上万个小男孩一样没有什么两样。我不需要你。你也不需要我。对你来说，我也只是一只狐狸，和其他成千上万的狐狸没有什么不同。但是，如果你驯养了我，我们就会彼此需要。对我来说，你就是我的世界里独一无二的了；我对你来说，也是你的世界里的唯一。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="计算机" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="设计模式" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
    
  </entry>
  
  <entry>
    <title>聚合模式</title>
    <link href="http://sqduan.github.io/2024/10/13/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E7%BB%84%E5%90%88%E6%A8%A1%E5%BC%8F/"/>
    <id>http://sqduan.github.io/2024/10/13/计算机/设计模式/组合模式/</id>
    <published>2024-10-13T01:59:46.521Z</published>
    <updated>2024-10-13T01:59:46.521Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><blockquote><p>“盒子里面是什么”</p><p>“谁知道呢，也许是另一个盒子”</p></blockquote><a id="more"></a><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><h3 id="什么是聚合模式"><a href="#什么是聚合模式" class="headerlink" title="什么是聚合模式"></a>什么是聚合模式</h3><p>在聚合模式中，对象模型可以被抽象为一颗树，例如一个盒子里放了五个盒子，这五个盒子又可以放下若干盒子，最后构成了一棵树</p><h3 id="聚合模式应用场景"><a href="#聚合模式应用场景" class="headerlink" title="聚合模式应用场景"></a>聚合模式应用场景</h3><p>如果应用的核心模型能够用树状结构表示，使用组合模式才有价值</p><h2 id="聚合模式结构"><a href="#聚合模式结构" class="headerlink" title="聚合模式结构"></a>聚合模式结构</h2><h2 id="C-实例"><a href="#C-实例" class="headerlink" title="C++实例"></a>C++实例</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><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;algorithm&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;list&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string&gt;</span></span></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The base Component class declares common operations for both simple and</span></span><br><span class="line"><span class="comment"> * complex objects of a composition.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Component</span> &#123;</span></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * @var Component</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line"> <span class="keyword">protected</span>:</span><br><span class="line">  Component *parent_;</span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * Optionally, the base Component can declare an interface for setting and</span></span><br><span class="line"><span class="comment">   * accessing a parent of the component in a tree structure. It can also</span></span><br><span class="line"><span class="comment">   * provide some default implementation for these methods.</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line">  <span class="keyword">virtual</span> ~Component() &#123;&#125;</span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">SetParent</span><span class="params">(Component *parent)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>-&gt;parent_ = parent;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="function">Component *<span class="title">GetParent</span><span class="params">()</span> <span class="keyword">const</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>-&gt;parent_;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * In some cases, it would be beneficial to define the child-management</span></span><br><span class="line"><span class="comment">   * operations right in the base Component class. This way, you won't need to</span></span><br><span class="line"><span class="comment">   * expose any concrete component classes to the client code, even during the</span></span><br><span class="line"><span class="comment">   * object tree assembly. The downside is that these methods will be empty for</span></span><br><span class="line"><span class="comment">   * the leaf-level components.</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Add</span><span class="params">(Component *component)</span> </span>&#123;&#125;</span><br><span class="line">  <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Remove</span><span class="params">(Component *component)</span> </span>&#123;&#125;</span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * You can provide a method that lets the client code figure out whether a</span></span><br><span class="line"><span class="comment">   * component can bear children.</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="function"><span class="keyword">virtual</span> <span class="keyword">bool</span> <span class="title">IsComposite</span><span class="params">()</span> <span class="keyword">const</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * The base Component may implement some default behavior or leave it to</span></span><br><span class="line"><span class="comment">   * concrete classes (by declaring the method containing the behavior as</span></span><br><span class="line"><span class="comment">   * "abstract").</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="function"><span class="keyword">virtual</span> <span class="built_in">std</span>::<span class="built_in">string</span> <span class="title">Operation</span><span class="params">()</span> <span class="keyword">const</span> </span>= <span class="number">0</span>;</span><br><span class="line">&#125;; <span class="comment">// class Component</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The Leaf class represents the end objects of a composition. A leaf can't have</span></span><br><span class="line"><span class="comment"> * any children.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * Usually, it's the Leaf objects that do the actual work, whereas Composite</span></span><br><span class="line"><span class="comment"> * objects only delegate to their sub-components.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Leaf</span> :</span> <span class="keyword">public</span> Component &#123;</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line">  <span class="function"><span class="built_in">std</span>::<span class="built_in">string</span> <span class="title">Operation</span><span class="params">()</span> <span class="keyword">const</span> <span class="keyword">override</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">"Leaf"</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The Composite class represents the complex components that may have children.</span></span><br><span class="line"><span class="comment"> * Usually, the Composite objects delegate the actual work to their children and</span></span><br><span class="line"><span class="comment"> * then "sum-up" the result.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Composite</span> :</span> <span class="keyword">public</span> Component &#123;</span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * @var \SplObjectStorage</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line"> <span class="keyword">protected</span>:</span><br><span class="line">  <span class="built_in">std</span>::<span class="built_in">list</span>&lt;Component *&gt; children_;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * A composite object can add or remove other components (both simple or</span></span><br><span class="line"><span class="comment">   * complex) to or from its child list.</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">Add</span><span class="params">(Component *component)</span> <span class="keyword">override</span> </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>-&gt;children_.push_back(component);</span><br><span class="line">    component-&gt;SetParent(<span class="keyword">this</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * Have in mind that this method removes the pointer to the list but doesn't</span></span><br><span class="line"><span class="comment">   * frees the</span></span><br><span class="line"><span class="comment">   *     memory, you should do it manually or better use smart pointers.</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">Remove</span><span class="params">(Component *component)</span> <span class="keyword">override</span> </span>&#123;</span><br><span class="line">    children_.remove(component);</span><br><span class="line">    component-&gt;SetParent(<span class="literal">nullptr</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="function"><span class="keyword">bool</span> <span class="title">IsComposite</span><span class="params">()</span> <span class="keyword">const</span> <span class="keyword">override</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * The Composite executes its primary logic in a particular way. It traverses</span></span><br><span class="line"><span class="comment">   * recursively through all its children, collecting and summing their results.</span></span><br><span class="line"><span class="comment">   * Since the composite's children pass these calls to their children and so</span></span><br><span class="line"><span class="comment">   * forth, the whole object tree is traversed as a result.</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="function"><span class="built_in">std</span>::<span class="built_in">string</span> <span class="title">Operation</span><span class="params">()</span> <span class="keyword">const</span> <span class="keyword">override</span> </span>&#123;</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">string</span> result;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">const</span> Component *c : children_) &#123;</span><br><span class="line">      <span class="keyword">if</span> (c == children_.back()) &#123;</span><br><span class="line">        result += c-&gt;Operation();</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        result += c-&gt;Operation() + <span class="string">"+"</span>;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">"Branch("</span> + result + <span class="string">")"</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The client code works with all of the components via the base interface.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">ClientCode</span><span class="params">(Component *component)</span> </span>&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"RESULT: "</span> &lt;&lt; component-&gt;Operation();</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Thanks to the fact that the child-management operations are declared in the</span></span><br><span class="line"><span class="comment"> * base Component class, the client code can work with any component, simple or</span></span><br><span class="line"><span class="comment"> * complex, without depending on their concrete classes.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">ClientCode2</span><span class="params">(Component *component1, Component *component2)</span> </span>&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  <span class="keyword">if</span> (component1-&gt;IsComposite()) &#123;</span><br><span class="line">    component1-&gt;Add(component2);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"RESULT: "</span> &lt;&lt; component1-&gt;Operation();</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * This way the client code can support the simple leaf components...</span></span><br><span class="line"><span class="comment"> */</span></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">  Component *simple = <span class="keyword">new</span> Leaf;</span><br><span class="line">  <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"Client: I've got a simple component:\n"</span>;</span><br><span class="line">  ClientCode(simple);</span><br><span class="line">  <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"\n\n"</span>;</span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * ...as well as the complex composites.</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line"></span><br><span class="line">  Component *tree = <span class="keyword">new</span> Composite;</span><br><span class="line">  Component *branch1 = <span class="keyword">new</span> Composite;</span><br><span class="line"></span><br><span class="line">  Component *leaf_1 = <span class="keyword">new</span> Leaf;</span><br><span class="line">  Component *leaf_2 = <span class="keyword">new</span> Leaf;</span><br><span class="line">  Component *leaf_3 = <span class="keyword">new</span> Leaf;</span><br><span class="line">  branch1-&gt;Add(leaf_1);</span><br><span class="line">  branch1-&gt;Add(leaf_2);</span><br><span class="line">  Component *branch2 = <span class="keyword">new</span> Composite;</span><br><span class="line">  branch2-&gt;Add(leaf_3);</span><br><span class="line">  tree-&gt;Add(branch1);</span><br><span class="line">  tree-&gt;Add(branch2);</span><br><span class="line">  <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"Client: Now I've got a composite tree:\n"</span>;</span><br><span class="line">  ClientCode(tree);</span><br><span class="line">  <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"\n\n"</span>;</span><br><span class="line"></span><br><span class="line">  <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"Client: I don't need to check the components classes even when managing the tree:\n"</span>;</span><br><span class="line">  ClientCode2(tree, branch1);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><div id="footnotes"><hr><div id="footnotelist"><ol><li id="fn:1"><a href="https://www.runoob.com/design-pattern/design-pattern-intro.html">设计模式简介</a><a href="#fnref:1" rev="footnote"> ↩</a></li></ol></div></div>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;“盒子里面是什么”&lt;/p&gt;
&lt;p&gt;“谁知道呢，也许是另一个盒子”&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="计算机" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="设计模式" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
    
  </entry>
  
  <entry>
    <title>设计模式介绍</title>
    <link href="http://sqduan.github.io/2024/10/13/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E7%AE%80%E4%BB%8B/"/>
    <id>http://sqduan.github.io/2024/10/13/计算机/设计模式/设计模式简介/</id>
    <published>2024-10-13T01:59:46.521Z</published>
    <updated>2024-10-13T01:59:46.521Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><blockquote><p>“每一段故事诞生的地方，都有一种“美”存在。</p><p>我们与其邂逅，被其触动，心生涟漪。</p><p>新的故事便随之诞生。” —— 赤木明登</p></blockquote><a id="more"></a><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>对于工程学，我们有必要建立起一套成体系的设计模式，确保可复用性、可理解性以及可靠性。对于软件工程来说，设计模式使代码编制真正工程化，使我们周围不断重复发生的问题，以及该问题的核心解决方案得以被精确地描述。这里，我们以GOF的 Design Patterns - Elements of Reusable Object-Oriented Software<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup>作为指导，对设计模式进行介绍。他们提出的设计模式主要基于以下原则：</p><ul><li>面向接口编程</li><li>优先使用对象组合</li></ul><h2 id="设计模式类型总结2"><a href="#设计模式类型总结2" class="headerlink" title="设计模式类型总结2"></a>设计模式类型总结<sup id="fnref:2"><a href="#fn:2" rel="footnote">2</a></sup></h2><div class="table-container"><table><thead><tr><th style="text-align:left">序号</th><th style="text-align:left">模式 &amp; 描述</th><th style="text-align:left">包括</th></tr></thead><tbody><tr><td style="text-align:left">1</td><td style="text-align:left"><strong>创建型模式</strong> <br />这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式，而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。</td><td style="text-align:left">工厂模式（Factory Pattern）<br />抽象工厂模式（Abstract Factory Pattern）<br />单例模式（Singleton Pattern）<br />建造者模式（Builder Pattern）<br />原型模式（Prototype Pattern）</td></tr><tr><td style="text-align:left">2</td><td style="text-align:left"><strong>结构型模式</strong> <br />这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。</td><td style="text-align:left">适配器模式（Adapter Pattern）<br />桥接模式（Bridge Pattern）<br />过滤器模式（Filter、Criteria Pattern）<br />组合模式（Composite Pattern）<br />装饰器模式（Decorator Pattern）<br />外观模式（Facade Pattern）<br />享元模式（Flyweight Pattern）<br />代理模式（Proxy Pattern）<br /></td></tr><tr><td style="text-align:left">3</td><td style="text-align:left"><strong>行为型模式</strong> <br />这些设计模式特别关注对象之间的通信。</td><td style="text-align:left">责任链模式（Chain of Responsibility Pattern）<br />命令模式（Command Pattern）<br />解释器模式（Interpreter Pattern）<br />迭代器模式（Iterator Pattern）<br />中介者模式（Mediator Pattern）<br />备忘录模式（Memento Pattern）<br />观察者模式（Observer Pattern）<br />状态模式（State Pattern）<br />空对象模式（Null Object Pattern）<br />策略模式（Strategy Pattern）<br />模板模式（Template Pattern）<br />访问者模式（Visitor Pattern）</td></tr><tr><td style="text-align:left">4</td><td style="text-align:left"><strong>J2EE 模式</strong> <br />这些设计模式特别关注表示层。这些模式是由 Sun Java Center 鉴定的。</td><td style="text-align:left">MVC 模式（MVC Pattern）<br />业务代表模式（Business Delegate Pattern）<br />组合实体模式（Composite Entity Pattern）<br />数据访问对象模式（Data Access Object Pattern）<br />前端控制器模式（Front Controller Pattern）<br />拦截过滤器模式（Intercepting Filter Pattern）<br />服务定位器模式（Service Locator Pattern）<br />传输对象模式（Transfer Object Pattern）</td></tr></tbody></table></div><h2 id="设计六原则"><a href="#设计六原则" class="headerlink" title="设计六原则"></a>设计六原则</h2><h3 id="1-开闭原则"><a href="#1-开闭原则" class="headerlink" title="1 开闭原则"></a>1 开闭原则</h3><p>开放扩展，关闭修改，使用接口和抽象，使程序扩展性好</p><h3 id="2-里氏代换"><a href="#2-里氏代换" class="headerlink" title="2 里氏代换"></a>2 里氏代换</h3><p>任何基类可以出现的地方，子类一定可以出现</p><h3 id="3-依赖倒置"><a href="#3-依赖倒置" class="headerlink" title="3 依赖倒置"></a>3 依赖倒置</h3><p>针对接口编程，依赖于抽象而不依赖于具体</p><h3 id="4-接口隔离"><a href="#4-接口隔离" class="headerlink" title="4 接口隔离"></a>4 接口隔离</h3><p>降低类间耦合度，使用多个隔离接口比使用单个接口好</p><h3 id="5-最少相互作用"><a href="#5-最少相互作用" class="headerlink" title="5 最少相互作用"></a>5 最少相互作用</h3><p>系统功能模块应当尽可能独立</p><h3 id="合成复用"><a href="#合成复用" class="headerlink" title="合成复用"></a>合成复用</h3><p>尽量使用组合，而不是继承</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><div id="footnotes"><hr><div id="footnotelist"><ol><li id="fn:1"><a href="http://asi.insa-rouen.fr/enseignement/siteUV/genie_logiciel/supports/ressources/exemples_de_la_vie_reelle_pour_illustrer_pattern.pdf">Gamma E. Design patterns: elements of reusable object-oriented software[M]. Pearson Education India, 1995.</a><a href="#fnref:1" rev="footnote"> ↩</a></li><li id="fn:2"><a href="https://www.runoob.com/design-pattern/design-pattern-intro.html">设计模式简介</a><a href="#fnref:2" rev="footnote"> ↩</a></li></ol></div></div>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;“每一段故事诞生的地方，都有一种“美”存在。&lt;/p&gt;
&lt;p&gt;我们与其邂逅，被其触动，心生涟漪。&lt;/p&gt;
&lt;p&gt;新的故事便随之诞生。” —— 赤木明登&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="计算机" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="设计模式" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
    
  </entry>
  
  <entry>
    <title>增删查改排序</title>
    <link href="http://sqduan.github.io/2024/10/13/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/%E5%A2%9E%E5%88%A0%E6%9F%A5%E6%94%B9%E6%8E%92%E5%BA%8F/"/>
    <id>http://sqduan.github.io/2024/10/13/计算机/设计模式/面向对象/增删查改排序/</id>
    <published>2024-10-13T01:59:46.521Z</published>
    <updated>2024-10-13T01:59:46.521Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>增删查改排序是处理多对象的几种常规操作，本文将针对面向对象过程中的增删查改排序操作进行总结。</p><a id="more"></a><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><div id="footnotes"><hr><div id="footnotelist"><ol><li id="fn:1"><a href="http://asi.insa-rouen.fr/enseignement/siteUV/genie_logiciel/supports/ressources/exemples_de_la_vie_reelle_pour_illustrer_pattern.pdf">Gamma E. Design patterns: elements of reusable object-oriented software[M]. Pearson Education India, 1995.</a><a href="#fnref:1" rev="footnote"> ↩</a></li><li id="fn:2"><a href="https://www.runoob.com/design-pattern/design-pattern-intro.html">设计模式简介</a><a href="#fnref:2" rev="footnote"> ↩</a></li></ol></div></div>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;增删查改排序是处理多对象的几种常规操作，本文将针对面向对象过程中的增删查改排序操作进行总结。&lt;/p&gt;
    
    </summary>
    
      <category term="计算机" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="设计模式" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
      <category term="面向对象" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/"/>
    
    
  </entry>
  
  <entry>
    <title>HTTP</title>
    <link href="http://sqduan.github.io/2024/10/13/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E7%BD%91%E7%BB%9C%E9%80%9A%E4%BF%A1/HTTP/"/>
    <id>http://sqduan.github.io/2024/10/13/计算机/网络通信/HTTP/</id>
    <published>2024-10-13T01:59:46.520Z</published>
    <updated>2024-10-13T01:59:46.520Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><blockquote><p><strong>HTTP/1.1 200 OK</strong></p></blockquote><a id="more"></a><h2 id="HTTP及其工作原理"><a href="#HTTP及其工作原理" class="headerlink" title="HTTP及其工作原理"></a>HTTP及其工作原理</h2><h3 id="HTTP头"><a href="#HTTP头" class="headerlink" title="HTTP头"></a>HTTP头</h3><p>HTTP头字段是HTTP的请求和响应消息中的消息头部分，它们定义了一个超文本传输协议事务中的操作参数，其头部字段可根据自己的需要定义。</p><h4 id="请求头信息"><a href="#请求头信息" class="headerlink" title="请求头信息"></a>请求头信息</h4><h5 id="请求头组成"><a href="#请求头组成" class="headerlink" title="请求头组成"></a>请求头组成</h5><h5 id="例子：一个GET请求头"><a href="#例子：一个GET请求头" class="headerlink" title="例子：一个GET请求头"></a>例子：一个GET请求头</h5><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></pre></td><td class="code"><pre><span class="line">GET /home.html HTTP/<span class="number">1.1</span></span><br><span class="line">Host: developer.mozilla.org</span><br><span class="line">User-Agent: Mozilla/<span class="number">5.0</span> (Macintosh; Intel Mac OS X <span class="number">10.9</span>; rv:<span class="number">50.0</span>) Gecko/<span class="number">20100101</span> Firefox/<span class="number">50.0</span></span><br><span class="line">Accept: text/html,application/xhtml+xml,application/xml;q=<span class="number">0.9</span>,*<span class="comment">/*;q=0.8</span></span><br><span class="line"><span class="comment">Accept-Language: en-US,en;q=0.5</span></span><br><span class="line"><span class="comment">Accept-Encoding: gzip, deflate, br</span></span><br><span class="line"><span class="comment">Referer: https://developer.mozilla.org/testpage.html</span></span><br><span class="line"><span class="comment">Connection: keep-alive</span></span><br><span class="line"><span class="comment">Upgrade-Insecure-Requests: 1</span></span><br><span class="line"><span class="comment">If-Modified-Since: Mon, 18 Jul 2016 02:36:04 GMT</span></span><br><span class="line"><span class="comment">If-None-Match: "c561c68d0ba92bbeb8b0fff2a9199f722e3a621a"</span></span><br><span class="line"><span class="comment">Cache-Control: max-age=0</span></span><br></pre></td></tr></table></figure><h4 id="响应头信息"><a href="#响应头信息" class="headerlink" title="响应头信息"></a>响应头信息</h4><h5 id="响应头组成"><a href="#响应头组成" class="headerlink" title="响应头组成"></a>响应头组成</h5><div class="table-container"><table><thead><tr><th style="text-align:left">响应头</th><th>说明</th></tr></thead><tbody><tr><td style="text-align:left">Allow：支持的请求方法</td><td>服务器支持哪些请求方法（如GET、POST等）。</td></tr><tr><td style="text-align:left">Content-Encoding：编码方法</td><td>文档的编码方法。只有解码才能正确得到Content-Type头指定的内容类型，一般的编码方式如gzip，通过压缩的方式使HTML文档下载时间显著减少。</td></tr><tr><td style="text-align:left">Content-Length：内容长度</td><td></td></tr><tr><td style="text-align:left">Content-Type：文档类型</td><td>表示后面的文档是什么类型，默认为text/plain，但一般会显式指定为text/html</td></tr><tr><td style="text-align:left">Date：时间</td><td></td></tr><tr><td style="text-align:left">Last-Modified：最后改动日期</td><td></td></tr><tr><td style="text-align:left">Server：服务器名字</td><td></td></tr><tr><td style="text-align:left">Set-Cookie：设置关联Cookie</td></tr></tbody></table></div><h5 id="例子：一个GET响应头"><a href="#例子：一个GET响应头" class="headerlink" title="例子：一个GET响应头"></a>例子：一个GET响应头</h5><figure class="highlight plain"><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">200 OK</span><br><span class="line">Access-Control-Allow-Origin: *</span><br><span class="line">Connection: Keep-Alive</span><br><span class="line">Content-Encoding: gzip</span><br><span class="line">Content-Type: text/html; charset=utf-8</span><br><span class="line">Date: Mon, 18 Jul 2016 16:06:00 GMT</span><br><span class="line">Etag: &quot;c561c68d0ba92bbeb8b0f612a9199f722e3a621a&quot;</span><br><span class="line">Keep-Alive: timeout=5, max=997</span><br><span class="line">Last-Modified: Mon, 18 Jul 2016 02:36:04 GMT</span><br><span class="line">Server: Apache</span><br><span class="line">Set-Cookie: mykey=myvalue; expires=Mon, 17-Jul-2017 16:06:00 GMT; Max-Age=31449600; Path=/; secure</span><br><span class="line">Transfer-Encoding: chunked</span><br><span class="line">Vary: Cookie, Accept-Encoding</span><br><span class="line">X-Backend-Server: developer2.webapp.scl3.mozilla.com</span><br><span class="line">X-Cache-Info: not cacheable; meta data too large</span><br><span class="line">X-kuma-revision: 1085259</span><br><span class="line">x-frame-options: DENY</span><br></pre></td></tr></table></figure><h3 id="HTTP请求流程"><a href="#HTTP请求流程" class="headerlink" title="HTTP请求流程"></a>HTTP请求流程</h3><pre class="mermaid">sequenceDiagram    participant DNS服务器    participant 客户    participant 服务器    客户->>DNS服务器:DNS域名解析    DNS服务器->>客户:服务器IP地址    loop 尝试TCP连接        客户->>服务器: TCP三次握手    end    客户->>服务器: 发起HTTP请求    服务器->>客户: 响应HTTP请求，发送HTML代码    服务器->>客户: 四次挥手，断开连接    客户->>客户: 浏览器解析HTML代码获得资源    客户->>客户: 浏览器渲染页面并呈现</pre><h4 id="为什么HTTP使用TCP"><a href="#为什么HTTP使用TCP" class="headerlink" title="为什么HTTP使用TCP"></a>为什么HTTP使用TCP</h4><p>由于打开网页需要传输很多数据，而TCP协议提供拥塞控制、数据重排以及错误纠正等机制，所以更适合HTTP使用场景。</p><h2 id="HTTP-发展历程"><a href="#HTTP-发展历程" class="headerlink" title="HTTP 发展历程"></a>HTTP 发展历程</h2><h3 id="HTTP1-0"><a href="#HTTP1-0" class="headerlink" title="HTTP1.0"></a>HTTP1.0</h3><p>最早版本的HTTP协议是HTTP1.0，其有点是浏览器与服务器只保持短暂连接，且连接不进行记录，这是为了提高效率，但是也造成了一些缺陷，主要包括如下缺点：</p><ol><li>访问复杂网页会导致多次请求和响应，每次请求响应都会建立新的连接</li><li>连接无法复用，导致重复经历三次握手和慢启动</li><li>队头阻塞，如果HTTP流水线中有多个请求出现，一个数据包的阻塞会导致后续的数据包都被阻塞</li></ol><h3 id="HTTP1-1"><a href="#HTTP1-1" class="headerlink" title="HTTP1.1"></a>HTTP1.1</h3><p>为了改进HTTP1.0的缺陷，人们又提出了1.1，相比1.0，1.1的优点如下：</p><h4 id="支持持久连接"><a href="#支持持久连接" class="headerlink" title="支持持久连接"></a>支持持久连接</h4><p>在1.1版本中，支持了持久连接，一个TCP连接可以传送多个HTTP请求与响应，减少了建立和关闭连接的消耗与延迟。一个复杂网页可以在一个连接中进行传输。此外，一些其他优点如下所示：</p><h4 id="更多请求和响应头"><a href="#更多请求和响应头" class="headerlink" title="更多请求和响应头"></a>更多请求和响应头</h4><p>例如Host响应头，Host使浏览器能够区分来自同一服务器的不同站点</p><ul><li>支持断点续传</li></ul><h3 id="HTTP2-0"><a href="#HTTP2-0" class="headerlink" title="HTTP2.0"></a>HTTP2.0</h3><p>HTTP2.0相比1.1在性能上有了更大提升，它包含如下特性：</p><h4 id="多路复用"><a href="#多路复用" class="headerlink" title="多路复用"></a>多路复用</h4><h4 id="二进制分帧"><a href="#二进制分帧" class="headerlink" title="二进制分帧"></a>二进制分帧</h4><h4 id="首部压缩"><a href="#首部压缩" class="headerlink" title="首部压缩"></a>首部压缩</h4><h4 id="服务端推送"><a href="#服务端推送" class="headerlink" title="服务端推送"></a>服务端推送</h4><h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><h3 id="HTTP-HTTPS区别"><a href="#HTTP-HTTPS区别" class="headerlink" title="HTTP/HTTPS区别"></a>HTTP/HTTPS区别</h3><p>HTTP运行于TCP上，明文传输，C/S端均无法验证对方身份；HTTPS为身披SSL外壳的HTTP，运行于SSL（安全传输协议）上，而SSL运行于TCP上，架构如下：</p><pre class="mermaid">graph TD;    A["HTTP: 80"]    B["HTTPS: 443"]    C["SSL"]    D["TCP"]    A-->D;    B-->C;    C-->D;</pre><p>区别：</p><ol><li>端口不同</li><li>开销不同，HTTPS由于要加密，需要更大开销</li><li>HTTPS需要购买证书</li></ol><h3 id="吃个甜点——Session-Cookie"><a href="#吃个甜点——Session-Cookie" class="headerlink" title="吃个甜点——Session/Cookie"></a>吃个甜点——Session/Cookie</h3><p>Session是服务器对用户进行辨识的机制，由于HTTP是无状态协议，因此服务端在辨识用户时，需要一些记录，从而催生了Session，Session和Cookie的区别是Cookie一般保存在C端，当用户第二次发起请求时，会自动将上次存储的cookie发送至服务器，实现身份辨识。</p><p>而Session保存在服务器端，默认存放在服务器一个文件夹中，更加安全，但是占用服务器资源。</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><div id="footnotes"><hr><div id="footnotelist"><ol><li id="fn:1"><a href="https://developer.mozilla.org/zh-CN/docs/Glossary/Response_header">HTTP响应头</a><a href="#fnref:1" rev="footnote"> ↩</a></li></ol></div></div>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;HTTP/1.1 200 OK&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="计算机" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="网络通信" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E7%BD%91%E7%BB%9C%E9%80%9A%E4%BF%A1/"/>
    
    
  </entry>
  
  <entry>
    <title>TCP/IP</title>
    <link href="http://sqduan.github.io/2024/10/13/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E7%BD%91%E7%BB%9C%E9%80%9A%E4%BF%A1/TCPIP/"/>
    <id>http://sqduan.github.io/2024/10/13/计算机/网络通信/TCPIP/</id>
    <published>2024-10-13T01:59:46.520Z</published>
    <updated>2024-10-13T01:59:46.520Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><blockquote><p>First, let us set up the connection</p></blockquote><a id="more"></a><h2 id="TCP-IP简介"><a href="#TCP-IP简介" class="headerlink" title="TCP/IP简介"></a>TCP/IP简介</h2><h3 id="什么是TCP-IP"><a href="#什么是TCP-IP" class="headerlink" title="什么是TCP/IP"></a>什么是TCP/IP</h3><p>TCP/IP是用于网络的通信协议，所谓通信协议，就是指通信双方相互必须遵守的规则，从而能够建立正确的通信。TCP/IP的全称是Transmission Control Protocol/ Internet Protocol，即传输控制协议/网际协议，实际是一套协议族。</p><h3 id="TCP-IP组成"><a href="#TCP-IP组成" class="headerlink" title="TCP/IP组成"></a>TCP/IP组成</h3><p>作为一个协议族，TCP/IP由以下五个协议组成</p><ul><li>TCP - 应用程序间点对点通信，固定连接</li><li>UDP - 应用程序间简单通信，无连接</li><li>IP - 计算机之间通信，无连接</li><li>ICMP（因特网消息控制协议）- 针对错误和状态</li><li>DHCP（动态主机配置协议）- 针对动态寻址</li></ul><h2 id="TCP协议"><a href="#TCP协议" class="headerlink" title="TCP协议"></a>TCP协议</h2><h3 id="TCP拥塞控制"><a href="#TCP拥塞控制" class="headerlink" title="TCP拥塞控制"></a>TCP拥塞控制</h3><p>理想情况下，TCP通信过程应当类似水流管道，吞吐量随需求规模线性增长，最后达到最大值。如果没有拥塞控制，那通信过程会类似于车辆堵车，最后彻底锁死。当输入负载达到一定吞吐量后，吞吐量不会增加，即一部分网络资源会被抛弃，网络的吞吐量维持在其所能控制的最大值。为了防止TCP传输发生死锁，TCP协议设置了四种拥塞控制算法，分别是：<strong>慢开始、拥塞控制、快重传、快恢复</strong></p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><div id="footnotes"><hr><div id="footnotelist"><ol><li id="fn:1"><a href="https://developer.mozilla.org/zh-CN/docs/Glossary/Response_header">HTTP响应头</a><a href="#fnref:1" rev="footnote"> ↩</a></li></ol></div></div>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;First, let us set up the connection&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="计算机" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="网络通信" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E7%BD%91%E7%BB%9C%E9%80%9A%E4%BF%A1/"/>
    
    
  </entry>
  
  <entry>
    <title>网络分析工具使用</title>
    <link href="http://sqduan.github.io/2024/10/13/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E7%BD%91%E7%BB%9C%E9%80%9A%E4%BF%A1/%E5%8C%85%E5%88%86%E6%9E%90/"/>
    <id>http://sqduan.github.io/2024/10/13/计算机/网络通信/包分析/</id>
    <published>2024-10-13T01:59:46.520Z</published>
    <updated>2024-10-13T01:59:46.520Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><blockquote><p><strong>HTTP/1.1 200 OK</strong></p></blockquote><a id="more"></a><p>本文将针对一些比较重要的抓包分析工具进行总结</p><h2 id="tcpdump"><a href="#tcpdump" class="headerlink" title="tcpdump"></a>tcpdump</h2><h3 id="命令使用实例"><a href="#命令使用实例" class="headerlink" title="命令使用实例"></a>命令使用实例</h3><h4 id="查看指定设备"><a href="#查看指定设备" class="headerlink" title="查看指定设备"></a>查看指定设备</h4><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">tcpdump -i eth0</span><br></pre></td></tr></table></figure><h2 id="telnet"><a href="#telnet" class="headerlink" title="telnet"></a>telnet</h2><h3 id="命令使用实例-1"><a href="#命令使用实例-1" class="headerlink" title="命令使用实例"></a>命令使用实例</h3><h4 id="查看端口状态"><a href="#查看端口状态" class="headerlink" title="查看端口状态"></a>查看端口状态</h4><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">netstat -ntulp |grep 22023</span><br></pre></td></tr></table></figure><p>查看22023号端口的情况</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;HTTP/1.1 200 OK&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="计算机" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="网络通信" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E7%BD%91%E7%BB%9C%E9%80%9A%E4%BF%A1/"/>
    
    
  </entry>
  
  <entry>
    <title>socket套接字编程——概述</title>
    <link href="http://sqduan.github.io/2024/10/13/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E7%BD%91%E7%BB%9C%E9%80%9A%E4%BF%A1/Socket%E5%A5%97%E6%8E%A5%E5%AD%97%E7%BC%96%E7%A8%8B/"/>
    <id>http://sqduan.github.io/2024/10/13/计算机/网络通信/Socket套接字编程/</id>
    <published>2024-10-13T01:59:46.520Z</published>
    <updated>2024-10-13T01:59:46.520Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><blockquote><p><strong>HTTP/1.1 200 OK</strong></p></blockquote><a id="more"></a><h2 id="基本原理"><a href="#基本原理" class="headerlink" title="基本原理"></a>基本原理</h2><h3 id="IP地址和端口号"><a href="#IP地址和端口号" class="headerlink" title="IP地址和端口号"></a>IP地址和端口号</h3><p>IP地址用于区分不同的主机，而端口号则用于区分某个主机上不同的应用程序，端口号为0-65535，一般1024以下保留做系统使用。当我们进行socket编程时，必须区分端口号从而找到正确的应用程序。</p><h2 id="socket通信过程"><a href="#socket通信过程" class="headerlink" title="socket通信过程"></a>socket通信过程</h2><h3 id="TCP（流式套接字SOCK-STREAM）"><a href="#TCP（流式套接字SOCK-STREAM）" class="headerlink" title="TCP（流式套接字SOCK_STREAM）"></a>TCP（流式套接字SOCK_STREAM）</h3><h4 id="服务器端编程步骤"><a href="#服务器端编程步骤" class="headerlink" title="服务器端编程步骤"></a>服务器端编程步骤</h4><ol><li>创建套接字(socket())</li><li>绑定套接字到一个IP地址和一个端口上(bind())；</li><li>将套接字设置为监听模式等待连接请求(listen())；</li><li>请求到来后，接受连接请求，返回一个新的对应于此次连接的套接字(accept())；</li><li>用返回的套接字和客户端进行通信(send()/recv())；</li><li>返回，等待另一连接请求；</li><li>关闭套接字(closesocket())。</li></ol><h4 id="客户端编程步骤"><a href="#客户端编程步骤" class="headerlink" title="客户端编程步骤"></a>客户端编程步骤</h4><ol><li>加载套接字库，创建套接字(WSAStartup()/socket())；</li><li>向服务器发出连接请求(connect())；</li><li>和服务器端进行通信(send()/recv())；</li><li>关闭套接字，关闭加载的套接字库(closesocket()/WSACleanup())。</li></ol><h3 id="UDP（报文式套接字SOCK-DGRAM）"><a href="#UDP（报文式套接字SOCK-DGRAM）" class="headerlink" title="UDP（报文式套接字SOCK_DGRAM）"></a>UDP（报文式套接字SOCK_DGRAM）</h3><h4 id="服务器端编程步骤-1"><a href="#服务器端编程步骤-1" class="headerlink" title="服务器端编程步骤"></a>服务器端编程步骤</h4><ol><li>socket：       建立一个socket</li><li>bind：          将这个socket绑定在某个端口上（AF_INET）</li><li>recvfrom：  如果没有客户端发起请求，则会阻塞在这个函数里</li><li>close：         通信完成后关闭socket</li></ol><h4 id="客户端编程步骤-1"><a href="#客户端编程步骤-1" class="headerlink" title="客户端编程步骤"></a>客户端编程步骤</h4><ol><li>socket：      建立一个socket</li><li>sendto：     向服务器的某个端口发起请求（AF_INET）</li><li>close：        通信完成后关闭socket</li></ol><h3 id="原始套接字（SOCK-RAW）"><a href="#原始套接字（SOCK-RAW）" class="headerlink" title="原始套接字（SOCK_RAW）"></a>原始套接字（SOCK_RAW）</h3><h2 id="常用API"><a href="#常用API" class="headerlink" title="常用API"></a>常用API</h2><h3 id="socket-函数"><a href="#socket-函数" class="headerlink" title="socket()函数"></a>socket()函数</h3><h4 id="声明"><a href="#声明" class="headerlink" title="声明"></a>声明</h4><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="function"><span class="keyword">int</span> <span class="title">socket</span><span class="params">(<span class="keyword">int</span> domain, <span class="keyword">int</span> type, <span class="keyword">int</span> protocol)</span></span>;</span><br></pre></td></tr></table></figure><p>类似于文件的打开操作，socket创建一个socket描述符，通过该描述符进行一些读写操作。</p><h4 id="参数"><a href="#参数" class="headerlink" title="参数"></a>参数</h4><ul><li>domain</li></ul><p>指定协议族（family），决定了socket的地址类型。</p><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">含义</th><th style="text-align:center">名称</th><th style="text-align:center">含义</th></tr></thead><tbody><tr><td style="text-align:center">PF_UNIX,PF_LOCAL</td><td style="text-align:center">本地通信</td><td style="text-align:center">PF_X25</td><td style="text-align:center">ITU-T X25 / ISO-8208协议</td></tr><tr><td style="text-align:center">AF_INET,PF_INET</td><td style="text-align:center">IPv4 Internet协议</td><td style="text-align:center">PF_AX25</td><td style="text-align:center">Amateur radio AX.25</td></tr><tr><td style="text-align:center">PF_INET6</td><td style="text-align:center">IPv6 Internet协议</td><td style="text-align:center">PF_ATMPVC</td><td style="text-align:center">原始ATM PVC访问</td></tr><tr><td style="text-align:center">PF_IPX</td><td style="text-align:center">IPX-Novell协议</td><td style="text-align:center">PF_APPLETALK</td><td style="text-align:center">Appletalk</td></tr><tr><td style="text-align:center">PF_NETLINK</td><td style="text-align:center">内核用户界面设备</td><td style="text-align:center">PF_PACKET</td><td style="text-align:center">底层包访问</td></tr></tbody></table></div><ul><li>type</li></ul><p>指定socket类型</p><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:left">含义</th></tr></thead><tbody><tr><td style="text-align:center">SOCK_STREAM</td><td style="text-align:left">Tcp连接，提供序列化的、可靠的、双向连接的字节流。支持带外数据传输</td></tr><tr><td style="text-align:center">SOCK_DGRAM</td><td style="text-align:left">支持UDP连接（无连接状态的消息）</td></tr><tr><td style="text-align:center">SOCK_SEQPACKET</td><td style="text-align:left">序列化包，提供一个序列化的、可靠的、双向的基本连接的数据传输通道，数据长度定常。每次调用读系统调用时数据需要将全部数据读出</td></tr><tr><td style="text-align:center">SOCK_RAW</td><td style="text-align:left">RAW类型，提供原始网络协议访问</td></tr><tr><td style="text-align:center">SOCK_RDM</td><td style="text-align:left">提供可靠的数据报文，不过可能数据会有乱序</td></tr></tbody></table></div><ul><li>protocol</li></ul><p>指定协议，常用的协议有IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等，分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。</p><h3 id="bind-函数"><a href="#bind-函数" class="headerlink" title="bind()函数"></a>bind()函数</h3><p>bind()函数将一个地址族中的特定地址赋给socket。例如对应AF_INET、AF_INET6就是把一个ipv4或ipv6地址和端口号组合赋给socket。</p><h4 id="声明-1"><a href="#声明-1" class="headerlink" title="声明"></a>声明</h4><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="function"><span class="keyword">int</span> <span class="title">bind</span><span class="params">(<span class="keyword">int</span> sockfd, <span class="keyword">const</span> struct sockaddr *addr, <span class="keyword">socklen_t</span> addrlen)</span></span>;</span><br></pre></td></tr></table></figure><h4 id="参数-1"><a href="#参数-1" class="headerlink" title="参数"></a>参数</h4><p><strong>sockfd</strong>，即要操作的socket描述字，socket()函数的返回值。</p><p><strong>addr</strong>，一个const struct sockaddr *指针，指向要绑定给sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">// ipv4</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> &#123;</span></span><br><span class="line">    <span class="keyword">sa_family_t</span>    sin_family; <span class="comment">/* address family: AF_INET */</span></span><br><span class="line">    <span class="keyword">in_port_t</span>      sin_port;   <span class="comment">/* port in network byte order */</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">in_addr</span> <span class="title">sin_addr</span>;</span>   <span class="comment">/* internet address */</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">/* Internet address. */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">in_addr</span> &#123;</span></span><br><span class="line">    <span class="keyword">uint32_t</span>       s_addr;     <span class="comment">/* address in network byte order */</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ipv6</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in6</span> &#123;</span> </span><br><span class="line">    <span class="keyword">sa_family_t</span>     sin6_family;   <span class="comment">/* AF_INET6 */</span> </span><br><span class="line">    <span class="keyword">in_port_t</span>       sin6_port;     <span class="comment">/* port number */</span> </span><br><span class="line">    <span class="keyword">uint32_t</span>        sin6_flowinfo; <span class="comment">/* IPv6 flow information */</span> </span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">in6_addr</span> <span class="title">sin6_addr</span>;</span>     <span class="comment">/* IPv6 address */</span> </span><br><span class="line">    <span class="keyword">uint32_t</span>        sin6_scope_id; <span class="comment">/* Scope ID (new in 2.4) */</span> </span><br><span class="line">&#125;;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">in6_addr</span> &#123;</span> </span><br><span class="line">    <span class="keyword">unsigned</span> <span class="keyword">char</span>   s6_addr[<span class="number">16</span>];   <span class="comment">/* IPv6 address */</span> </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Unix</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> UNIX_PATH_MAX    108</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_un</span> &#123;</span> </span><br><span class="line">    <span class="keyword">sa_family_t</span> sun_family;               <span class="comment">/* AF_UNIX */</span> </span><br><span class="line">    <span class="keyword">char</span>        sun_path[UNIX_PATH_MAX];  <span class="comment">/* pathname */</span> </span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p><strong>addrlen</strong>，对应地址的长度。<br> 通常服务器在启动的时候都会绑定一个众所周知的地址（如ip地址+端口号），用于提供服务，客户可以通过它来接连服务器；而客户端就不用指定，有系统自动分配一个端口号和自身的ip地址组合。所以通常服务器端在listen之前会调用bind()，而客户端就不会调用，而是在connect()时由系统随机生成一个。</p><h3 id="listen-与connect-函数"><a href="#listen-与connect-函数" class="headerlink" title="listen()与connect()函数"></a>listen()与connect()函数</h3><h4 id="声明-2"><a href="#声明-2" class="headerlink" title="声明"></a>声明</h4><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// listen    服务器端，用于监听套接字</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">listen</span><span class="params">(<span class="keyword">int</span> sockfd, <span class="keyword">int</span> backlog)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// connect   客户端，用于发起连接请求</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">connect</span><span class="params">(<span class="keyword">int</span> sockfd, <span class="keyword">const</span> struct sockaddr *addr, <span class="keyword">socklen_t</span> addrlen)</span></span>;</span><br></pre></td></tr></table></figure><h4 id="参数-2"><a href="#参数-2" class="headerlink" title="参数"></a>参数</h4><ul><li>listen</li></ul><p>第一个参数sockfd即为要监听的socket描述字，第二个参数blacklog为相应socket可以排队的最大连接个数。<br>socket()函数创建的socket默认是一个主动类型的，listen函数将socket变为被动类型的，等待客户的连接请求。</p><ul><li>connect</li></ul><p>第一个参数sockfd即为客户端的socket描述字，第二个参数addr为要连接的服务器的socket地址，第三个参数addrlen为服务器socket地址的长度。客户端通过调用connect()函数来建立与TCP服务器的连接。</p><h3 id="accept"><a href="#accept" class="headerlink" title="accept()"></a>accept()</h3><p>在服务端开始监听套接字、客户端发起connect请求后，TCP服务器监听到该请求后，会调用accept()函数接收请求，之后可以开始网络IO操作，类似于普通文件读写。</p><h4 id="声明-3"><a href="#声明-3" class="headerlink" title="声明"></a>声明</h4><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="function"><span class="keyword">int</span> <span class="title">accept</span><span class="params">(<span class="keyword">int</span> sockfd, struct sockaddr *addr, <span class="keyword">socklen_t</span> *addrlen)</span></span>;</span><br></pre></td></tr></table></figure><h4 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h4><p>accept的第一个参数为服务器的socket描述字，是服务器开始调用socket()函数生成的，称为监听socket描述字；而accept函数返回的是已连接的socket描述字。一个服务器通常通常仅仅只创建一个监听socket描述字，它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字，当服务器完成了对某个客户的服务，相应的已连接socket描述字就被关闭。</p><h3 id="recvfrom"><a href="#recvfrom" class="headerlink" title="recvfrom()"></a>recvfrom()</h3><p>UDP中接收客户端发起的请求，如果没有，那么会阻塞在这个函数中</p><h3 id="sendto"><a href="#sendto" class="headerlink" title="sendto()"></a>sendto()</h3><p>UDP中发送数据</p><h2 id="实例"><a href="#实例" class="headerlink" title="实例"></a>实例</h2><h3 id="TCP套接字编程"><a href="#TCP套接字编程" class="headerlink" title="TCP套接字编程"></a>TCP套接字编程</h3><h4 id="server端"><a href="#server端" class="headerlink" title="server端"></a>server端</h4><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;cstdio&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;cstdlib&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;cerrno&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;cstring&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/socket.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/types.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;netinet/in.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;arpa/inet.h&gt;</span></span></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="comment">// 创建监听socket</span></span><br><span class="line">    <span class="keyword">int</span> listenfd = socket(AF_INET, SOCK_STREAM, <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">if</span> (listenfd == <span class="number">-1</span>) &#123;</span><br><span class="line">        <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"Error: socket"</span> &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 将socket与IP和端口号进行绑定</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">addr</span>;</span></span><br><span class="line">    addr.sin_family = AF_INET;</span><br><span class="line">    addr.sin_port = htons(<span class="number">8000</span>);</span><br><span class="line">    addr.sin_addr.s_addr = INADDR_ANY;</span><br><span class="line">    <span class="keyword">if</span> (bind(listenfd, (struct sockaddr*)&amp;addr, <span class="keyword">sizeof</span>(addr)) == <span class="number">-1</span>) &#123;</span><br><span class="line">        <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"Error: bind"</span> &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 监听套接字</span></span><br><span class="line">    <span class="keyword">if</span>(listen(listenfd, <span class="number">5</span>) == <span class="number">-1</span>) &#123;</span><br><span class="line">        <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"Error: listen"</span> &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 接受连接</span></span><br><span class="line">    <span class="keyword">int</span> conn;</span><br><span class="line">    <span class="keyword">char</span> clientIP[INET_ADDRSTRLEN] = <span class="string">""</span>;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">clientAddr</span>;</span></span><br><span class="line">    <span class="keyword">socklen_t</span> clientAddrLen = <span class="keyword">sizeof</span>(clientAddr);</span><br><span class="line">    <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">        <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"...listening"</span> &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">        conn = accept(listenfd, (struct sockaddr*)&amp;clientAddr, &amp;clientAddrLen);</span><br><span class="line">        <span class="keyword">if</span> (conn &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"Error: accept"</span> &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        inet_ntop(AF_INET, &amp;clientAddr.sin_addr, clientIP, INET_ADDRSTRLEN);</span><br><span class="line">        <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"...connect "</span> &lt;&lt; clientIP &lt;&lt; <span class="string">":"</span> &lt;&lt; ntohs(clientAddr.sin_port) &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">char</span> buf[<span class="number">255</span>];</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">            <span class="built_in">memset</span>(buf, <span class="number">0</span>, <span class="keyword">sizeof</span>(buf));</span><br><span class="line">            <span class="keyword">int</span> len = recv(conn, buf, <span class="keyword">sizeof</span>(buf), <span class="number">0</span>);</span><br><span class="line">            buf[len] = <span class="string">'\0'</span>;</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">strcmp</span>(buf, <span class="string">"exit"</span>) == <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"...disconnect "</span> &lt;&lt; clientIP &lt;&lt; <span class="string">":"</span> &lt;&lt; ntohs(clientAddr.sin_port) &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; buf &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">            send(conn, buf, len, <span class="number">0</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        close(conn);</span><br><span class="line">    &#125;</span><br><span class="line">    close(listenfd);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="client端"><a href="#client端" class="headerlink" title="client端"></a>client端</h4><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;cstdio&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;cstdlib&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;cerrno&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;cstring&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/socket.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/types.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;netinet/in.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;arpa/inet.h&gt;</span></span></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="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"This is client"</span> &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">    <span class="comment">// socket</span></span><br><span class="line">    <span class="keyword">int</span> client = socket(AF_INET, SOCK_STREAM, <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">if</span> (client == <span class="number">-1</span>) &#123;</span><br><span class="line">        <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"Error: socket"</span> &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// connect</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">serverAddr</span>;</span></span><br><span class="line">    serverAddr.sin_family = AF_INET;</span><br><span class="line">    serverAddr.sin_port = htons(<span class="number">8000</span>);</span><br><span class="line">    serverAddr.sin_addr.s_addr = inet_addr(<span class="string">"127.0.0.1"</span>);</span><br><span class="line">    <span class="keyword">if</span> (connect(client, (struct sockaddr*)&amp;serverAddr, <span class="keyword">sizeof</span>(serverAddr)) &lt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"Error: connect"</span> &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"...connect"</span> &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">    <span class="keyword">char</span> data[<span class="number">255</span>];</span><br><span class="line">    <span class="keyword">char</span> buf[<span class="number">255</span>];</span><br><span class="line">    <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">        <span class="built_in">std</span>::<span class="built_in">cin</span> &gt;&gt; data;</span><br><span class="line">        send(client, data, <span class="built_in">strlen</span>(data), <span class="number">0</span>);</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">strcmp</span>(data, <span class="string">"exit"</span>) == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"...disconnect"</span> &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">memset</span>(buf, <span class="number">0</span>, <span class="keyword">sizeof</span>(buf));</span><br><span class="line">        <span class="keyword">int</span> len = recv(client, buf, <span class="keyword">sizeof</span>(buf), <span class="number">0</span>);</span><br><span class="line">        buf[len] = <span class="string">'\0'</span>;</span><br><span class="line">        <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; buf &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    close(client);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="UDP套接字编程"><a href="#UDP套接字编程" class="headerlink" title="UDP套接字编程"></a>UDP套接字编程</h3><h4 id="server端-1"><a href="#server端-1" class="headerlink" title="server端"></a>server端</h4><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="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;   </span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/types.h&gt;   </span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/socket.h&gt;   </span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;netinet/in.h&gt;   </span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;unistd.h&gt;   </span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;errno.h&gt;   </span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;   </span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdlib.h&gt;   </span></span></span><br><span class="line">  </span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> SERV_PORT   8000   </span></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></span><br><span class="line"><span class="function"></span>&#123;  </span><br><span class="line">    <span class="comment">/* sock_fd --- socket文件描述符 创建udp套接字*/</span>  </span><br><span class="line">    <span class="keyword">int</span> sock_fd = socket(AF_INET, SOCK_DGRAM, <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">if</span>(sock_fd &lt; <span class="number">0</span>)  </span><br><span class="line">    &#123;  </span><br><span class="line">        perror(<span class="string">"socket"</span>);  </span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);  </span><br><span class="line">    &#125;  </span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 将套接字和IP、端口绑定 */</span>  </span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">addr_serv</span>;</span>  </span><br><span class="line">    <span class="keyword">int</span> len;  </span><br><span class="line">    <span class="built_in">memset</span>(&amp;addr_serv, <span class="number">0</span>, <span class="keyword">sizeof</span>(struct sockaddr_in));  <span class="comment">//每个字节都用0填充</span></span><br><span class="line">    addr_serv.sin_family = AF_INET;  　　　　　　　　　　　 <span class="comment">//使用IPV4地址</span></span><br><span class="line">    addr_serv.sin_port = htons(SERV_PORT);  　　　　　　　 <span class="comment">//端口</span></span><br><span class="line">    <span class="comment">/* INADDR_ANY表示不管是哪个网卡接收到数据，只要目的端口是SERV_PORT，就会被该应用程序接收到 */</span>  </span><br><span class="line">    addr_serv.sin_addr.s_addr = htonl(INADDR_ANY);  <span class="comment">//自动获取IP地址</span></span><br><span class="line">    len = <span class="keyword">sizeof</span>(addr_serv);  </span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 绑定socket */</span>  </span><br><span class="line">    <span class="keyword">if</span>(bind(sock_fd, (struct sockaddr *)&amp;addr_serv, <span class="keyword">sizeof</span>(addr_serv)) &lt; <span class="number">0</span>)  </span><br><span class="line">    &#123;  </span><br><span class="line">        perror(<span class="string">"bind error:"</span>);  </span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);  </span><br><span class="line">    &#125;  </span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> recv_num;  </span><br><span class="line">    <span class="keyword">int</span> send_num;  </span><br><span class="line">    <span class="keyword">char</span> send_buf[<span class="number">20</span>] = <span class="string">"i am server!"</span>;  </span><br><span class="line">    <span class="keyword">char</span> recv_buf[<span class="number">20</span>];  </span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">addr_client</span>;</span>  </span><br><span class="line">  </span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)  </span><br><span class="line">    &#123;  </span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"server wait:\n"</span>);  </span><br><span class="line"></span><br><span class="line">        recv_num = recvfrom(sock_fd, recv_buf, <span class="keyword">sizeof</span>(recv_buf), <span class="number">0</span>, (struct sockaddr *)&amp;addr_client, (<span class="keyword">socklen_t</span> *)&amp;len);  </span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(recv_num &lt; <span class="number">0</span>)  </span><br><span class="line">        &#123;  </span><br><span class="line">            perror(<span class="string">"recvfrom error:"</span>);  </span><br><span class="line">            <span class="built_in">exit</span>(<span class="number">1</span>);  </span><br><span class="line">        &#125;  </span><br><span class="line"></span><br><span class="line">        recv_buf[recv_num] = <span class="string">'\0'</span>;  </span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"server receive %d bytes: %s\n"</span>, recv_num, recv_buf);  </span><br><span class="line"></span><br><span class="line">        send_num = sendto(sock_fd, send_buf, recv_num, <span class="number">0</span>, (struct sockaddr *)&amp;addr_client, len);  </span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(send_num &lt; <span class="number">0</span>)  </span><br><span class="line">        &#123;  </span><br><span class="line">            perror(<span class="string">"sendto error:"</span>);  </span><br><span class="line">            <span class="built_in">exit</span>(<span class="number">1</span>);  </span><br><span class="line">        &#125;  </span><br><span class="line">    &#125;  </span><br><span class="line"></span><br><span class="line">    close(sock_fd);  </span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="client端-1"><a href="#client端-1" class="headerlink" title="client端"></a>client端</h4><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></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 class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;   </span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;errno.h&gt;   </span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdlib.h&gt;   </span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;unistd.h&gt;   </span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/types.h&gt;   </span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sys/socket.h&gt;   </span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;netinet/in.h&gt;   </span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;arpa/inet.h&gt;   </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> DEST_PORT 8000   </span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> DSET_IP_ADDRESS  <span class="meta-string">"127.0.0.1"</span>   </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></span><br><span class="line"><span class="function"></span>&#123;  </span><br><span class="line">    <span class="comment">/* socket文件描述符 */</span>  </span><br><span class="line">    <span class="keyword">int</span> sock_fd;  </span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 建立udp socket */</span>  </span><br><span class="line">    sock_fd = socket(AF_INET, SOCK_DGRAM, <span class="number">0</span>);  </span><br><span class="line">    <span class="keyword">if</span>(sock_fd &lt; <span class="number">0</span>)  </span><br><span class="line">    &#123;  </span><br><span class="line">        perror(<span class="string">"socket"</span>);  </span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);  </span><br><span class="line">    &#125;  </span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 设置address */</span>  </span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">addr_serv</span>;</span>  </span><br><span class="line">    <span class="keyword">int</span> len;  </span><br><span class="line">    <span class="built_in">memset</span>(&amp;addr_serv, <span class="number">0</span>, <span class="keyword">sizeof</span>(addr_serv));  </span><br><span class="line">    addr_serv.sin_family = AF_INET;  </span><br><span class="line">    addr_serv.sin_addr.s_addr = inet_addr(DSET_IP_ADDRESS);  </span><br><span class="line">    addr_serv.sin_port = htons(DEST_PORT);  </span><br><span class="line">    len = <span class="keyword">sizeof</span>(addr_serv);  </span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> send_num;  </span><br><span class="line">    <span class="keyword">int</span> recv_num;  </span><br><span class="line">    <span class="keyword">char</span> send_buf[<span class="number">20</span>] = <span class="string">"hey, who are you?"</span>;  </span><br><span class="line">    <span class="keyword">char</span> recv_buf[<span class="number">20</span>];  </span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"client send: %s\n"</span>, send_buf);  </span><br><span class="line"></span><br><span class="line">    send_num = sendto(sock_fd, send_buf, <span class="built_in">strlen</span>(send_buf), <span class="number">0</span>, (struct sockaddr *)&amp;addr_serv, len);  </span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(send_num &lt; <span class="number">0</span>)  </span><br><span class="line">    &#123;  </span><br><span class="line">        perror(<span class="string">"sendto error:"</span>);  </span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);  </span><br><span class="line">    &#125;  </span><br><span class="line"></span><br><span class="line">    recv_num = recvfrom(sock_fd, recv_buf, <span class="keyword">sizeof</span>(recv_buf), <span class="number">0</span>, (struct sockaddr *)&amp;addr_serv, (<span class="keyword">socklen_t</span> *)&amp;len);  </span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(recv_num &lt; <span class="number">0</span>)  </span><br><span class="line">    &#123;  </span><br><span class="line">        perror(<span class="string">"recvfrom error:"</span>);  </span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);  </span><br><span class="line">    &#125;  </span><br><span class="line"></span><br><span class="line">    recv_buf[recv_num] = <span class="string">'\0'</span>;  </span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"client receive %d bytes: %s\n"</span>, recv_num, recv_buf);  </span><br><span class="line"></span><br><span class="line">    close(sock_fd);  </span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="注意事项-1"><a href="#注意事项-1" class="headerlink" title="注意事项"></a>注意事项</h2><h3 id="区分监听套接字与已连接套接字"><a href="#区分监听套接字与已连接套接字" class="headerlink" title="区分监听套接字与已连接套接字"></a>区分监听套接字与已连接套接字</h3><p>在服务器端中，我们实际上用到了两个socket，它们分别为：</p><ul><li>由listen函数将socket函数创建的socket转换而成的监听套接字，记作listenfd</li><li>由accept函数等待来自客户端的连接请求到达listenfd，返回一个已连接套接字，记作connfd</li></ul><p>两者区别如下：</p><p>监听套接字，是服务器作为客户端连接请求的一个端点，它被创建一次，并存在于服务器的整个生命周期。</p><p>已连接套接字是客户端与服务器之间已经建立起来了的连接的一个端点，服务器每次接受连接请求时都会创建一次已连接套接字，它只存在于服务器为一个客户端服务的过程中。</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><div id="footnotes"><hr><div id="footnotelist"><ol><li id="fn:1"><a href="https://www.jianshu.com/p/3b233facd6bb">linux c++套接字编程</a><a href="#fnref:1" rev="footnote"> ↩</a></li></ol></div></div>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;HTTP/1.1 200 OK&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
      <category term="计算机" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="网络通信" scheme="http://sqduan.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E7%BD%91%E7%BB%9C%E9%80%9A%E4%BF%A1/"/>
    
    
  </entry>
  
</feed>
