<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>嘿_BUG</title>
  
  
  <link href="https://blog.agebbs.cn/atom.xml" rel="self"/>
  
  <link href="https://blog.agebbs.cn/"/>
  <updated>2024-04-17T03:26:56.489Z</updated>
  <id>https://blog.agebbs.cn/</id>
  
  <author>
    <name>嘿_BUG</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>零拷贝</title>
    <link href="https://blog.agebbs.cn/2024/04/17/%E9%9B%B6%E6%8B%B7%E8%B4%9D/"/>
    <id>https://blog.agebbs.cn/2024/04/17/%E9%9B%B6%E6%8B%B7%E8%B4%9D/</id>
    <published>2024-04-17T02:51:33.000Z</published>
    <updated>2024-04-17T03:26:56.489Z</updated>
    
    <content type="html"><![CDATA[<h2 id="传统文件传输过程"><a href="#传统文件传输过程" class="headerlink" title="传统文件传输过程"></a>传统文件传输过程</h2><h3 id="分享数据过程"><a href="#分享数据过程" class="headerlink" title="分享数据过程"></a>分享数据过程</h3><p><img src="https://blog.agebbs.cn/statics/image/%E9%9B%B6%E6%8B%B7%E8%B4%9D/%E5%88%86%E4%BA%AB%E6%95%B0%E6%8D%AE%E8%BF%87%E7%A8%8B.png" alt="avatar"></p><blockquote><p>read 从磁盘读取文件数据 write 通过网络协议发送给客户端，但在操作系统中过程就不像图中表现的这么简单。</p></blockquote><h3 id="Linux传统文件传输过程"><a href="#Linux传统文件传输过程" class="headerlink" title="Linux传统文件传输过程"></a>Linux传统文件传输过程</h3><p>在linux系统文件传输的过程中，发生了4次用户态与内核态的上下文切换，和4次数据拷贝。<br><img src="https://blog.agebbs.cn/statics/image/%E9%9B%B6%E6%8B%B7%E8%B4%9D/%E4%BC%A0%E7%BB%9F%E6%96%87%E4%BB%B6%E4%BC%A0%E8%BE%93%E8%BF%87%E7%A8%8B.png" alt="avatar"></p><blockquote><p>操作系统为了隔离系统程序和应用程序把系统分为了用户空间和内核空间，而用户空间和内核空间的权限也不同。</p></blockquote>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;传统文件传输过程&quot;&gt;&lt;a href=&quot;#传统文件传输过程&quot; class=&quot;headerlink&quot; title=&quot;传统文件传输过程&quot;&gt;&lt;/a&gt;传统文件传输过程&lt;/h2&gt;&lt;h3 id=&quot;分享数据过程&quot;&gt;&lt;a href=&quot;#分享数据过程&quot; class=&quot;headerli</summary>
      
    
    
    
    <category term="操作系统" scheme="https://blog.agebbs.cn/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
    
    
    <category term="操作系统" scheme="https://blog.agebbs.cn/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
    
  </entry>
  
  <entry>
    <title>网站常见的漏洞</title>
    <link href="https://blog.agebbs.cn/2023/04/15/%E7%BD%91%E7%AB%99%E5%B8%B8%E8%A7%81%E7%9A%84%E6%BC%8F%E6%B4%9E/"/>
    <id>https://blog.agebbs.cn/2023/04/15/%E7%BD%91%E7%AB%99%E5%B8%B8%E8%A7%81%E7%9A%84%E6%BC%8F%E6%B4%9E/</id>
    <published>2023-04-15T06:32:40.000Z</published>
    <updated>2024-04-17T02:50:43.818Z</updated>
    
    <content type="html"><![CDATA[<p><img src="https://blog.agebbs.cn/statics/image/%E7%BD%91%E7%AB%99%E5%AE%89%E5%85%A8/%E7%BD%91%E7%AB%99%E6%94%BB%E5%87%BB%E6%96%B9%E5%BC%8F.png" alt="avatar"></p><span id="more"></span><h1 id="服务端常见漏洞"><a href="#服务端常见漏洞" class="headerlink" title="服务端常见漏洞"></a>服务端常见漏洞</h1><h2 id="SQL-注入"><a href="#SQL-注入" class="headerlink" title="SQL 注入"></a>SQL 注入</h2><p>sql 语句的静态模版和动态数据没有严格的区分，如果在数据项中加入某些sql关键字,这些关键字就很可能在写入或读取数据时得到执行。</p><ul><li>应对SQL注入的方式<ol><li>尽量不使用DB的Raw方法拼接sql，规范使用ORM或预编译 </li><li>使用ORM时，多关注框架中的特性，可能存在不安全的写法，例如 GORM Order函数</li><li>在一些必须使用sql拼接的场景下，需对外部输入进行转义</li></ol></li></ul><h2 id="RCE-命令注入"><a href="#RCE-命令注入" class="headerlink" title="RCE/命令注入"></a>RCE/命令注入</h2><p>代码中遇到需要调用某个命令才能完成的功能时，会涉及到命令拼接，如果命令拼接没有做好安全过滤，那么将会导致命令输入风险。</p><ul><li>应对命令注入的方式<ol><li>对动态的值建立白名单进行验证</li><li>如果某些位置无法白名单，就加强对数据类型的校验</li><li>特殊字符黑名单过滤或者转义</li></ol></li></ul><h2 id="越权漏洞"><a href="#越权漏洞" class="headerlink" title="越权漏洞"></a>越权漏洞</h2><p>资源访问或操作的时候主体权限没有进行校验就会造成越权问题，细分为：未授权，水平越权和垂直越权</p><ul><li>水平越权 <ul><li>发生场景<br>  例如在订单查询功能提供Id就能查询订单详情，这里攻击者可通过遍历orderId获取其他用户的订单信息</li><li>防护方式<br>  尽量使用长ID（遍历难度变大），同时最重要的做好资源属主的校验</li></ul></li><li>垂直越权<ul><li>发生场景<br>  攻击者通过开通另外的测试账户抓包未获得权限的接口，绕过前端直接尝试访问后端接口，获取数据。</li><li>防护方式<br>  对路由级别进行分组，对不同的API分组引用Middle进行权限拦截，Middle获取当前用户角色已确定是否可以访问当前接口。</li></ul></li></ul><h2 id="SSRF"><a href="#SSRF" class="headerlink" title="SSRF"></a>SSRF</h2><p>服务器请求伪造攻击，指攻击者利用后端服务器为跳板，让后端服务向非预期网络地址（例如：内网地址）发出恶意请求，获取敏感信息或执行恶意操作。</p><ul><li>发生场景<br>  服务端请求socketAPI,获取结果返回。将socketAPi 参数改为内网地址访问内网资源。</li><li>防护方式<br>  将url和host进行白名单过滤，获取对host解析的ip判定是否为内网地址。</li></ul><h2 id="文件上传"><a href="#文件上传" class="headerlink" title="文件上传"></a>文件上传</h2><p>恶意上传文件或脚本，用于攻击或分享。</p><ul><li>发生场景<br>  上传一些服务端的脚本，（例如.php）文件上传后可直接读取文件或找公开的上传点上传恶意文件，获取文件url，然后直接分享url到外部恶意网站。</li><li>防护方案<ol><li>限制文件类型: 如果系统只需要图片类型，可以从服务端解析文件格式，限制只能传入特定的文件格式。</li><li>站库分离：应用部署的位置和上传的文件分离，如：oss等。</li><li>防止图床：对图片访问链接进行限制，包括时间，访问身份等。</li></ol></li></ul><h2 id="WebSocket"><a href="#WebSocket" class="headerlink" title="WebSocket"></a>WebSocket</h2><p>本质上http服务端的漏洞在websocket上也会存在。</p><ul><li>发生场景<ol><li>wss 和 ws wss提供加密信道</li><li>数据校验 SQL/XSS/RCE仍然存在</li><li>CSWSH，在cookie作为认证方式的时候，如果websocket服务没有校验好请求来源，将会导致websocket会话劫持。</li></ol></li><li>防护方案<ol><li>Cookie鉴权</li><li>token或ticket鉴权</li></ol></li></ul><h2 id="第三方组件漏洞"><a href="#第三方组件漏洞" class="headerlink" title="第三方组件漏洞"></a>第三方组件漏洞</h2><ol><li>打印日志服务器就中病毒了</li><li>解析一个json服务器就中病毒了</li><li>查看现有组件漏洞工具<ul><li>go 官方工具 <pre><code>go install golang.org/x/vuln/cmd/govulncheck@latestgovulncheck ./..// 可通过 -db 指定一个外部的Go漏洞</code></pre></li><li>编辑器集成插件</li></ul></li></ol><h1 id="客户端常见漏洞"><a href="#客户端常见漏洞" class="headerlink" title="客户端常见漏洞"></a>客户端常见漏洞</h1><h2 id="开放重定向"><a href="#开放重定向" class="headerlink" title="开放重定向"></a>开放重定向</h2><p>某些需要重定向到其他站点的功能，参数中经常携带需要重定向的URL,但是实际程序中没有控制重定向的范围，导致攻击者可以构造恶意的链接，诱导用户到恶意网站。</p><ul><li>发生场景<br>  钓鱼攻击，恶意的链接，诱导用户到恶意网站。</li><li>防护方案<br>  对重定向进行严格的白名单控制，并正确的校验匹配白名单。</li></ul><h2 id="XSS"><a href="#XSS" class="headerlink" title="XSS"></a>XSS</h2><p>跨站脚本攻击，攻击者往目标WEB页面插入恶意的代码，当用户访问时，嵌入代码会被执行，达到恶意攻击用户的目的</p><ul><li>发生场景<br>  攻击者在输入框输入一些客户端的代码，可分为反射型，存储型，Dom型，通常的危害包括窃取用户敏感信息，以用户身份执行敏感操作。</li><li>防护方案<ol><li>输入过滤：对特殊的字符进行拦截,禁止提交一些特殊字符。</li><li>输出过滤：当字符输出的时候镜像html encode，避免xss ，使用框架时使用v-html/v-text这样的安全指令。</li><li>富文本场景：在发布文章等场景，本身需要提供富文本的功能，这时候需要严格限制tag和attribute，可以在代码地城做白名单和黑名单。</li><li>CSP：用于缓解XSS,理念是对当前站点允许加载什么的资源，发送什么请求进行限制，配置 header 中的 Content-Security-Policy</li></ol></li></ul><h2 id="CSRF"><a href="#CSRF" class="headerlink" title="CSRF"></a>CSRF</h2><p>跨站请求伪造：允许攻击者诱导用户访问恶意链接，执行用户非预期执行的操作</p><ul><li>发生场景<br>  用户执行敏感操作，如关注其他用户或者更改账户的安全邮箱等。</li><li>防护方案<br>  核心方案就是判断请求来源是否合法<ol><li>CsrfToken 首次访问客户端时传递一个token，客户端每次访问都要携带上此token</li><li>禁止某些场景发送第三方cookie</li><li>校验Referer来源是否合法</li></ol></li></ul><h2 id="点击劫持"><a href="#点击劫持" class="headerlink" title="点击劫持"></a>点击劫持</h2><p>在网页中将恶意代码等隐藏值看似无害的内容或按钮中，并引导用户点击，用户点击后往往会执行一些非预期操作。</p><ul><li>发生场景<br>  用户访问链接，点击a按钮时，实际点击的是b</li><li>防护方案<ol><li>X-Frame-Options：DENY/SAMEORIGIN</li><li>CSP: frame-ancestors</li></ol></li></ul><h2 id="CORS跨域配置错误"><a href="#CORS跨域配置错误" class="headerlink" title="CORS跨域配置错误"></a>CORS跨域配置错误</h2><p>cors本身不存在漏洞，但由于开发时的错误配置，导致非预期的网站可以进行跨域访问。</p><ul><li><p>发生场景<br>  以需要跨域访问 example.com 所有子域名为例：</p><ol><li>前缀/后缀/包含/正则匹配 可用 example.com.a.com等方式绕过</li><li>反射 在Access-control-Allow-Origin中反射请求的Origin值理论上可以任意域名绕过</li><li>攻击者还可以从任意域下通过iframe sandbox构造 origin为null的跨域请求</li><li>http 传输存在被劫持篡改的可能，攻击者可能通过劫持通信流量注入恶意脚本方式窃取敏感信息</li></ol></li><li><p>防护方案</p><ol><li>代码层 用中间件统一处理</li><li>网关层 nginx反向代理统一拦截</li></ol></li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;https://blog.agebbs.cn/statics/image/%E7%BD%91%E7%AB%99%E5%AE%89%E5%85%A8/%E7%BD%91%E7%AB%99%E6%94%BB%E5%87%BB%E6%96%B9%E5%BC%8F.png&quot; alt=&quot;avatar&quot;&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="网络安全" scheme="https://blog.agebbs.cn/categories/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
    
    
    <category term="网络安全" scheme="https://blog.agebbs.cn/tags/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
    
  </entry>
  
  <entry>
    <title>1.排序算法</title>
    <link href="https://blog.agebbs.cn/2023/04/10/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95/"/>
    <id>https://blog.agebbs.cn/2023/04/10/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95/</id>
    <published>2023-04-10T05:13:03.000Z</published>
    <updated>2024-04-15T06:31:53.083Z</updated>
    
    <content type="html"><![CDATA[<h2 id="经典的排序算法"><a href="#经典的排序算法" class="headerlink" title="经典的排序算法"></a>经典的排序算法</h2><h3 id="insertion-sort-插入排序"><a href="#insertion-sort-插入排序" class="headerlink" title="insertion sort 插入排序"></a>insertion sort 插入排序</h3><p><img src="https://blog.agebbs.cn/statics/image/%E7%AE%97%E6%B3%95/%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F.png" alt="avatar"></p><h4 id="排序实现原理"><a href="#排序实现原理" class="headerlink" title="排序实现原理"></a>排序实现原理</h4><p>将元素不断地插入已经排序好的 array 中</p><ul><li>起始只有一个元素5，其本身是一个有序序列</li><li>后续元素插入序列中，不断交换，直到找到第一个比其小的元素<span id="more"></span></li></ul><h4 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h4><pre><code>// 插入排序func InsertionSort(arr []int) []int &#123;    for i := 1; i &lt; len(arr); i++ &#123;        k := arr[i]        j := i - 1        for j &gt;= 0 &amp;&amp; arr[j] &gt; k &#123;            arr[j+1] = arr[j]            j = j - 1        &#125;        arr[j+1] = k    &#125;    return arr&#125;//i = 1 k = 5 j = 0 arr[j] = 6 arr = 6 6 2 1 3 4 j = -1 arr = 5 6 2 1 3 4//i = 2 k = 2 j = 1 arr[j] = 6 arr = 5 6 6 1 3 4 j = 0 arr = 5 5 6 1 3 4 j = -1 arr = 2 5 6 1 3 4......func TestInsertionSort(t *testing.T) &#123;    a := []int&#123;6, 5, 2, 1, 3, 4&#125;    fmt.Println(InsertionSort(a))&#125;</code></pre><h4 id="时间复杂度"><a href="#时间复杂度" class="headerlink" title="时间复杂度"></a>时间复杂度</h4><p><img src="https://blog.agebbs.cn/statics/image/%E7%AE%97%E6%B3%95/%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F%E6%97%B6%E9%97%B4%E5%91%A8%E6%9C%9F.png" alt="avatar"></p><ul><li>最好情况时间复杂度O(n)</li><li>平均最坏情况时间复杂度O(n^2)</li></ul><h3 id="quick-sort-快速排序"><a href="#quick-sort-快速排序" class="headerlink" title="quick sort 快速排序"></a>quick sort 快速排序</h3><p><img src="https://blog.agebbs.cn/statics/image/%E7%AE%97%E6%B3%95/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F.avif" alt="avatar"></p><h4 id="排序实现原理-1"><a href="#排序实现原理-1" class="headerlink" title="排序实现原理"></a>排序实现原理</h4><p>分治思想，不断分割序列直到序列整体有序</p><ul><li>选定一个pivot(轴点)</li><li>使用pivot分割序列，分成元素比pivot大和元素比pivot小两个序列</li></ul><h4 id="代码实现-1"><a href="#代码实现-1" class="headerlink" title="代码实现"></a>代码实现</h4><pre><code>func QuickSort(arr []int, left, right int) []int &#123;    if left &gt;= right &#123;        return arr    &#125; // 左指针大于右指针递归结束    l, r, cur := left, right, arr[left]    for l &lt; r &#123; // l == r 所有值被变量        for l &lt; r &amp;&amp; arr[r] &gt;= cur &#123; //轴点取最左边值 需要先移动右指针            r--        &#125;        for l &lt; r &amp;&amp; arr[l] &lt;= cur &#123;            l++        &#125;        arr[l], arr[r] = arr[r], arr[l]    &#125;    arr[left], arr[r] = arr[r], arr[left]    QuickSort(arr, left, l-1)    QuickSort(arr, l+1, right)    return arr&#125;// cur = 6 l = 0 l = 1 l = 2 l = 3 l = 4 l = 5  r = 5  arr[r] = 4 arr[l] = 6-&gt;5-&gt;2-&gt;1-&gt;3-&gt;4  a[5],a[5] = a[5],a[5] [6,5,2,1,3,4] [4,5,2,1,3,6]// cur = 4 l = 0 l = 1 r = 4 arr[r] = 3 arr[l] = 4-&gt;5 a[1],a[4] = a[4],a[1] [4,3,2,1,5,6] l &lt; r  l = 1 r = 3 a[1] = 3 l = 2 a[2] = 2 l = 3 a[3] = 1 [4,3,2,1,5,6] [1,3,2,4,5,6]......</code></pre><h4 id="时间复杂度-1"><a href="#时间复杂度-1" class="headerlink" title="时间复杂度"></a>时间复杂度</h4><p><img src="https://blog.agebbs.cn/statics/image/%E7%AE%97%E6%B3%95/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F%E6%97%B6%E9%97%B4%E5%91%A8%E6%9C%9F.png" alt="avatar"></p><ul><li>最好平均情况时间复杂度O(n&amp;logn)</li><li>最坏情况时间复杂度O(n^2)</li></ul><h3 id="heap-sort-堆排序"><a href="#heap-sort-堆排序" class="headerlink" title="heap sort 堆排序"></a>heap sort 堆排序</h3><h4 id="排序实现原理-2"><a href="#排序实现原理-2" class="headerlink" title="排序实现原理"></a>排序实现原理</h4><p>利用堆的性质形成的排序算法</p><ul><li>构造一个大顶堆</li><li>将根节点（最大元素）交换到最后一个位置，调整整个堆，如此反复</li></ul>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;经典的排序算法&quot;&gt;&lt;a href=&quot;#经典的排序算法&quot; class=&quot;headerlink&quot; title=&quot;经典的排序算法&quot;&gt;&lt;/a&gt;经典的排序算法&lt;/h2&gt;&lt;h3 id=&quot;insertion-sort-插入排序&quot;&gt;&lt;a href=&quot;#insertion-sort-插入排序&quot; class=&quot;headerlink&quot; title=&quot;insertion sort 插入排序&quot;&gt;&lt;/a&gt;insertion sort 插入排序&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;https://blog.agebbs.cn/statics/image/%E7%AE%97%E6%B3%95/%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F.png&quot; alt=&quot;avatar&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;排序实现原理&quot;&gt;&lt;a href=&quot;#排序实现原理&quot; class=&quot;headerlink&quot; title=&quot;排序实现原理&quot;&gt;&lt;/a&gt;排序实现原理&lt;/h4&gt;&lt;p&gt;将元素不断地插入已经排序好的 array 中&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;起始只有一个元素5，其本身是一个有序序列&lt;/li&gt;
&lt;li&gt;后续元素插入序列中，不断交换，直到找到第一个比其小的元素</summary>
    
    
    
    <category term="算法" scheme="https://blog.agebbs.cn/categories/%E7%AE%97%E6%B3%95/"/>
    
    
    <category term="算法" scheme="https://blog.agebbs.cn/tags/%E7%AE%97%E6%B3%95/"/>
    
  </entry>
  
  <entry>
    <title>1. 网络模型剖析</title>
    <link href="https://blog.agebbs.cn/2022/11/04/%E7%BD%91%E7%BB%9C%E6%A8%A1%E5%9E%8B/"/>
    <id>https://blog.agebbs.cn/2022/11/04/%E7%BD%91%E7%BB%9C%E6%A8%A1%E5%9E%8B/</id>
    <published>2022-11-04T07:56:43.000Z</published>
    <updated>2024-04-10T03:06:05.360Z</updated>
    
    <content type="html"><![CDATA[<h1 id="OSI-网络模型"><a href="#OSI-网络模型" class="headerlink" title="OSI 网络模型"></a>OSI 网络模型</h1><h2 id="1-什么是OSI网络模型"><a href="#1-什么是OSI网络模型" class="headerlink" title="1. 什么是OSI网络模型"></a>1. 什么是OSI网络模型</h2><p>为了使得多种设备能通过网络相互通信，和为了解决各种不同设备在网络互联中的兼容性问题，国际标准化组织制定了开放式系统互联通信参考模型（Open System Interconnection Reference Model），也就是 OSI 网络模型</p><span id="more"></span><h2 id="2-OSI网络模型是如何组成的"><a href="#2-OSI网络模型是如何组成的" class="headerlink" title="2. OSI网络模型是如何组成的"></a>2. OSI网络模型是如何组成的</h2><p><img src="https://blog.agebbs.cn/statics/image/OSI%E4%BD%93%E7%B3%BB%E7%BB%93%E6%9E%84.png" alt="avatar"><br>OSI 模型把网络通信的工作分为 7 层，从下到上分别是物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。OSI 只是存在于概念和理论上的一种模型，它的缺点是分层太多，增加了网络工作的复杂性，所以没有大规模应用。后来人们对 OSI 进行了简化，合并了一些层，最终只保留了 4 层，从下到上分别是接口层、网络层、传输层和应用层，形成常用的 TCP/IP 模型。</p><h2 id="3-OSI网络模型每一层的作用"><a href="#3-OSI网络模型每一层的作用" class="headerlink" title="3. OSI网络模型每一层的作用"></a>3. OSI网络模型每一层的作用</h2><ul><li>应用层，负责给应用程序提供统一的接口；</li><li>表示层，负责把数据转换成兼容另一个系统能识别的格式；</li><li>会话层，负责建立、管理和终止表示层实体之间的通信会话；</li><li>传输层，负责端到端的数据传输；</li><li>网络层，负责数据的路由、转发、分片；</li><li>数据链路层，负责数据的封帧和差错检测，以及 MAC 寻址；</li><li>物理层，负责在物理网络中传输数据帧；</li></ul><h1 id="TCP-IP-网络模型"><a href="#TCP-IP-网络模型" class="headerlink" title="TCP/IP 网络模型"></a>TCP/IP 网络模型</h1><h2 id="1-什么是-TCP-IP-网络模型"><a href="#1-什么是-TCP-IP-网络模型" class="headerlink" title="1.什么是 TCP/IP 网络模型"></a>1.什么是 TCP/IP 网络模型</h2><p>对于同一台设备上的进程间通信，有很多种方式，比如有管道、消息队列、共享内存、信号等方式，而对于不同设备上的进程间通信，就需要网络通信，而设备是多样性的，所以要兼容多种多样的设备，就需要有一套通用的网络协议。</p><h2 id="2-TCP-IP-网络模型是如何组成的"><a href="#2-TCP-IP-网络模型是如何组成的" class="headerlink" title="2. TCP/IP 网络模型是如何组成的"></a>2. TCP/IP 网络模型是如何组成的</h2><p><img src="https://blog.agebbs.cn/statics/image/TCP-IP%E4%BD%93%E7%B3%BB%E7%BB%93%E6%9E%84.png" alt="avatar"></p><h2 id="3-TCP-IP-网络模型每一层的作用"><a href="#3-TCP-IP-网络模型每一层的作用" class="headerlink" title="3.TCP/IP 网络模型每一层的作用"></a>3.TCP/IP 网络模型每一层的作用</h2><h3 id="1-应用层（负责向用户提供一组应用程序，比如-HTTP、DNS、FTP-等）-gt-数据分片"><a href="#1-应用层（负责向用户提供一组应用程序，比如-HTTP、DNS、FTP-等）-gt-数据分片" class="headerlink" title="1. 应用层（负责向用户提供一组应用程序，比如 HTTP、DNS、FTP 等）-&gt; 数据分片"></a>1. 应用层（负责向用户提供一组应用程序，比如 HTTP、DNS、FTP 等）-&gt; 数据分片</h3><p>最上层，我们能直接接触的就是应用层（Application Layer）,像电脑或手机软件都是在应用层上实现的，所有应用层只需要专注为用户提供应用功能，如： HTTP,FTP,DNS 等，应用层是不关注数据是如何去运输的，而且应用层是工作在操作系统的用户态，运输层及以下则工作在内核态。</p><h3 id="2-运输层-（负责端到端的通信，比如-TCP、UDP-等）-gt-tcp-udp-header-上层数据-gt-数据包"><a href="#2-运输层-（负责端到端的通信，比如-TCP、UDP-等）-gt-tcp-udp-header-上层数据-gt-数据包" class="headerlink" title="2. 运输层 （负责端到端的通信，比如 TCP、UDP 等）-&gt; tcp|udp header+上层数据 -&gt; 数据包"></a>2. 运输层 （负责端到端的通信，比如 TCP、UDP 等）-&gt; tcp|udp header+上层数据 -&gt; 数据包</h3><p>第二层，应用层的数据包会传给运输层，运输层（Transport Layer）是为应用层提供网络支持的，当两个不同设备的应用需要通信的时候，应用就把应用数据传给下一层，也就是运输层，运输层有两个协议TCP和UDP，当设备作为接收方时，传输层则要负责把数据包传给应用，但是一台设备上可能会有很多应用在接收或者传输数据，因此需要用一个编号将应用区分开来，这个编号就是端口</p><ul><li><p>TCP的全程叫运输控制协议，大部分的应用都是使用的TCP运输协议如：HTTP，TCP相对于UDP多了很多特性，如流量控制,超时重传,拥塞控制等，这些特性都是为了能保证数据包能可靠的运输给对方，当应用中需要传输的数据非常大，如果直接传输就不好控制，因此传输层的数据包大小超过MSS（TCP最大报文长度），就会将数据包分块，这样就算其中一个分块传输丢失或损坏只需重新发送这一个，而不是整个数据包，在TCP协议中，把每一个分块称为TCP段（TCP Segment）。</p></li><li><p>UDP相对TCP要简单很多，只负责发送数据包，不保证数据包能否抵达对方，这样运输会提高运输效率，如果把TCP的特性在应用层实现也可以让UDP实现可靠运输。</p></li></ul><h3 id="3-网络层-负责网络包的封装、分片、路由、转发，比如-IP、ICMP-等-gt-ip报头-tcp-udp-header-上层数据-gt-数据段"><a href="#3-网络层-负责网络包的封装、分片、路由、转发，比如-IP、ICMP-等-gt-ip报头-tcp-udp-header-上层数据-gt-数据段" class="headerlink" title="3. 网络层  (负责网络包的封装、分片、路由、转发，比如 IP、ICMP 等) -&gt; ip报头+tcp|udp header+上层数据 -&gt; 数据段"></a>3. 网络层  (负责网络包的封装、分片、路由、转发，比如 IP、ICMP 等) -&gt; ip报头+tcp|udp header+上层数据 -&gt; 数据段</h3><p>第三层，传输层只负责服务好应用，作为应用间数据传输的媒介，帮助实现应用到应用的通信，而实际的传输功能就交给下一层，也就是网络层（Internet Layer），网络层最常使用的是 IP 协议（Internet Protocol），IP 协议会将传输层的报文作为数据部分，再加上 IP 包头组装成 IP 报文，如果 IP 报文大小超过 MTU（以太网中一般为 1500 字节）就会再次进行分片，得到一个即将发送到网络的 IP 报文。<br><img src="https://blog.agebbs.cn/statics/image/TCP-IP%E4%BC%A0%E8%BE%93%E6%B7%BB%E5%8A%A0Ip%E6%8A%A5%E5%A4%B4.png" alt="avatar"></p><h3 id="4-网络接口层-（负责网络包在物理网络中的传输，比如网络包的封帧、-MAC-寻址、差错检测，以及通过网卡传输网络帧等；）-gt-Mac地址-ip报头-tcp-udp-header-上层数据-gt-数据帧"><a href="#4-网络接口层-（负责网络包在物理网络中的传输，比如网络包的封帧、-MAC-寻址、差错检测，以及通过网卡传输网络帧等；）-gt-Mac地址-ip报头-tcp-udp-header-上层数据-gt-数据帧" class="headerlink" title="4. 网络接口层 （负责网络包在物理网络中的传输，比如网络包的封帧、 MAC 寻址、差错检测，以及通过网卡传输网络帧等；）-&gt; Mac地址+ip报头+tcp|udp header+上层数据 -&gt; 数据帧"></a>4. 网络接口层 （负责网络包在物理网络中的传输，比如网络包的封帧、 MAC 寻址、差错检测，以及通过网卡传输网络帧等；）-&gt; Mac地址+ip报头+tcp|udp header+上层数据 -&gt; 数据帧</h3><p>生成了 IP 头部之后，接下来要交给网络接口层（Link Layer）在 IP 头部的前面加上 MAC 头部，并封装成数据帧（Data frame）发送到网络上，MAC 头部是以太网使用的头部，它包含了接收方和发送方的 MAC 地址等信息，我们可以通过 ARP 协议获取对方的 MAC 地址，所以说，网络接口层主要为网络层提供「链路级别」传输的服务，负责在以太网、WiFi 这样的底层网络上发送原始数据包，工作在网卡这个层次，使用 MAC 地址来标识网络上的设备。<br><img src="https://blog.agebbs.cn/statics/image/TCP-IP%E6%95%B0%E6%8D%AE%E6%A8%A1%E5%9E%8B.png" alt="avatar"></p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>由于 OSI 模型实在太复杂，提出的也只是概念理论上的分层，并没有提供具体的实现方案。比较常见，也比较实用的是四层模型，即 TCP/IP 网络模型<br><img src="https://blog.agebbs.cn/statics/image/TCPIP-OSI%E4%BD%93%E7%B3%BB%E7%BB%93%E6%9E%84.png" alt="avatar"></p>]]></content>
    
    
    <summary type="html">&lt;h1 id=&quot;OSI-网络模型&quot;&gt;&lt;a href=&quot;#OSI-网络模型&quot; class=&quot;headerlink&quot; title=&quot;OSI 网络模型&quot;&gt;&lt;/a&gt;OSI 网络模型&lt;/h1&gt;&lt;h2 id=&quot;1-什么是OSI网络模型&quot;&gt;&lt;a href=&quot;#1-什么是OSI网络模型&quot; class=&quot;headerlink&quot; title=&quot;1. 什么是OSI网络模型&quot;&gt;&lt;/a&gt;1. 什么是OSI网络模型&lt;/h2&gt;&lt;p&gt;为了使得多种设备能通过网络相互通信，和为了解决各种不同设备在网络互联中的兼容性问题，国际标准化组织制定了开放式系统互联通信参考模型（Open System Interconnection Reference Model），也就是 OSI 网络模型&lt;/p&gt;</summary>
    
    
    
    <category term="网络编程" scheme="https://blog.agebbs.cn/categories/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/"/>
    
    
    <category term="网络编程" scheme="https://blog.agebbs.cn/tags/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>Go学习笔记1.8 结构体剖析</title>
    <link href="https://blog.agebbs.cn/2022/11/04/Go%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B01-8%E7%BB%93%E6%9E%84%E4%BD%93%E5%89%96%E6%9E%90/"/>
    <id>https://blog.agebbs.cn/2022/11/04/Go%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B01-8%E7%BB%93%E6%9E%84%E4%BD%93%E5%89%96%E6%9E%90/</id>
    <published>2022-11-04T07:06:17.000Z</published>
    <updated>2022-11-04T07:06:57.405Z</updated>
    
    
    
    
    <category term="Golang" scheme="https://blog.agebbs.cn/categories/Golang/"/>
    
    
    <category term="Go" scheme="https://blog.agebbs.cn/tags/Go/"/>
    
  </entry>
  
  <entry>
    <title>Go学习笔记1.7 数组剖析</title>
    <link href="https://blog.agebbs.cn/2022/11/04/Go%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B01-7%E6%95%B0%E7%BB%84%E5%89%96%E6%9E%90/"/>
    <id>https://blog.agebbs.cn/2022/11/04/Go%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B01-7%E6%95%B0%E7%BB%84%E5%89%96%E6%9E%90/</id>
    <published>2022-11-04T07:06:06.000Z</published>
    <updated>2022-11-04T07:07:03.929Z</updated>
    
    
    
    
    <category term="Golang" scheme="https://blog.agebbs.cn/categories/Golang/"/>
    
    
    <category term="Go" scheme="https://blog.agebbs.cn/tags/Go/"/>
    
  </entry>
  
  <entry>
    <title>Go学习笔记1.5 接口剖析</title>
    <link href="https://blog.agebbs.cn/2022/11/04/Go%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B01-5%E6%8E%A5%E5%8F%A3%E5%89%96%E6%9E%90/"/>
    <id>https://blog.agebbs.cn/2022/11/04/Go%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B01-5%E6%8E%A5%E5%8F%A3%E5%89%96%E6%9E%90/</id>
    <published>2022-11-04T03:39:43.000Z</published>
    <updated>2022-11-04T07:04:38.828Z</updated>
    
    <content type="html"><![CDATA[<h2 id="interface"><a href="#interface" class="headerlink" title="interface"></a>interface</h2><p>Go语言中提供了一种类型叫做接口类型。接口是一种抽象类型，它并没有暴露所含数据的布局或内部结构，当然也没有哪些数据的基本操作，它所提供的仅仅是一些方法而已。如果你拿到一个接口类型的值，你无从知道它是什么，你能知道的仅仅是它能做什么，也就是说，仅仅能知道它提供了哪些方法。</p><blockquote><p>一个接口类型定义了一套方法，如果一个具体的类型要实现该接口，那么必须实现该接口类型定义中的所有方法。</p></blockquote><span id="more"></span><h2 id="声明接口类型"><a href="#声明接口类型" class="headerlink" title="声明接口类型"></a>声明接口类型</h2><pre><code>//声明 A 接口 包含 B 和 C 两个方法type A interface &#123;    B() float64    C() float64&#125;//也可以组合已有的接口得到新的接口type AA interface &#123;    A    D(int) float64 &#125;</code></pre><h2 id="接口实现"><a href="#接口实现" class="headerlink" title="接口实现"></a>接口实现</h2><p>一个接口可以被多个类型实现，一个类型也可以实现多个接口</p><pre><code>type Shape interface &#123;    GetArea() float64    GetPerimeter() float64&#125;type Circle struct &#123;    R float64&#125;// Circle实现Shape接口的GetArea方法func (c Circle)GetArea() float64 &#123;    return 3.14 * c.R * c.R&#125;// Circle实现Shape接口的GetPerimeter方法func (c Circle)GetPerimeter() float64 &#123;    return 3.14 * 2 * c.R&#125;</code></pre>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;interface&quot;&gt;&lt;a href=&quot;#interface&quot; class=&quot;headerlink&quot; title=&quot;interface&quot;&gt;&lt;/a&gt;interface&lt;/h2&gt;&lt;p&gt;Go语言中提供了一种类型叫做接口类型。接口是一种抽象类型，它并没有暴露所含数据的布局或内部结构，当然也没有哪些数据的基本操作，它所提供的仅仅是一些方法而已。如果你拿到一个接口类型的值，你无从知道它是什么，你能知道的仅仅是它能做什么，也就是说，仅仅能知道它提供了哪些方法。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一个接口类型定义了一套方法，如果一个具体的类型要实现该接口，那么必须实现该接口类型定义中的所有方法。&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Golang" scheme="https://blog.agebbs.cn/categories/Golang/"/>
    
    
    <category term="Go" scheme="https://blog.agebbs.cn/tags/Go/"/>
    
  </entry>
  
  <entry>
    <title>Go学习笔记1.6 函数剖析</title>
    <link href="https://blog.agebbs.cn/2022/11/04/Go%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B01-6%E5%87%BD%E6%95%B0%E5%89%96%E6%9E%90/"/>
    <id>https://blog.agebbs.cn/2022/11/04/Go%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B01-6%E5%87%BD%E6%95%B0%E5%89%96%E6%9E%90/</id>
    <published>2022-11-04T03:37:14.000Z</published>
    <updated>2022-11-04T07:26:18.642Z</updated>
    
    <content type="html"><![CDATA[<h2 id="函数定义"><a href="#函数定义" class="headerlink" title="函数定义"></a>函数定义</h2><p>Go语言支持：函数、匿名函数和闭包 定义函数使用func关键字</p><pre><code>//函数名：由字母、数字、下划线组成。但函数名的第一个字母不能是数字。在同一个包内，函数名也不能重名func 函数名(参数)(返回值) &#123;    函数体&#125;</code></pre><span id="more"></span><h2 id="函数传参"><a href="#函数传参" class="headerlink" title="函数传参"></a>函数传参</h2><p>Go语言中调用函数，传递过来的变量就是函数的实参，函数可以通过两种方式来传递参数：值传递 和 引用传递</p><blockquote><p>值传递: 调用函数时将实际参数复制一份传递到函数中，这样在函数中如果对参数进行修改，将不会影响到实际参数<br>引用传递: 调用函数时将实际参数的地址传递到函数中，那么在函数中对参数所进行的修改，将影响到实际参数</p></blockquote><ol><li>参数传递</li></ol><pre><code>func swap(x, y *int)&#123;    var temp int        temp = *x /* 保存 x 的值 */    *x = *y   /* 将 y 值赋给 x */    *y = temp /* 将 temp 值赋给 y*/&#125;</code></pre><ol start="2"><li>不定参数传递<br>可变参数本质上就是slice，只能有一个，且必须是最后一个。</li></ol><pre><code>//其中args是一个slice，我们可以通过arg[index]依次访问所有参数,通过len(arg)来判断传递参数的个数func myfunc(args ...int)&#123;    //0个或多个参数&#125;//任意类型的不定参数func myfunc(args ...interface&#123;&#125;)&#123;&#125;</code></pre><h2 id="函数返回值"><a href="#函数返回值" class="headerlink" title="函数返回值"></a>函数返回值</h2><h2 id="匿名函数"><a href="#匿名函数" class="headerlink" title="匿名函数"></a>匿名函数</h2><h2 id="闭包函数"><a href="#闭包函数" class="headerlink" title="闭包函数"></a>闭包函数</h2><h2 id="递归函数"><a href="#递归函数" class="headerlink" title="递归函数"></a>递归函数</h2>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;函数定义&quot;&gt;&lt;a href=&quot;#函数定义&quot; class=&quot;headerlink&quot; title=&quot;函数定义&quot;&gt;&lt;/a&gt;函数定义&lt;/h2&gt;&lt;p&gt;Go语言支持：函数、匿名函数和闭包 定义函数使用func关键字&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;//函数名：由字母、数字、下划线组成。但函数名的第一个字母不能是数字。在同一个包内，函数名也不能重名
func 函数名(参数)(返回值) &amp;#123;
    函数体
&amp;#125;
&lt;/code&gt;&lt;/pre&gt;</summary>
    
    
    
    <category term="Golang" scheme="https://blog.agebbs.cn/categories/Golang/"/>
    
    
    <category term="Go" scheme="https://blog.agebbs.cn/tags/Go/"/>
    
  </entry>
  
  <entry>
    <title>Go依赖注入wire</title>
    <link href="https://blog.agebbs.cn/2022/10/17/Go%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5wire/"/>
    <id>https://blog.agebbs.cn/2022/10/17/Go%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5wire/</id>
    <published>2022-10-17T02:27:58.000Z</published>
    <updated>2022-10-17T02:29:31.539Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Wire"><a href="#Wire" class="headerlink" title="Wire"></a>Wire</h1><blockquote><p>wire 是由 google 开源的一个供 Go 语言使用的依赖注入代码生成工具。它能够根据你的代码，生成相应的依赖注入 go 代码。<br>与其他依赖注入工具不同，比如 Uber 的 Dig 和 Facebook 的 Inject，这 2 个工具都是使用反射实现的依赖注入，而且是运行时注入（runtime dependency injection）。<br>wire 是编译代码时生成代码的依赖注入，是编译期间注入依赖代码（compile-time dependency injection）。而且代码生成期间，如果依赖注入有问题，生成依赖代码时就会出错，就可以报出问题来，而不必等到代码运行时才暴露出问题。</p></blockquote>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;Wire&quot;&gt;&lt;a href=&quot;#Wire&quot; class=&quot;headerlink&quot; title=&quot;Wire&quot;&gt;&lt;/a&gt;Wire&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;wire 是由 google 开源的一个供 Go 语言使用的依赖注入代码生成工具。它能够根据你的代</summary>
      
    
    
    
    <category term="Golang" scheme="https://blog.agebbs.cn/categories/Golang/"/>
    
    
    <category term="Go" scheme="https://blog.agebbs.cn/tags/Go/"/>
    
  </entry>
  
  <entry>
    <title>Go生成汇编的方式</title>
    <link href="https://blog.agebbs.cn/2022/10/11/Go%E7%94%9F%E6%88%90%E6%B1%87%E7%BC%96%E7%9A%84%E6%96%B9%E5%BC%8F/"/>
    <id>https://blog.agebbs.cn/2022/10/11/Go%E7%94%9F%E6%88%90%E6%B1%87%E7%BC%96%E7%9A%84%E6%96%B9%E5%BC%8F/</id>
    <published>2022-10-11T07:49:27.000Z</published>
    <updated>2022-10-11T07:50:57.859Z</updated>
    
    <content type="html"><![CDATA[<h3 id="通过-tool-compile-生成"><a href="#通过-tool-compile-生成" class="headerlink" title="通过 tool compile 生成"></a>通过 tool compile 生成</h3><pre><code>go tool compile -N -l -S test.go</code></pre>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;通过-tool-compile-生成&quot;&gt;&lt;a href=&quot;#通过-tool-compile-生成&quot; class=&quot;headerlink&quot; title=&quot;通过 tool compile 生成&quot;&gt;&lt;/a&gt;通过 tool compile 生成&lt;/h3&gt;&lt;pre&gt;&lt;cod</summary>
      
    
    
    
    <category term="Golang" scheme="https://blog.agebbs.cn/categories/Golang/"/>
    
    
    <category term="Go" scheme="https://blog.agebbs.cn/tags/Go/"/>
    
  </entry>
  
  <entry>
    <title>Go学习笔记1.4 slice剖析</title>
    <link href="https://blog.agebbs.cn/2022/10/10/Go%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B01-4-slice%E5%89%96%E6%9E%90/"/>
    <id>https://blog.agebbs.cn/2022/10/10/Go%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B01-4-slice%E5%89%96%E6%9E%90/</id>
    <published>2022-10-10T10:07:16.000Z</published>
    <updated>2022-11-04T05:13:07.964Z</updated>
    
    <content type="html"><![CDATA[<h2 id="slice-类型"><a href="#slice-类型" class="headerlink" title="slice 类型"></a>slice 类型</h2><blockquote><p>slice表示切片(分片)，例如对一个数组进行切片，取出数组中的一部分值slice依赖于数组<br>slice自身维护了一个指针属性，指向它底层数组的某些元素的集合</p></blockquote><span id="more"></span><h3 id="slice-存储原理"><a href="#slice-存储原理" class="headerlink" title="slice 存储原理"></a>slice 存储原理</h3><blockquote><p>slice代表变长的序列，它的底层是数组。一个切片由3部分组成：指针、长度和容量。指针指向底层数组，长度代表slice当前的长度，容量代表底层数组的长度。</p></blockquote><pre><code>    可以在runtime中的slice中查看slice的实现原理    type slice struct &#123;        array unsafe.Pointer        len   int        cap   int    &#125;</code></pre><h3 id="slice-类型创建"><a href="#slice-类型创建" class="headerlink" title="slice 类型创建"></a>slice 类型创建</h3><ol><li>make 创建<br> <code>slice := make([]T, 5) 第二个参数定义长度 若是不传长度则与cap相同</code></li><li>通过字面量创建<br> <code>slice := []int&#123;1, 2, 3, 4&#125; 创建长度与容量都为4的切片</code></li><li>通过切片创建<br> <code>slice[i:j:k] i 起始位置 j 切片长度 k 切片容量 简写 slice[i:] slice[:j] slice[:]</code></li></ol><h3 id="slice-常用函数"><a href="#slice-常用函数" class="headerlink" title="slice 常用函数"></a>slice 常用函数</h3><ol><li>copy() 将一个slice拷贝到另一个slice中</li><li>append() 追加元素 如果切片的容量已经不能容纳将要追加的数据，就会创建一个新的扩容后的底层数组，将之前的数据拷贝过去后，再执行扩容操作</li></ol><h3 id="slice-常见问题"><a href="#slice-常见问题" class="headerlink" title="slice 常见问题"></a>slice 常见问题</h3><ul><li>slice 传参<blockquote><p>函数的参数都是按值传递的，因此在调用函数时，会将参数的副本传递给函数。<br>在传递slice时 虽然传递的是副本 但是副本同样指向了源slice的底层数组<br>所以在函数内部修改slice，有可能会影响到底层数组，进而影响到其他slice 但是如果传参后切片长度发生变化，切片会自动扩容为新的地址，就无法影响到其他slice 如果想实现影响其他slice 可通过函数返回 或 指针实现</p></blockquote></li></ul><ol><li>不改变slice容量</li></ol><pre><code>//测试Slice/*    传参前内容 地址 [1 2], addr is 0x1400011e018, ptr is 0x1400012c010    传参的内容 地址 [3 2], addr is 0x1400011e060, ptr is 0x1400012c010    传参后内容 地址 [3 2], addr is 0x1400011e018, ptr is 0x1400012c010*/func TestSlice() &#123;    slice := []int&#123;1, 2&#125;    fmt.Printf(&quot;传参前内容 地址 %v, addr is %p, ptr is %p\n&quot;, slice, &amp;slice, slice)    TestSliceChange(slice)    fmt.Printf(&quot;传参后内容 地址 %v, addr is %p, ptr is %p\n&quot;, slice, &amp;slice, slice)    return&#125;func TestSliceChange(slice []int) &#123;    slice[0] = 3    // [3 2] 0xc00000e090 0xc00001a2d0    fmt.Printf(&quot;传参的内容 地址 %v, addr is %p, ptr is %p\n&quot;, slice, &amp;slice, slice)&#125;</code></pre><ol start="2"><li>改变slice容量</li></ol><pre><code>/*    传参前内容 地址 [1 2], addr is 0x1400011e018, ptr is 0x1400012c010    传参的内容 地址 [1 2 3], addr is 0x1400011e060, ptr is 0x14000134000    传参后内容 地址 [1 2], addr is 0x1400011e018, ptr is 0x1400012c010    传参后再修改内容 地址 [4 2], addr is 0x1400011e018, ptr is 0x1400012c010*/func TestSlice() &#123;    slice := []int&#123;1, 2&#125;    fmt.Printf(&quot;传参前内容 地址 %v, addr is %p, ptr is %p\n&quot;, slice, &amp;slice, slice)    TestSliceChange(slice)    fmt.Printf(&quot;传参后内容 地址 %v, addr is %p, ptr is %p\n&quot;, slice, &amp;slice, slice)    slice[0] = 4    fmt.Printf(&quot;传参后再修改内容 地址 %v, addr is %p, ptr is %p\n&quot;, slice, &amp;slice, slice)    return&#125;func TestSliceChange(slice []int) &#123;    //传参    // slice[0] = 3    //使用函数append    slice = append(slice, 3)    fmt.Printf(&quot;传参的内容 地址 %v, addr is %p, ptr is %p\n&quot;, slice, &amp;slice, slice)&#125;</code></pre><ul><li>slice nil 和 slice  </li></ul><pre><code>// 不同slice声明    var c []int    d := []int&#123;&#125;    e := make([]int, 0)    jc, _ := json.Marshal(c) // null    jd, _ := json.Marshal(d) // []    je, _ := json.Marshal(e) // []// 使用 []int&#123;&#125; 或 make([]int,0) 声明的slice 不为nil </code></pre><ul><li>slice 和 array 的区别</li></ul><pre><code>//slice 可以不指定长度 array 必须指定长度    slice:         var a []int        a := make([]int,len,cap)    array:         var b [lenght]int//slice 长度可以动态扩缩容 array 长度是编译时静态计算的    func() &#123;        var s []int        // s := make([]int, 100)        fmt.Println(len(s))        fmt.Println(cap(s))    &#125;()    func() &#123;        var a [100]int        fmt.Println(len(a))        fmt.Println(cap(a))    &#125;()    run 输出    0    0    100    100// benchmake slice array go1.18    func TestCallSlice(s []int) &#123;    &#125;    func TestCallArray(a [10000]int) &#123;    &#125;    //go:noinline    func BenchmarkTestCallSlice(b *testing.B) &#123;        s := make([]int, 10000)        for n := 0; n &lt; b.N; n++ &#123;            TestCallSlice(s)        &#125;    &#125;    //go:noinline    func BenchmarkTestCallArray(b *testing.B) &#123;        var a [10000]int        for n := 0; n &lt; b.N; n++ &#123;            TestCallArray(a)        &#125;    &#125;    goos: darwin    goarch: arm64    pkg: test    BenchmarkTestCallSlice    BenchmarkTestCallSlice-8        1000000000               0.3501 ns/op          0 B/op          0 allocs/op    BenchmarkTestCallArray    BenchmarkTestCallArray-8        1000000000               0.3494 ns/op          0 B/op          0 allocs/op    PASS    ok      test    0.848s    </code></pre><ul><li>slice 扩缩容</li></ul><pre><code>/*    声明 []int , len is 0, cap is 0    append 1 , len is 1, cap is 1    append 2 , len is 2, cap is 2    append 3 , len is 3, cap is 4    append 4 , len is 4, cap is 4    ...    append 254, len is 256, cap is 256    append 511, len is 512, cap is 512    append 511, len is 513, cap is 848    append 846, len is 848, cap is 848    append 847, len is 849, cap is 1280*/    func TestSliceLen() &#123;        var a []int        fmt.Printf(&quot;声明 []int , len is %v, cap is %v\n&quot;, len(a), cap(a))        a = append(a, 0)        fmt.Printf(&quot;append 1 , len is %v, cap is %v\n&quot;, len(a), cap(a))        a = append(a, 1)        fmt.Printf(&quot;append 2 , len is %v, cap is %v\n&quot;, len(a), cap(a))        a = append(a, 2)        fmt.Printf(&quot;append 3 , len is %v, cap is %v\n&quot;, len(a), cap(a))        a = append(a, 3)        fmt.Printf(&quot;append 4 , len is %v, cap is %v\n&quot;, len(a), cap(a))        a = append(a, 4)        fmt.Printf(&quot;append 5 , len is %v, cap is %v\n&quot;, len(a), cap(a))        a = append(a, 5)        fmt.Printf(&quot;append 6 , len is %v, cap is %v\n&quot;, len(a), cap(a))        for i := 5; i &lt; 1025; i++ &#123;            a = append(a, i)            fmt.Printf(&quot;append %v, len is %v, cap is %v\n&quot;, i, len(a), cap(a))        &#125;    &#125;    // 由此可见slice 每次扩容都是成倍扩容 但是在达到一定阀值后 就以1.25倍扩容后判断et.size 等于 1 不做任何操作 goarch.PtrSize 编译器将除法/乘法优化为一个常量的移位。 对于2的幂，使用可变移位。    // /src/runtime/slice.go growslice    newcap := old.cap    doublecap := newcap + newcap    if cap &gt; doublecap &#123;        newcap = cap    &#125; else &#123;        const threshold = 256        if old.cap &lt; threshold &#123;            newcap = doublecap        &#125; else &#123;            // Check 0 &lt; newcap to detect overflow            // and prevent an infinite loop.            for 0 &lt; newcap &amp;&amp; newcap &lt; cap &#123;                // Transition from growing 2x for small slices                // to growing 1.25x for large slices. This formula                // gives a smooth-ish transition between the two.                newcap += (newcap + 3*threshold) / 4            &#125;            // Set newcap to the requested cap when            // the newcap calculation overflowed.            if newcap &lt;= 0 &#123;                newcap = cap            &#125;        &#125;    &#125;    var overflow bool    var lenmem, newlenmem, capmem uintptr    // Specialize for common values of et.size.    // For 1 we don&#39;t need any division/multiplication.    // For goarch.PtrSize, compiler will optimize division/multiplication into a shift by a constant.    // For powers of 2, use a variable shift.    switch &#123;    case et.size == 1:        lenmem = uintptr(old.len)        newlenmem = uintptr(cap)        capmem = roundupsize(uintptr(newcap))        overflow = uintptr(newcap) &gt; maxAlloc        newcap = int(capmem)    case et.size == goarch.PtrSize:        lenmem = uintptr(old.len) * goarch.PtrSize        newlenmem = uintptr(cap) * goarch.PtrSize        capmem = roundupsize(uintptr(newcap) * goarch.PtrSize)        overflow = uintptr(newcap) &gt; maxAlloc/goarch.PtrSize        newcap = int(capmem / goarch.PtrSize)    case isPowerOfTwo(et.size):        var shift uintptr        if goarch.PtrSize == 8 &#123;            // Mask shift for better code generation.            shift = uintptr(sys.Ctz64(uint64(et.size))) &amp; 63        &#125; else &#123;            shift = uintptr(sys.Ctz32(uint32(et.size))) &amp; 31        &#125;        lenmem = uintptr(old.len) &lt;&lt; shift        newlenmem = uintptr(cap) &lt;&lt; shift        capmem = roundupsize(uintptr(newcap) &lt;&lt; shift)        overflow = uintptr(newcap) &gt; (maxAlloc &gt;&gt; shift)        newcap = int(capmem &gt;&gt; shift)    default:        lenmem = uintptr(old.len) * et.size        newlenmem = uintptr(cap) * et.size        capmem, overflow = math.MulUintptr(et.size, uintptr(newcap))        capmem = roundupsize(capmem)        newcap = int(capmem / et.size)    &#125;    // 但是append批量赋值的时候 go 会对其进行内存优化 例:     append(s,1,2,3) len 3 cap 3    // 预先分配内存可以提升性能 index赋值要比append赋值性能好    func BenchmarkAppend(b *testing.B) &#123;        b.ReportAllocs()        for i := 0; i &lt; b.N; i++ &#123;            var a []int            for j := 0; j &lt; 10000; j++ &#123;                a = append(a, j)            &#125;        &#125;    &#125;    BenchmarkAppend-8          46833             25123 ns/op          357626 B/op         19 allocs/op    func BenchmarkAppendStaticCap(b *testing.B) &#123;        b.ReportAllocs()        for i := 0; i &lt; b.N; i++ &#123;            a := make([]int, 0, 10000)            for j := 0; j &lt; 10000; j++ &#123;                a = append(a, j)            &#125;        &#125;    &#125;    BenchmarkAppendStaticCap-8        150697              7362 ns/op           81920 B/op          1 allocs/op    func BenchmarkAppendIndex(b *testing.B) &#123;        b.ReportAllocs()        for i := 0; i &lt; b.N; i++ &#123;            a := make([]int, 10000)            for j := 0; j &lt; 10000; j++ &#123;                a[j] = j            &#125;        &#125;    &#125;    BenchmarkAppendIndex-8            165469              6781 ns/op           81920 B/op          1 allocs/op</code></pre><ul><li>slice bce优化 边界检查消除</li></ul><pre><code>    func TestSliceBce(a []int) &#123;        _ = a[3] //检查边界        s := 0        s += a[0] //边界检查消除        s += a[1] //边界检查消除        s += a[2] //边界检查消除        s += a[3] //边界检查消除        _ = a[index:] // 需要边界检查        _ = a[:index] // 边界检查消除        // print(s)    &#125;</code></pre>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;slice-类型&quot;&gt;&lt;a href=&quot;#slice-类型&quot; class=&quot;headerlink&quot; title=&quot;slice 类型&quot;&gt;&lt;/a&gt;slice 类型&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;slice表示切片(分片)，例如对一个数组进行切片，取出数组中的一部分值slice依赖于数组&lt;br&gt;slice自身维护了一个指针属性，指向它底层数组的某些元素的集合&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Golang" scheme="https://blog.agebbs.cn/categories/Golang/"/>
    
    
    <category term="Go" scheme="https://blog.agebbs.cn/tags/Go/"/>
    
  </entry>
  
  <entry>
    <title>Go学习笔记1.3 map剖析</title>
    <link href="https://blog.agebbs.cn/2022/10/10/Go%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B01-3-map%E5%89%96%E6%9E%90/"/>
    <id>https://blog.agebbs.cn/2022/10/10/Go%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B01-3-map%E5%89%96%E6%9E%90/</id>
    <published>2022-10-10T08:36:15.000Z</published>
    <updated>2022-11-04T05:13:19.174Z</updated>
    
    <content type="html"><![CDATA[<h2 id="map类型"><a href="#map类型" class="headerlink" title="map类型"></a>map类型</h2><blockquote><p>是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据，key 类似于索引，指向数据的值。Map 是一种集合，所以我们可以像迭代数组和切片那样迭代它。不过，Map 是无序的，我们无法决定它的返回顺序，这是因为 Map 是使用 hash 表来实现的。</p></blockquote><span id="more"></span><h3 id="使用方法"><a href="#使用方法" class="headerlink" title="使用方法"></a>使用方法</h3><ol><li>基本语法</li></ol><blockquote><p>声明是不会分配内存的，初始化分配内存后才能赋值和使用。</p></blockquote><pre><code>    声明         var map t map[int]int    初始化        t = make(map[int]int,0)        var t map[int]int = map[int]int&#123;1,1&#125;        t = map[int]int&#123;1&#125;    读取        i := m[&quot;a&quot;]         i,ok := m[&quot;b&quot;] //可用第二个参数判断是否存在    删除        delete(m,&quot;a&quot;)</code></pre><ol start="2"><li>key类型</li></ol><pre><code>key类型可以是 bool、数字、string、指针、channel , 只包含前面几个类型的接口、结构体、数组，通常为 int 或 string 但是 slice map func 不可以</code></pre><ol start="3"><li>value类型</li></ol><pre><code>value类型基本和key一样 通常为数字 字符串 接口</code></pre><ol start="4"><li>常用案例</li></ol><pre><code>map[string]stringmap[int]stringmap[int]interface</code></pre><h3 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h3><ol><li>赋值问题</li></ol><pre><code>map 在 go 中属于 reference 类型 作为传参或返回类型的时候 传递的也是地址 不是map本体 map 在没有初始化的时候为 nil</code></pre><ol start="2"><li>append对未初始化的map</li></ol><pre><code>append 支持 非nil和nil的map，都能进行成功的append</code></pre><ol start="3"><li>map线程安全</li></ol><pre><code>map 是非线程安全的 可以通过内嵌的结构体加 读写锁 实现线程安全声明    var counter = struct&#123;        sync.RWMutex        m map[string]int    &#125;&#123;m: make(map[string]int)&#125;读    counter.RLock()    n := counter.m[&quot;some_key&quot;]    counter.RUnlock()    fmt.Println(&quot;some_key:&quot;, n)写    counter.Lock()    counter.m[&quot;some_key&quot;]++    counter.Unlock()</code></pre><ol start="4"><li>读取顺序问题</li></ol><pre><code>map 是 hashmap 读取遍历是无法保证顺序的 如果保证key的遍历顺序可以通过将key保存到一个slice        import &quot;sort&quot;    var m map[int]string    var keys []int    for k := range m &#123;        keys = append(keys, k)    &#125;    sort.Ints(keys)    for _, k := range keys &#123;        fmt.Println(&quot;Key:&quot;, k, &quot;Value:&quot;, m[k])    &#125;</code></pre><ol start="5"><li>map 扩容和缩容<pre><code>map 无法打印 kay value 的值 会随着map的扩容改变但是 map 删除key 无法自动缩容 </code></pre></li></ol>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;map类型&quot;&gt;&lt;a href=&quot;#map类型&quot; class=&quot;headerlink&quot; title=&quot;map类型&quot;&gt;&lt;/a&gt;map类型&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据，key 类似于索引，指向数据的值。Map 是一种集合，所以我们可以像迭代数组和切片那样迭代它。不过，Map 是无序的，我们无法决定它的返回顺序，这是因为 Map 是使用 hash 表来实现的。&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Golang" scheme="https://blog.agebbs.cn/categories/Golang/"/>
    
    
    <category term="Go" scheme="https://blog.agebbs.cn/tags/Go/"/>
    
  </entry>
  
  <entry>
    <title>Mysql底层原理剖析</title>
    <link href="https://blog.agebbs.cn/2022/08/22/Mysql%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86%E5%89%96%E6%9E%90/"/>
    <id>https://blog.agebbs.cn/2022/08/22/Mysql%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86%E5%89%96%E6%9E%90/</id>
    <published>2022-08-22T01:52:23.000Z</published>
    <updated>2022-08-22T01:52:23.287Z</updated>
    
    
    
    
    
  </entry>
  
  <entry>
    <title>hexo版本升级的坑</title>
    <link href="https://blog.agebbs.cn/2022/08/04/hexo%E7%89%88%E6%9C%AC%E5%8D%87%E7%BA%A7%E7%9A%84%E5%9D%91/"/>
    <id>https://blog.agebbs.cn/2022/08/04/hexo%E7%89%88%E6%9C%AC%E5%8D%87%E7%BA%A7%E7%9A%84%E5%9D%91/</id>
    <published>2022-08-04T05:41:17.000Z</published>
    <updated>2022-10-11T07:59:02.374Z</updated>
    
    <content type="html"><![CDATA[<h3 id="没事别乱改模板！！！"><a href="#没事别乱改模板！！！" class="headerlink" title="没事别乱改模板！！！"></a>没事别乱改模板！！！</h3><h3 id="没事别乱改模板！！！-1"><a href="#没事别乱改模板！！！-1" class="headerlink" title="没事别乱改模板！！！"></a>没事别乱改模板！！！</h3><h3 id="没事别乱改模板！！！-2"><a href="#没事别乱改模板！！！-2" class="headerlink" title="没事别乱改模板！！！"></a>没事别乱改模板！！！</h3><blockquote><p>最近发现别人的hexo访问速度是我hexo的好几倍，于是就抱着试一试的心态把next从5.0 升级到6.0，下面记录升级后遇到的坑</p></blockquote><ul><li>文章内容不展示<blockquote><p>可能是版本太高和之前的模块不兼容造成的，有部分页面没有渲染, 尝试把hexo版本退回3.2问题没有解决，可能是因为next版本的问题尝试升级回hexo5.0，next修改为5.4 内容出现，之前修改的mores标签无法使用 导致文章部分缺失~~~</p></blockquote></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;没事别乱改模板！！！&quot;&gt;&lt;a href=&quot;#没事别乱改模板！！！&quot; class=&quot;headerlink&quot; title=&quot;没事别乱改模板！！！&quot;&gt;&lt;/a&gt;没事别乱改模板！！！&lt;/h3&gt;&lt;h3 id=&quot;没事别乱改模板！！！-1&quot;&gt;&lt;a href=&quot;#没事别乱改模板！！</summary>
      
    
    
    
    <category term="Hexo" scheme="https://blog.agebbs.cn/categories/Hexo/"/>
    
    
    <category term="Hexo" scheme="https://blog.agebbs.cn/tags/Hexo/"/>
    
  </entry>
  
  <entry>
    <title>Hexo文章截断修改</title>
    <link href="https://blog.agebbs.cn/2022/08/04/Hexo%E6%96%87%E7%AB%A0%E6%88%AA%E6%96%AD%E4%BF%AE%E6%94%B9/"/>
    <id>https://blog.agebbs.cn/2022/08/04/Hexo%E6%96%87%E7%AB%A0%E6%88%AA%E6%96%AD%E4%BF%AE%E6%94%B9/</id>
    <published>2022-08-04T05:01:11.000Z</published>
    <updated>2022-08-04T05:19:03.790Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>在文章下方可以使用more语句进行截断，这样博客首页只会出现文章的前面一小部分，看起来很清爽简约，但是要想展示文章的中间段落就无法实现，我们可以修改为被<code>&lt;!--mores--&gt;</code>包裹的段落展示在首页</p></blockquote>]]></content>
    
    
      
      
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;在文章下方可以使用more语句进行截断，这样博客首页只会出现文章的前面一小部分，看起来很清爽简约，但是要想展示文章的中间段落就无法实现，我们可以修改为被&lt;code&gt;&amp;lt;!--mores--&amp;gt;&lt;/code&gt;包裹的段落展示在首页&lt;/p&gt;
&lt;/</summary>
      
    
    
    
    <category term="Hexo" scheme="https://blog.agebbs.cn/categories/Hexo/"/>
    
    
    <category term="Hexo" scheme="https://blog.agebbs.cn/tags/Hexo/"/>
    
    <category term="more" scheme="https://blog.agebbs.cn/tags/more/"/>
    
  </entry>
  
  <entry>
    <title>Go语言扩展 1.RPC</title>
    <link href="https://blog.agebbs.cn/2022/01/24/Go%E8%AF%AD%E8%A8%80%E6%89%A9%E5%B1%951-RPC/"/>
    <id>https://blog.agebbs.cn/2022/01/24/Go%E8%AF%AD%E8%A8%80%E6%89%A9%E5%B1%951-RPC/</id>
    <published>2022-01-24T07:57:16.000Z</published>
    <updated>2022-08-06T05:34:08.520Z</updated>
    
    <content type="html"><![CDATA[<h3 id="go-net的rpc"><a href="#go-net的rpc" class="headerlink" title="go net的rpc"></a>go net的rpc</h3><blockquote><p>Go语言的rpc包的是建立在net包基础之上的（内置）。基于TCP协议</p></blockquote><span id="more"></span><ul><li><p>服务端</p><pre><code>#server.gopackage mainimport (    &quot;net&quot;    &quot;net/http&quot;    &quot;net/rpc&quot;)// 计算type Sum struct &#123;&#125;// 计算方法func (s *Sum) sums(req string, resp *string) error &#123;    *resp = req+1    return nil&#125;func main() &#123;    // 功能对象注册    sum :=  new(Sum)    err := rpc.Register(sum)      if err != nil &#123;        panic(err.Error())    &#125;    // HTTP注册    rpc.HandleHTTP()    // 端口监听    listen, err := net.Listen(&quot;tcp&quot;, &quot;:8081&quot;)    if err != nil &#123;        panic(err.Error())    &#125;    // 启动服务    _ = http.Serve(listen, nil)&#125;</code></pre></li><li><p>客户端</p><pre><code>#client.gopackage mainimport (    &quot;fmt&quot;    &quot;net/rpc&quot;)func main()  &#123;    // 建立连接    client, err := rpc.DialHTTP(&quot;tcp&quot;, &quot;localhost:8081&quot;)    if err != nil &#123;        panic(err.Error())    &#125;    // 参数    req := 100    var resp *string    // 同步调用    err = client.Call(&quot;Sum.sums&quot;, req, &amp;resp)    if err!= nil &#123;        panic(err.Error())    &#125;    fmt.Println(*resp)    // 异步调用    syncCall := client.Go(&quot;Sum.sums&quot;, req, &amp;resp, nil)    // 阻塞，异步调用成功后解除阻塞    replayDone := &lt;-syncCall.Done    fmt.Println(replayDone)    fmt.Println(*resp)&#125;</code></pre></li></ul><h3 id="基于Json实现-rpc"><a href="#基于Json实现-rpc" class="headerlink" title="基于Json实现 rpc"></a>基于Json实现 rpc</h3><blockquote><p>Go语言的rpc包的是建立在net包基础之上的（内置）。基于TCP协议,再原有的rpc结构上把传入传出方式改为json字符串就可以实现jsonrpc</p></blockquote><h3 id="grpc-实现-rpc"><a href="#grpc-实现-rpc" class="headerlink" title="grpc 实现 rpc"></a>grpc 实现 rpc</h3><blockquote><p>rpc 结合 protobuf 实现grpc</p></blockquote><ul><li><p>1.首先要安装grpc(基于mac)</p><pre><code>brew tap grpc/grpcbrew install protobufgo get -u github.com/golang/protobuf/protogo get -u github.com/golang/protobuf/protoc-gen-go</code></pre></li><li><p>2.编写 proto 条件</p><pre><code>#message.protosyntax = &quot;proto3&quot;;package message;option go_package = &quot;./;message&quot;;// 加密请求message  EncryptionRequest&#123;string str = 1;&#125;// 加密返回值message EncryptionResult&#123;string result = 1;&#125;</code></pre></li><li><p>3.编译proto<br>  <code>protoc ./message.proto --go_out=./ </code></p></li><li><p>4.编写程序</p><ul><li><p>服务端<br>  ```<br>  #server.go<br>  // 加密服务实现类<br>  type EncryptionServiceImpl struct {<br>  }</p><p>  // 加密程序<br>  func (es *EncryptionServiceImpl) Encryption(ctx context.Context, request *message.EncryptionRequest) (*message.EncryptionResult, error){</p><pre><code>  md5Str := ToMd5(request.GetStr())  res := message.EncryptionResult&#123;Result:md5Str&#125;  return &amp;res,nil</code></pre><p>  }</p><p>  // 简单封装的 md5<br>  func ToMd5(s string) string{</p><pre><code>  m := md5.New()  m.Write([]byte (s))  return hex.EncodeToString(m.Sum(nil))</code></pre><p>  }</p></li></ul></li></ul><pre><code>    func main() &#123;        // 创建一个 grpc server        server := grpc.NewServer()        message.RegisterEncryptionServiceServer(server, new(EncryptionServiceImpl))        lis, err := net.Listen(&quot;tcp&quot;, &quot;:8081&quot;)        if err != nil &#123;            panic(err.Error())        &#125;        _ = server.Serve(lis)    &#125;    ```* 客户端    ```    #client_test.go    func TestClient(t *testing.T)  &#123;        //1、Dail连接        conn, err := grpc.Dial(&quot;localhost:8081&quot;, grpc.WithInsecure())        if err != nil &#123;            panic(err.Error())        &#125;        defer conn.Close()        encryptionServiceClient := message.NewEncryptionServiceClient(conn)        orderRequest := &amp;message.EncryptionRequest&#123;Str: &quot;mclink&quot;&#125;        result, err := encryptionServiceClient.Encryption(context.Background(), orderRequest)        if result != nil &#123;            fmt.Println(result.Result)        &#125;    &#125;    ```</code></pre><h3 id="rpcx-实现-rpc"><a href="#rpcx-实现-rpc" class="headerlink" title="rpcx 实现 rpc"></a>rpcx 实现 rpc</h3><h3 id="KiteX-实现-rpc"><a href="#KiteX-实现-rpc" class="headerlink" title="KiteX 实现 rpc"></a>KiteX 实现 rpc</h3>]]></content>
    
    
    <summary type="html">&lt;h3 id=&quot;go-net的rpc&quot;&gt;&lt;a href=&quot;#go-net的rpc&quot; class=&quot;headerlink&quot; title=&quot;go net的rpc&quot;&gt;&lt;/a&gt;go net的rpc&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;Go语言的rpc包的是建立在net包基础之上的（内置）。基于TCP协议&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Golang" scheme="https://blog.agebbs.cn/categories/Golang/"/>
    
    
    <category term="Go" scheme="https://blog.agebbs.cn/tags/Go/"/>
    
    <category term="RPC" scheme="https://blog.agebbs.cn/tags/RPC/"/>
    
  </entry>
  
  <entry>
    <title>微服务1.RPC技术架构</title>
    <link href="https://blog.agebbs.cn/2022/01/24/%E5%BE%AE%E6%9C%8D%E5%8A%A11-RPC%E6%8A%80%E6%9C%AF%E6%9E%B6%E6%9E%84/"/>
    <id>https://blog.agebbs.cn/2022/01/24/%E5%BE%AE%E6%9C%8D%E5%8A%A11-RPC%E6%8A%80%E6%9C%AF%E6%9E%B6%E6%9E%84/</id>
    <published>2022-01-24T06:35:08.000Z</published>
    <updated>2022-01-24T07:54:03.636Z</updated>
    
    <content type="html"><![CDATA[<h2 id="1-什么是RPC"><a href="#1-什么是RPC" class="headerlink" title="1.什么是RPC"></a>1.什么是RPC</h2><blockquote><p>RPC是Remote Procedure Call Protocol 单词首字母缩写，简称RPC，中文叫：远程过程调用协议 ,特点：这种调用的过程跨越了物理服务器的限制，是在网络中完成的，在调用远端服务器上程序的过程中，本地程序等待返回调用结果，直到远端程序执行完毕，将结果进行返回本地，最终完成一次完整的调用。</p></blockquote><span id="more"></span><h2 id="2-RPC技术架构"><a href="#2-RPC技术架构" class="headerlink" title="2. RPC技术架构"></a>2. RPC技术架构</h2><h3 id="2-1-RPC技术架构的组成"><a href="#2-1-RPC技术架构的组成" class="headerlink" title="2.1 RPC技术架构的组成"></a>2.1 RPC技术架构的组成</h3><ul><li>客户端：服务调用发起方，又称之为服务消费者</li><li>服务器：远端计算机机器上运行的程序，其中包含客户端要调用和访问的方法。</li><li>客户端存根：存放服务端地址，端口消息，把客户端请求参数打包成网络消息，发送到服务方，接受服务方返回的数据包。程序运行在客户端</li><li>服务端存根：接受客户端数据包，解析数据调用具体服务方法，将调用结果打包发送给客户端，程序在服务端运行</li></ul><h3 id="2-2-RPC原理图"><a href="#2-2-RPC原理图" class="headerlink" title="2.2 RPC原理图"></a>2.2 RPC原理图</h3><p><img src="https://blog.agebbs.cn/statics/image/RPC%E5%8E%9F%E7%90%86%E5%9B%BE.png" alt="avatar"></p>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;1-什么是RPC&quot;&gt;&lt;a href=&quot;#1-什么是RPC&quot; class=&quot;headerlink&quot; title=&quot;1.什么是RPC&quot;&gt;&lt;/a&gt;1.什么是RPC&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;RPC是Remote Procedure Call Protocol 单词首字母缩写，简称RPC，中文叫：远程过程调用协议 ,特点：这种调用的过程跨越了物理服务器的限制，是在网络中完成的，在调用远端服务器上程序的过程中，本地程序等待返回调用结果，直到远端程序执行完毕，将结果进行返回本地，最终完成一次完整的调用。&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="微服务" scheme="https://blog.agebbs.cn/categories/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
    
    <category term="RPC" scheme="https://blog.agebbs.cn/tags/RPC/"/>
    
  </entry>
  
  <entry>
    <title>Go学习笔记 1.2 channel刨析</title>
    <link href="https://blog.agebbs.cn/2021/11/25/Go%E5%AD%A6%E4%B9%A0%E6%89%8B%E8%AE%B01-2channel%E5%88%A8%E6%9E%90/"/>
    <id>https://blog.agebbs.cn/2021/11/25/Go%E5%AD%A6%E4%B9%A0%E6%89%8B%E8%AE%B01-2channel%E5%88%A8%E6%9E%90/</id>
    <published>2021-11-25T07:25:57.000Z</published>
    <updated>2022-11-04T06:32:03.744Z</updated>
    
    <content type="html"><![CDATA[<h2 id="channel"><a href="#channel" class="headerlink" title="channel"></a>channel</h2><blockquote><p>Goroutine 和 channel 是 Go 语言并发编程的 两大基石。Goroutine 用于执行并发任务，channel 用于 goroutine 之间的同步、通信。<br>Channel 在 gouroutine 间架起了一条管道，在管道里传输数据，实现 gouroutine 间的通信；由于它是线程安全的，所以用起来非常方便；channel 还提供“先进先出”的特性；它还能影响 goroutine 的阻塞和唤醒。<br>Go中不通过共享内存来通信，而是通过通信来共享内存，</p></blockquote><p>Channel是Go中的一个核心类型，可以把它看成一个管道，通过它并发核心单元就可以发送或者接收数据进行通讯(communication)。它的操作符是箭头 &lt;-</p><pre><code>ch &lt;- v    // 发送值v到Channel ch中v := &lt;-ch  // 从Channel ch中接收数据，并将数据赋值给v//箭头的指向就是数据的流向</code></pre><span id="more"></span>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;channel&quot;&gt;&lt;a href=&quot;#channel&quot; class=&quot;headerlink&quot; title=&quot;channel&quot;&gt;&lt;/a&gt;channel&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;Goroutine 和 channel 是 Go 语言并发编程的 两大基石。Goroutine 用于执行并发任务，channel 用于 goroutine 之间的同步、通信。&lt;br&gt;Channel 在 gouroutine 间架起了一条管道，在管道里传输数据，实现 gouroutine 间的通信；由于它是线程安全的，所以用起来非常方便；channel 还提供“先进先出”的特性；它还能影响 goroutine 的阻塞和唤醒。&lt;br&gt;Go中不通过共享内存来通信，而是通过通信来共享内存，&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Channel是Go中的一个核心类型，可以把它看成一个管道，通过它并发核心单元就可以发送或者接收数据进行通讯(communication)。它的操作符是箭头 &amp;lt;-&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ch &amp;lt;- v    // 发送值v到Channel ch中
v := &amp;lt;-ch  // 从Channel ch中接收数据，并将数据赋值给v
//箭头的指向就是数据的流向
&lt;/code&gt;&lt;/pre&gt;</summary>
    
    
    
    <category term="Golang" scheme="https://blog.agebbs.cn/categories/Golang/"/>
    
    
    <category term="Go" scheme="https://blog.agebbs.cn/tags/Go/"/>
    
  </entry>
  
  <entry>
    <title>Go学习笔记 1.1 指针刨析</title>
    <link href="https://blog.agebbs.cn/2021/11/25/Go%E5%AD%A6%E4%B9%A0%E6%89%8B%E8%AE%B01-1%E6%8C%87%E9%92%88%E5%88%A8%E6%9E%90/"/>
    <id>https://blog.agebbs.cn/2021/11/25/Go%E5%AD%A6%E4%B9%A0%E6%89%8B%E8%AE%B01-1%E6%8C%87%E9%92%88%E5%88%A8%E6%9E%90/</id>
    <published>2021-11-25T07:25:43.000Z</published>
    <updated>2022-11-04T05:49:22.573Z</updated>
    
    <content type="html"><![CDATA[<h2 id="指针"><a href="#指针" class="headerlink" title="指针"></a>指针</h2><p>指针是指向另一个地址的值（pointer）在Go语言中可以被拆分为两个核心概念：<br>类型指针，允许对这个指针类型的数据进行修改，传递数据可以直接使用指针，而无须拷贝数据，类型指针不能进行偏移和运算。<br>切片，由指向起始元素的原始指针、元素数量和容量组成。</p><span id="more"></span><h2 id="指针类型和值"><a href="#指针类型和值" class="headerlink" title="指针类型和值"></a>指针类型和值</h2><p>在Go中，指针类型的字面形式为*T，其中T为一个任意类型。类型T称为指针类型 <em>T 的基类型（base type）如果一个指针类型的基类型为T，则我们可以称此指针类型为一个T指针类型。如果一个指针类型的底层类型是</em>T，则它的基类型为T。如果两个无名指针类型的基类型为同一类型，则这两个无名指针类型亦为同一类型。</p><pre><code>*int  // 一个基类型为int的无名指针类型。**int // 一个多级无名指针类型，它的基类型为*int。type Ptr *int // Ptr是一个具名指针类型，它的基类型为int。type PP *Ptr  // PP是一个具名多级指针类型，它的基类型为Ptr。</code></pre><p>指针类型的零值的字面量使用预声明的nil来表示。一个nil指针（常称为空指针）中不存储任何地址。如果一个指针类型的基类型为T，则此指针类型的值只能存储类型为T的值的地址。</p><h2 id="指针使用"><a href="#指针使用" class="headerlink" title="指针使用"></a>指针使用</h2><blockquote><p>&amp;取出地址，*根据地址取出地址指向的值。</p></blockquote><ol><li>定义指针变量</li></ol><pre><code>var ptr *int  //指针声明</code></pre><ol start="2"><li>为指针变量赋值</li></ol><pre><code>var a int=1ptr = &amp;a</code></pre><ol start="3"><li>获取指针内容</li></ol><pre><code>fmt.Println(*ptr)</code></pre><ol start="4"><li>零值与空指针(nil)<br>变量声明而没有赋值，默认为零值，不同类型零值不同，例如字符串零值为空字符串；<br>指针声明而没有赋值，默认为nil，即该指针没有任何指向。当指针没有指向的时候，不能对(*point)进行操作包括读取，否则会报空指针异常。</li></ol><pre><code>func main() &#123;    var a *int    fmt.Println(&quot;%#v&quot;,a) //(*int)(nil)    *a = 1  //保错 panic: runtime error: invalid memory address or nil pointer dereference --&gt;    //解决办法 初始化一个内存，并把该内存地址赋予指针变量    *a = &amp;b 或 *a = new(int)    *a = 1&#125;</code></pre><ol start="5"><li>指针指向结构体</li><li>指针指向数组</li><li>内部指针</li><li>隐式指针</li><li>指针和接口</li></ol><h2 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h2><ol><li>函数参数和返回值使用变量和使用指针<br>函数返回变量的值会对整个值进行拷贝而使用引用只拷贝内存理论上传指针比传值快，但是实际指针传递要比值传递慢的多，造成原因是stack(栈)和heap(堆)的内存管理有关系，变量分配在stack上还是heap上，对性能是会有影响的。</li></ol><pre><code>/*    stack上分配内存效率比heap更高，而且stack上分配的内存不用做GC，超出了作用域，就自动回收内存。    放在heap上的内存，需要由GC来做内存回收，而且容易产生内存碎片。    编译器在编译期决定变量分配在stack还是heap上，需要做逃逸分析(escape analysis)，逃逸分析在编译阶段就完成了。*/type foo struct &#123;    a string    b int&#125;/*    变量result定义的时候会在这个goroutine的stack上分配result的内存空间。    当函数返回时，getFooValue的调用方如果有接收返回值，那result的值会被拷贝给对应的接收变量。    stack上变量result的内存空间会被释放(标记为不可用，不能再被访问，除非这块空间再次被分配给其它变量)    但是如果foo占用的内存空间比较小，约0.3KB，goroutine的stack空间足够存储，如果foo占用的空间过大，在stack里存储不了，就会分配内存到heap上。*/func getValueFoo () foo &#123;    var r foo    return r&#125;/*    函数getFooPointer因为返回的是一个指针，如果变量result分配在stack上，那函数返回后，result的内存空间会被释放，就会导致接受函数返回值的变量无法访问原本result的内存空间，成为一个悬浮指针(dangling pointer)。    所以这种情况会发生内存逃逸，result会分配在heap上，而不是stack上。*/func getFooPointer() *foo &#123;    var result foo    return &amp;result&#125;func f(p *foo) &#123;    fmt.Println(p)&#125;func main()  &#123;    p := &amp;foo&#123;&#125;    f(p)&#125;</code></pre>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;指针&quot;&gt;&lt;a href=&quot;#指针&quot; class=&quot;headerlink&quot; title=&quot;指针&quot;&gt;&lt;/a&gt;指针&lt;/h2&gt;&lt;p&gt;指针是指向另一个地址的值（pointer）在Go语言中可以被拆分为两个核心概念：&lt;br&gt;类型指针，允许对这个指针类型的数据进行修改，传递数据可以直接使用指针，而无须拷贝数据，类型指针不能进行偏移和运算。&lt;br&gt;切片，由指向起始元素的原始指针、元素数量和容量组成。&lt;/p&gt;</summary>
    
    
    
    <category term="Golang" scheme="https://blog.agebbs.cn/categories/Golang/"/>
    
    
    <category term="Go" scheme="https://blog.agebbs.cn/tags/Go/"/>
    
  </entry>
  
  <entry>
    <title>Go学习笔记 1. 数据类型</title>
    <link href="https://blog.agebbs.cn/2021/11/24/Go%E5%AD%A6%E4%B9%A0%E6%89%8B%E8%AE%B01%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B/"/>
    <id>https://blog.agebbs.cn/2021/11/24/Go%E5%AD%A6%E4%B9%A0%E6%89%8B%E8%AE%B01%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B/</id>
    <published>2021-11-24T06:55:23.000Z</published>
    <updated>2022-11-04T05:14:33.773Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-GO语言数据类型"><a href="#1-GO语言数据类型" class="headerlink" title="1. GO语言数据类型"></a>1. GO语言数据类型</h1><blockquote><p>在Go语言中数据类型数据类型用于声明函数和变量。不同数据类型把数据分成所需内存大小，编程时根据数据的不同大小使用不同的数据类型，起到充分利用内存的作用</p></blockquote><span id="more"></span><h2 id="1-1-Go语言的几种数据类型"><a href="#1-1-Go语言的几种数据类型" class="headerlink" title="1.1 Go语言的几种数据类型"></a>1.1 Go语言的几种数据类型</h2><ol><li>布尔型 值可以为常量 true 和 false</li><li>数字类型 Go 语言支持整型和浮点型数字，并且支持复数，其中位的运算采用补码<ul><li>整型<ul><li>uint8 无符号 8 位整型 (0 到 255)</li><li>uint16 无符号 16 位整型 (0 到 65535)</li><li>uint32 无符号 32 位整型 (0 到 4294967295)</li><li>uint64 无符号 64 位整型 (0 到 18446744073709551615)</li><li>int8 有符号 8 位整型 (-128 到 127)</li><li>int16 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)</li><li>int32 有符号 32 位整型 (-2147483648 到 2147483647)</li><li>int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)</li></ul></li><li>浮点型<ul><li>float32 IEEE-754 32位浮点型数</li><li>float64 IEEE-754 64位浮点型数</li><li>complex32 32 位实数和虚数</li><li>complex64 32 位实数和虚数</li></ul></li><li>其他数字类型<ul><li>byte 类似 uint8</li><li>rune 类似 int32</li><li>uint 32 或 64 位</li><li>int 与 uint 一样大小</li><li>uintptr 无符号整型，用于存放一个指针</li></ul></li></ul></li><li>字符串类型 Go的字符串由单个字节连接组成，使用 utf-8 编码 标识 Unicode 文本 string 不包含内存空间，只有一个内存的地址，这样做的好处是string变得非常轻量，可以很方便的进行传递而不用担心内存拷贝。</li></ol><pre><code>    // src/runtime/string.go    type stringStruct struct &#123;        str unsafe.Pointer //指针指向存储实际字符串的内存地址        len int    &#125;</code></pre><ol start="4"><li><p>派生类型</p><ul><li><p>指针类型</p><p>  一个指针变量可以指向任何一个值的内存地址，它所指向的值的内存地址在 32 和 64 位机器上分别占用 4 或 8 个字节，占用字节的大小与所指向的值的大小无关。当一个指针被定义后没有分配到任何变量时，它的默认值为 nil。指针变量通常缩写为 ptr，ptr 的类型为 * T，称做 T 的指针类型，*代表指针。变量、指针和地址三者的关系是，每个变量都拥有地址，指针的值就是地址。  </p><p>  Go 语言的取地址符是 &amp;，放到一个变量前使用就会返回相应变量的内存地址，在指针类型前面加上 * 号（前缀）来获取指针所指向的内容，当一个指针被定义后没有分配到任何变量时，它的值为 nil</p><pre><code>  获取变量地址 &amp;变量  声明指针变量 var 变量 *类型  指针变量赋值 指针变量 = &amp;变量  指针变量取值 *指针变量输出变量 指针输出地址  空指针       输出nil </code></pre></li><li><p>数组类型</p><p>  数组是具有相同唯一类型的一组已编号且长度固定的数据项序列，这种类型可以是任意的原始类型例如整型、字符串或者自定义类型。</p><pre><code>  数组声明 var 变量名称 [长度] 类型  数组初始化 var 变量 = [长度]类型&#123;初始化值*长度&#125; //如果不确定长度可用...代替 [...]  数组元素赋值 变量[下标] = 值  数组元素取值 变量[下标]</code></pre></li><li><p>结构体类型</p><p>  结构体是由一系列具有相同类型或不同类型的数据构成的数据集合，数组中只能存储同一类型的数据，但在结构体中我们可以为不同项定义不同的数据类型。</p><pre><code>  结构体定义 type 结构体名 struct &#123;不同类型数据....&#125;  结构体创建 结构体名&#123;对应的不同数据...&#125;  结构体访问 结构体.结构体数据  结构体做数据类型 var 变量 结构体名称  结构体指针 var 变量 *结构体  结构体指针赋值 (*结构体).结构体数据 = 值 或 结构体.结构体数据 := 值</code></pre></li><li><p>Channel 类型</p><p>  Channel是Go语言中比较重要的部分，经常在Go中的并发中使用。Channel可以看成一个管道，通过它并发核心单元就可以发送或者接收数据进行通讯(communication)。它的操作符是箭头 &lt;- 。</p><pre><code>  创建channel 变量 make(chan 类型,容量)      &gt;  chan   T        // 可以接收和发送类型为 T 的数据      &gt;  chan&lt;- float64  // 只可以用来发送 float64 类型的数据      &gt;  &lt;-chan int      // 只可以用来接收 int 类型的数据</code></pre></li><li><p>函数类型</p><p>  函数是基本的代码块，用于执行一个任务。Go 语言最少有个 main() 函数。可以通过函数来划分不同功能，逻辑上每个函数执行的是指定的任务</p><pre><code>  函数声明 func 函数名称 (传参) 返回参数 &#123; 作用域 &#125;</code></pre></li><li><p>切片类型</p><p>  Go 语言切片是对数组的抽象。Go 数组的长度不可改变，在特定场景中这样的集合就不太适用，Go 中提供了一种灵活，功能强悍的内置类型切片(“动态数组”)，与数组相比切片的长度是不固定的，可以追加元素，在追加时可能使切片的容量增大</p><pre><code>  切片创建  var 变量 []类型  make([]类型, 长度)  切片初始化  变量 := [] int &#123;值&#125;  切片截取  变量 := [开始:结束]  空切片  空切片默认为nil  切片操作  len() 获取切片长度  cap() 获取切片最长长度  append() 向切片添加内容 copy() 赋值切片</code></pre></li><li><p>接口类型</p><p>  所有的具有共性的方法定义在一起，任何其他类型只要实现了这些方法就是实现了这个接口</p><pre><code>  接口定义 type 接口名称 interface &#123; 接口内容 &#125;</code></pre></li><li><p>Map类型</p><p>  Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据，key 类似于索引，指向数据的值。Map 是一种集合，所以我们可以像迭代数组和切片那样迭代它。不过，Map 是无序的，我们无法决定它的返回顺序，这是因为 Map 是使用 hash 表来实现的。</p><pre><code>  创建Map var 变量 map[类型]类型 make(map[类型]类型)</code></pre></li></ul></li></ol>]]></content>
    
    
    <summary type="html">&lt;h1 id=&quot;1-GO语言数据类型&quot;&gt;&lt;a href=&quot;#1-GO语言数据类型&quot; class=&quot;headerlink&quot; title=&quot;1. GO语言数据类型&quot;&gt;&lt;/a&gt;1. GO语言数据类型&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;在Go语言中数据类型数据类型用于声明函数和变量。不同数据类型把数据分成所需内存大小，编程时根据数据的不同大小使用不同的数据类型，起到充分利用内存的作用&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="Golang" scheme="https://blog.agebbs.cn/categories/Golang/"/>
    
    
    <category term="Go" scheme="https://blog.agebbs.cn/tags/Go/"/>
    
  </entry>
  
</feed>
