胖梁的技术笔记
2024-03-17T09:09:45-05:00
http://www.zhaoxiaodan.com
胖梁
liang8305@gmail.com
Fileinputstream关闭在mac和linux上行文不同的问题
2019-11-15T00:00:00-06:00
http://www.zhaoxiaodan.com/java/FileInputStream关闭在mac和linux上行文不同的问题
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">java.io.InputStream</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.concurrent.CountDownLatch</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Test</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">Exception</span> <span class="o">{</span>
<span class="nc">CountDownLatch</span> <span class="n">threadStartLock</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">CountDownLatch</span><span class="o">(</span><span class="mi">1</span><span class="o">);</span>
<span class="nc">CountDownLatch</span> <span class="n">mainLock</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">CountDownLatch</span><span class="o">(</span><span class="mi">1</span><span class="o">);</span>
<span class="nc">InputStream</span> <span class="n">in</span> <span class="o">=</span> <span class="nc">System</span><span class="o">.</span><span class="na">in</span><span class="o">;</span>
<span class="k">new</span> <span class="nf">Thread</span><span class="o">(()</span> <span class="o">-></span> <span class="o">{</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"thread start..."</span><span class="o">);</span>
<span class="n">threadStartLock</span><span class="o">.</span><span class="na">countDown</span><span class="o">();</span>
<span class="k">try</span> <span class="o">{</span>
<span class="kt">byte</span><span class="o">[]</span> <span class="n">buf</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">byte</span><span class="o">[</span><span class="mi">1024</span><span class="o">];</span>
<span class="kt">int</span> <span class="n">len</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
<span class="k">while</span><span class="o">((</span><span class="n">len</span> <span class="o">=</span> <span class="n">in</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="n">buf</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">buf</span><span class="o">.</span><span class="na">length</span><span class="o">))</span> <span class="o">></span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="k">new</span> <span class="nc">String</span><span class="o">(</span><span class="n">buf</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">len</span><span class="o">));</span>
<span class="o">}</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"thread return..."</span> <span class="o">+</span> <span class="n">len</span><span class="o">);</span>
<span class="o">}</span><span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
<span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
<span class="o">}</span><span class="k">finally</span> <span class="o">{</span>
<span class="n">mainLock</span><span class="o">.</span><span class="na">countDown</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">}).</span><span class="na">start</span><span class="o">();</span>
<span class="n">threadStartLock</span><span class="o">.</span><span class="na">await</span><span class="o">();</span>
<span class="nc">Thread</span><span class="o">.</span><span class="na">sleep</span><span class="o">(</span><span class="mi">3</span> <span class="o">*</span> <span class="mi">1000</span><span class="o">);</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"closeing..."</span><span class="o">);</span>
<span class="n">in</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"closed..."</span><span class="o">);</span>
<span class="n">mainLock</span><span class="o">.</span><span class="na">await</span><span class="o">();</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"exit"</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<ul>
<li>InputStream 在一个线程中读, 当无输入时会阻塞在 <code class="language-plaintext highlighter-rouge">read()</code> 方法</li>
<li>在另一个线程中主动close关闭</li>
</ul>
<p>我们一般都会认为, <code class="language-plaintext highlighter-rouge">inputstream.read()</code> 方法的阻塞会中断并返回-1或抛异常; SocketInputStream 也确实就是这样的行为(抛异常)</p>
<p>但是</p>
<p><code class="language-plaintext highlighter-rouge">FileInputStream</code> 在 <code class="language-plaintext highlighter-rouge">mac</code> 和 <code class="language-plaintext highlighter-rouge">linux</code> 上会有不同的行为</p>
<ul>
<li>在mac上, close后, read的阻塞中断且返回-1</li>
<li>在linux上, close后, 如果一直没有读到数据, 一直阻塞, 读到数据, 返回-1</li>
</ul>
<p>也就是说, 在linux上, 如果没有可读数据, read会一直阻塞, close也无法中断</p>
<p>这样的场景下, 正确做法</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">byte</span><span class="o">[]</span> <span class="n">buf</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">byte</span><span class="o">[</span><span class="mi">1024</span><span class="o">];</span>
<span class="kt">int</span> <span class="n">len</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
<span class="k">do</span> <span class="o">{</span>
<span class="k">while</span><span class="o">((</span><span class="n">len</span> <span class="o">=</span> <span class="n">in</span><span class="o">.</span><span class="na">available</span><span class="o">())</span> <span class="o">==</span> <span class="mi">0</span><span class="o">){</span>
<span class="nc">Thread</span><span class="o">.</span><span class="na">sleep</span><span class="o">(</span><span class="mi">50</span><span class="o">);</span>
<span class="o">}</span>
<span class="n">len</span> <span class="o">=</span> <span class="n">in</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="n">buf</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">len</span><span class="o">);</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="k">new</span> <span class="nc">String</span><span class="o">(</span><span class="n">buf</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">len</span><span class="o">));</span>
<span class="o">}</span><span class="k">while</span><span class="o">(</span><span class="n">len</span> <span class="o">></span> <span class="mi">0</span><span class="o">);</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">in.available()</code> EOF和无可读都返回0, 因此无法区分两者; 只能使用在<code class="language-plaintext highlighter-rouge">无限流</code>的场景</p>
Nsq源码阅读(6) 全局唯一messageid
2017-01-16T00:00:00-06:00
http://www.zhaoxiaodan.com/%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB/nsq源码阅读(6)-全局唯一MessageID
<p>客户端发送消息, 进行PUB命令时, nsqd 会使用建立msgid来标识一个msg</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">//客户端推送消息</span>
<span class="k">func</span> <span class="p">(</span><span class="n">p</span> <span class="o">*</span><span class="n">protocolV2</span><span class="p">)</span> <span class="n">PUB</span><span class="p">(</span><span class="n">client</span> <span class="o">*</span><span class="n">clientV2</span><span class="p">,</span> <span class="n">params</span> <span class="p">[][]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
<span class="c">//创建 Message 对象</span>
<span class="n">msg</span> <span class="o">:=</span> <span class="n">NewMessage</span><span class="p">(</span><span class="o"><-</span><span class="n">p</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">nsqd</span><span class="o">.</span><span class="n">idChan</span><span class="p">,</span> <span class="n">messageBody</span><span class="p">)</span>
<span class="c">//topic 发送消息</span>
<span class="n">err</span> <span class="o">=</span> <span class="n">topic</span><span class="o">.</span><span class="n">PutMessage</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>从这看出, MessageID是从一个idChan缓存队列中取出来的; 还是利用chan的思路来减少竞争; idChan的输入端, 也就是id生成器, 是在nsqd启动的时候, 单独起了一个idPump线程进行</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">n</span> <span class="o">*</span><span class="n">NSQD</span><span class="p">)</span> <span class="n">Main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">n</span><span class="o">.</span><span class="n">waitGroup</span><span class="o">.</span><span class="n">Wrap</span><span class="p">(</span><span class="k">func</span><span class="p">()</span> <span class="p">{</span>
<span class="n">n</span><span class="o">.</span><span class="n">idPump</span><span class="p">()</span>
<span class="p">})</span>
<span class="p">}</span>
</code></pre></div></div>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// 一直生产 序列化 id</span>
<span class="k">func</span> <span class="p">(</span><span class="n">n</span> <span class="o">*</span><span class="n">NSQD</span><span class="p">)</span> <span class="n">idPump</span><span class="p">()</span> <span class="p">{</span>
<span class="n">factory</span> <span class="o">:=</span> <span class="o">&</span><span class="n">guidFactory</span><span class="p">{}</span>
<span class="n">lastError</span> <span class="o">:=</span> <span class="n">time</span><span class="o">.</span><span class="n">Unix</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">)</span>
<span class="c">//因为可以做成 nsqd 集群, 所以 msgid 关联 nsq id</span>
<span class="n">workerID</span> <span class="o">:=</span> <span class="n">n</span><span class="o">.</span><span class="n">getOpts</span><span class="p">()</span><span class="o">.</span><span class="n">ID</span>
<span class="k">for</span> <span class="p">{</span>
<span class="n">id</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">factory</span><span class="o">.</span><span class="n">NewGUID</span><span class="p">(</span><span class="n">workerID</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">now</span> <span class="o">:=</span> <span class="n">time</span><span class="o">.</span><span class="n">Now</span><span class="p">()</span>
<span class="k">if</span> <span class="n">now</span><span class="o">.</span><span class="n">Sub</span><span class="p">(</span><span class="n">lastError</span><span class="p">)</span> <span class="o">></span> <span class="n">time</span><span class="o">.</span><span class="n">Second</span> <span class="p">{</span>
<span class="c">// only print the error once/second</span>
<span class="n">n</span><span class="o">.</span><span class="n">logf</span><span class="p">(</span><span class="s">"ERROR: %s"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
<span class="n">lastError</span> <span class="o">=</span> <span class="n">now</span>
<span class="p">}</span>
<span class="n">runtime</span><span class="o">.</span><span class="n">Gosched</span><span class="p">()</span>
<span class="k">continue</span>
<span class="p">}</span>
<span class="c">//这里利用了 golang 的chan 满了阻塞的特性,</span>
<span class="c">//如果n.idChan 这个id缓存池满了, 就阻塞不会继续生产</span>
<span class="k">select</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">n</span><span class="o">.</span><span class="n">idChan</span> <span class="o"><-</span> <span class="n">id</span><span class="o">.</span><span class="n">Hex</span><span class="p">()</span><span class="o">:</span>
<span class="k">case</span> <span class="o"><-</span><span class="n">n</span><span class="o">.</span><span class="n">exitChan</span><span class="o">:</span>
<span class="k">goto</span> <span class="n">exit</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">exit</span><span class="o">:</span>
<span class="n">n</span><span class="o">.</span><span class="n">logf</span><span class="p">(</span><span class="s">"ID: closing"</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">f</span> <span class="o">*</span><span class="n">guidFactory</span><span class="p">)</span> <span class="n">NewGUID</span><span class="p">(</span><span class="n">workerID</span> <span class="kt">int64</span><span class="p">)</span> <span class="p">(</span><span class="n">guid</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
<span class="c">// divide by 1048576, giving pseudo-milliseconds</span>
<span class="n">ts</span> <span class="o">:=</span> <span class="n">time</span><span class="o">.</span><span class="n">Now</span><span class="p">()</span><span class="o">.</span><span class="n">UnixNano</span><span class="p">()</span> <span class="o">>></span> <span class="m">20</span>
<span class="k">if</span> <span class="n">ts</span> <span class="o"><</span> <span class="n">f</span><span class="o">.</span><span class="n">lastTimestamp</span> <span class="p">{</span>
<span class="k">return</span> <span class="m">0</span><span class="p">,</span> <span class="n">ErrTimeBackwards</span>
<span class="p">}</span>
<span class="k">if</span> <span class="n">f</span><span class="o">.</span><span class="n">lastTimestamp</span> <span class="o">==</span> <span class="n">ts</span> <span class="p">{</span>
<span class="n">f</span><span class="o">.</span><span class="n">sequence</span> <span class="o">=</span> <span class="p">(</span><span class="n">f</span><span class="o">.</span><span class="n">sequence</span> <span class="o">+</span> <span class="m">1</span><span class="p">)</span> <span class="o">&</span> <span class="n">sequenceMask</span>
<span class="k">if</span> <span class="n">f</span><span class="o">.</span><span class="n">sequence</span> <span class="o">==</span> <span class="m">0</span> <span class="p">{</span>
<span class="k">return</span> <span class="m">0</span><span class="p">,</span> <span class="n">ErrSequenceExpired</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">f</span><span class="o">.</span><span class="n">sequence</span> <span class="o">=</span> <span class="m">0</span>
<span class="p">}</span>
<span class="n">f</span><span class="o">.</span><span class="n">lastTimestamp</span> <span class="o">=</span> <span class="n">ts</span>
<span class="c">// id = [ 37位ts + ?位 workerId + 12位 sequence ]</span>
<span class="n">id</span> <span class="o">:=</span> <span class="n">guid</span><span class="p">(((</span><span class="n">ts</span> <span class="o">-</span> <span class="n">twepoch</span><span class="p">)</span> <span class="o"><<</span> <span class="n">timestampShift</span><span class="p">)</span> <span class="o">|</span>
<span class="p">(</span><span class="n">workerID</span> <span class="o"><<</span> <span class="n">workerIDShift</span><span class="p">)</span> <span class="o">|</span>
<span class="n">f</span><span class="o">.</span><span class="n">sequence</span><span class="p">)</span>
<span class="k">if</span> <span class="n">id</span> <span class="o"><=</span> <span class="n">f</span><span class="o">.</span><span class="n">lastID</span> <span class="p">{</span>
<span class="k">return</span> <span class="m">0</span><span class="p">,</span> <span class="n">ErrIDBackwards</span>
<span class="p">}</span>
<span class="n">f</span><span class="o">.</span><span class="n">lastID</span> <span class="o">=</span> <span class="n">id</span>
<span class="k">return</span> <span class="n">id</span><span class="p">,</span> <span class="no">nil</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="workerid-的长度">workerid 的长度</h2>
<p>光看这个代码, 就很疑惑, 既然参数 workerID 是 int64类型, 就是64位, 那岂不是在 跟 ts 拼的时候, 当workerid大于某个值的时候, 位数会”溢出”, “侵入” 到 ts 的值中? 那岂不是 特定的两个workerid会在特定的 ts 时得到相同的 guid? 比如:</p>
<table>
<thead>
<tr>
<th>ts</th>
<th style="text-align: right">workid</th>
<th>guid</th>
</tr>
</thead>
<tbody>
<tr>
<td>1110110001110011101101100011011110100</td>
<td style="text-align: right">111111111111000000000000</td>
<td>11101100011100111011011000110111101111111111111000000000000…</td>
</tr>
<tr>
<td>1110110001110011101101100011011110111</td>
<td style="text-align: right">1111111111000000000000</td>
<td>11101100011100111011011000110111101111111111111000000000000…</td>
</tr>
</tbody>
</table>
<p>搜了下issues, <a href="https://github.com/nsqio/nsq/issues/592">#592</a> 就有人提出来, workerid 必须 小于 1024, 也就是10位; 而这个是靠启动nsqd的时候检查配置项实现的</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="n">opts</span><span class="o">.</span><span class="n">ID</span> <span class="o"><</span> <span class="m">0</span> <span class="o">||</span> <span class="n">opts</span><span class="o">.</span><span class="n">ID</span> <span class="o">>=</span> <span class="m">1024</span> <span class="p">{</span>
<span class="n">n</span><span class="o">.</span><span class="n">logf</span><span class="p">(</span><span class="s">"FATAL: --worker-id must be [0,1024)"</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">Exit</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>最开始觉得, 这个从代码角度讲, 很不够处女座, guid 中workID 只有10位, 而参数 workID 是 int64 类型 , 不如干脆减少squence 或者 ts的位数, 让workerID是16位使用 int16;</p>
<p>后来仔细想想这个逻辑, ts 是 nano » 20, 也就是 1 ts = 1048576 nano, 而 1 milliseconds = 1000000 nano; 这就是注释里说的 “giving pseudo-milliseconds” ,ts 近似等于 1毫秒</p>
<p>然后两次生成, 当ts 相同时, 靠 sequence 来区分, sequence 是 12位, 最大值 4096; 那也就是说, 这个生成算法, 同一个workerid, 在1毫秒内的最多能生成 4096个id;</p>
<p>所以, 假如减少 ts的位数或者 sequence 的位数, 让workerid的位数增加, 都会降低生成器的”性能”; 所以这三者的长度是一个权衡</p>
<p>这也是 NewGUID 错误处理中 做一次 runtime.Gosched() 的原因</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">{</span>
<span class="n">id</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">factory</span><span class="o">.</span><span class="n">NewGUID</span><span class="p">(</span><span class="n">workerID</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="c">// 生成的太快了, 一个ts 中 生成了 4096个 id(sequence)</span>
<span class="c">// ts + sequence 满了, 休息一会, 让ts 发生改变</span>
<span class="n">runtime</span><span class="o">.</span><span class="n">Gosched</span><span class="p">()</span>
<span class="k">continue</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
Nsq源码阅读(5) 至少被成功投递一次的实现
2017-01-13T00:00:00-06:00
http://www.zhaoxiaodan.com/%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB/nsq源码阅读(5)-至少被成功投递一次的实现
<h2 id="保证只被成功投递一次">保证只被成功投递一次?</h2>
<p>上次阅读了 Message 投递的整个数据流程(正常情况下的流程); 阅读中, 心中的一个疑问还是没有解开, 就是 “消息如何保证 被 且 只被 成功投递一次?”; 并且粗略阅读过程中, 发现消息被发送给Client之前, 会被塞到另外一个 InFlight 队列中, 更加疑惑, 既然消息已经做过了发送, 为什么还要塞到另外一个队列里?</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">p</span> <span class="o">*</span><span class="n">protocolV2</span><span class="p">)</span> <span class="n">messagePump</span><span class="p">(</span><span class="n">client</span> <span class="o">*</span><span class="n">clientV2</span><span class="p">,</span> <span class="n">startedChan</span> <span class="k">chan</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">{</span>
<span class="k">select</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">msg</span> <span class="o">:=</span> <span class="o"><-</span><span class="n">memoryMsgChan</span><span class="o">:</span>
<span class="c">//TODO 这个是什么作用? 下面不是已经发送了? 怎么又塞到一个队列里?</span>
<span class="n">subChannel</span><span class="o">.</span><span class="n">StartInFlightTimeout</span><span class="p">(</span><span class="n">msg</span><span class="p">,</span> <span class="n">client</span><span class="o">.</span><span class="n">ID</span><span class="p">,</span> <span class="n">msgTimeout</span><span class="p">)</span>
<span class="n">client</span><span class="o">.</span><span class="n">SendingMessage</span><span class="p">()</span>
<span class="n">err</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">SendMessage</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">msg</span><span class="p">,</span> <span class="o">&</span><span class="n">buf</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>查看nsq 的文档, 在 FEATURES & GUARANTEES 一章, 列出了 4个 保证:</p>
<ul>
<li>messages are not durable (by default)</li>
<li>messages are delivered at least once</li>
<li>messages received are un-ordered</li>
<li>consumers eventually find all topic producers</li>
</ul>
<p>好嘛… nsq 并没有保证 “仅一次”, 只保证了 “至少一次”</p>
<h2 id="至少一次的实现">至少一次的实现</h2>
<p>这个函数叫做channle.StartInFlightTimeout(), 根据字面意思理解, <code class="language-plaintext highlighter-rouge">InFlight</code>, 开始”投递了”, <code class="language-plaintext highlighter-rouge">InFlightTimeout</code>投递超时, <code class="language-plaintext highlighter-rouge">StartInFlightTimeout</code>开始一个(防止)投递超时的(动作)</p>
<p>看下函数实现</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">c</span> <span class="o">*</span><span class="n">Channel</span><span class="p">)</span> <span class="n">StartInFlightTimeout</span><span class="p">(</span><span class="n">msg</span> <span class="o">*</span><span class="n">Message</span><span class="p">,</span> <span class="n">clientID</span> <span class="kt">int64</span><span class="p">,</span> <span class="n">timeout</span> <span class="n">time</span><span class="o">.</span><span class="n">Duration</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
<span class="n">now</span> <span class="o">:=</span> <span class="n">time</span><span class="o">.</span><span class="n">Now</span><span class="p">()</span>
<span class="n">msg</span><span class="o">.</span><span class="n">clientID</span> <span class="o">=</span> <span class="n">clientID</span>
<span class="n">msg</span><span class="o">.</span><span class="n">deliveryTS</span> <span class="o">=</span> <span class="n">now</span>
<span class="c">// msg的 优先级是 超时的时间戳</span>
<span class="n">msg</span><span class="o">.</span><span class="n">pri</span> <span class="o">=</span> <span class="n">now</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="n">timeout</span><span class="p">)</span><span class="o">.</span><span class="n">UnixNano</span><span class="p">()</span>
<span class="c">//放到缓存, 按msg.ID, 记录一下, 估计方便之后查找: c.inFlightMessages[msg.ID]</span>
<span class="n">err</span> <span class="o">:=</span> <span class="n">c</span><span class="o">.</span><span class="n">pushInFlightMessage</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">err</span>
<span class="p">}</span>
<span class="c">//放到一个叫 Inflight的队列里</span>
<span class="n">c</span><span class="o">.</span><span class="n">addToInFlightPQ</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>
<span class="k">return</span> <span class="no">nil</span>
<span class="p">}</span>
</code></pre></div></div>
<p>函数里, “至少一次” 如何实现, 并没有很清楚, 只不过又塞到另一个InFlight队列里;这里插入队列, 所以还是查找这个队列在哪里被”读”, 找到在 channel.processInFlightQueue()</p>
<p>这个函数的功能是 把 InFlightQueue 里, 优先级 小于 参数 t 的, 全部重新发送</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">//把 InFlightQueue 里, 优先级 小于 参数 t 的, 全部重新发送</span>
<span class="k">func</span> <span class="p">(</span><span class="n">c</span> <span class="o">*</span><span class="n">Channel</span><span class="p">)</span> <span class="n">processInFlightQueue</span><span class="p">(</span><span class="n">t</span> <span class="kt">int64</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
<span class="c">// 先 检查是否已经 退出</span>
<span class="n">c</span><span class="o">.</span><span class="n">exitMutex</span><span class="o">.</span><span class="n">RLock</span><span class="p">()</span>
<span class="k">defer</span> <span class="n">c</span><span class="o">.</span><span class="n">exitMutex</span><span class="o">.</span><span class="n">RUnlock</span><span class="p">()</span>
<span class="k">if</span> <span class="n">c</span><span class="o">.</span><span class="n">Exiting</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="no">false</span>
<span class="p">}</span>
<span class="n">dirty</span> <span class="o">:=</span> <span class="no">false</span>
<span class="k">for</span> <span class="p">{</span>
<span class="n">c</span><span class="o">.</span><span class="n">inFlightMutex</span><span class="o">.</span><span class="n">Lock</span><span class="p">()</span>
<span class="c">// 如果 栈顶元素的优先级 小于参数</span>
<span class="c">// 弹出 栈顶元素并返回</span>
<span class="n">msg</span><span class="p">,</span> <span class="n">_</span> <span class="o">:=</span> <span class="n">c</span><span class="o">.</span><span class="n">inFlightPQ</span><span class="o">.</span><span class="n">PeekAndShift</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
<span class="n">c</span><span class="o">.</span><span class="n">inFlightMutex</span><span class="o">.</span><span class="n">Unlock</span><span class="p">()</span>
<span class="c">//如果 栈顶 元素的优先级 大于参数, 返回 nil</span>
<span class="k">if</span> <span class="n">msg</span> <span class="o">==</span> <span class="no">nil</span> <span class="p">{</span>
<span class="c">// 没有大于 指定参数优先级的元素, 什么也不做, 返回 deirty = false</span>
<span class="k">goto</span> <span class="n">exit</span>
<span class="p">}</span>
<span class="c">//标记是 "脏" 的</span>
<span class="n">dirty</span> <span class="o">=</span> <span class="no">true</span>
<span class="c">//把之前存起来的 msg 取出来</span>
<span class="c">//TODO: inFlightPQ 里存的不就是msg 么, 怎么又存一次?</span>
<span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">c</span><span class="o">.</span><span class="n">popInFlightMessage</span><span class="p">(</span><span class="n">msg</span><span class="o">.</span><span class="n">clientID</span><span class="p">,</span> <span class="n">msg</span><span class="o">.</span><span class="n">ID</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">goto</span> <span class="n">exit</span>
<span class="p">}</span>
<span class="n">atomic</span><span class="o">.</span><span class="n">AddUint64</span><span class="p">(</span><span class="o">&</span><span class="n">c</span><span class="o">.</span><span class="n">timeoutCount</span><span class="p">,</span> <span class="m">1</span><span class="p">)</span>
<span class="n">c</span><span class="o">.</span><span class="n">RLock</span><span class="p">()</span>
<span class="n">client</span><span class="p">,</span> <span class="n">ok</span> <span class="o">:=</span> <span class="n">c</span><span class="o">.</span><span class="n">clients</span><span class="p">[</span><span class="n">msg</span><span class="o">.</span><span class="n">clientID</span><span class="p">]</span>
<span class="n">c</span><span class="o">.</span><span class="n">RUnlock</span><span class="p">()</span>
<span class="k">if</span> <span class="n">ok</span> <span class="p">{</span>
<span class="c">//找出这个msg 原来是发给哪个client 发的</span>
<span class="c">//通知它这个msg timeout了, nsqd 要重发了</span>
<span class="n">client</span><span class="o">.</span><span class="n">TimedOutMessage</span><span class="p">()</span>
<span class="p">}</span>
<span class="c">// 重发, 重新塞入队列: channel.memoryMsgChan <- m:</span>
<span class="n">c</span><span class="o">.</span><span class="n">doRequeue</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>
<span class="p">}</span> <span class="c">//循环直到队列里 没有满足条件的</span>
<span class="n">exit</span><span class="o">:</span>
<span class="k">return</span> <span class="n">dirty</span>
<span class="p">}</span>
</code></pre></div></div>
<p>看来, 这个消息 “至少被投递一次” 的保证实现的有点粗暴? 直接起个延时, 超时了就重新发送?</p>
<p>再网上追追, processInFlightQueue 的调用在哪里:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// 这里应该是 "生产者/消费者" 模式</span>
<span class="c">// 这个函数是 队列扫描 "消费者", 消费的是 扫描 channel InFlightQueue 和 DeferredQueue 的任务</span>
<span class="k">func</span> <span class="p">(</span><span class="n">n</span> <span class="o">*</span><span class="n">NSQD</span><span class="p">)</span> <span class="n">queueScanWorker</span><span class="p">(</span><span class="n">workCh</span> <span class="k">chan</span> <span class="o">*</span><span class="n">Channel</span><span class="p">,</span> <span class="n">responseCh</span> <span class="k">chan</span> <span class="kt">bool</span><span class="p">,</span> <span class="n">closeCh</span> <span class="k">chan</span> <span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">{</span>
<span class="k">select</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">c</span> <span class="o">:=</span> <span class="o"><-</span><span class="n">workCh</span><span class="o">:</span>
<span class="c">// InFlightQueue 是 container/head 包实现的一个优先级队列, 队列的顶部的优先级最小</span>
<span class="c">// head 是一个 堆数据结构, 优先级队列 是一个 小根堆</span>
<span class="c">// TODO: 堆算法 https://idisfkj.github.io/2016/06/19/%E7%AE%97%E6%B3%95-%E4%B8%83-%E5%A0%86%E6%8E%92%E5%BA%8F/</span>
<span class="c">// 然后将这个 优先级队列做了一个转换, 当做 "超时队列" 来用,</span>
<span class="c">// 具体办法是将 超时时间 作为优先级</span>
<span class="c">// 那么, 队列的顶端的任务的 "优先级最小", 也就是它 应该"最早超时"</span>
<span class="n">now</span> <span class="o">:=</span> <span class="n">time</span><span class="o">.</span><span class="n">Now</span><span class="p">()</span><span class="o">.</span><span class="n">UnixNano</span><span class="p">()</span>
<span class="n">dirty</span> <span class="o">:=</span> <span class="no">false</span>
<span class="k">if</span> <span class="n">c</span><span class="o">.</span><span class="n">processInFlightQueue</span><span class="p">(</span><span class="n">now</span><span class="p">)</span> <span class="p">{</span>
<span class="n">dirty</span> <span class="o">=</span> <span class="no">true</span>
<span class="p">}</span>
<span class="c">// DeferredQueue 也是一个优先级队列</span>
<span class="c">// 然后同样将这个 优先级队列 转换为 延时队列 来使用</span>
<span class="c">// 将 任务"触发(发动)时间" 当做优先级, 放到队列里</span>
<span class="k">if</span> <span class="n">c</span><span class="o">.</span><span class="n">processDeferredQueue</span><span class="p">(</span><span class="n">now</span><span class="p">)</span> <span class="p">{</span>
<span class="n">dirty</span> <span class="o">=</span> <span class="no">true</span>
<span class="p">}</span>
<span class="n">responseCh</span> <span class="o"><-</span> <span class="n">dirty</span>
<span class="c">//注意这个地方, 跟 之前的close(exitChan) 用法不同</span>
<span class="c">//这里是启动多个worker, 然后当判断worker太多了, 需要关闭一个多余的worker时</span>
<span class="c">//给 closeCh <- 1 发个消息, 利用golang chan 随机分发的特性</span>
<span class="c">//这样就会随机的关闭掉一个 worker, 也就是随机退出一个 queueScanWorker 的 循环</span>
<span class="k">case</span> <span class="o"><-</span><span class="n">closeCh</span><span class="o">:</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>把 InFlightQueue 的 写入和读取结合起来看:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">//写入</span>
<span class="n">msg</span><span class="o">.</span><span class="n">pri</span> <span class="o">=</span> <span class="n">now</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="n">timeout</span><span class="p">)</span><span class="o">.</span><span class="n">UnixNano</span><span class="p">()</span> <span class="c">// now + timetou 作为 优先级</span>
<span class="n">c</span><span class="o">.</span><span class="n">addToInFlightPQ</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>
<span class="c">//读取</span>
<span class="n">now</span> <span class="o">:=</span> <span class="n">time</span><span class="o">.</span><span class="n">Now</span><span class="p">()</span><span class="o">.</span><span class="n">UnixNano</span><span class="p">()</span> <span class="c">//跟 当前实现比较, 也就是比较是否超时</span>
<span class="n">dirty</span> <span class="o">:=</span> <span class="no">false</span>
<span class="k">if</span> <span class="n">c</span><span class="o">.</span><span class="n">processInFlightQueue</span><span class="p">(</span><span class="n">now</span><span class="p">)</span> <span class="p">{</span>
<span class="n">dirty</span> <span class="o">=</span> <span class="no">true</span>
<span class="p">}</span>
</code></pre></div></div>
<p>现在重新看一下 processInFlightQueue(), 就明白 这个函数的 整个作用是: 把 InFlightQueue 里, 超时的 msg 全部重新发送出去</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">//把 InFlightQueue 里, 超时的 msg 全部重新发送出去</span>
<span class="k">func</span> <span class="p">(</span><span class="n">c</span> <span class="o">*</span><span class="n">Channel</span><span class="p">)</span> <span class="n">processInFlightQueue</span><span class="p">(</span><span class="n">t</span> <span class="kt">int64</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
<span class="n">dirty</span> <span class="o">:=</span> <span class="no">false</span>
<span class="k">for</span> <span class="p">{</span>
<span class="c">// t 是当前时间 now, 假如 inFlightPQ 顶端的优先级(也就是超时时间) 小于 now</span>
<span class="c">// 那返回的msg 就是 "超时" 了</span>
<span class="n">msg</span><span class="p">,</span> <span class="n">_</span> <span class="o">:=</span> <span class="n">c</span><span class="o">.</span><span class="n">inFlightPQ</span><span class="o">.</span><span class="n">PeekAndShift</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
<span class="c">//标记是 "脏" 的</span>
<span class="n">dirty</span> <span class="o">=</span> <span class="no">true</span>
<span class="c">// 重发, 重新塞入队列: channel.memoryMsgChan <- m:</span>
<span class="n">c</span><span class="o">.</span><span class="n">doRequeue</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="queuescanworker">queueScanWorker</h2>
<p>现在已经知道了, “至少投递一次” 这个保证是由 queueScanWorker 不断的扫描 InFlightQueue 实现的, 之前猜测 queueScanWorker 用了 “生产者/消费者模式”, 所以再来看下 queueScanWorker 的启动</p>
<p>nsqd 启动的时候就 起了一个 queueScanLoop 线程, 间隔性的派发 scan 任务, 并适时调整 worker 的数量</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">func</span> <span class="p">(</span><span class="n">n</span> <span class="o">*</span><span class="n">NSQD</span><span class="p">)</span> <span class="n">Main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">n</span><span class="o">.</span><span class="n">waitGroup</span><span class="o">.</span><span class="n">Wrap</span><span class="p">(</span><span class="k">func</span><span class="p">()</span> <span class="p">{</span>
<span class="n">n</span><span class="o">.</span><span class="n">queueScanLoop</span><span class="p">()</span>
<span class="p">})</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">n</span> <span class="o">*</span><span class="n">NSQD</span><span class="p">)</span> <span class="n">queueScanLoop</span><span class="p">()</span> <span class="p">{</span>
<span class="c">//任务派发 队列</span>
<span class="n">workCh</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="k">chan</span> <span class="o">*</span><span class="n">Channel</span><span class="p">,</span> <span class="n">n</span><span class="o">.</span><span class="n">getOpts</span><span class="p">()</span><span class="o">.</span><span class="n">QueueScanSelectionCount</span><span class="p">)</span>
<span class="c">//任务结果 队列</span>
<span class="n">responseCh</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="k">chan</span> <span class="kt">bool</span><span class="p">,</span> <span class="n">n</span><span class="o">.</span><span class="n">getOpts</span><span class="p">()</span><span class="o">.</span><span class="n">QueueScanSelectionCount</span><span class="p">)</span>
<span class="c">// 用来优雅关闭</span>
<span class="n">closeCh</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="k">chan</span> <span class="kt">int</span><span class="p">)</span>
<span class="n">workTicker</span> <span class="o">:=</span> <span class="n">time</span><span class="o">.</span><span class="n">NewTicker</span><span class="p">(</span><span class="n">n</span><span class="o">.</span><span class="n">getOpts</span><span class="p">()</span><span class="o">.</span><span class="n">QueueScanInterval</span><span class="p">)</span>
<span class="n">refreshTicker</span> <span class="o">:=</span> <span class="n">time</span><span class="o">.</span><span class="n">NewTicker</span><span class="p">(</span><span class="n">n</span><span class="o">.</span><span class="n">getOpts</span><span class="p">()</span><span class="o">.</span><span class="n">QueueScanRefreshInterval</span><span class="p">)</span>
<span class="n">channels</span> <span class="o">:=</span> <span class="n">n</span><span class="o">.</span><span class="n">channels</span><span class="p">()</span>
<span class="c">// 确切一点, 这里应该叫 resizeWorkerPool</span>
<span class="c">// 就是调整 queue scan 任务的 worker 的数量</span>
<span class="c">// 当然, 一开始就是启动一些 worker</span>
<span class="n">n</span><span class="o">.</span><span class="n">resizePool</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">channels</span><span class="p">),</span> <span class="n">workCh</span><span class="p">,</span> <span class="n">responseCh</span><span class="p">,</span> <span class="n">closeCh</span><span class="p">)</span>
<span class="k">for</span> <span class="p">{</span>
<span class="k">select</span> <span class="p">{</span>
<span class="k">case</span> <span class="o"><-</span><span class="n">workTicker</span><span class="o">.</span><span class="n">C</span><span class="o">:</span> <span class="c">// 开始一次任务的派发</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">channels</span><span class="p">)</span> <span class="o">==</span> <span class="m">0</span> <span class="p">{</span>
<span class="k">continue</span>
<span class="p">}</span>
<span class="k">case</span> <span class="o"><-</span><span class="n">refreshTicker</span><span class="o">.</span><span class="n">C</span><span class="o">:</span> <span class="c">// 重新调整 worker 数量</span>
<span class="n">channels</span> <span class="o">=</span> <span class="n">n</span><span class="o">.</span><span class="n">channels</span><span class="p">()</span>
<span class="n">n</span><span class="o">.</span><span class="n">resizePool</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">channels</span><span class="p">),</span> <span class="n">workCh</span><span class="p">,</span> <span class="n">responseCh</span><span class="p">,</span> <span class="n">closeCh</span><span class="p">)</span>
<span class="k">continue</span>
<span class="k">case</span> <span class="o"><-</span><span class="n">n</span><span class="o">.</span><span class="n">exitChan</span><span class="o">:</span> <span class="c">// 退出</span>
<span class="k">goto</span> <span class="n">exit</span>
<span class="p">}</span>
<span class="n">num</span> <span class="o">:=</span> <span class="n">n</span><span class="o">.</span><span class="n">getOpts</span><span class="p">()</span><span class="o">.</span><span class="n">QueueScanSelectionCount</span>
<span class="k">if</span> <span class="n">num</span> <span class="o">></span> <span class="nb">len</span><span class="p">(</span><span class="n">channels</span><span class="p">)</span> <span class="p">{</span>
<span class="n">num</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">channels</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">loop</span><span class="o">:</span>
<span class="c">// 随机取出几个 channel, 派发给 worker 进行 扫描</span>
<span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">i</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">util</span><span class="o">.</span><span class="n">UniqRands</span><span class="p">(</span><span class="n">num</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">channels</span><span class="p">))</span> <span class="p">{</span>
<span class="n">workCh</span> <span class="o"><-</span> <span class="n">channels</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="p">}</span>
<span class="c">// 接收 扫描结果, 统一 有多少 channel 是 "脏" 的</span>
<span class="n">numDirty</span> <span class="o">:=</span> <span class="m">0</span>
<span class="k">for</span> <span class="n">i</span> <span class="o">:=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">num</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span> <span class="p">{</span>
<span class="k">if</span> <span class="o"><-</span><span class="n">responseCh</span> <span class="p">{</span>
<span class="n">numDirty</span><span class="o">++</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c">// 假如 "脏" 的 "比例" 大于阀值, 则不等待 workTicker</span>
<span class="c">// 马上进行下一轮 扫描</span>
<span class="k">if</span> <span class="kt">float64</span><span class="p">(</span><span class="n">numDirty</span><span class="p">)</span> <span class="o">/</span> <span class="kt">float64</span><span class="p">(</span><span class="n">num</span><span class="p">)</span> <span class="o">></span> <span class="n">n</span><span class="o">.</span><span class="n">getOpts</span><span class="p">()</span><span class="o">.</span><span class="n">QueueScanDirtyPercent</span> <span class="p">{</span>
<span class="k">goto</span> <span class="n">loop</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">exit</span><span class="o">:</span>
<span class="n">n</span><span class="o">.</span><span class="n">logf</span><span class="p">(</span><span class="s">"QUEUESCAN: closing"</span><span class="p">)</span>
<span class="nb">close</span><span class="p">(</span><span class="n">closeCh</span><span class="p">)</span>
<span class="n">workTicker</span><span class="o">.</span><span class="n">Stop</span><span class="p">()</span>
<span class="n">refreshTicker</span><span class="o">.</span><span class="n">Stop</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">n</span> <span class="o">*</span><span class="n">NSQD</span><span class="p">)</span> <span class="n">resizePool</span><span class="p">(</span><span class="n">num</span> <span class="kt">int</span><span class="p">,</span> <span class="n">workCh</span> <span class="k">chan</span> <span class="o">*</span><span class="n">Channel</span><span class="p">,</span> <span class="n">responseCh</span> <span class="k">chan</span> <span class="kt">bool</span><span class="p">,</span> <span class="n">closeCh</span> <span class="k">chan</span> <span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
<span class="c">// 设置 queueScanWorker 的 数量 为 当前 nsqd 所有channel 个数的 1/4</span>
<span class="n">idealPoolSize</span> <span class="o">:=</span> <span class="kt">int</span><span class="p">(</span><span class="kt">float64</span><span class="p">(</span><span class="n">num</span><span class="p">)</span> <span class="o">*</span> <span class="m">0.25</span><span class="p">)</span>
<span class="k">if</span> <span class="n">idealPoolSize</span> <span class="o"><</span> <span class="m">1</span> <span class="p">{</span>
<span class="n">idealPoolSize</span> <span class="o">=</span> <span class="m">1</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="n">idealPoolSize</span> <span class="o">></span> <span class="n">n</span><span class="o">.</span><span class="n">getOpts</span><span class="p">()</span><span class="o">.</span><span class="n">QueueScanWorkerPoolMax</span> <span class="p">{</span>
<span class="n">idealPoolSize</span> <span class="o">=</span> <span class="n">n</span><span class="o">.</span><span class="n">getOpts</span><span class="p">()</span><span class="o">.</span><span class="n">QueueScanWorkerPoolMax</span>
<span class="p">}</span>
<span class="k">for</span> <span class="p">{</span>
<span class="c">//queueScanWorker 多了就减少一个, 少了就增加一个</span>
<span class="c">//一直循环, 直到 queueScanWorker 的数量满足要求</span>
<span class="k">if</span> <span class="n">idealPoolSize</span> <span class="o">==</span> <span class="n">n</span><span class="o">.</span><span class="n">poolSize</span> <span class="p">{</span>
<span class="k">break</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="n">idealPoolSize</span> <span class="o"><</span> <span class="n">n</span><span class="o">.</span><span class="n">poolSize</span> <span class="p">{</span>
<span class="c">// queueScanWorker 多了, 减少一个</span>
<span class="c">// 利用 chan 的特性, 向closeCh 推一个消息, 这样 所有的 worCh 就会随机有一个收到这个消息, 然后关闭</span>
<span class="c">// 细节: 这里跟 exitCh 的用法不同, exitCh 是要告知 "所有的" looper 退出, 所以使用的是 close(exitCh) 的用法</span>
<span class="c">// 而如果想 让其中 一个 退出, 则使用 exitCh <- 1 的用法</span>
<span class="n">closeCh</span> <span class="o"><-</span> <span class="m">1</span>
<span class="n">n</span><span class="o">.</span><span class="n">poolSize</span><span class="o">--</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="c">// queueScanWorker 少了, 增加一个</span>
<span class="n">n</span><span class="o">.</span><span class="n">waitGroup</span><span class="o">.</span><span class="n">Wrap</span><span class="p">(</span><span class="k">func</span><span class="p">()</span> <span class="p">{</span>
<span class="n">n</span><span class="o">.</span><span class="n">queueScanWorker</span><span class="p">(</span><span class="n">workCh</span><span class="p">,</span> <span class="n">responseCh</span><span class="p">,</span> <span class="n">closeCh</span><span class="p">)</span>
<span class="p">})</span>
<span class="n">n</span><span class="o">.</span><span class="n">poolSize</span><span class="o">++</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="message-的完成">Message 的”完成”</h2>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">p</span> <span class="o">*</span><span class="n">protocolV2</span><span class="p">)</span> <span class="n">Exec</span><span class="p">(</span><span class="n">client</span> <span class="o">*</span><span class="n">clientV2</span><span class="p">,</span> <span class="n">params</span> <span class="p">[][]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
<span class="k">switch</span> <span class="p">{</span>
<span class="c">// 当一个客户端成功接收到msg 并处理完成, 按照协议会向nsqd 发送一个 "FIN" 命令通知nsqd, 这时候nsqd 会将这条msg 从InFlight队列中删除</span>
<span class="k">case</span> <span class="n">bytes</span><span class="o">.</span><span class="n">Equal</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="m">0</span><span class="p">],</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">(</span><span class="s">"FIN"</span><span class="p">))</span><span class="o">:</span>
<span class="k">return</span> <span class="n">p</span><span class="o">.</span><span class="n">FIN</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">p</span> <span class="o">*</span><span class="n">protocolV2</span><span class="p">)</span> <span class="n">FIN</span><span class="p">(</span><span class="n">client</span> <span class="o">*</span><span class="n">clientV2</span><span class="p">,</span> <span class="n">params</span> <span class="p">[][]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
<span class="n">id</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">getMessageID</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="m">1</span><span class="p">])</span>
<span class="n">err</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">Channel</span><span class="o">.</span><span class="n">FinishMessage</span><span class="p">(</span><span class="n">client</span><span class="o">.</span><span class="n">ID</span><span class="p">,</span> <span class="o">*</span><span class="n">id</span><span class="p">)</span>
<span class="c">// 做些 client 计数</span>
<span class="n">client</span><span class="o">.</span><span class="n">FinishedMessage</span><span class="p">()</span>
<span class="k">return</span> <span class="no">nil</span><span class="p">,</span> <span class="no">nil</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">c</span> <span class="o">*</span><span class="n">Channel</span><span class="p">)</span> <span class="n">FinishMessage</span><span class="p">(</span><span class="n">clientID</span> <span class="kt">int64</span><span class="p">,</span> <span class="n">id</span> <span class="n">MessageID</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
<span class="c">//删除 inFlightMessages 缓存</span>
<span class="n">msg</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">c</span><span class="o">.</span><span class="n">popInFlightMessage</span><span class="p">(</span><span class="n">clientID</span><span class="p">,</span> <span class="n">id</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">err</span>
<span class="p">}</span>
<span class="c">// 从超时队列中删除</span>
<span class="n">c</span><span class="o">.</span><span class="n">removeFromInFlightPQ</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>
<span class="k">if</span> <span class="n">c</span><span class="o">.</span><span class="n">e2eProcessingLatencyStream</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">c</span><span class="o">.</span><span class="n">e2eProcessingLatencyStream</span><span class="o">.</span><span class="n">Insert</span><span class="p">(</span><span class="n">msg</span><span class="o">.</span><span class="n">Timestamp</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">return</span> <span class="no">nil</span>
<span class="p">}</span>
</code></pre></div></div>
<p>nsqd 收到客户端发来的”FIN” 只会, 就会从超时队列中删除这条msg; 这个时候, 一条msg 在nsqd 中的流转完成</p>
<h2 id="细节1-减少类型推断-提高性能">细节1: 减少类型推断, 提高性能</h2>
<p>InFlightQueue 和 DeferredQueue 为什么要实现两次? DeferredQueue 用 head 包实现, InFlightQueue 自己又实现了一次heap, 其实跟 DeferredQueue 不是一样的么?</p>
<p>之前两个就真是是一样的, 后来有一个提交,里面的注释是: this eliminates the use of container/heap and the associated cost of boxing and interface type assertions.</p>
<blockquote>
<p>https://github.com/nsqio/nsq/commit/74bfde101934700cb0cd980d01b6dfe2fe5a6a53</p>
</blockquote>
<p>意思就是说, 这些 队列里 存的是 Message 这个类型, 如果使用 heap, 需要存到 heap.Item 的 Value 里,而这个value 是一个 interface{} , 赋值 和 取值 都需要做类型推断 和 包装,那么作为 InFlightQueue 这个 “高负荷” 的队列, 减少这种 “类型推断和包装” , 有利于提高性能</p>
<p>写个test试一下</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="n">Item</span> <span class="k">struct</span> <span class="p">{</span>
<span class="n">d1</span> <span class="kt">int</span>
<span class="n">d2</span> <span class="kt">int</span>
<span class="p">}</span>
<span class="k">func</span> <span class="n">BenchmarkT1</span><span class="p">(</span><span class="n">b</span> <span class="o">*</span><span class="n">testing</span><span class="o">.</span><span class="n">B</span><span class="p">)</span> <span class="p">{</span>
<span class="n">q</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="o">*</span><span class="n">Item</span><span class="p">,</span> <span class="m">0</span><span class="p">)</span> <span class="c">// 不需要类型推断的 slice</span>
<span class="k">for</span> <span class="n">i</span> <span class="o">:=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">b</span><span class="o">.</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span> <span class="p">{</span>
<span class="n">q</span> <span class="o">=</span> <span class="nb">append</span><span class="p">(</span><span class="n">q</span><span class="p">,</span> <span class="o">&</span><span class="n">Item</span><span class="p">{</span><span class="n">i</span><span class="p">,</span> <span class="n">i</span><span class="p">})</span>
<span class="p">}</span>
<span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">hero</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">q</span> <span class="p">{</span>
<span class="n">hero</span><span class="o">.</span><span class="n">d1</span><span class="o">++</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">func</span> <span class="n">BenchmarkT2</span><span class="p">(</span><span class="n">b</span> <span class="o">*</span><span class="n">testing</span><span class="o">.</span><span class="n">B</span><span class="p">)</span> <span class="p">{</span>
<span class="n">q</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="k">interface</span><span class="p">{},</span> <span class="m">0</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="o">:=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">b</span><span class="o">.</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span> <span class="p">{</span>
<span class="n">q</span> <span class="o">=</span> <span class="nb">append</span><span class="p">(</span><span class="n">q</span><span class="p">,</span> <span class="o">&</span><span class="n">Item</span><span class="p">{</span><span class="n">i</span><span class="p">,</span> <span class="n">i</span><span class="p">})</span>
<span class="p">}</span>
<span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">hero</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">q</span> <span class="p">{</span>
<span class="n">hero</span><span class="o">.</span><span class="p">(</span><span class="o">*</span><span class="n">Item</span><span class="p">)</span><span class="o">.</span><span class="n">d1</span><span class="o">++</span> <span class="c">// 需要做类型推断</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>结果还是很明显, 做类型推断的更耗时, 并且有50%的多余负载:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>BenchmarkT1-8 10000000 241 ns/op
BenchmarkT2-8 5000000 332 ns/op
</code></pre></div></div>
Nsq源码阅读(4) Message的传递路劲
2017-01-13T00:00:00-06:00
http://www.zhaoxiaodan.com/%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB/nsq源码阅读(4)-Message的传递路劲
<p>上文阅读了nsq是如何接收client连接, 读取请求消息, 执行并返回结果响应的流程. 看到具体的执行命令代码, 其中有一个是”PUB”命令, 也就是消息推送, 现在就来看一下, 一个消息从 被一个client 做PUB发送, 到订阅了消息的Client接收, 在nsqd里是如何流转的</p>
<h2 id="1-客户端-层面-的-pub-消息">1. 客户端 层面 的 PUB 消息</h2>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">p</span> <span class="o">*</span><span class="n">protocolV2</span><span class="p">)</span> <span class="n">Exec</span><span class="p">(</span><span class="n">client</span> <span class="o">*</span><span class="n">clientV2</span><span class="p">,</span> <span class="n">params</span> <span class="p">[][]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
<span class="k">switch</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">bytes</span><span class="o">.</span><span class="n">Equal</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="m">0</span><span class="p">],</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">(</span><span class="s">"PUB"</span><span class="p">))</span><span class="o">:</span>
<span class="k">return</span> <span class="n">p</span><span class="o">.</span><span class="n">PUB</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>看 PUB方法的具体实现:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">//客户端推送消息</span>
<span class="k">func</span> <span class="p">(</span><span class="n">p</span> <span class="o">*</span><span class="n">protocolV2</span><span class="p">)</span> <span class="n">PUB</span><span class="p">(</span><span class="n">client</span> <span class="o">*</span><span class="n">clientV2</span><span class="p">,</span> <span class="n">params</span> <span class="p">[][]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
<span class="k">var</span> <span class="n">err</span> <span class="kt">error</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">params</span><span class="p">)</span> <span class="o"><</span> <span class="m">2</span> <span class="p">{</span>
<span class="k">return</span> <span class="no">nil</span><span class="p">,</span> <span class="n">protocol</span><span class="o">.</span><span class="n">NewFatalClientErr</span><span class="p">(</span><span class="no">nil</span><span class="p">,</span> <span class="s">"E_INVALID"</span><span class="p">,</span> <span class="s">"PUB insufficient number of parameters"</span><span class="p">)</span>
<span class="p">}</span>
<span class="c">//请求格式: [ PUB topicName ...]</span>
<span class="n">topicName</span> <span class="o">:=</span> <span class="kt">string</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="m">1</span><span class="p">])</span>
<span class="k">if</span> <span class="o">!</span><span class="n">protocol</span><span class="o">.</span><span class="n">IsValidTopicName</span><span class="p">(</span><span class="n">topicName</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="no">nil</span><span class="p">,</span> <span class="n">protocol</span><span class="o">.</span><span class="n">NewFatalClientErr</span><span class="p">(</span><span class="no">nil</span><span class="p">,</span> <span class="s">"E_BAD_TOPIC"</span><span class="p">,</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"PUB topic name %q is not valid"</span><span class="p">,</span> <span class="n">topicName</span><span class="p">))</span>
<span class="p">}</span>
<span class="c">//消息体, 在client 请求的下一行开始, 头4个字节是消息体长度</span>
<span class="n">bodyLen</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">readLen</span><span class="p">(</span><span class="n">client</span><span class="o">.</span><span class="n">Reader</span><span class="p">,</span> <span class="n">client</span><span class="o">.</span><span class="n">lenSlice</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">return</span> <span class="no">nil</span><span class="p">,</span> <span class="n">protocol</span><span class="o">.</span><span class="n">NewFatalClientErr</span><span class="p">(</span><span class="n">err</span><span class="p">,</span> <span class="s">"E_BAD_MESSAGE"</span><span class="p">,</span> <span class="s">"PUB failed to read message body size"</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">if</span> <span class="n">bodyLen</span> <span class="o"><=</span> <span class="m">0</span> <span class="p">{</span>
<span class="k">return</span> <span class="no">nil</span><span class="p">,</span> <span class="n">protocol</span><span class="o">.</span><span class="n">NewFatalClientErr</span><span class="p">(</span><span class="no">nil</span><span class="p">,</span> <span class="s">"E_BAD_MESSAGE"</span><span class="p">,</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"PUB invalid message body size %d"</span><span class="p">,</span> <span class="n">bodyLen</span><span class="p">))</span>
<span class="p">}</span>
<span class="c">//长度是否过大</span>
<span class="k">if</span> <span class="kt">int64</span><span class="p">(</span><span class="n">bodyLen</span><span class="p">)</span> <span class="o">></span> <span class="n">p</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">nsqd</span><span class="o">.</span><span class="n">getOpts</span><span class="p">()</span><span class="o">.</span><span class="n">MaxMsgSize</span> <span class="p">{</span>
<span class="k">return</span> <span class="no">nil</span><span class="p">,</span> <span class="n">protocol</span><span class="o">.</span><span class="n">NewFatalClientErr</span><span class="p">(</span><span class="no">nil</span><span class="p">,</span> <span class="s">"E_BAD_MESSAGE"</span><span class="p">,</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"PUB message too big %d > %d"</span><span class="p">,</span> <span class="n">bodyLen</span><span class="p">,</span> <span class="n">p</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">nsqd</span><span class="o">.</span><span class="n">getOpts</span><span class="p">()</span><span class="o">.</span><span class="n">MaxMsgSize</span><span class="p">))</span>
<span class="p">}</span>
<span class="c">//简历缓冲区并读入</span>
<span class="n">messageBody</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="n">bodyLen</span><span class="p">)</span>
<span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">io</span><span class="o">.</span><span class="n">ReadFull</span><span class="p">(</span><span class="n">client</span><span class="o">.</span><span class="n">Reader</span><span class="p">,</span> <span class="n">messageBody</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">return</span> <span class="no">nil</span><span class="p">,</span> <span class="n">protocol</span><span class="o">.</span><span class="n">NewFatalClientErr</span><span class="p">(</span><span class="n">err</span><span class="p">,</span> <span class="s">"E_BAD_MESSAGE"</span><span class="p">,</span> <span class="s">"PUB failed to read message body"</span><span class="p">)</span>
<span class="p">}</span>
<span class="c">//client 是否有权限对这个 topic 做 PUB 操作</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">p</span><span class="o">.</span><span class="n">CheckAuth</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="s">"PUB"</span><span class="p">,</span> <span class="n">topicName</span><span class="p">,</span> <span class="s">""</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">return</span> <span class="no">nil</span><span class="p">,</span> <span class="n">err</span>
<span class="p">}</span>
<span class="n">topic</span> <span class="o">:=</span> <span class="n">p</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">nsqd</span><span class="o">.</span><span class="n">GetTopic</span><span class="p">(</span><span class="n">topicName</span><span class="p">)</span>
<span class="c">//创建 Message 对象, TODO: nsqd 的 唯一id 发生器</span>
<span class="n">msg</span> <span class="o">:=</span> <span class="n">NewMessage</span><span class="p">(</span><span class="o"><-</span><span class="n">p</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">nsqd</span><span class="o">.</span><span class="n">idChan</span><span class="p">,</span> <span class="n">messageBody</span><span class="p">)</span>
<span class="c">//topic 发送消息</span>
<span class="n">err</span> <span class="o">=</span> <span class="n">topic</span><span class="o">.</span><span class="n">PutMessage</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">return</span> <span class="no">nil</span><span class="p">,</span> <span class="n">protocol</span><span class="o">.</span><span class="n">NewFatalClientErr</span><span class="p">(</span><span class="n">err</span><span class="p">,</span> <span class="s">"E_PUB_FAILED"</span><span class="p">,</span> <span class="s">"PUB failed "</span><span class="o">+</span><span class="n">err</span><span class="o">.</span><span class="n">Error</span><span class="p">())</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">okBytes</span><span class="p">,</span> <span class="no">nil</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="2-topic-层的-putmessage">2. Topic 层的 PutMessage</h2>
<p>PUB 方法做一系列检查, 然后调用 topic.PutMessage(msg) 做具体的发送</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">t</span> <span class="o">*</span><span class="n">Topic</span><span class="p">)</span> <span class="n">PutMessage</span><span class="p">(</span><span class="n">m</span> <span class="o">*</span><span class="n">Message</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
<span class="c">//TODO 这里是个 ReadLock, 应该是为了锁住 exitFlag ?</span>
<span class="n">t</span><span class="o">.</span><span class="n">RLock</span><span class="p">()</span>
<span class="k">defer</span> <span class="n">t</span><span class="o">.</span><span class="n">RUnlock</span><span class="p">()</span>
<span class="c">//这里使用了一个 atomic Int32 类型的 exitFlag 退出标志, 如果已经退出了就不在 put 了</span>
<span class="k">if</span> <span class="n">atomic</span><span class="o">.</span><span class="n">LoadInt32</span><span class="p">(</span><span class="o">&</span><span class="n">t</span><span class="o">.</span><span class="n">exitFlag</span><span class="p">)</span> <span class="o">==</span> <span class="m">1</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">errors</span><span class="o">.</span><span class="n">New</span><span class="p">(</span><span class="s">"exiting"</span><span class="p">)</span>
<span class="p">}</span>
<span class="c">//发送</span>
<span class="n">err</span> <span class="o">:=</span> <span class="n">t</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">m</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">err</span>
<span class="p">}</span>
<span class="c">//做个计数</span>
<span class="n">atomic</span><span class="o">.</span><span class="n">AddUint64</span><span class="p">(</span><span class="o">&</span><span class="n">t</span><span class="o">.</span><span class="n">messageCount</span><span class="p">,</span> <span class="m">1</span><span class="p">)</span>
<span class="k">return</span> <span class="no">nil</span>
<span class="p">}</span>
</code></pre></div></div>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">t</span> <span class="o">*</span><span class="n">Topic</span><span class="p">)</span> <span class="n">put</span><span class="p">(</span><span class="n">m</span> <span class="o">*</span><span class="n">Message</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
<span class="c">// 这里巧妙利用了 chan 的特性</span>
<span class="c">// 先写入 memoryMsgChan 这个队列,假如 memoryMsgChan 已满, 不可写入</span>
<span class="c">// golang 就会执行 default 语句,</span>
<span class="k">select</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">t</span><span class="o">.</span><span class="n">memoryMsgChan</span> <span class="o"><-</span> <span class="n">m</span><span class="o">:</span> <span class="c">//'变量' 不都是内存? 这个 '内存', 是相对于 下面这个 DiskQueue 来讲的)</span>
<span class="k">default</span><span class="o">:</span>
<span class="c">// 利用 sync.Pool 包 做了一个 bytes.Buffer 的缓存池, 从池中取出一个来用</span>
<span class="c">// <<go语言的官方包sync.Pool的实现原理和适用场景>> :</span>
<span class="c">// http://blog.csdn.net/yongjian_lian/article/details/42058893</span>
<span class="n">b</span> <span class="o">:=</span> <span class="n">bufferPoolGet</span><span class="p">()</span>
<span class="c">// 这个 t.backend, 在NewTopic 函数中初始化, 它具体是一个 DiskQueue</span>
<span class="c">//TODO 先不去管他的具体实现, 暂时认识它是把消息 写入硬盘 就好</span>
<span class="c">//t.backend = newDiskQueue(topicName,</span>
<span class="c">// ctx.nsqd.getOpts().DataPath,</span>
<span class="c">// ctx.nsqd.getOpts().MaxBytesPerFile,</span>
<span class="c">// int32(minValidMsgLength),</span>
<span class="c">// int32(ctx.nsqd.getOpts().MaxMsgSize)+minValidMsgLength,</span>
<span class="c">// ctx.nsqd.getOpts().SyncEvery,</span>
<span class="c">// ctx.nsqd.getOpts().SyncTimeout,</span>
<span class="c">// ctx.nsqd.getOpts().Logger)</span>
<span class="n">err</span> <span class="o">:=</span> <span class="n">writeMessageToBackend</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="n">m</span><span class="p">,</span> <span class="n">t</span><span class="o">.</span><span class="n">backend</span><span class="p">)</span>
<span class="c">// 放回缓存池</span>
<span class="n">bufferPoolPut</span><span class="p">(</span><span class="n">b</span><span class="p">)</span>
<span class="n">t</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">nsqd</span><span class="o">.</span><span class="n">SetHealth</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">t</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">nsqd</span><span class="o">.</span><span class="n">logf</span><span class="p">(</span>
<span class="s">"TOPIC(%s) ERROR: failed to write message to backend - %s"</span><span class="p">,</span>
<span class="n">t</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
<span class="k">return</span> <span class="n">err</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="no">nil</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Message 的数据流看代码, 到这个地方断掉了; 不过既然使用了 chan , 很明显 chan的 推和拉不在一个线程中</p>
<p>用IDE 对 t.memoryMsgChan 做 ‘Find Usages’ 操作, 很快定位到了 ‘<-memoryMsgChan’ 操作在 Topic 类的 messagePump 函数中</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// 每个 topic 自己有一个 messagePump 消息投递 loop</span>
<span class="k">func</span> <span class="p">(</span><span class="n">t</span> <span class="o">*</span><span class="n">Topic</span><span class="p">)</span> <span class="n">messagePump</span><span class="p">()</span> <span class="p">{</span>
<span class="c">//避免 锁竞争, 所以缓存 已存在的 channel</span>
<span class="n">t</span><span class="o">.</span><span class="n">RLock</span><span class="p">()</span>
<span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">c</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">t</span><span class="o">.</span><span class="n">channelMap</span> <span class="p">{</span>
<span class="n">chans</span> <span class="o">=</span> <span class="nb">append</span><span class="p">(</span><span class="n">chans</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">t</span><span class="o">.</span><span class="n">RUnlock</span><span class="p">()</span>
<span class="k">for</span> <span class="p">{</span>
<span class="k">select</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">msg</span> <span class="o">=</span> <span class="o"><-</span><span class="n">memoryMsgChan</span><span class="o">:</span>
<span class="k">case</span> <span class="o"><-</span><span class="n">t</span><span class="o">.</span><span class="n">channelUpdateChan</span><span class="o">:</span>
<span class="c">//之前 避免 锁竞争, 缓存了 已存在的 channel</span>
<span class="c">//假如更新了 channel 会发一个 消息过来, 重新读取 channel</span>
<span class="n">chans</span> <span class="o">=</span> <span class="n">chans</span><span class="p">[</span><span class="o">:</span><span class="m">0</span><span class="p">]</span>
<span class="n">t</span><span class="o">.</span><span class="n">RLock</span><span class="p">()</span>
<span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">c</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">t</span><span class="o">.</span><span class="n">channelMap</span> <span class="p">{</span>
<span class="n">chans</span> <span class="o">=</span> <span class="nb">append</span><span class="p">(</span><span class="n">chans</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">t</span><span class="o">.</span><span class="n">RUnlock</span><span class="p">()</span>
<span class="k">continue</span>
<span class="p">}</span>
<span class="c">// 这里需要 遍历 t.channelMap, 普通的写法, 会导致锁竞争太大</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">channel</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">chans</span> <span class="p">{</span>
<span class="n">chanMsg</span> <span class="o">:=</span> <span class="n">msg</span>
<span class="c">// copy the message because each channel</span>
<span class="c">// needs a unique instance but...</span>
<span class="c">// fastpath to avoid copy if its the first channel</span>
<span class="c">// (the topic already created the first copy)</span>
<span class="k">if</span> <span class="n">i</span> <span class="o">></span> <span class="m">0</span> <span class="p">{</span>
<span class="n">chanMsg</span> <span class="o">=</span> <span class="n">NewMessage</span><span class="p">(</span><span class="n">msg</span><span class="o">.</span><span class="n">ID</span><span class="p">,</span> <span class="n">msg</span><span class="o">.</span><span class="n">Body</span><span class="p">)</span>
<span class="n">chanMsg</span><span class="o">.</span><span class="n">Timestamp</span> <span class="o">=</span> <span class="n">msg</span><span class="o">.</span><span class="n">Timestamp</span>
<span class="n">chanMsg</span><span class="o">.</span><span class="n">deferred</span> <span class="o">=</span> <span class="n">msg</span><span class="o">.</span><span class="n">deferred</span>
<span class="p">}</span>
<span class="k">if</span> <span class="n">chanMsg</span><span class="o">.</span><span class="n">deferred</span> <span class="o">!=</span> <span class="m">0</span> <span class="p">{</span>
<span class="n">channel</span><span class="o">.</span><span class="n">StartDeferredTimeout</span><span class="p">(</span><span class="n">chanMsg</span><span class="p">,</span> <span class="n">chanMsg</span><span class="o">.</span><span class="n">deferred</span><span class="p">)</span>
<span class="k">continue</span>
<span class="p">}</span>
<span class="n">err</span> <span class="o">:=</span> <span class="n">channel</span><span class="o">.</span><span class="n">PutMessage</span><span class="p">(</span><span class="n">chanMsg</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">t</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">nsqd</span><span class="o">.</span><span class="n">logf</span><span class="p">(</span>
<span class="s">"TOPIC(%s) ERROR: failed to put msg(%s) to channel(%s) - %s"</span><span class="p">,</span>
<span class="n">t</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">msg</span><span class="o">.</span><span class="n">ID</span><span class="p">,</span> <span class="n">channel</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>这个 messagePump 函数是 在 NewTopic() 也就是创建 Topic 的时候启动的一个线程</p>
<p>也就是说, topic 是自己 负责 把自己的 memoryMsgChan 做 Pump 出来 , 再塞到 二级 channel 的 memoryMsgChan 里</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">NewTopic</span><span class="p">(</span><span class="n">topicName</span> <span class="kt">string</span><span class="p">,</span> <span class="n">ctx</span> <span class="o">*</span><span class="n">context</span><span class="p">,</span> <span class="n">deleteCallback</span> <span class="k">func</span><span class="p">(</span><span class="o">*</span><span class="n">Topic</span><span class="p">))</span> <span class="o">*</span><span class="n">Topic</span> <span class="p">{</span>
<span class="n">t</span> <span class="o">:=</span> <span class="o">&</span><span class="n">Topic</span><span class="p">{</span>
<span class="o">...</span>
<span class="p">}</span>
<span class="o">...</span>
<span class="n">t</span><span class="o">.</span><span class="n">waitGroup</span><span class="o">.</span><span class="n">Wrap</span><span class="p">(</span><span class="k">func</span><span class="p">()</span> <span class="p">{</span> <span class="n">t</span><span class="o">.</span><span class="n">messagePump</span><span class="p">()</span> <span class="p">})</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="3-channel-层的-putmessage">3. Channel 层的 PutMessage</h2>
<p>topic 的 put 调用 topic下含有的所有 channel 的 PutMessage;</p>
<p>channel 跟topic 其实是一样的, 可以这么理解: channel 就是二级topic</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">c</span> <span class="o">*</span><span class="n">Channel</span><span class="p">)</span> <span class="n">PutMessage</span><span class="p">(</span><span class="n">m</span> <span class="o">*</span><span class="n">Message</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
<span class="n">c</span><span class="o">.</span><span class="n">RLock</span><span class="p">()</span>
<span class="k">defer</span> <span class="n">c</span><span class="o">.</span><span class="n">RUnlock</span><span class="p">()</span>
<span class="k">if</span> <span class="n">atomic</span><span class="o">.</span><span class="n">LoadInt32</span><span class="p">(</span><span class="o">&</span><span class="n">c</span><span class="o">.</span><span class="n">exitFlag</span><span class="p">)</span> <span class="o">==</span> <span class="m">1</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">errors</span><span class="o">.</span><span class="n">New</span><span class="p">(</span><span class="s">"exiting"</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">err</span> <span class="o">:=</span> <span class="n">c</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">m</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">err</span>
<span class="p">}</span>
<span class="n">atomic</span><span class="o">.</span><span class="n">AddUint64</span><span class="p">(</span><span class="o">&</span><span class="n">c</span><span class="o">.</span><span class="n">messageCount</span><span class="p">,</span> <span class="m">1</span><span class="p">)</span>
<span class="k">return</span> <span class="no">nil</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">c</span> <span class="o">*</span><span class="n">Channel</span><span class="p">)</span> <span class="n">put</span><span class="p">(</span><span class="n">m</span> <span class="o">*</span><span class="n">Message</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
<span class="k">select</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">c</span><span class="o">.</span><span class="n">memoryMsgChan</span> <span class="o"><-</span> <span class="n">m</span><span class="o">:</span>
<span class="k">default</span><span class="o">:</span>
<span class="n">b</span> <span class="o">:=</span> <span class="n">bufferPoolGet</span><span class="p">()</span>
<span class="n">err</span> <span class="o">:=</span> <span class="n">writeMessageToBackend</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="n">m</span><span class="p">,</span> <span class="n">c</span><span class="o">.</span><span class="n">backend</span><span class="p">)</span>
<span class="n">bufferPoolPut</span><span class="p">(</span><span class="n">b</span><span class="p">)</span>
<span class="n">c</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">nsqd</span><span class="o">.</span><span class="n">SetHealth</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">c</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">nsqd</span><span class="o">.</span><span class="n">logf</span><span class="p">(</span><span class="s">"CHANNEL(%s) ERROR: failed to write message to backend - %s"</span><span class="p">,</span>
<span class="n">c</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
<span class="k">return</span> <span class="n">err</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="no">nil</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="4-client-层的-messagepump">4. Client 层的 messagePump</h2>
<p>但是会发现, Channel 自己并没有 messagePump 函数, 再次通过Usage 查找, <-channel.memoryMsgChan 读取操作在 protocolV2.messagePump(), 这也就是上文中, 每个Client连接的处理函数IOLoop, 每个Client 都会有一个 protocolV2.messagePump() 线程</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">p</span> <span class="o">*</span><span class="n">protocolV2</span><span class="p">)</span> <span class="n">IOLoop</span><span class="p">(</span><span class="n">conn</span> <span class="n">net</span><span class="o">.</span><span class="n">Conn</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
<span class="n">messagePumpStartedChan</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="k">chan</span> <span class="kt">bool</span><span class="p">)</span>
<span class="k">go</span> <span class="n">p</span><span class="o">.</span><span class="n">messagePump</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">messagePumpStartedChan</span><span class="p">)</span>
<span class="o"><-</span><span class="n">messagePumpStartedChan</span>
<span class="k">for</span> <span class="p">{</span>
<span class="n">line</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">Reader</span><span class="o">.</span><span class="n">ReadSlice</span><span class="p">(</span><span class="sc">'\n'</span><span class="p">)</span> <span class="c">// 读取</span>
<span class="n">params</span> <span class="o">:=</span> <span class="n">bytes</span><span class="o">.</span><span class="n">Split</span><span class="p">(</span><span class="n">line</span><span class="p">,</span> <span class="n">separatorBytes</span><span class="p">)</span> <span class="c">// 解析</span>
<span class="n">response</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">Exec</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span> <span class="c">// 执行</span>
<span class="n">err</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">Send</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">frameTypeResponse</span><span class="p">,</span> <span class="n">response</span><span class="p">)</span> <span class="c">//发送结果</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>客户端的其他操作我们先忽略不管, 主要看 Message 的数据路劲, 精简掉 messagePump 的代码:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">p</span> <span class="o">*</span><span class="n">protocolV2</span><span class="p">)</span> <span class="n">messagePump</span><span class="p">(</span><span class="n">client</span> <span class="o">*</span><span class="n">clientV2</span><span class="p">,</span> <span class="n">startedChan</span> <span class="k">chan</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">subChannel</span> <span class="o">==</span> <span class="no">nil</span> <span class="o">||</span> <span class="o">!</span><span class="n">client</span><span class="o">.</span><span class="n">IsReadyForMessages</span><span class="p">()</span> <span class="p">{</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="c">// we're buffered (if there isn't any more data we should flush)...</span>
<span class="c">// select on the flusher ticker channel, too</span>
<span class="n">memoryMsgChan</span> <span class="o">=</span> <span class="n">subChannel</span><span class="o">.</span><span class="n">memoryMsgChan</span>
<span class="n">backendMsgChan</span> <span class="o">=</span> <span class="n">subChannel</span><span class="o">.</span><span class="n">backend</span><span class="o">.</span><span class="n">ReadChan</span><span class="p">()</span>
<span class="n">flusherChan</span> <span class="o">=</span> <span class="n">outputBufferTicker</span><span class="o">.</span><span class="n">C</span>
<span class="p">}</span>
<span class="c">// 这里负责执行Client 的各种事件</span>
<span class="k">select</span> <span class="p">{</span>
<span class="c">//Client 需要发送一个SUB 请求 来订阅Channel, 并切一个Client只能订阅一个Channel</span>
<span class="k">case</span> <span class="n">subChannel</span> <span class="o">=</span> <span class="o"><-</span><span class="n">subEventChan</span><span class="o">:</span> <span class="c">// 做了订阅</span>
<span class="n">subEventChan</span> <span class="o">=</span> <span class="no">nil</span>
<span class="k">case</span> <span class="n">msg</span> <span class="o">:=</span> <span class="o"><-</span><span class="n">memoryMsgChan</span><span class="o">:</span>
<span class="c">//TODO 样本率? 做测试用的吗?</span>
<span class="k">if</span> <span class="n">sampleRate</span> <span class="o">></span> <span class="m">0</span> <span class="o">&&</span> <span class="n">rand</span><span class="o">.</span><span class="n">Int31n</span><span class="p">(</span><span class="m">100</span><span class="p">)</span> <span class="o">></span> <span class="n">sampleRate</span> <span class="p">{</span>
<span class="k">continue</span>
<span class="p">}</span>
<span class="n">msg</span><span class="o">.</span><span class="n">Attempts</span><span class="o">++</span>
<span class="c">//TODO 这个是什么作用? 下面不是已经发送了? 怎么又塞到一个队列里?</span>
<span class="n">subChannel</span><span class="o">.</span><span class="n">StartInFlightTimeout</span><span class="p">(</span><span class="n">msg</span><span class="p">,</span> <span class="n">client</span><span class="o">.</span><span class="n">ID</span><span class="p">,</span> <span class="n">msgTimeout</span><span class="p">)</span>
<span class="n">client</span><span class="o">.</span><span class="n">SendingMessage</span><span class="p">()</span>
<span class="c">// protocol 进行消息格式的打包, 再发送给Client</span>
<span class="c">// 这里, Message 就发送给了 client</span>
<span class="n">err</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">SendMessage</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">msg</span><span class="p">,</span> <span class="o">&</span><span class="n">buf</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">goto</span> <span class="n">exit</span>
<span class="p">}</span>
<span class="n">flushed</span> <span class="o">=</span> <span class="no">false</span>
<span class="k">case</span> <span class="o"><-</span><span class="n">client</span><span class="o">.</span><span class="n">ExitChan</span><span class="o">:</span>
<span class="k">goto</span> <span class="n">exit</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="5-整体-message-数据流-精简总结">5. 整体 Message 数据流 精简总结:</h2>
<p>总体来讲, 一个Message的推送, 经过了 两个chan 队列:</p>
<ul>
<li>topic.memoryMsgChan</li>
<li>channel.memoryMsgChan</li>
</ul>
<p>两个队列的两头(读, 写), 分别有一个线程进行, 也就是3个线程:</p>
<ul>
<li>protocol.IOLoop(clientConn)</li>
<li>topic.messagePump()</li>
<li>protocol.messagePump()</li>
</ul>
<p>Message 的数据流就是:</p>
<div class="sequence">
ClientConn->topic.memoryMsgChan: protocol.IOLoop(clientConn)
topic.memoryMsgChan->channel.memoryMsgChan: topic.messagePump()
channel.memoryMsgChan-->ClientConn: protocol.messagePump()
</div>
<p>从client连接进来, 到message 投递给Client 的所有流程代码, 精简如下:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">clientConn</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">listener</span><span class="o">.</span><span class="n">Accept</span><span class="p">()</span>
<span class="c">// Client ==> Client 请求响应线程 ==> topic.memoryMsgChan</span>
<span class="k">go</span> <span class="n">handler</span><span class="o">.</span><span class="n">Handle</span><span class="p">(</span><span class="n">clientConn</span><span class="p">)</span> <span class="p">{</span>
<span class="n">err</span> <span class="o">=</span> <span class="n">prot</span><span class="o">.</span><span class="n">IOLoop</span><span class="p">(</span><span class="n">clientConn</span><span class="p">){</span>
<span class="k">for</span> <span class="p">{</span>
<span class="n">line</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">Reader</span><span class="o">.</span><span class="n">ReadSlice</span><span class="p">(</span><span class="sc">'\n'</span><span class="p">)</span> <span class="c">// <==Client</span>
<span class="n">params</span> <span class="o">:=</span> <span class="n">bytes</span><span class="o">.</span><span class="n">Split</span><span class="p">(</span><span class="n">line</span><span class="p">,</span> <span class="n">separatorBytes</span><span class="p">)</span>
<span class="n">response</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">Exec</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">params</span><span class="p">){</span>
<span class="k">switch</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">bytes</span><span class="o">.</span><span class="n">Equal</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="m">0</span><span class="p">],</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">(</span><span class="s">"PUB"</span><span class="p">))</span><span class="o">:</span>
<span class="k">return</span> <span class="n">p</span><span class="o">.</span><span class="n">PUB</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">params</span><span class="p">){</span>
<span class="n">topic</span><span class="o">.</span><span class="n">PutMessage</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span> <span class="c">// ==>topic.memoryMsgChan</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c">// topic.memoryMsgChan ==> Topic.messagePump 线程 ==> echa channel.memoryMsgChan</span>
<span class="k">func</span> <span class="n">NewTopic</span><span class="p">(</span><span class="n">topicName</span> <span class="kt">string</span><span class="p">,</span> <span class="n">ctx</span> <span class="o">*</span><span class="n">context</span><span class="p">,</span> <span class="n">deleteCallback</span> <span class="k">func</span><span class="p">(</span><span class="o">*</span><span class="n">Topic</span><span class="p">))</span> <span class="o">*</span><span class="n">Topic</span> <span class="p">{</span>
<span class="n">t</span><span class="o">.</span><span class="n">waitGroup</span><span class="o">.</span><span class="n">Wrap</span><span class="p">(</span><span class="k">func</span><span class="p">()</span> <span class="p">{</span><span class="n">t</span><span class="o">.</span><span class="n">messagePump</span><span class="p">(){</span>
<span class="k">for</span> <span class="p">{</span>
<span class="k">select</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">msg</span> <span class="o">=</span> <span class="o"><-</span><span class="n">memoryMsgChan</span><span class="o">:</span> <span class="c">// <==topic.memoryMsgChan</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">channel</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">chans</span> <span class="p">{</span>
<span class="n">err</span> <span class="o">:=</span> <span class="n">channel</span><span class="o">.</span><span class="n">PutMessage</span><span class="p">(</span><span class="n">chanMsg</span><span class="p">)</span> <span class="c">// ==>channel.memoryMsgChan</span>
<span class="p">}</span>
<span class="p">}}</span>
<span class="p">})</span>
<span class="p">}</span>
<span class="c">// client.subChannel.memoryMsgChan ==> Client的messagePump 线程 ==> Client</span>
<span class="k">func</span> <span class="p">(</span><span class="n">p</span> <span class="o">*</span><span class="n">protocolV2</span><span class="p">)</span> <span class="n">IOLoop</span><span class="p">(</span><span class="n">conn</span> <span class="n">net</span><span class="o">.</span><span class="n">Conn</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
<span class="k">go</span> <span class="n">p</span><span class="o">.</span><span class="n">messagePump</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">messagePumpStartedChan</span><span class="p">){</span>
<span class="k">for</span> <span class="p">{</span>
<span class="k">select</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">subChannel</span> <span class="o">=</span> <span class="o"><-</span><span class="n">subEventChan</span><span class="o">:</span>
<span class="k">case</span> <span class="n">msg</span> <span class="o">:=</span> <span class="o"><-</span><span class="n">subChannel</span><span class="o">.</span><span class="n">memoryMsgChan</span><span class="o">:</span> <span class="c">// <==client.subChannel.memoryMsgChan</span>
<span class="n">err</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">SendMessage</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">msg</span><span class="p">,</span> <span class="o">&</span><span class="n">buf</span><span class="p">)</span> <span class="c">// ==>Client</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>虽然 一个channel 会被多个Client 订阅, 从代码上来讲, 也就是 一个 channel.memoryMsgChan 会被多个 client 线程的 messagePump() 方法中进行读取</p>
<p>但因为chan 的特性, 一条缓存在 channel.memoryMsgChan 中的消息, 只会被一个 client 读取到</p>
<h2 id="topic-遍历-channel时-做的缓存">topic 遍历 channel时 做的缓存</h2>
<p>topic 类中 messagePump 需要遍历 所有的channel, 也就是遍历 t.channelMap; 多线程中遍历类中一个成员变量的操作, 需要给 类加锁, 普通的写法像这样:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">t</span><span class="o">.</span><span class="n">RLock</span><span class="p">()</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">channel</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">t</span><span class="o">.</span><span class="n">channelMaps</span> <span class="p">{</span>
<span class="p">}</span>
<span class="n">t</span><span class="o">.</span><span class="n">RUnlock</span><span class="p">()</span>
</code></pre></div></div>
<p>但是问题这里是在一个 for 循环中, 整个操作就相当于这样:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">{</span>
<span class="n">t</span><span class="o">.</span><span class="n">RLock</span><span class="p">()</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">channel</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">t</span><span class="o">.</span><span class="n">channelMaps</span> <span class="p">{</span>
<span class="p">}</span>
<span class="n">t</span><span class="o">.</span><span class="n">RUnlock</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p>这个锁范围太大了, 锁竞争很大, 所以nsq 做了一个缓存, 并且接收 t.channelUpdateChan 通知来 更新 缓存, 就像这样:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">t</span> <span class="o">*</span><span class="n">Topic</span><span class="p">)</span> <span class="n">messagePump</span><span class="p">()</span> <span class="p">{</span>
<span class="k">var</span> <span class="n">chans</span> <span class="p">[]</span><span class="o">*</span><span class="n">Channel</span>
<span class="c">//避免 锁竞争, 所以缓存 已存在的 channel</span>
<span class="n">t</span><span class="o">.</span><span class="n">RLock</span><span class="p">()</span>
<span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">c</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">t</span><span class="o">.</span><span class="n">channelMap</span> <span class="p">{</span>
<span class="n">chans</span> <span class="o">=</span> <span class="nb">append</span><span class="p">(</span><span class="n">chans</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">t</span><span class="o">.</span><span class="n">RUnlock</span><span class="p">()</span>
<span class="k">for</span> <span class="p">{</span>
<span class="k">select</span> <span class="p">{</span>
<span class="k">case</span> <span class="o"><-</span><span class="n">t</span><span class="o">.</span><span class="n">channelUpdateChan</span><span class="o">:</span>
<span class="c">//接到channle 变更的消息, 刷新缓存</span>
<span class="n">chans</span> <span class="o">=</span> <span class="n">chans</span><span class="p">[</span><span class="o">:</span><span class="m">0</span><span class="p">]</span>
<span class="n">t</span><span class="o">.</span><span class="n">RLock</span><span class="p">()</span>
<span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">c</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">t</span><span class="o">.</span><span class="n">channelMap</span> <span class="p">{</span>
<span class="n">chans</span> <span class="o">=</span> <span class="nb">append</span><span class="p">(</span><span class="n">chans</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">t</span><span class="o">.</span><span class="n">RUnlock</span><span class="p">()</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">chans</span><span class="p">)</span> <span class="o">==</span> <span class="m">0</span> <span class="o">||</span> <span class="n">t</span><span class="o">.</span><span class="n">IsPaused</span><span class="p">()</span> <span class="p">{</span>
<span class="n">memoryMsgChan</span> <span class="o">=</span> <span class="no">nil</span>
<span class="n">backendChan</span> <span class="o">=</span> <span class="no">nil</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">memoryMsgChan</span> <span class="o">=</span> <span class="n">t</span><span class="o">.</span><span class="n">memoryMsgChan</span>
<span class="n">backendChan</span> <span class="o">=</span> <span class="n">t</span><span class="o">.</span><span class="n">backend</span><span class="o">.</span><span class="n">ReadChan</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">continue</span>
<span class="p">}</span>
<span class="c">// 遍历 已经缓存的 chans</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">channel</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">chans</span> <span class="p">{</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
Nsq源码阅读(3) 与client的交互处理ioloop
2017-01-12T00:00:00-06:00
http://www.zhaoxiaodan.com/%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB/nsq源码阅读(3)-与Client的交互处理IOLoop
<h2 id="具体-每个命令处理的封装">具体 每个’命令’处理的封装</h2>
<p>前面看到, listener.Accept() 之后, 解析了版本号, 当前由 protocol_v2 具体实现了 IOLoop</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">p</span> <span class="o">*</span><span class="n">protocolV2</span><span class="p">)</span> <span class="n">IOLoop</span><span class="p">(</span><span class="n">conn</span> <span class="n">net</span><span class="o">.</span><span class="n">Conn</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
<span class="k">var</span> <span class="n">err</span> <span class="kt">error</span>
<span class="k">var</span> <span class="n">line</span> <span class="p">[]</span><span class="kt">byte</span>
<span class="k">var</span> <span class="n">zeroTime</span> <span class="n">time</span><span class="o">.</span><span class="n">Time</span>
<span class="c">// nsqd 范围的 clientID 序列</span>
<span class="n">clientID</span> <span class="o">:=</span> <span class="n">atomic</span><span class="o">.</span><span class="n">AddInt64</span><span class="p">(</span><span class="o">&</span><span class="n">p</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">nsqd</span><span class="o">.</span><span class="n">clientIDSequence</span><span class="p">,</span> <span class="m">1</span><span class="p">)</span>
<span class="n">client</span> <span class="o">:=</span> <span class="n">newClientV2</span><span class="p">(</span><span class="n">clientID</span><span class="p">,</span> <span class="n">conn</span><span class="p">,</span> <span class="n">p</span><span class="o">.</span><span class="n">ctx</span><span class="p">)</span>
<span class="c">// 这个messagePump 名字很形象, 把要发送给client 的messgae 从 缓存池子里Pump 抽出来 做具体的发送</span>
<span class="n">messagePumpStartedChan</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="k">chan</span> <span class="kt">bool</span><span class="p">)</span>
<span class="k">go</span> <span class="n">p</span><span class="o">.</span><span class="n">messagePump</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">messagePumpStartedChan</span><span class="p">)</span>
<span class="o"><-</span><span class="n">messagePumpStartedChan</span> <span class="c">//TODO messagePump 里要做初始化, 具体是什么? 为什么需要这样阻塞一下IOLoop ?</span>
<span class="c">// 底下这个地方是 处理 client 的请求的</span>
<span class="k">for</span> <span class="p">{</span>
<span class="c">// 如果 client 设置需要做心跳检查, 则设置 读超时为 两倍 心跳检查间隔</span>
<span class="c">// 也就是说, 在这个时间里, 正常情况下, client肯定会在这个间隔内发一个心跳包过来</span>
<span class="c">// read 不会因为client 本来就没消息而超时,</span>
<span class="c">// 但是如果还是超时了, 那就肯定是 网络连接除了问题</span>
<span class="k">if</span> <span class="n">client</span><span class="o">.</span><span class="n">HeartbeatInterval</span> <span class="o">></span> <span class="m">0</span> <span class="p">{</span>
<span class="n">client</span><span class="o">.</span><span class="n">SetReadDeadline</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">Now</span><span class="p">()</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="n">client</span><span class="o">.</span><span class="n">HeartbeatInterval</span> <span class="o">*</span> <span class="m">2</span><span class="p">))</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="c">// 加入client 不设置做心跳则不设置读超时</span>
<span class="n">client</span><span class="o">.</span><span class="n">SetReadDeadline</span><span class="p">(</span><span class="n">zeroTime</span><span class="p">)</span>
<span class="p">}</span>
<span class="c">//TODO 没太懂这个什么意思.</span>
<span class="c">// ReadSlice does not allocate new space for the data each request</span>
<span class="c">// ie. the returned slice is only valid until the next call to it</span>
<span class="n">line</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">Reader</span><span class="o">.</span><span class="n">ReadSlice</span><span class="p">(</span><span class="sc">'\n'</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">==</span> <span class="n">io</span><span class="o">.</span><span class="n">EOF</span> <span class="p">{</span>
<span class="n">err</span> <span class="o">=</span> <span class="no">nil</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">err</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"failed to read command - %s"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">break</span>
<span class="p">}</span>
<span class="c">// 这个V2 版本的协议, 一行是一个命令</span>
<span class="n">line</span> <span class="o">=</span> <span class="n">line</span><span class="p">[</span><span class="o">:</span><span class="nb">len</span><span class="p">(</span><span class="n">line</span><span class="p">)</span><span class="o">-</span><span class="m">1</span><span class="p">]</span>
<span class="c">// optionally trim the '\r' , 处理一下\r, 有可能win版本的回车是 \r\n</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="o">></span> <span class="m">0</span> <span class="o">&&</span> <span class="n">line</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">line</span><span class="p">)</span><span class="o">-</span><span class="m">1</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'\r'</span> <span class="p">{</span>
<span class="n">line</span> <span class="o">=</span> <span class="n">line</span><span class="p">[</span><span class="o">:</span><span class="nb">len</span><span class="p">(</span><span class="n">line</span><span class="p">)</span><span class="o">-</span><span class="m">1</span><span class="p">]</span>
<span class="p">}</span>
<span class="c">// 每个命令, 用 " "空格来划分 参数</span>
<span class="n">params</span> <span class="o">:=</span> <span class="n">bytes</span><span class="o">.</span><span class="n">Split</span><span class="p">(</span><span class="n">line</span><span class="p">,</span> <span class="n">separatorBytes</span><span class="p">)</span>
<span class="k">if</span> <span class="n">p</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">nsqd</span><span class="o">.</span><span class="n">getOpts</span><span class="p">()</span><span class="o">.</span><span class="n">Verbose</span> <span class="p">{</span>
<span class="n">p</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">nsqd</span><span class="o">.</span><span class="n">logf</span><span class="p">(</span><span class="s">"PROTOCOL(V2): [%s] %s"</span><span class="p">,</span> <span class="n">client</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span>
<span class="p">}</span>
<span class="c">// 执行命令</span>
<span class="k">var</span> <span class="n">response</span> <span class="p">[]</span><span class="kt">byte</span>
<span class="n">response</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">Exec</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">ctx</span> <span class="o">:=</span> <span class="s">""</span>
<span class="k">if</span> <span class="n">parentErr</span> <span class="o">:=</span> <span class="n">err</span><span class="o">.</span><span class="p">(</span><span class="n">protocol</span><span class="o">.</span><span class="n">ChildErr</span><span class="p">)</span><span class="o">.</span><span class="n">Parent</span><span class="p">();</span> <span class="n">parentErr</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">ctx</span> <span class="o">=</span> <span class="s">" - "</span> <span class="o">+</span> <span class="n">parentErr</span><span class="o">.</span><span class="n">Error</span><span class="p">()</span>
<span class="p">}</span>
<span class="n">p</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">nsqd</span><span class="o">.</span><span class="n">logf</span><span class="p">(</span><span class="s">"ERROR: [%s] - %s%s"</span><span class="p">,</span> <span class="n">client</span><span class="p">,</span> <span class="n">err</span><span class="p">,</span> <span class="n">ctx</span><span class="p">)</span>
<span class="n">sendErr</span> <span class="o">:=</span> <span class="n">p</span><span class="o">.</span><span class="n">Send</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">frameTypeError</span><span class="p">,</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">(</span><span class="n">err</span><span class="o">.</span><span class="n">Error</span><span class="p">()))</span>
<span class="k">if</span> <span class="n">sendErr</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">p</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">nsqd</span><span class="o">.</span><span class="n">logf</span><span class="p">(</span><span class="s">"ERROR: [%s] - %s%s"</span><span class="p">,</span> <span class="n">client</span><span class="p">,</span> <span class="n">sendErr</span><span class="p">,</span> <span class="n">ctx</span><span class="p">)</span>
<span class="k">break</span>
<span class="p">}</span>
<span class="c">// errors of type FatalClientErr should forceably close the connection</span>
<span class="k">if</span> <span class="n">_</span><span class="p">,</span> <span class="n">ok</span> <span class="o">:=</span> <span class="n">err</span><span class="o">.</span><span class="p">(</span><span class="o">*</span><span class="n">protocol</span><span class="o">.</span><span class="n">FatalClientErr</span><span class="p">);</span> <span class="n">ok</span> <span class="p">{</span>
<span class="k">break</span>
<span class="p">}</span>
<span class="k">continue</span>
<span class="p">}</span>
<span class="c">// 加入命令是有 '响应' 的, 发送响应</span>
<span class="k">if</span> <span class="n">response</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">err</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">Send</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">frameTypeResponse</span><span class="p">,</span> <span class="n">response</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">err</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"failed to send response - %s"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
<span class="k">break</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">p</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">nsqd</span><span class="o">.</span><span class="n">logf</span><span class="p">(</span><span class="s">"PROTOCOL(V2): [%s] exiting ioloop"</span><span class="p">,</span> <span class="n">client</span><span class="p">)</span>
<span class="n">conn</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
<span class="c">// 通知 messagePump 退出</span>
<span class="nb">close</span><span class="p">(</span><span class="n">client</span><span class="o">.</span><span class="n">ExitChan</span><span class="p">)</span>
<span class="k">if</span> <span class="n">client</span><span class="o">.</span><span class="n">Channel</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">client</span><span class="o">.</span><span class="n">Channel</span><span class="o">.</span><span class="n">RemoveClient</span><span class="p">(</span><span class="n">client</span><span class="o">.</span><span class="n">ID</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">err</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">p</span> <span class="o">*</span><span class="n">protocolV2</span><span class="p">)</span> <span class="n">Send</span><span class="p">(</span><span class="n">client</span> <span class="o">*</span><span class="n">clientV2</span><span class="p">,</span> <span class="n">frameType</span> <span class="kt">int32</span><span class="p">,</span> <span class="n">data</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
<span class="c">// 因为client 的处理 还有一个 messagePump 线程, 所以 发送要锁</span>
<span class="n">client</span><span class="o">.</span><span class="n">writeLock</span><span class="o">.</span><span class="n">Lock</span><span class="p">()</span>
<span class="k">var</span> <span class="n">zeroTime</span> <span class="n">time</span><span class="o">.</span><span class="n">Time</span>
<span class="k">if</span> <span class="n">client</span><span class="o">.</span><span class="n">HeartbeatInterval</span> <span class="o">></span> <span class="m">0</span> <span class="p">{</span>
<span class="n">client</span><span class="o">.</span><span class="n">SetWriteDeadline</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">Now</span><span class="p">()</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="n">client</span><span class="o">.</span><span class="n">HeartbeatInterval</span><span class="p">))</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">client</span><span class="o">.</span><span class="n">SetWriteDeadline</span><span class="p">(</span><span class="n">zeroTime</span><span class="p">)</span>
<span class="p">}</span>
<span class="c">//V2 协议版本 发送给client, 是 使用 [(4byte)消息长度 , (4byte)消息类型, (载体)] 的 帧格式</span>
<span class="c">//但是为什么这个 格式的封装不是 写在 protocal_v2.go 而是在 protocaol 定义上?</span>
<span class="c">//个人觉得, 具体封包格式的 '具体实现' 应该在 '协议的具体实现'里, 也就是 protocal_v2, 而不是 '协议的定义' 里</span>
<span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">protocol</span><span class="o">.</span><span class="n">SendFramedResponse</span><span class="p">(</span><span class="n">client</span><span class="o">.</span><span class="n">Writer</span><span class="p">,</span> <span class="n">frameType</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">client</span><span class="o">.</span><span class="n">writeLock</span><span class="o">.</span><span class="n">Unlock</span><span class="p">()</span>
<span class="k">return</span> <span class="n">err</span>
<span class="p">}</span>
<span class="k">if</span> <span class="n">frameType</span> <span class="o">!=</span> <span class="n">frameTypeMessage</span> <span class="p">{</span>
<span class="n">err</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">Flush</span><span class="p">()</span>
<span class="p">}</span>
<span class="n">client</span><span class="o">.</span><span class="n">writeLock</span><span class="o">.</span><span class="n">Unlock</span><span class="p">()</span>
<span class="k">return</span> <span class="n">err</span>
<span class="p">}</span>
<span class="k">func</span> <span class="n">SendFramedResponse</span><span class="p">(</span><span class="n">w</span> <span class="n">io</span><span class="o">.</span><span class="n">Writer</span><span class="p">,</span> <span class="n">frameType</span> <span class="kt">int32</span><span class="p">,</span> <span class="n">data</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
<span class="n">beBuf</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="m">4</span><span class="p">)</span>
<span class="n">size</span> <span class="o">:=</span> <span class="kt">uint32</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">))</span> <span class="o">+</span> <span class="m">4</span>
<span class="n">binary</span><span class="o">.</span><span class="n">BigEndian</span><span class="o">.</span><span class="n">PutUint32</span><span class="p">(</span><span class="n">beBuf</span><span class="p">,</span> <span class="n">size</span><span class="p">)</span>
<span class="n">n</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">w</span><span class="o">.</span><span class="n">Write</span><span class="p">(</span><span class="n">beBuf</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">n</span><span class="p">,</span> <span class="n">err</span>
<span class="p">}</span>
<span class="n">binary</span><span class="o">.</span><span class="n">BigEndian</span><span class="o">.</span><span class="n">PutUint32</span><span class="p">(</span><span class="n">beBuf</span><span class="p">,</span> <span class="kt">uint32</span><span class="p">(</span><span class="n">frameType</span><span class="p">))</span>
<span class="n">n</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">w</span><span class="o">.</span><span class="n">Write</span><span class="p">(</span><span class="n">beBuf</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">n</span> <span class="o">+</span> <span class="m">4</span><span class="p">,</span> <span class="n">err</span>
<span class="p">}</span>
<span class="n">n</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">w</span><span class="o">.</span><span class="n">Write</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="k">return</span> <span class="n">n</span> <span class="o">+</span> <span class="m">8</span><span class="p">,</span> <span class="n">err</span>
<span class="p">}</span>
</code></pre></div></div>
<p>读取并解析请求之后, 传给 p.Exec(client, params) 执行具体的请求</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">p</span> <span class="o">*</span><span class="n">protocolV2</span><span class="p">)</span> <span class="n">Exec</span><span class="p">(</span><span class="n">client</span> <span class="o">*</span><span class="n">clientV2</span><span class="p">,</span> <span class="n">params</span> <span class="p">[][]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
<span class="c">//这个命令不需要做认证</span>
<span class="k">if</span> <span class="n">bytes</span><span class="o">.</span><span class="n">Equal</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="m">0</span><span class="p">],</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">(</span><span class="s">"IDENTIFY"</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">p</span><span class="o">.</span><span class="n">IDENTIFY</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span>
<span class="p">}</span>
<span class="c">// client 是否做了认证</span>
<span class="n">err</span> <span class="o">:=</span> <span class="n">enforceTLSPolicy</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">p</span><span class="p">,</span> <span class="n">params</span><span class="p">[</span><span class="m">0</span><span class="p">])</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">return</span> <span class="no">nil</span><span class="p">,</span> <span class="n">err</span>
<span class="p">}</span>
<span class="c">//一行一个请求, 一个命令中用 " " 空格分割参数, 第一个参数是 命令标志</span>
<span class="k">switch</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">bytes</span><span class="o">.</span><span class="n">Equal</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="m">0</span><span class="p">],</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">(</span><span class="s">"FIN"</span><span class="p">))</span><span class="o">:</span>
<span class="k">return</span> <span class="n">p</span><span class="o">.</span><span class="n">FIN</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span>
<span class="k">case</span> <span class="n">bytes</span><span class="o">.</span><span class="n">Equal</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="m">0</span><span class="p">],</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">(</span><span class="s">"RDY"</span><span class="p">))</span><span class="o">:</span>
<span class="k">return</span> <span class="n">p</span><span class="o">.</span><span class="n">RDY</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span>
<span class="k">case</span> <span class="n">bytes</span><span class="o">.</span><span class="n">Equal</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="m">0</span><span class="p">],</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">(</span><span class="s">"REQ"</span><span class="p">))</span><span class="o">:</span>
<span class="k">return</span> <span class="n">p</span><span class="o">.</span><span class="n">REQ</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span>
<span class="k">case</span> <span class="n">bytes</span><span class="o">.</span><span class="n">Equal</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="m">0</span><span class="p">],</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">(</span><span class="s">"PUB"</span><span class="p">))</span><span class="o">:</span>
<span class="k">return</span> <span class="n">p</span><span class="o">.</span><span class="n">PUB</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span>
<span class="k">case</span> <span class="n">bytes</span><span class="o">.</span><span class="n">Equal</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="m">0</span><span class="p">],</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">(</span><span class="s">"MPUB"</span><span class="p">))</span><span class="o">:</span>
<span class="k">return</span> <span class="n">p</span><span class="o">.</span><span class="n">MPUB</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span>
<span class="k">case</span> <span class="n">bytes</span><span class="o">.</span><span class="n">Equal</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="m">0</span><span class="p">],</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">(</span><span class="s">"DPUB"</span><span class="p">))</span><span class="o">:</span>
<span class="k">return</span> <span class="n">p</span><span class="o">.</span><span class="n">DPUB</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span>
<span class="k">case</span> <span class="n">bytes</span><span class="o">.</span><span class="n">Equal</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="m">0</span><span class="p">],</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">(</span><span class="s">"NOP"</span><span class="p">))</span><span class="o">:</span>
<span class="k">return</span> <span class="n">p</span><span class="o">.</span><span class="n">NOP</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span>
<span class="k">case</span> <span class="n">bytes</span><span class="o">.</span><span class="n">Equal</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="m">0</span><span class="p">],</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">(</span><span class="s">"TOUCH"</span><span class="p">))</span><span class="o">:</span>
<span class="k">return</span> <span class="n">p</span><span class="o">.</span><span class="n">TOUCH</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span>
<span class="k">case</span> <span class="n">bytes</span><span class="o">.</span><span class="n">Equal</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="m">0</span><span class="p">],</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">(</span><span class="s">"SUB"</span><span class="p">))</span><span class="o">:</span>
<span class="k">return</span> <span class="n">p</span><span class="o">.</span><span class="n">SUB</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span>
<span class="k">case</span> <span class="n">bytes</span><span class="o">.</span><span class="n">Equal</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="m">0</span><span class="p">],</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">(</span><span class="s">"CLS"</span><span class="p">))</span><span class="o">:</span>
<span class="k">return</span> <span class="n">p</span><span class="o">.</span><span class="n">CLS</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span>
<span class="k">case</span> <span class="n">bytes</span><span class="o">.</span><span class="n">Equal</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="m">0</span><span class="p">],</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">(</span><span class="s">"AUTH"</span><span class="p">))</span><span class="o">:</span>
<span class="k">return</span> <span class="n">p</span><span class="o">.</span><span class="n">AUTH</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">return</span> <span class="no">nil</span><span class="p">,</span> <span class="n">protocol</span><span class="o">.</span><span class="n">NewFatalClientErr</span><span class="p">(</span><span class="no">nil</span><span class="p">,</span> <span class="s">"E_INVALID"</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"invalid command %s"</span><span class="p">,</span> <span class="n">params</span><span class="p">[</span><span class="m">0</span><span class="p">]))</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="精简总流程">精简总流程:</h3>
<p>同样, 代码精简一下, IOLoop 里主要就是启动了两个线程, 一个处理订阅的消息的发送, 一个处理client 发过来的命令请求</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">p</span> <span class="o">*</span><span class="n">protocolV2</span><span class="p">)</span> <span class="n">IOLoop</span><span class="p">(</span><span class="n">conn</span> <span class="n">net</span><span class="o">.</span><span class="n">Conn</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
<span class="n">messagePumpStartedChan</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="k">chan</span> <span class="kt">bool</span><span class="p">)</span>
<span class="k">go</span> <span class="n">p</span><span class="o">.</span><span class="n">messagePump</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">messagePumpStartedChan</span><span class="p">)</span>
<span class="o"><-</span><span class="n">messagePumpStartedChan</span>
<span class="k">for</span> <span class="p">{</span>
<span class="n">line</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">Reader</span><span class="o">.</span><span class="n">ReadSlice</span><span class="p">(</span><span class="sc">'\n'</span><span class="p">)</span> <span class="c">// 读取</span>
<span class="n">params</span> <span class="o">:=</span> <span class="n">bytes</span><span class="o">.</span><span class="n">Split</span><span class="p">(</span><span class="n">line</span><span class="p">,</span> <span class="n">separatorBytes</span><span class="p">)</span> <span class="c">// 解析</span>
<span class="n">response</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">Exec</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span> <span class="c">// 执行</span>
<span class="n">err</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">Send</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">frameTypeResponse</span><span class="p">,</span> <span class="n">response</span><span class="p">)</span> <span class="c">//发送结果</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
Nsq源码阅读(2) Tcpserver封装
2017-01-12T00:00:00-06:00
http://www.zhaoxiaodan.com/%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB/nsq源码阅读(2)-TCPServer封装
<h2 id="tcphandler">TCPHandler</h2>
<p>上文理解了nsq怎么做启动和退出, 并且知道nsqlookup 启动了tcp 和 http 两个服务进程.</p>
<p>现在我们直接看 nsqd.Main()</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">n</span> <span class="o">*</span><span class="n">NSQD</span><span class="p">)</span> <span class="n">Main</span><span class="p">()</span> <span class="p">{</span>
<span class="o">...</span>
<span class="c">// 启动tcp listener</span>
<span class="n">tcpListener</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">net</span><span class="o">.</span><span class="n">Listen</span><span class="p">(</span><span class="s">"tcp"</span><span class="p">,</span> <span class="n">n</span><span class="o">.</span><span class="n">getOpts</span><span class="p">()</span><span class="o">.</span><span class="n">TCPAddress</span><span class="p">)</span>
<span class="c">//实现了具体的Handler</span>
<span class="n">tcpServer</span> <span class="o">:=</span> <span class="o">&</span><span class="n">tcpServer</span><span class="p">{</span><span class="n">ctx</span><span class="o">:</span> <span class="n">ctx</span><span class="p">}</span>
<span class="c">//上一篇说的, 用以优雅退出</span>
<span class="n">n</span><span class="o">.</span><span class="n">waitGroup</span><span class="o">.</span><span class="n">Wrap</span><span class="p">(</span><span class="k">func</span><span class="p">()</span> <span class="p">{</span>
<span class="n">protocol</span><span class="o">.</span><span class="n">TCPServer</span><span class="p">(</span><span class="n">n</span><span class="o">.</span><span class="n">tcpListener</span><span class="p">,</span> <span class="n">tcpServer</span><span class="p">,</span> <span class="n">n</span><span class="o">.</span><span class="n">getOpts</span><span class="p">()</span><span class="o">.</span><span class="n">Logger</span><span class="p">)</span>
<span class="p">})</span>
<span class="o">...</span>
<span class="p">}</span>
</code></pre></div></div>
<p>nsq 封装了一个TCPHandler, 当 listener.Accept() 接到client 的连接获取到connect 时, 交给 TCPHandler.Handle(net.Conn) 函数处理</p>
<p>所有的 tcp 服务都可以复用此代码, 不同的服务内容, 只需要实现不同的TCPHandler即可</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">type</span> <span class="n">TCPHandler</span> <span class="k">interface</span> <span class="p">{</span>
<span class="n">Handle</span><span class="p">(</span><span class="n">net</span><span class="o">.</span><span class="n">Conn</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">func</span> <span class="n">TCPServer</span><span class="p">(</span><span class="n">listener</span> <span class="n">net</span><span class="o">.</span><span class="n">Listener</span><span class="p">,</span> <span class="n">handler</span> <span class="n">TCPHandler</span><span class="p">,</span> <span class="n">l</span> <span class="n">app</span><span class="o">.</span><span class="n">Logger</span><span class="p">)</span> <span class="p">{</span>
<span class="n">l</span><span class="o">.</span><span class="n">Output</span><span class="p">(</span><span class="m">2</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"TCP: listening on %s"</span><span class="p">,</span> <span class="n">listener</span><span class="o">.</span><span class="n">Addr</span><span class="p">()))</span>
<span class="k">for</span> <span class="p">{</span>
<span class="c">// 有客户端连接</span>
<span class="n">clientConn</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">listener</span><span class="o">.</span><span class="n">Accept</span><span class="p">()</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">nerr</span><span class="p">,</span> <span class="n">ok</span> <span class="o">:=</span> <span class="n">err</span><span class="o">.</span><span class="p">(</span><span class="n">net</span><span class="o">.</span><span class="n">Error</span><span class="p">);</span> <span class="n">ok</span> <span class="o">&&</span> <span class="n">nerr</span><span class="o">.</span><span class="n">Temporary</span><span class="p">()</span> <span class="p">{</span>
<span class="n">l</span><span class="o">.</span><span class="n">Output</span><span class="p">(</span><span class="m">2</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"NOTICE: temporary Accept() failure - %s"</span><span class="p">,</span> <span class="n">err</span><span class="p">))</span>
<span class="n">runtime</span><span class="o">.</span><span class="n">Gosched</span><span class="p">()</span> <span class="c">// 是临时的错误, 暂停一下继续</span>
<span class="k">continue</span>
<span class="p">}</span>
<span class="c">// theres no direct way to detect this error because it is not exposed</span>
<span class="k">if</span> <span class="o">!</span><span class="n">strings</span><span class="o">.</span><span class="n">Contains</span><span class="p">(</span><span class="n">err</span><span class="o">.</span><span class="n">Error</span><span class="p">(),</span> <span class="s">"use of closed network connection"</span><span class="p">)</span> <span class="p">{</span>
<span class="n">l</span><span class="o">.</span><span class="n">Output</span><span class="p">(</span><span class="m">2</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"ERROR: listener.Accept() - %s"</span><span class="p">,</span> <span class="n">err</span><span class="p">))</span>
<span class="p">}</span>
<span class="k">break</span>
<span class="p">}</span>
<span class="c">//启动一个线程, 交给 handler 处理, 这里使用的是 one connect per thread 模式</span>
<span class="c">//因为golang的特性, one connect per thread 模式 实际上是 one connect per goroutine</span>
<span class="c">//再加上golang将io操作都做了封装, 那么实际上这里是 one connect per event loop 模式</span>
<span class="k">go</span> <span class="n">handler</span><span class="o">.</span><span class="n">Handle</span><span class="p">(</span><span class="n">clientConn</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">l</span><span class="o">.</span><span class="n">Output</span><span class="p">(</span><span class="m">2</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"TCP: closing %s"</span><span class="p">,</span> <span class="n">listener</span><span class="o">.</span><span class="n">Addr</span><span class="p">()))</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="协议不同版本-的-封装处理">协议不同版本 的 封装处理</h2>
<p>接到连接之后, 交给TCPHandler 处理, 这里是由tcpServer 实现:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">func</span> <span class="p">(</span><span class="n">p</span> <span class="o">*</span><span class="n">tcpServer</span><span class="p">)</span> <span class="n">Handle</span><span class="p">(</span><span class="n">clientConn</span> <span class="n">net</span><span class="o">.</span><span class="n">Conn</span><span class="p">)</span> <span class="p">{</span>
<span class="n">p</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">nsqd</span><span class="o">.</span><span class="n">logf</span><span class="p">(</span><span class="s">"TCP: new client(%s)"</span><span class="p">,</span> <span class="n">clientConn</span><span class="o">.</span><span class="n">RemoteAddr</span><span class="p">())</span>
<span class="c">// 先读取4个字节 作为 协议版本号</span>
<span class="n">buf</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="m">4</span><span class="p">)</span>
<span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">io</span><span class="o">.</span><span class="n">ReadFull</span><span class="p">(</span><span class="n">clientConn</span><span class="p">,</span> <span class="n">buf</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">p</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">nsqd</span><span class="o">.</span><span class="n">logf</span><span class="p">(</span><span class="s">"ERROR: failed to read protocol version - %s"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="n">protocolMagic</span> <span class="o">:=</span> <span class="kt">string</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">nsqd</span><span class="o">.</span><span class="n">logf</span><span class="p">(</span><span class="s">"CLIENT(%s): desired protocol magic '%s'"</span><span class="p">,</span>
<span class="n">clientConn</span><span class="o">.</span><span class="n">RemoteAddr</span><span class="p">(),</span> <span class="n">protocolMagic</span><span class="p">)</span>
<span class="k">var</span> <span class="n">prot</span> <span class="n">protocol</span><span class="o">.</span><span class="n">Protocol</span>
<span class="k">switch</span> <span class="n">protocolMagic</span> <span class="p">{</span>
<span class="k">case</span> <span class="s">" V2"</span><span class="o">:</span>
<span class="c">// protocolV2 实现了 V2 这个版本的协议</span>
<span class="n">prot</span> <span class="o">=</span> <span class="o">&</span><span class="n">protocolV2</span><span class="p">{</span><span class="n">ctx</span><span class="o">:</span> <span class="n">p</span><span class="o">.</span><span class="n">ctx</span><span class="p">}</span>
<span class="c">//假如有另外一个版本的协议</span>
<span class="c">//case " V3":</span>
<span class="c">// prot = &protocolV3{ctx: p.ctx}</span>
<span class="k">default</span><span class="o">:</span>
<span class="n">protocol</span><span class="o">.</span><span class="n">SendFramedResponse</span><span class="p">(</span><span class="n">clientConn</span><span class="p">,</span> <span class="n">frameTypeError</span><span class="p">,</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">(</span><span class="s">"E_BAD_PROTOCOL"</span><span class="p">))</span>
<span class="n">clientConn</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
<span class="n">p</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">nsqd</span><span class="o">.</span><span class="n">logf</span><span class="p">(</span><span class="s">"ERROR: client(%s) bad protocol magic '%s'"</span><span class="p">,</span>
<span class="n">clientConn</span><span class="o">.</span><span class="n">RemoteAddr</span><span class="p">(),</span> <span class="n">protocolMagic</span><span class="p">)</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="c">//交给具体protocol实现类处理每个连接</span>
<span class="n">err</span> <span class="o">=</span> <span class="n">prot</span><span class="o">.</span><span class="n">IOLoop</span><span class="p">(</span><span class="n">clientConn</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">p</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">nsqd</span><span class="o">.</span><span class="n">logf</span><span class="p">(</span><span class="s">"ERROR: client(%s) - %s"</span><span class="p">,</span> <span class="n">clientConn</span><span class="o">.</span><span class="n">RemoteAddr</span><span class="p">(),</span> <span class="n">err</span><span class="p">)</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Handle 函数先读取4个字节的字符串作为版本号, 根据版本号选用具体的 protocol 实现, 这里的 “ V2”这个版本的协议由protocolV2 实现</p>
<p>将协议封装, 方便以后不同协议的扩展</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="n">Protocol</span> <span class="k">interface</span> <span class="p">{</span>
<span class="n">IOLoop</span><span class="p">(</span><span class="n">conn</span> <span class="n">net</span><span class="o">.</span><span class="n">Conn</span><span class="p">)</span> <span class="kt">error</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="精简总流程">精简总流程:</h3>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">n</span> <span class="o">*</span><span class="n">NSQD</span><span class="p">)</span> <span class="n">Main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">tcpListener</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">net</span><span class="o">.</span><span class="n">Listen</span><span class="p">(</span><span class="s">"tcp"</span><span class="p">,</span> <span class="n">n</span><span class="o">.</span><span class="n">getOpts</span><span class="p">()</span><span class="o">.</span><span class="n">TCPAddress</span><span class="p">)</span>
<span class="n">tcpServer</span> <span class="o">:=</span> <span class="o">&</span><span class="n">tcpServer</span><span class="p">{</span><span class="n">ctx</span><span class="o">:</span> <span class="n">ctx</span><span class="p">}</span>
<span class="n">n</span><span class="o">.</span><span class="n">waitGroup</span><span class="o">.</span><span class="n">Wrap</span><span class="p">(</span><span class="k">func</span><span class="p">()</span> <span class="p">{</span>
<span class="n">protocol</span><span class="o">.</span><span class="n">TCPServer</span><span class="p">(</span><span class="n">n</span><span class="o">.</span><span class="n">tcpListener</span><span class="p">,</span> <span class="n">tcpServer</span><span class="p">,</span> <span class="n">n</span><span class="o">.</span><span class="n">getOpts</span><span class="p">()</span><span class="o">.</span><span class="n">Logger</span><span class="p">)</span>
<span class="p">})</span>
<span class="p">}</span>
<span class="k">func</span> <span class="n">TCPServer</span><span class="p">(</span><span class="n">listener</span> <span class="n">net</span><span class="o">.</span><span class="n">Listener</span><span class="p">,</span> <span class="n">handler</span> <span class="n">TCPHandler</span><span class="p">,</span> <span class="n">l</span> <span class="n">app</span><span class="o">.</span><span class="n">Logger</span><span class="p">)</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">{</span>
<span class="n">clientConn</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">listener</span><span class="o">.</span><span class="n">Accept</span><span class="p">()</span>
<span class="k">go</span> <span class="n">handler</span><span class="o">.</span><span class="n">Handle</span><span class="p">(</span><span class="n">clientConn</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">p</span> <span class="o">*</span><span class="n">tcpServer</span><span class="p">)</span> <span class="n">Handle</span><span class="p">(</span><span class="n">clientConn</span> <span class="n">net</span><span class="o">.</span><span class="n">Conn</span><span class="p">)</span> <span class="p">{</span>
<span class="n">buf</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="m">4</span><span class="p">)</span>
<span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">io</span><span class="o">.</span><span class="n">ReadFull</span><span class="p">(</span><span class="n">clientConn</span><span class="p">,</span> <span class="n">buf</span><span class="p">)</span>
<span class="n">protocolMagic</span> <span class="o">:=</span> <span class="kt">string</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span>
<span class="k">var</span> <span class="n">prot</span> <span class="n">protocol</span><span class="o">.</span><span class="n">Protocol</span>
<span class="k">switch</span> <span class="n">protocolMagic</span> <span class="p">{</span>
<span class="k">case</span> <span class="s">" V2"</span><span class="o">:</span>
<span class="n">prot</span> <span class="o">=</span> <span class="o">&</span><span class="n">protocolV2</span><span class="p">{</span><span class="n">ctx</span><span class="o">:</span> <span class="n">p</span><span class="o">.</span><span class="n">ctx</span><span class="p">}</span>
<span class="p">}</span>
<span class="n">err</span> <span class="o">=</span> <span class="n">prot</span><span class="o">.</span><span class="n">IOLoop</span><span class="p">(</span><span class="n">clientConn</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
Nsq源码阅读(1) 启动和优雅退出
2017-01-12T00:00:00-06:00
http://www.zhaoxiaodan.com/%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB/nsq源码阅读(1)-启动和优雅退出
<h2 id="启动-利用svc框架">启动, 利用svc框架</h2>
<p>nsq使用了svc框架来启动一个service, Run 时, 分别调用prg 实现的 Init 和 Start 方法 启动’program’,然后监听 后两个参数的信号量, 当信号量到达, 调用 prg 实现的 Stop 方法来退出</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">prg</span> <span class="o">:=</span> <span class="o">&</span><span class="n">program</span><span class="p">{}</span>
<span class="c">// svc 框架的Run 方法启动一个service</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">svc</span><span class="o">.</span><span class="n">Run</span><span class="p">(</span><span class="n">prg</span><span class="p">,</span> <span class="n">syscall</span><span class="o">.</span><span class="n">SIGINT</span><span class="p">,</span> <span class="n">syscall</span><span class="o">.</span><span class="n">SIGTERM</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">log</span><span class="o">.</span><span class="n">Fatal</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// svc 框架, 启动 service, 也可以理解为 daemon</span>
<span class="c">//</span>
<span class="c">// 方法会一直阻塞, 直到 指定的信号量到来</span>
<span class="c">// 如果指定信号为空, 默认是 syscall.SIGINT and syscall.SIGTERM</span>
<span class="k">func</span> <span class="n">Run</span><span class="p">(</span><span class="n">service</span> <span class="n">Service</span><span class="p">,</span> <span class="n">sig</span> <span class="o">...</span><span class="n">os</span><span class="o">.</span><span class="n">Signal</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
<span class="n">env</span> <span class="o">:=</span> <span class="n">environment</span><span class="p">{}</span>
<span class="c">//先调用Init 初始化</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">service</span><span class="o">.</span><span class="n">Init</span><span class="p">(</span><span class="n">env</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">err</span>
<span class="p">}</span>
<span class="c">//再调用Start</span>
<span class="c">//其实有时候根本分不太清楚哪里是init哪里是start</span>
<span class="c">//作为一个框架, 当然要做的优雅一点</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">service</span><span class="o">.</span><span class="n">Start</span><span class="p">();</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">err</span>
<span class="p">}</span>
<span class="c">//准备信号量处理</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">sig</span><span class="p">)</span> <span class="o">==</span> <span class="m">0</span> <span class="p">{</span>
<span class="n">sig</span> <span class="o">=</span> <span class="p">[]</span><span class="n">os</span><span class="o">.</span><span class="n">Signal</span><span class="p">{</span><span class="n">syscall</span><span class="o">.</span><span class="n">SIGINT</span><span class="p">,</span> <span class="n">syscall</span><span class="o">.</span><span class="n">SIGTERM</span><span class="p">}</span>
<span class="p">}</span>
<span class="n">signalChan</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="k">chan</span> <span class="n">os</span><span class="o">.</span><span class="n">Signal</span><span class="p">,</span> <span class="m">1</span><span class="p">)</span>
<span class="n">signalNotify</span><span class="p">(</span><span class="n">signalChan</span><span class="p">,</span> <span class="n">sig</span><span class="o">...</span><span class="p">)</span>
<span class="c">//整个程序会阻塞在这里, 等待系统信号量</span>
<span class="o"><-</span><span class="n">signalChan</span>
<span class="c">// 当信号来到, 调用stop 方法停掉</span>
<span class="k">return</span> <span class="n">service</span><span class="o">.</span><span class="n">Stop</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p>svc 生命周期三个方法的实现:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">p</span> <span class="o">*</span><span class="n">program</span><span class="p">)</span> <span class="n">Init</span><span class="p">(</span><span class="n">env</span> <span class="n">svc</span><span class="o">.</span><span class="n">Environment</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
<span class="c">//svc 封装了不同操作系统的Deamon服务进程相关的东西</span>
<span class="c">//TODO window deamon的不同点</span>
<span class="k">if</span> <span class="n">env</span><span class="o">.</span><span class="n">IsWindowsService</span><span class="p">()</span> <span class="p">{</span>
<span class="n">dir</span> <span class="o">:=</span> <span class="n">filepath</span><span class="o">.</span><span class="n">Dir</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">0</span><span class="p">])</span>
<span class="k">return</span> <span class="n">os</span><span class="o">.</span><span class="n">Chdir</span><span class="p">(</span><span class="n">dir</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">return</span> <span class="no">nil</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">p</span> <span class="o">*</span><span class="n">program</span><span class="p">)</span> <span class="n">Start</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span>
<span class="c">// 配置参数解析</span>
<span class="n">flagSet</span><span class="o">.</span><span class="n">Parse</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">1</span><span class="o">:</span><span class="p">])</span>
<span class="k">if</span> <span class="o">*</span><span class="n">showVersion</span> <span class="p">{</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="n">version</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="s">"nsqlookupd"</span><span class="p">))</span>
<span class="n">os</span><span class="o">.</span><span class="n">Exit</span><span class="p">(</span><span class="m">0</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">var</span> <span class="n">cfg</span> <span class="k">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="k">interface</span><span class="p">{}</span>
<span class="k">if</span> <span class="o">*</span><span class="n">config</span> <span class="o">!=</span> <span class="s">""</span> <span class="p">{</span>
<span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">toml</span><span class="o">.</span><span class="n">DecodeFile</span><span class="p">(</span><span class="o">*</span><span class="n">config</span><span class="p">,</span> <span class="o">&</span><span class="n">cfg</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">log</span><span class="o">.</span><span class="n">Fatalf</span><span class="p">(</span><span class="s">"ERROR: failed to load config file %s - %s"</span><span class="p">,</span> <span class="o">*</span><span class="n">config</span><span class="p">,</span> <span class="n">err</span><span class="o">.</span><span class="n">Error</span><span class="p">())</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">opts</span> <span class="o">:=</span> <span class="n">nsqlookupd</span><span class="o">.</span><span class="n">NewOptions</span><span class="p">()</span>
<span class="n">options</span><span class="o">.</span><span class="n">Resolve</span><span class="p">(</span><span class="n">opts</span><span class="p">,</span> <span class="n">flagSet</span><span class="p">,</span> <span class="n">cfg</span><span class="p">)</span>
<span class="n">daemon</span> <span class="o">:=</span> <span class="n">nsqlookupd</span><span class="o">.</span><span class="n">New</span><span class="p">(</span><span class="n">opts</span><span class="p">)</span>
<span class="c">// 这里是主要的启动方法</span>
<span class="n">daemon</span><span class="o">.</span><span class="n">Main</span><span class="p">()</span>
<span class="n">p</span><span class="o">.</span><span class="n">nsqlookupd</span> <span class="o">=</span> <span class="n">daemon</span>
<span class="k">return</span> <span class="no">nil</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">p</span> <span class="o">*</span><span class="n">program</span><span class="p">)</span> <span class="n">Stop</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">p</span><span class="o">.</span><span class="n">nsqlookupd</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">p</span><span class="o">.</span><span class="n">nsqlookupd</span><span class="o">.</span><span class="n">Exit</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">return</span> <span class="no">nil</span>
<span class="p">}</span>
</code></pre></div></div>
<p>去掉svc框架, 把启动流程整个精简拼凑:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">p</span><span class="o">.</span><span class="n">nsqlookupd</span><span class="o">.</span><span class="n">Main</span><span class="p">()</span>
<span class="o"><-</span><span class="n">signalChan</span>
<span class="n">p</span><span class="o">.</span><span class="n">nsqlookupd</span><span class="o">.</span><span class="n">Exit</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="优雅退出">优雅退出</h2>
<p>NSQLookupd 的真正的启动函数是Main</p>
<p>lookup服务主要有通过两种方式提供服务, tcp 和 http</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">func</span> <span class="p">(</span><span class="n">l</span> <span class="o">*</span><span class="n">NSQLookupd</span><span class="p">)</span> <span class="n">Main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">ctx</span> <span class="o">:=</span> <span class="o">&</span><span class="n">Context</span><span class="p">{</span><span class="n">l</span><span class="p">}</span>
<span class="n">tcpListener</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">net</span><span class="o">.</span><span class="n">Listen</span><span class="p">(</span><span class="s">"tcp"</span><span class="p">,</span> <span class="n">l</span><span class="o">.</span><span class="n">opts</span><span class="o">.</span><span class="n">TCPAddress</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">l</span><span class="o">.</span><span class="n">logf</span><span class="p">(</span><span class="s">"FATAL: listen (%s) failed - %s"</span><span class="p">,</span> <span class="n">l</span><span class="o">.</span><span class="n">opts</span><span class="o">.</span><span class="n">TCPAddress</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">Exit</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
<span class="p">}</span>
<span class="c">// 这里为什么要加锁?</span>
<span class="c">// 当前场景不好理解, 换个场景:</span>
<span class="c">//if l.playerList.count() > 0 {</span>
<span class="c">// 如果这里l 不 lock的话, 多线程 执行了 l.playerList = anotherPlayerList</span>
<span class="c">// 然后再 执行delete 就不对了</span>
<span class="c">// l.playerList.delete(n)</span>
<span class="c">//}</span>
<span class="n">l</span><span class="o">.</span><span class="n">Lock</span><span class="p">()</span>
<span class="n">l</span><span class="o">.</span><span class="n">tcpListener</span> <span class="o">=</span> <span class="n">tcpListener</span>
<span class="n">l</span><span class="o">.</span><span class="n">Unlock</span><span class="p">()</span>
<span class="n">tcpServer</span> <span class="o">:=</span> <span class="o">&</span><span class="n">tcpServer</span><span class="p">{</span><span class="n">ctx</span><span class="o">:</span> <span class="n">ctx</span><span class="p">}</span> <span class="c">// 这个就是handler</span>
<span class="c">//起一个tcp处理进程, 主要处理 client 和nsq server的连接</span>
<span class="n">l</span><span class="o">.</span><span class="n">waitGroup</span><span class="o">.</span><span class="n">Wrap</span><span class="p">(</span><span class="k">func</span><span class="p">()</span> <span class="p">{</span>
<span class="n">protocol</span><span class="o">.</span><span class="n">TCPServer</span><span class="p">(</span><span class="n">tcpListener</span><span class="p">,</span> <span class="n">tcpServer</span><span class="p">,</span> <span class="n">l</span><span class="o">.</span><span class="n">opts</span><span class="o">.</span><span class="n">Logger</span><span class="p">)</span>
<span class="p">})</span>
<span class="c">//nsqlookup 还支持http操作, 所以再起一个线程</span>
<span class="n">httpListener</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">net</span><span class="o">.</span><span class="n">Listen</span><span class="p">(</span><span class="s">"tcp"</span><span class="p">,</span> <span class="n">l</span><span class="o">.</span><span class="n">opts</span><span class="o">.</span><span class="n">HTTPAddress</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">l</span><span class="o">.</span><span class="n">logf</span><span class="p">(</span><span class="s">"FATAL: listen (%s) failed - %s"</span><span class="p">,</span> <span class="n">l</span><span class="o">.</span><span class="n">opts</span><span class="o">.</span><span class="n">HTTPAddress</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">Exit</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">l</span><span class="o">.</span><span class="n">Lock</span><span class="p">()</span>
<span class="n">l</span><span class="o">.</span><span class="n">httpListener</span> <span class="o">=</span> <span class="n">httpListener</span>
<span class="n">l</span><span class="o">.</span><span class="n">Unlock</span><span class="p">()</span>
<span class="n">httpServer</span> <span class="o">:=</span> <span class="n">newHTTPServer</span><span class="p">(</span><span class="n">ctx</span><span class="p">)</span>
<span class="n">l</span><span class="o">.</span><span class="n">waitGroup</span><span class="o">.</span><span class="n">Wrap</span><span class="p">(</span><span class="k">func</span><span class="p">()</span> <span class="p">{</span>
<span class="n">http_api</span><span class="o">.</span><span class="n">Serve</span><span class="p">(</span><span class="n">httpListener</span><span class="p">,</span> <span class="n">httpServer</span><span class="p">,</span> <span class="s">"HTTP"</span><span class="p">,</span> <span class="n">l</span><span class="o">.</span><span class="n">opts</span><span class="o">.</span><span class="n">Logger</span><span class="p">)</span>
<span class="p">})</span>
<span class="p">}</span>
</code></pre></div></div>
<p>现在我们就有了3个线程:</p>
<ul>
<li>Main线程, 启动两个服务进程后阻塞等待系统信号量</li>
<li>tcp 服务线程, 循环调用 listener.Accept() 接收连接请求</li>
<li>http 服务线程, 其实跟tcp一样, 经过http封装后, 循环处理http request 请求</li>
</ul>
<p>假如Main线程在接收到系统信号量之后直接退出, 那么其他两个线程也会跟着销毁. 于是这里就会出现一个问题, tcp服务线程 和 http服务线程, 是业务逻辑实现的主要线程, 里面可能正在”干活”, 此时需要退出, 是否有数据需要持久化了? 事务是否正好执行到一半?</p>
<p>所以退出时需要做优雅退出处理. Main所在线程退出之前, 需要 ‘通知’ 并且 ‘等待’ 两个服务线程退出后才能退出</p>
<p>为了实现这一点, nsq 利用了 sync包中的 waitgroup 组件</p>
<p>nsq 为了方便使用, 做了一个封装:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="n">WaitGroupWrapper</span> <span class="k">struct</span> <span class="p">{</span>
<span class="n">sync</span><span class="o">.</span><span class="n">WaitGroup</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">w</span> <span class="o">*</span><span class="n">WaitGroupWrapper</span><span class="p">)</span> <span class="n">Wrap</span><span class="p">(</span><span class="n">cb</span> <span class="k">func</span><span class="p">())</span> <span class="p">{</span>
<span class="n">w</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="m">1</span><span class="p">)</span> <span class="c">// +1</span>
<span class="c">// 起一个线程, 就是go 的 goroutine</span>
<span class="k">go</span> <span class="k">func</span><span class="p">()</span> <span class="p">{</span>
<span class="n">cb</span><span class="p">()</span> <span class="c">//执行任务</span>
<span class="n">w</span><span class="o">.</span><span class="n">Done</span><span class="p">()</span> <span class="c">// -1</span>
<span class="p">}()</span>
<span class="p">}</span>
</code></pre></div></div>
<p>那么比如nsq 启动 tcp 服务线程 的代码, 就相当于:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">w</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
<span class="k">go</span> <span class="k">func</span><span class="p">()</span> <span class="p">{</span>
<span class="n">protocol</span><span class="o">.</span><span class="n">TCPServer</span><span class="p">(</span><span class="n">tcpListener</span><span class="p">,</span> <span class="n">tcpServer</span><span class="p">,</span> <span class="n">l</span><span class="o">.</span><span class="n">opts</span><span class="o">.</span><span class="n">Logger</span><span class="p">)</span>
<span class="n">w</span><span class="o">.</span><span class="n">Done</span><span class="p">()</span>
<span class="p">}()</span>
<span class="n">w</span><span class="o">.</span><span class="n">Wait</span><span class="p">()</span> <span class="c">//Main 线程就会阻塞在这里; 等待TCPServer 结束时调用w.Done()后才退出</span>
</code></pre></div></div>
<h2 id="通知-goroutine-退出">通知 goroutine 退出</h2>
<p>好了, 现在Main 会等待TCPServer 退出之后再退出了, 那, Main要退出的时候, 如何通知 TCPServer 退出?</p>
<p>protocol.TCPServer 就是循环的监听 tcp listener的 accept 连接, 交给 handler 处理; 然后这个for 循环是一个死循环, 依靠 accept 返回 error 才退出</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// ==================== 注意, 这里的 TCPServer 运行在一个新的 goroutine 中</span>
<span class="c">// 这个TCPServer 也是一个可以复用的比较不错的 tcp 程序 accept 模式</span>
<span class="k">func</span> <span class="n">TCPServer</span><span class="p">(</span><span class="n">listener</span> <span class="n">net</span><span class="o">.</span><span class="n">Listener</span><span class="p">,</span> <span class="n">handler</span> <span class="n">TCPHandler</span><span class="p">,</span> <span class="n">l</span> <span class="n">app</span><span class="o">.</span><span class="n">Logger</span><span class="p">)</span> <span class="p">{</span>
<span class="n">l</span><span class="o">.</span><span class="n">Output</span><span class="p">(</span><span class="m">2</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"TCP: listening on %s"</span><span class="p">,</span> <span class="n">listener</span><span class="o">.</span><span class="n">Addr</span><span class="p">()))</span>
<span class="k">for</span> <span class="p">{</span>
<span class="n">clientConn</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">listener</span><span class="o">.</span><span class="n">Accept</span><span class="p">()</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">nerr</span><span class="p">,</span> <span class="n">ok</span> <span class="o">:=</span> <span class="n">err</span><span class="o">.</span><span class="p">(</span><span class="n">net</span><span class="o">.</span><span class="n">Error</span><span class="p">);</span> <span class="n">ok</span> <span class="o">&&</span> <span class="n">nerr</span><span class="o">.</span><span class="n">Temporary</span><span class="p">()</span> <span class="p">{</span>
<span class="n">l</span><span class="o">.</span><span class="n">Output</span><span class="p">(</span><span class="m">2</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"NOTICE: temporary Accept() failure - %s"</span><span class="p">,</span> <span class="n">err</span><span class="p">))</span>
<span class="n">runtime</span><span class="o">.</span><span class="n">Gosched</span><span class="p">()</span> <span class="c">// 是临时的错误, 暂停一下继续</span>
<span class="k">continue</span>
<span class="p">}</span>
<span class="c">// theres no direct way to detect this error because it is not exposed</span>
<span class="k">if</span> <span class="o">!</span><span class="n">strings</span><span class="o">.</span><span class="n">Contains</span><span class="p">(</span><span class="n">err</span><span class="o">.</span><span class="n">Error</span><span class="p">(),</span> <span class="s">"use of closed network connection"</span><span class="p">)</span> <span class="p">{</span>
<span class="n">l</span><span class="o">.</span><span class="n">Output</span><span class="p">(</span><span class="m">2</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"ERROR: listener.Accept() - %s"</span><span class="p">,</span> <span class="n">err</span><span class="p">))</span>
<span class="p">}</span>
<span class="k">break</span>
<span class="p">}</span>
<span class="k">go</span> <span class="n">handler</span><span class="o">.</span><span class="n">Handle</span><span class="p">(</span><span class="n">clientConn</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">l</span><span class="o">.</span><span class="n">Output</span><span class="p">(</span><span class="m">2</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"TCP: closing %s"</span><span class="p">,</span> <span class="n">listener</span><span class="o">.</span><span class="n">Addr</span><span class="p">()))</span>
<span class="p">}</span>
</code></pre></div></div>
<p>再看Exit函数:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// ==================== 注意, 这里 是 Main 所在线程</span>
<span class="k">func</span> <span class="p">(</span><span class="n">l</span> <span class="o">*</span><span class="n">NSQLookupd</span><span class="p">)</span> <span class="n">Exit</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">l</span><span class="o">.</span><span class="n">tcpListener</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="c">//Close 之后, listener.Accept() 就会返回error,</span>
<span class="c">//从而退出循环, TCPServer 线程退出</span>
<span class="n">l</span><span class="o">.</span><span class="n">tcpListener</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">if</span> <span class="n">l</span><span class="o">.</span><span class="n">httpListener</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">l</span><span class="o">.</span><span class="n">httpListener</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
<span class="p">}</span>
<span class="n">l</span><span class="o">.</span><span class="n">waitGroup</span><span class="o">.</span><span class="n">Wait</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p>把整个流程代码的主干拼凑在一起, 就比较明了了:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">l</span> <span class="o">*</span><span class="n">NSQLookupd</span><span class="p">)</span> <span class="n">Main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">w</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
<span class="k">go</span> <span class="k">func</span><span class="p">()</span> <span class="p">{</span>
<span class="c">//=== TCPServer 线程 start</span>
<span class="k">for</span> <span class="p">{</span>
<span class="n">clientConn</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">listener</span><span class="o">.</span><span class="n">Accept</span><span class="p">()</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">break</span>
<span class="p">}</span>
<span class="k">go</span> <span class="n">handler</span><span class="o">.</span><span class="n">Handle</span><span class="p">(</span><span class="n">clientConn</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">w</span><span class="o">.</span><span class="n">Done</span><span class="p">()</span>
<span class="c">//=== TCPServer 线程 end</span>
<span class="p">}()</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">l</span> <span class="o">*</span><span class="n">NSQLookupd</span><span class="p">)</span> <span class="n">Exit</span><span class="p">()</span> <span class="p">{</span>
<span class="n">l</span><span class="o">.</span><span class="n">tcpListener</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
<span class="n">l</span><span class="o">.</span><span class="n">waitGroup</span><span class="o">.</span><span class="n">Wait</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p>如果把 svc 的也套进来, 整个程序的 启动流程就是:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
<span class="c">// 告诉wg 准备要启动一个goroutine</span>
<span class="n">waitGroup</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
<span class="k">go</span> <span class="k">func</span><span class="p">()</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">{</span>
<span class="n">clientConn</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">listener</span><span class="o">.</span><span class="n">Accept</span><span class="p">()</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">break</span>
<span class="p">}</span>
<span class="k">go</span> <span class="n">handler</span><span class="o">.</span><span class="n">Handle</span><span class="p">(</span><span class="n">clientConn</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">waitGroup</span><span class="o">.</span><span class="n">Done</span><span class="p">()</span>
<span class="p">}()</span>
<span class="c">// 当退出信号到达</span>
<span class="o"><-</span><span class="n">signalChan</span>
<span class="c">// 关闭listener</span>
<span class="n">tcpListener</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
<span class="c">//检查并等待 所有的 goroutine 都结束, 而不是强制退出</span>
<span class="n">waitGroup</span><span class="o">.</span><span class="n">Wait</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p>这里信号量到达之后, 做了一个 tcpListener.Close(), 其实这个动作的目的是通知 tcpListener 服务所在的线程退出</p>
<p>这里直接利用了 listener 被 close 之后再 accept 调用会返回error的特性, 既退出了线程, 又关闭了 listener, 一举两得</p>
<p>nsq 里还有各种退出线程的方式</p>
<h3 id="精简总流程">精简总流程:</h3>
<p>这里的大框架是app 启动和优雅退出的总流程</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
<span class="c">// 告诉wg 准备要启动一个goroutine</span>
<span class="n">waitGroup</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
<span class="k">go</span> <span class="k">func</span><span class="p">()</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">{</span>
<span class="n">clientConn</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">listener</span><span class="o">.</span><span class="n">Accept</span><span class="p">()</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">break</span>
<span class="p">}</span>
<span class="k">go</span> <span class="n">handler</span><span class="o">.</span><span class="n">Handle</span><span class="p">(</span><span class="n">clientConn</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">waitGroup</span><span class="o">.</span><span class="n">Done</span><span class="p">()</span>
<span class="p">}()</span>
<span class="c">// 当退出信号到达</span>
<span class="o"><-</span><span class="n">signalChan</span>
<span class="c">// 关闭listener</span>
<span class="c">// tcp 服务线程也会因为accept 返回error 而退出</span>
<span class="n">tcpListener</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
<span class="c">//检查并等待 所有的 goroutine 都结束, 而不是强制退出</span>
<span class="n">waitGroup</span><span class="o">.</span><span class="n">Wait</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
如何把android设备中的固件dump出来
2016-11-10T00:00:00-06:00
http://www.zhaoxiaodan.com/android/如何把android设备中的固件dump出来
<p>android固件是在mtdblock中, 但是会有很多个block,</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@android: <span class="c"># cat /proc/partitions</span>
major minor <span class="c">#blocks name</span>
31 0 4096 mtdblock0
31 1 16384 mtdblock1
31 2 16384 mtdblock2
31 3 16384 mtdblock3
31 4 393216 mtdblock4
31 5 131072 mtdblock5
31 6 2097152 mtdblock6
31 7 4096 mtdblock7
31 8 524288 mtdblock8
31 9 4509696 mtdblock9
179 0 3941376 mmcblk0
179 1 3941344 mmcblk0p1
</code></pre></div></div>
<p>各个block分别对应哪个分区?</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@android: <span class="c"># cat /proc/mtd</span>
dev: size erasesize name
mtd0: 00400000 00004000 <span class="s2">"misc"</span>
mtd1: 01000000 00004000 <span class="s2">"kernel"</span>
mtd2: 01000000 00004000 <span class="s2">"boot"</span>
mtd3: 01000000 00004000 <span class="s2">"recovery"</span>
mtd4: 18000000 00004000 <span class="s2">"backup"</span>
mtd5: 08000000 00004000 <span class="s2">"cache"</span>
mtd6: 80000000 00004000 <span class="s2">"userdata"</span>
mtd7: 00400000 00004000 <span class="s2">"kpanic"</span>
mtd8: 20000000 00004000 <span class="s2">"system"</span>
mtd9: 113400000 00004000 <span class="s2">"user"</span>
</code></pre></div></div>
<p>这样我们就知道了, mtd1 是kernel 所在, 所以我们想dump kernel.img 就用dd即可:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@android: <span class="c"># dd if=/dev/block/mtdblock1 of=/mnt/external_sd/dd.out/kernel.img</span>
</code></pre></div></div>
七周七并发模型笔记
2016-08-31T00:00:00-05:00
http://www.zhaoxiaodan.com/java/七周七并发模型笔记
<h3 id="1-交替锁">1. 交替锁</h3>
<p>书中内容:</p>
<blockquote>
<blockquote>
</blockquote>
</blockquote>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="kd">class</span> <span class="nc">ConcurrentSortedList</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">class</span> <span class="nc">Node</span> <span class="o">{</span>
<span class="kt">int</span> <span class="n">value</span><span class="o">;</span>
<span class="nc">Node</span> <span class="n">prev</span><span class="o">;</span>
<span class="nc">Node</span> <span class="n">next</span><span class="o">;</span>
<span class="nc">ReentrantLock</span> <span class="n">lock</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ReentrantLock</span><span class="o">();</span>
<span class="nc">Node</span><span class="o">()</span> <span class="o">{}</span>
<span class="nc">Node</span><span class="o">(</span><span class="kt">int</span> <span class="n">value</span><span class="o">,</span> <span class="nc">Node</span> <span class="n">prev</span><span class="o">,</span> <span class="nc">Node</span> <span class="n">next</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">value</span> <span class="o">=</span> <span class="n">value</span><span class="o">;</span> <span class="k">this</span><span class="o">.</span><span class="na">prev</span> <span class="o">=</span> <span class="n">prev</span><span class="o">;</span> <span class="k">this</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="n">next</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="nc">Node</span> <span class="n">head</span><span class="o">;</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="nc">Node</span> <span class="n">tail</span><span class="o">;</span>
<span class="kd">public</span> <span class="nf">ConcurrentSortedList</span><span class="o">()</span> <span class="o">{</span>
<span class="n">head</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Node</span><span class="o">();</span> <span class="n">tail</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Node</span><span class="o">();</span>
<span class="n">head</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="n">tail</span><span class="o">;</span> <span class="n">tail</span><span class="o">.</span><span class="na">prev</span> <span class="o">=</span> <span class="n">head</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">insert</span><span class="o">(</span><span class="kt">int</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">Node</span> <span class="n">current</span> <span class="o">=</span> <span class="n">head</span><span class="o">;</span>
<span class="n">current</span><span class="o">.</span><span class="na">lock</span><span class="o">.</span><span class="na">lock</span><span class="o">();</span>
<span class="nc">Node</span> <span class="n">next</span> <span class="o">=</span> <span class="n">current</span><span class="o">.</span><span class="na">next</span><span class="o">;</span>
<span class="k">try</span> <span class="o">{</span>
<span class="k">while</span> <span class="o">(</span><span class="kc">true</span><span class="o">)</span> <span class="o">{</span>
<span class="n">next</span><span class="o">.</span><span class="na">lock</span><span class="o">.</span><span class="na">lock</span><span class="o">();</span>
<span class="k">try</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">next</span> <span class="o">==</span> <span class="n">tail</span> <span class="o">||</span> <span class="n">next</span><span class="o">.</span><span class="na">value</span> <span class="o"><</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">Node</span> <span class="n">node</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Node</span><span class="o">(</span><span class="n">value</span><span class="o">,</span> <span class="n">current</span><span class="o">,</span> <span class="n">next</span><span class="o">);</span>
<span class="n">next</span><span class="o">.</span><span class="na">prev</span> <span class="o">=</span> <span class="n">node</span><span class="o">;</span>
<span class="n">current</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="n">node</span><span class="o">;</span>
<span class="k">return</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span> <span class="k">finally</span> <span class="o">{</span> <span class="n">current</span><span class="o">.</span><span class="na">lock</span><span class="o">.</span><span class="na">unlock</span><span class="o">();</span> <span class="o">}</span>
<span class="n">current</span> <span class="o">=</span> <span class="n">next</span><span class="o">;</span>
<span class="n">next</span> <span class="o">=</span> <span class="n">current</span><span class="o">.</span><span class="na">next</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span> <span class="k">finally</span> <span class="o">{</span> <span class="n">next</span><span class="o">.</span><span class="na">lock</span><span class="o">.</span><span class="na">unlock</span><span class="o">();</span> <span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<blockquote>
<p>insert()方法保证链表是有序的:遍历链表直到找到第一个值小于新插入值的节点位置,在这个位置前插入新节点。</p>
</blockquote>
<blockquote>
<p>第26行锁住了链表的头节点,第30行锁住了下一个节点。接下来检测两个节点之间是否是待插入位置。如果不是,则在第38行解锁当前节点并继续遍历。如果找到待插入位置,第33~36行构造新节点并将其插入链表后返回。两把锁的解锁操作在两个finally块中进行(第38行和第42行)。</p>
</blockquote>
<blockquote>
<p>这种方案不仅可以让多个线程并发地进行链表插入操作,还能让其他的链表操作安全地并发。比如计算链表节点个数,只需倒序遍历链表即可:</p>
</blockquote>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">int</span> <span class="nf">size</span><span class="o">()</span> <span class="o">{</span>
<span class="nc">Node</span> <span class="n">current</span> <span class="o">=</span> <span class="n">tail</span><span class="o">;</span>
<span class="kt">int</span> <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
<span class="k">while</span> <span class="o">(</span><span class="n">current</span><span class="o">.</span><span class="na">prev</span> <span class="o">!=</span> <span class="n">head</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">ReentrantLock</span> <span class="n">lock</span> <span class="o">=</span> <span class="n">current</span><span class="o">.</span><span class="na">lock</span><span class="o">;</span>
<span class="n">lock</span><span class="o">.</span><span class="na">lock</span><span class="o">();</span>
<span class="k">try</span> <span class="o">{</span>
<span class="o">++</span><span class="n">count</span><span class="o">;</span>
<span class="n">current</span> <span class="o">=</span> <span class="n">current</span><span class="o">.</span><span class="na">prev</span><span class="o">;</span>
<span class="o">}</span> <span class="k">finally</span> <span class="o">{</span> <span class="n">lock</span><span class="o">.</span><span class="na">unlock</span><span class="o">();</span> <span class="o">}</span>
<span class="o">}</span>
<span class="k">return</span> <span class="n">count</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<p>最开始读到这里的时候就想, 这种不把整个列表锁住的size() 方法, 其实计算出来的列表<code class="language-plaintext highlighter-rouge">个数</code>这个状态是很奇怪的; 因为, 你说这个个数是这个链表在什么<code class="language-plaintext highlighter-rouge">时间点</code>下的个数?</p>
<p>计算结束时刻? 100个元素的链表, 照上面的方法, 计算到第1个的时候, 是可以在第98和99个之间插入元素的. 那么计算结束得到的个数是100个, 但是结束的那一刹那, 列表实际的元素个数是101个; 计算开始时刻? 跟结束时刻同理, 计算开始时, 实际个数是100个, 中途在”遍历指针”之前插入元素, 计算得到的结果就会比 开始时刻多.</p>
<p>我们姑且不去计较这个”个数”是什么时间点的, 只要得到就行; 那么一下子的反应就是, 既然不用计较时间点, 那么我还需要加锁干嘛呢? 直接一个个遍历就是了;</p>
<p>仔细想了下, 这里有个容易出错的地方, 比如一个A-B的链表中插入C, 这个时候计算size, 假如插入的赋值顺序写成了:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">B</span><span class="o">.</span><span class="na">prev</span> <span class="o">=</span> <span class="no">C</span> <span class="c1">//(1)</span>
<span class="no">C</span><span class="o">.</span><span class="na">prev</span> <span class="o">=</span> <span class="no">A</span>
</code></pre></div></div>
<p>那这样在并发size() 的时候, 插入操作刚执行完1, 就会导致 C.prev 是空的情况, size()要么异常退出, 要么计算不正确. 所以正确的方式应该是:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">C</span><span class="o">.</span><span class="na">prev</span> <span class="o">=</span> <span class="no">A</span> <span class="c1">// (1)</span>
<span class="no">B</span><span class="o">.</span><span class="na">prev</span> <span class="o">=</span> <span class="no">C</span> <span class="c1">// (2)</span>
</code></pre></div></div>
<p>但是问题就恰恰在这里, 这也是很容易忽略的地方; 这个仅仅是”正确的执行顺序”, 而不是正确的写法; 这么写并不会得到这么执行这个结果; 这两个语句的执行顺序不是”一定”的; 也就是指令乱序或者编译器优化导致乱序的情况</p>
<p>所以, 这个插入操作必须作为一个”互斥块”, 就算是相对于”只读” 的size() 操作</p>
Rk3128机顶盒编译
2016-07-06T00:00:00-05:00
http://www.zhaoxiaodan.com/mac/rk3128机顶盒编译
<h1 id="rk3128编译过程">RK3128编译过程</h1>
<ol>
<li>安装ubuntu14.04系统,Java jdk1.6。</li>
<li>安装android编译依赖</li>
</ol>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">sudo </span>apt-get <span class="nb">install </span>vim git git-core zip unzip rar unrar curl minicom valgrind gawk
<span class="nb">sudo </span>apt-get <span class="nb">install </span>gnupg flex bison gperf build-essential zlib1g-dev gcc-multilib <span class="se">\</span>
g++-multilib libc6-dev libc6-dev-i386 lib32ncurses5-dev x11proto-core-dev libx11-dev <span class="se">\</span>
xsltproc lib32readline-gplv2-dev lib32z1-dev libxml2-utils imagemagick lzop libesd0-dev <span class="se">\</span>
libwxgtk2.8-dev zlib1g-dev libncurses5-dev lib32z1-dev lib32bz2-dev lib32ncurses5-dev <span class="se">\</span>
lib32z-dev libgl1-mesa-dev mingw32 tofrodos gcc-4.4 g++-4.4 g++-4.4-multilib
<span class="c">#降级gcc和g++版本</span>
<span class="nb">sudo rm</span> /usr/bin/gcc /usr/bin/g++
<span class="nb">sudo ln</span> <span class="nt">-s</span> /usr/bin/gcc-4.4 /usr/bin/gcc
<span class="nb">sudo ln</span> <span class="nt">-s</span> /usr/bin/g++-4.4 /usr/bin/g++
</code></pre></div></div>
<ol>
<li>同步源码</li>
</ol>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">mkdir </span>rk3128
<span class="nb">tar </span>xvf rk3128_box_kitkat_rel_v1.00_141010.tar –C rk3128
<span class="nb">cd </span>rk3128
.repo/repo/repo <span class="nb">sync</span> <span class="nt">-l</span>
</code></pre></div></div>
<ol>
<li>编译内核</li>
</ol>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">cd </span>kernel
make rockchip_defconfig
make rk3128-box-rk88.img(我们的板是RK88 样机)
(编译完后将在 kernel 目录生成 resource.img 和 kernel.img)
</code></pre></div></div>
<ol>
<li>编译uboot</li>
</ol>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code> make rk3128_defconfig
make
</code></pre></div></div>
<ol>
<li>Android sdk编译</li>
</ol>
<p>注意:此 SDK 默认是 512M 内存版本,对低内存做了优化,但此优化会对性能产生一些影响,
并且对系统做了些裁剪,如果是 1G 的内存,请把\device\rockchip\rk312x\ BoardConfig.mk
BOARD_USE_LOW_MEM := true 设置为 false(我们rk88样机的板512M的也可以使用1G的软件)</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">source </span>build/envsetup.sh
make
(默认 SDK 以 eng 模式编译。如要使用 user 模式编译固件,可屏蔽 buildspec.mk 中的注释,如下:TARGET_BUILD_VARIANT:<span class="o">=</span>user)
</code></pre></div></div>
<ol>
<li>生成可以启动的img文件</li>
</ol>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ./mkimage.sh
<span class="o">(</span>生成的img文件在根目录的rockdev目录下,烧写镜像要使用该目录下的img文件而不是out/target/product/rkxx目录下的img文件<span class="o">)</span>
</code></pre></div></div>
<h1 id="工厂工具使用的固件打包方式">工厂工具使用的固件打包方式:</h1>
<h3 id="1updateimg-简介">1.update.img 简介</h3>
<p>update.img 固件是以RK自己的打包方式生成的。使用烧写工具生成。</p>
<h3 id="2制作过程">2.制作过程</h3>
<ol>
<li>在ubuntu中系统源码编译完毕后,执行./mkimage.sh在根目录rockdev中生成可以启动的boot.img,misc.img, system.img, recovery.img等文件,拷贝到windows系统中。</li>
<li>切换到windows中,进入烧写工具目录中。</li>
<li>拷贝uboot-rk3128.img ,kernel.img ,boot.img, recovery.img,misc.img,resource.img,system.img 到rockdev\Image目录下。</li>
<li>双击执行rockdev\目录下的mkupdate.bat脚本,会在rockdev目录下生成update.img镜像</li>
</ol>
<h3 id="3烧写过程">3.烧写过程</h3>
<p>使用工厂工具升级</p>
<h1 id="ota完整升级包打包方式">OTA完整升级包打包方式</h1>
<h3 id="1-ota--完整包简介">1. OTA 完整包简介</h3>
<p>OTA 完整包会先把 kernel.img 通过命令打包到 boot.img 文件中,然后把
system.img, boot.img,recovery.img,misc.img 文件打包到 update.zip 文件中去,
另外可以通过特定的方式把 bootloader 和 paramter 打包到 update.zip 中。所以 OTA
完整包会比较大,并不太适合远程服务器升级,远程服务器升级一般使用差异包。不
过完整包少了差异包的一些限制,具有更好的通用性。</p>
<h3 id="2-制作过程">2. 制作过程</h3>
<ol>
<li>在ubuntu中编译完kernel,uboot,sdk系统后</li>
<li>执行./mkimage.sh ota 命令</li>
<li>执行 make otapackage -j8 命令</li>
<li>在out/target/product/rkxxsdk/目录下, 生成文件名类似 rkxxsdk-ota-eng.root.zip 格式的ota包。
<h3 id="3-升级过程">3. 升级过程</h3>
<p>把rkxxsdk-ota-eng.root.zip文件重命名 update.zip 并拷贝到 SD 卡,FLASH 以及 U 盘的根目录下。升级应用在收到
Intent.ACTION_BOOT_COMPLETEDD,Intent.ACTION_MEDIA_MOUNTED,
UsbManager.ACTION_USB_STATE,ConnectivityManager.CONNECTIVITY_ACTION 等广播
时候就会去检测上述路径。 当检测到有 update.zip 后会去验证校验, 验证成功后会弹
回对话框提示升级。</p>
</li>
</ol>
黑苹果折腾记
2016-05-24T00:00:00-05:00
http://www.zhaoxiaodan.com/mac/黑苹果折腾记
<h2 id="mbr转gpt分区-导致mac分区无法启动">mbr转gpt分区, 导致mac分区无法启动</h2>
<p>clover引导后启动, 出现一个圆圈禁止;分区软件看,mac分区不太正常;需要将分区GUID更改为:48465300-0000-11AA-AA11-00306543ECAC(HFS)</p>
<p>使用diskpart 恢复</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list disk
select disk X
list partition
select partition X
set id=48465300-0000-11AA-AA11-00306543ECAC
</code></pre></div></div>
<h2 id="系统升级之后nvidia驱动不支持的解决">系统升级之后NVIDIA驱动不支持的解决</h2>
<p>今天手贱升级了mac10.11.6 beta 版本, 结果n卡的驱动无法加载了; 重新安装还提示新版系统不支持; 解压开驱动的安装包<code class="language-plaintext highlighter-rouge">WebDriver-346.03.10f01.pkg</code>, 里面有个<code class="language-plaintext highlighter-rouge">Distribution</code> 文件里有段js脚本是检查系统版本的:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">function</span> <span class="nf">validateSoftware</span><span class="o">()</span>
<span class="o">{</span>
<span class="kt">var</span> <span class="n">supportedOSVer</span> <span class="o">=</span> <span class="s">"10.11.5"</span><span class="o">;</span>
<span class="kt">var</span> <span class="n">supportedOSBuildVer</span> <span class="o">=</span> <span class="s">"15F34"</span><span class="o">;</span>
<span class="kt">var</span> <span class="n">targetBuild</span> <span class="o">=</span> <span class="n">system</span><span class="o">.</span><span class="na">version</span><span class="o">.</span><span class="na">ProductBuildVersion</span><span class="o">;</span>
<span class="kt">var</span> <span class="n">result</span> <span class="o">=</span> <span class="n">compareBuildVersions</span><span class="o">(</span><span class="n">targetBuild</span><span class="o">,</span> <span class="n">supportedOSBuildVer</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">result</span> <span class="o">!=</span> <span class="mi">0</span><span class="o">)</span>
<span class="o">{</span>
<span class="kt">var</span> <span class="n">title</span><span class="o">;</span>
<span class="kt">var</span> <span class="n">msg</span><span class="o">;</span>
<span class="kt">var</span> <span class="n">osVersionString</span> <span class="o">=</span> <span class="n">system</span><span class="o">.</span><span class="na">version</span><span class="o">.</span><span class="na">ProductVersion</span><span class="o">;</span>
<span class="k">if</span> <span class="o">((</span><span class="n">targetBuild</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="mi">2</span><span class="o">)</span> <span class="o">==</span> <span class="n">supportedOSBuildVer</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="mi">2</span><span class="o">))</span> <span class="o">&</span><span class="n">amp</span><span class="o">;&</span><span class="n">amp</span><span class="o">;</span> <span class="o">(</span><span class="n">result</span> <span class="o">&</span><span class="n">lt</span><span class="o">;</span> <span class="mi">0</span><span class="o">))</span>
<span class="o">{</span>
<span class="n">title</span> <span class="o">=</span> <span class="n">system</span><span class="o">.</span><span class="na">localizedString</span><span class="o">(</span><span class="s">"need_os_update_title"</span><span class="o">);</span>
<span class="n">msg</span> <span class="o">=</span> <span class="n">system</span><span class="o">.</span><span class="na">localizedStringWithFormat</span><span class="o">(</span><span class="s">"need_os_update_message"</span><span class="o">,</span> <span class="n">supportedOSVer</span><span class="o">,</span> <span class="n">supportedOSBuildVer</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">else</span>
<span class="o">{</span>
<span class="n">title</span> <span class="o">=</span> <span class="n">system</span><span class="o">.</span><span class="na">localizedString</span><span class="o">(</span><span class="s">"visit_website_title"</span><span class="o">);</span>
<span class="n">msg</span> <span class="o">=</span> <span class="n">system</span><span class="o">.</span><span class="na">localizedStringWithFormat</span><span class="o">(</span><span class="s">"visit_website_message"</span><span class="o">,</span> <span class="n">osVersionString</span><span class="o">,</span> <span class="n">targetBuild</span><span class="o">);</span>
<span class="o">}</span>
<span class="n">my</span><span class="o">.</span><span class="na">result</span><span class="o">.</span><span class="na">title</span> <span class="o">=</span> <span class="n">title</span><span class="o">;</span>
<span class="n">my</span><span class="o">.</span><span class="na">result</span><span class="o">.</span><span class="na">message</span> <span class="o">=</span> <span class="n">msg</span><span class="o">;</span>
<span class="n">my</span><span class="o">.</span><span class="na">result</span><span class="o">.</span><span class="na">type</span> <span class="o">=</span> <span class="s">"Fatal"</span><span class="o">;</span>
<span class="k">return</span> <span class="kc">false</span><span class="o">;</span>
<span class="o">}</span>
<span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<p>那么尝试修改里面的值为’10.11.6’ 和 ‘15G37’, 再次安装, 检测可以通过, 但是安装到一半还是报错; 好吧, 既然安装脚本改了不行, 那想办法把系统版本号’10.11.6’改回’10.11.5’行不行?</p>
<p>系统版本的配置在这个文件里: <code class="language-plaintext highlighter-rouge">/System/Library/CoreServices/System Events.app'</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ProductBuildVersion</key>
<string>15G37</string>
<key>ProductCopyright</key>
<string>1983-2016 Apple Inc.</string>
<key>ProductName</key>
<string>Mac OS X</string>
<key>ProductUserVisibleVersion</key>
<string>10.11.6</string>
<key>ProductVersion</key>
<string>10.11.6</string>
</dict>
</plist>
</code></pre></div></div>
<p>把里面相应的数值修改保存之后, 安装成功; 重启后, 驱动正确加载!</p>
<h2 id="wifi频繁掉线的解决办法">wifi频繁掉线的解决办法</h2>
<p>之前买了个pci的wifi双频网卡, 免驱, 很完美, 但是后来升级了10.11.5之后, wifi开始频繁掉线; 搜索了一下, 需要重置 nvram.</p>
<p>苹果官方的办法, 在macbook上 :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>重置 NVRAM
关闭 Mac。
在键盘上找到以下按键:Command (⌘)、Option、P 和 R。
打开 Mac。
听到启动声后立即按住 Command-Option-P-R 键。
按住这些按键直到电脑重新启动,然后您会再次听到启动声。
松开这些按键。
</code></pre></div></div>
<p>其实不用这么麻烦, 有个命令叫’nvram’</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nvram: (usage: no such option as -h)
nvram [-x] [-p] [-f filename] [-d name] [-c] name[=value] ...
-x use XML format for printing or reading variables
(must appear before -p or -f)
-p print all firmware variables
-f set firmware variables from a text file
-d delete the named variable
-c delete all variables
name=value set named variable
name print variable
Note that arguments and options are executed in order.
</code></pre></div></div>
<p>所以, 使用<code class="language-plaintext highlighter-rouge">nvram -c</code> 即可重置;</p>
<p>重置之后显示所有参数值:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>➜ ~ nvram -p
bootercfg (%00
prev-lang:kbd en:0
fmm-computer-name iMac
security-mode none
SystemAudioVolumeDB %ef
SystemAudioVolume /
csr-active-config g%00%00%00
</code></pre></div></div>
<h2 id="e3-1245-v2-显卡-p4000-驱动">E3-1245 v2 显卡 P4000 驱动</h2>
<p>使用<code class="language-plaintext highlighter-rouge">https://github.com/RehabMan/OS-X-Fake-PCI-ID</code> 这个工程里的 <code class="language-plaintext highlighter-rouge">FakePCIID.kext</code> 和 <code class="language-plaintext highlighter-rouge">FakePCIID_Intel_HD_Graphics.kext</code> 放到 S/L/E 中 然后重建缓存;
config.plist 中 <code class="language-plaintext highlighter-rouge">Devices</code> -> <code class="language-plaintext highlighter-rouge">IntelGFX</code> 的 FakeID 设置为<code class="language-plaintext highlighter-rouge">0x01668086</code> , 这个是 P4000 的 设备id, 这个id 可以从 开始菜单 -> 设备报告 -> 图形卡/显示器 中看到; 0x016A 就是 设备ID, 0x8086 是供应商id;</p>
Rust学习笔记
2016-03-25T00:00:00-05:00
http://www.zhaoxiaodan.com/%E5%85%B6%E4%BB%96/Rust学习笔记
<p>初步学习, 感觉Rust的内存安全是靠编译的时候检查你的”错误写法”来实现的. 按网上的说法: c++开发, 经验教你做人; Rust开发, 编译器教你做人;
那么Rust在设计的时候, 就使用一些’原则’来避免你写出并发错误的程序. Rust就像那些C++书一样, 在不停的告诉你, 你不能这样写不能那样写;</p>
<h3 id="1-原则">1. 原则</h3>
<p>就比如:多线程编程最容易写错的就是并发写, 而这个问题最最必要的一个条件就是:”当 2 个或更多个指针同时访问同一内存位置,当它们中至少有 1 个在写”;
两个线程访问一块内存, 肯定有两个分别位于各自线程的指针, 指向这个内存; 那试想一下,我规定一个内存只能有一个可写指针引用, 你可能写出错误的程序吗? 所以, Rust整个设计就是围绕这个部分展开的;</p>
<h3 id="2-所有权-和-move-语意">2. 所有权 和 move 语意</h3>
<p>为了满足这个基本原则, Rust对所有资源(也可以理解为内存)做了’所有权’的概念. 同时设计了第一个原则:</p>
<ul>
<li>一个资源只能有一个’变量’拥有所有权;</li>
</ul>
<p>那当你把一个变量引用的资源像java哪样’赋值给’另一个变量的时候, 得到的结果不再是:<code class="language-plaintext highlighter-rouge">两个变量同时引用(指向)这个资源</code>, 而是<code class="language-plaintext highlighter-rouge">把资源的所有权交给它, 自己不再拥有(指向)资源</code></p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">let</span> <span class="n">v1</span> <span class="o">=</span> <span class="n">vec</span><span class="o">!</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">];</span>
<span class="n">println</span><span class="o">!</span><span class="p">(</span><span class="s">"v1: {:p} = {:?}"</span><span class="p">,</span> <span class="o">&</span><span class="n">v1</span><span class="p">,</span> <span class="n">v1</span><span class="p">);</span>
<span class="p">{</span>
<span class="n">let</span> <span class="n">v2</span> <span class="o">=</span> <span class="n">v1</span><span class="p">;</span> <span class="c1">// v1 的所有权 move 给了v2</span>
<span class="n">println</span><span class="o">!</span><span class="p">(</span><span class="s">"v2: {:p} = {:?}"</span><span class="p">,</span> <span class="o">&</span><span class="n">v2</span><span class="p">,</span> <span class="n">v2</span><span class="p">);</span> <span class="c1">// v2 和 v1 是不同的地址</span>
<span class="c1">// println!("v1: {:p} = {:?}", &v1, v1); // 这里 v1 就不能再使用了</span>
<span class="p">}</span>
<span class="c1">// println!("v1: {:p} = {:?}", &v1, v1); // 即使v2 离开作用域, 也不行. 交了就是交了, 不会还回来</span>
</code></pre></div></div>
<p>好了, 现在v2获得所有权之后是不会再还给v1的; 满足了原则, 但是不能’=’操作了, 程序没法写了. 于是rust又做了一个<code class="language-plaintext highlighter-rouge">借用</code>的概念, 也就是说:</p>
<ul>
<li>在v2的作用域内, v1借给v2资源</li>
<li>v2离开作用域, 还给v1</li>
</ul>
<h3 id="3-借用">3. 借用</h3>
<p>好, 既然可以借用, 那在v1v2共同的作用域内, 岂不是又有两个’变量’‘指向’同一个资源? 于是, Rust又设计了这样一个原则:</p>
<blockquote>
<p>在同一个作用域内, 一个资源只可以有:</p>
<ul>
<li>0 个或 N 个资源的不可变引用(&T)</li>
<li>只有 1 个可变引用((&mut T)</li>
</ul>
</blockquote>
<p>这不就是读写锁嘛. 对, rust就是在’变量赋值引用’做了hack, 进行内存的读写隔离!</p>
<p>看下面的代码:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">let</span> <span class="n">mut</span> <span class="n">v1</span> <span class="o">=</span> <span class="n">vec</span><span class="o">!</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">];</span>
<span class="n">println</span><span class="o">!</span><span class="p">(</span><span class="s">"v1: {:p} = {:?}"</span><span class="p">,</span> <span class="o">&</span><span class="n">v1</span><span class="p">,</span> <span class="n">v1</span><span class="p">);</span>
<span class="p">{</span>
<span class="c1">// <----------------作用域开始</span>
<span class="n">let</span> <span class="n">v2</span> <span class="o">=</span> <span class="o">&</span><span class="n">v1</span><span class="p">;</span> <span class="c1">// 对资源的不可变引用</span>
<span class="n">let</span> <span class="n">v3</span> <span class="o">=</span> <span class="o">&</span><span class="n">v1</span><span class="p">;</span> <span class="c1">// 不可变引用 2</span>
<span class="n">println</span><span class="o">!</span><span class="p">(</span><span class="s">"v2: {:p} = {:?}"</span><span class="p">,</span> <span class="n">v2</span><span class="p">,</span> <span class="o">*</span><span class="n">v2</span><span class="p">);</span>
<span class="n">println</span><span class="o">!</span><span class="p">(</span><span class="s">"v3: {:p} = {:?}"</span><span class="p">,</span> <span class="n">v3</span><span class="p">,</span> <span class="o">*</span><span class="n">v3</span><span class="p">);</span>
<span class="c1">// let v4 = &mut v1; // 已经有了不可变引用v2v3</span>
<span class="c1">// v1[1] = 99; // 作用域内, 有不可变引用, 就不能有可变引用; v1[1] 也是可变引用</span>
<span class="p">}</span> <span class="c1">// <---------------作用域结束</span>
<span class="n">let</span> <span class="n">v5</span> <span class="o">=</span> <span class="o">&</span><span class="n">mut</span> <span class="n">v1</span><span class="p">;</span> <span class="c1">// <--- 在这里, 对资源只有一个可变引用, v2v3不在这个作用域</span>
<span class="n">v5</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">99</span><span class="p">;</span>
<span class="n">println</span><span class="o">!</span><span class="p">(</span><span class="s">"v5: {:p} = {:?}"</span><span class="p">,</span> <span class="o">&</span><span class="n">v5</span><span class="p">,</span> <span class="n">v5</span><span class="p">);</span>
</code></pre></div></div>
<p>看下输出的结果:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">v1:</span> <span class="mh">0x700000403990</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
<span class="n">v2</span><span class="p">(</span><span class="mh">0x7000004038b8</span><span class="p">)</span><span class="o">:</span> <span class="mh">0x700000403990</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
<span class="n">v3</span><span class="p">(</span><span class="mh">0x7000004038b0</span><span class="p">)</span><span class="o">:</span> <span class="mh">0x700000403990</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
<span class="n">v5</span><span class="p">(</span><span class="mh">0x700000403708</span><span class="p">)</span><span class="o">:</span> <span class="mh">0x700000403990</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">99</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
</code></pre></div></div>
<p>那么其实借用跟c的指针是一样的. 我们建了一个数组, 地址在’0x700000403990’, 然后建了一个指针v2指向这个数组, 指针本身的地址是’0x7000004038b8’ , 值是’0x700000403990’</p>
<p>说白了, rust的这个规则其实并不是’新的’, 我们写多线程程序也是要注意这些问题的; 只不过rust用’编译器检查’的方式帮你review代码;</p>
<h3 id="作用域">作用域</h3>
<p>再看借用的语法:</p>
<ul>
<li>在你的作用域内, 我借给你</li>
<li>你离开作用域, 还给我</li>
</ul>
<p>仔细考虑这个语法, 实际上包含了一个隐含原则:</p>
<ul>
<li>借用者的作用域 比 被借用者的 作用域要 短</li>
</ul>
<p>c 程序 一个无效指针的rust写法就是:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
<span class="n">let</span> <span class="n">mut</span> <span class="n">v1</span> <span class="o">=</span> <span class="n">vec</span><span class="o">!</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">];</span>
<span class="n">println</span><span class="o">!</span><span class="p">(</span><span class="s">"v1: {:p} = {:?}"</span><span class="p">,</span> <span class="o">&</span><span class="n">v1</span><span class="p">,</span> <span class="n">v1</span><span class="p">);</span>
<span class="n">let</span> <span class="n">v2</span> <span class="o">=</span> <span class="o">&</span><span class="n">v1</span><span class="p">;</span> <span class="c1">// v2 引用v1, 然后进入线程</span>
<span class="n">let</span> <span class="n">th</span> <span class="o">=</span> <span class="kr">thread</span><span class="o">::</span><span class="n">spawn</span><span class="p">(</span><span class="n">move</span> <span class="o">||</span><span class="p">{</span>
<span class="n">println</span><span class="o">!</span><span class="p">(</span><span class="s">"v2: {:p} = {:?}"</span><span class="p">,</span> <span class="o">&</span><span class="n">v2</span><span class="p">,</span> <span class="n">v2</span><span class="p">);</span> <span class="c1">//线程还在作用域</span>
<span class="p">});</span>
<span class="n">th</span><span class="p">.</span><span class="n">join</span><span class="p">();</span>
<span class="p">}</span> <span class="c1">// <-- v1 退出作用域了, 但是v2却不一定, 因为v1v2在两个不同线程</span>
</code></pre></div></div>
又是一代树莓派
2016-03-22T00:00:00-05:00
http://www.zhaoxiaodan.com/%E5%85%B6%E4%BB%96/又是一代树莓派
<p>又到了折腾树莓派的时候了, 之前老早就玩过一代B+, 图形界面那个慢啊; 当时还是云服务器正盛行的时候, 就感觉用这树莓派还不如搞个云服跑跑, 所以装好系统就直接落灰了; 现在家里当年那个黑天猫盒子也要挂了; 折腾个XMPC或者下载机什么的玩玩吧</p>
<p>系统可以选Ubuntu mate 或者原版Raspbian, 不折腾, 用原版</p>
<h2 id="1烧系统">1.烧系统</h2>
<ul>
<li>去<a href="https://www.raspberrypi.org/downloads">官网</a>下镜像</li>
<li>某东买个SanDisk的64G micro Sd卡, 用自带的卡套插到本子卡槽</li>
<li>运行<code class="language-plaintext highlighter-rouge">df -h</code>, 找到sd卡设备名称 <code class="language-plaintext highlighter-rouge">disk2s1</code>, 那我们sd卡的驱动器号就是<code class="language-plaintext highlighter-rouge">2</code></li>
</ul>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>➜ ~ <span class="nb">df</span> <span class="nt">-h</span>
Filesystem Size Used Avail Capacity iused ifree %iused Mounted on
/dev/disk1 84Gi 61Gi 22Gi 74% 16044435 5888331 73% /
devfs 193Ki 193Ki 0Bi 100% 666 0 100% /dev
/dev/disk0s4 32Gi 5.7Gi 27Gi 18% 1493112 7018211 18% /Volumes/d
/dev/disk0s5 61Gi 54Gi 7.6Gi 88% 14024726 1982736 88% /Volumes/f
/dev/disk0s6 55Gi 43Gi 12Gi 79% 11378368 3050584 79% /Volumes/g
map <span class="nt">-hosts</span> 0Bi 0Bi 0Bi 100% 0 0 100% /net
map auto_home 0Bi 0Bi 0Bi 100% 0 0 100% /home
/dev/disk2s1 59Gi 8.5Mi 59Gi 1% 68 486924 0% /Volumes/Untitled <<span class="o">===</span>这个就是
</code></pre></div></div>
<ul>
<li>卸挂磁盘</li>
</ul>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> diskutil unmountDisk /dev/rdisk2
</code></pre></div></div>
<ul>
<li>烧写镜像到sd卡; SanDisk MicroSDXC UHS-I Class10 的卡, 15M/s左右写入速度;</li>
</ul>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo dd </span><span class="nv">bs</span><span class="o">=</span>1m <span class="k">if</span><span class="o">=</span>./2016-03-18-raspbian-jessie.img <span class="nv">of</span><span class="o">=</span>/dev/rdisk2
</code></pre></div></div>
<p>烧写完成, 插到树莓派上, 通电开机</p>
<h2 id="2-kodi">2. kodi</h2>
<ul>
<li>修改默认用户<code class="language-plaintext highlighter-rouge">pi</code>的密码</li>
<li>修改软件源为阿里云, 这里要注意, 阿里云网页上的地址是老版本的, 其实修改前面url部分就行</li>
</ul>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>deb http://mirrors.aliyun.com/raspbian/raspbian/ jessie main contrib non-free rpi
deb-src http://mirrors.aliyun.com/raspbian/raspbian/ jessie main contrib non-free rpi
</code></pre></div></div>
<p>这回阿里云没有这个kodi的镜像, 让我对aliyun的印象打了点折扣…科大的强悍:<code class="language-plaintext highlighter-rouge">https://mirrors.ustc.edu.cn/</code></p>
<p>再加上这个源:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>deb http://mirrors.ustc.edu.cn/archive.raspberrypi.org/debian/ jessie main
</code></pre></div></div>
<p>然后安装kodi即可</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt-get install kodi
</code></pre></div></div>
传奇2服务器 地图文件格式
2016-03-05T00:00:00-06:00
http://www.zhaoxiaodan.com/java/传奇2服务器-地图文件格式
<p>地图格式这文说的很清楚了, 服务器我们只关心这个格子是否可以进入, 别的不关心</p>
<p>写个loader, <code class="language-plaintext highlighter-rouge">G003</code> load完之后进入标志打出来就像这样:</p>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2016/20160305080602.png" alt="" /></p>
<p>原始地图是这个样子的</p>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2016/20160305082312.png" alt="" /></p>
用netty做传奇2服务器 代理服务器
2016-02-26T00:00:00-06:00
http://www.zhaoxiaodan.com/java/用Netty做传奇2服务器-代理服务器
<p>传奇2的源码, 估计被改了N手了, 封包格式那是相当的乱, 大部分时间都浪费在找正确的封包格式上了; 就比如 <code class="language-plaintext highlighter-rouge">Ability</code>的Struct, 在C#和Delphi就能找到一个<code class="language-plaintext highlighter-rouge">TAbility</code>和一个<code class="language-plaintext highlighter-rouge">TOAbility</code>, 然后你发现用的是C#里的<code class="language-plaintext highlighter-rouge">TOAbility</code>. 完了吧, 到<code class="language-plaintext highlighter-rouge">StdItem</code>这个, 又发现Delphi版本里的<code class="language-plaintext highlighter-rouge">TStdItem</code>是对的.</p>
<p>索性, 写个代理服务器将真服务器和真客户端的封包打印出来看看. 省的用MockClient 太麻烦了;</p>
<p>当前Netty服务器的封包流程, 也就是在Pipeline的流向:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[服务器原封包] -> 6BitDecoder -> [解密后的明文封包] -> PacketDecoder -> [ClientPacket对象] -> Handler处理
</code></pre></div></div>
<p>Handler发出去的是 ServerPacket, 所以流程是:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[ServerPacket] -> PacketEncode -> [明文封包] -> 6BitEncode -> [加密封包] -> 客户端
</code></pre></div></div>
<p>代理服务器实际上做这么两个准备:</p>
<ul>
<li>连接传奇服务器, 拿到<code class="language-plaintext highlighter-rouge">serverChannel</code></li>
<li>监听一个端口比如7000<code class="language-plaintext highlighter-rouge">伪装成服务器</code>, 等待客户端连接, 当客户端连接之后, 拿到<code class="language-plaintext highlighter-rouge">clientChannel</code></li>
</ul>
<p>然后, 就是做这么两件事:</p>
<ul>
<li>服务器Handler收到的东西,调用<code class="language-plaintext highlighter-rouge">clientChannel.writeAndFlush</code> 发给客户端</li>
<li>客户端Handler收到的东西,调用<code class="language-plaintext highlighter-rouge">serverChannel.writeAndFlush</code> 发给服务器</li>
</ul>
<p>那么传奇服务器的封包通过代理服务器走到客户端的流程就是:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-------------------- 服务器连接Channel的Pipeline ----------------------------------------------
[服务器原封包] -> 6BitDecoder -> [解密后的明文封包] -> PacketDecoder -> [ClientPacket对象] ↘ |
---------------------------------------------------------------------------------------------
-------------------
| 代理服务器Handler |
-------------------
------------------- 客户端连接Channel的Pipeline -----------------------------------------------
[还原的加密封包] <- 6BitEncoder <- 还原的明文 <- ClientPacketEncoder <- [ClientPacket对象] ↙ |
---------------------------------------------------------------------------------------------
</code></pre></div></div>
<p>客户端发给传奇服务器的流程就是反过来</p>
<p>所以, 两端的pipeline分别就是:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="c1">// 服务器连接端的</span>
<span class="n">ch</span><span class="o">.</span><span class="na">pipeline</span><span class="o">().</span><span class="na">addLast</span><span class="o">(</span>
<span class="c1">//这里是服务器的连接端</span>
<span class="c1">//来自服务器的封包, 解码成Packet</span>
<span class="k">new</span> <span class="nf">DelimiterBasedFrameDecoder</span><span class="o">(</span><span class="mi">2048</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="nc">Unpooled</span><span class="o">.</span><span class="na">wrappedBuffer</span><span class="o">(</span><span class="k">new</span> <span class="kt">byte</span><span class="o">[]{</span><span class="sc">'!'</span><span class="o">})),</span>
<span class="k">new</span> <span class="nf">PacketBit6Decoder</span><span class="o">(),</span>
<span class="k">new</span> <span class="nf">PacketDecoder</span><span class="o">(</span><span class="nc">ServerPackets</span><span class="o">.</span><span class="na">class</span><span class="o">.</span><span class="na">getCanonicalName</span><span class="o">()),</span>
<span class="c1">//来自客户端的封包, 发给</span>
<span class="k">new</span> <span class="nf">ClientPacketBit6Encoder</span><span class="o">(),</span>
<span class="k">new</span> <span class="nf">PacketEncoder</span><span class="o">(),</span>
<span class="k">new</span> <span class="nf">ChannelHandlerAdapter</span><span class="o">()</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">channelRead</span><span class="o">(</span><span class="nc">ChannelHandlerContext</span> <span class="n">ctx</span><span class="o">,</span> <span class="nc">Object</span> <span class="n">msg</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">Exception</span> <span class="o">{</span>
<span class="c1">// 来自服务器的封包, 通过 client 连接端 发给客户端</span>
<span class="n">clientChannel</span><span class="o">.</span><span class="na">writeAndFlush</span><span class="o">(</span><span class="n">packet</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">},</span>
<span class="k">new</span> <span class="nf">ExceptionHandler</span><span class="o">()</span>
<span class="o">);</span>
<span class="c1">// 客户端连接端的</span>
<span class="n">ch</span><span class="o">.</span><span class="na">pipeline</span><span class="o">().</span><span class="na">addLast</span><span class="o">(</span>
<span class="c1">//这里是客户端的连接端</span>
<span class="c1">//客户端发来的封包, 解码成 Packet</span>
<span class="k">new</span> <span class="nf">DelimiterBasedFrameDecoder</span><span class="o">(</span><span class="mi">2048</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="nc">Unpooled</span><span class="o">.</span><span class="na">wrappedBuffer</span><span class="o">(</span><span class="k">new</span> <span class="kt">byte</span><span class="o">[]{</span><span class="sc">'!'</span><span class="o">})),</span>
<span class="k">new</span> <span class="nf">ClientPacketBit6Decoder</span><span class="o">(),</span>
<span class="k">new</span> <span class="nf">ClientPacketDecoder</span><span class="o">(</span><span class="nc">ClientPackets</span><span class="o">.</span><span class="na">class</span><span class="o">.</span><span class="na">getCanonicalName</span><span class="o">()),</span>
<span class="c1">//发给客户端的封包, 从Packet还原成加密封包</span>
<span class="k">new</span> <span class="nf">PacketBit6Encoder</span><span class="o">(),</span>
<span class="k">new</span> <span class="nf">PacketEncoder</span><span class="o">(),</span>
<span class="k">new</span> <span class="nf">ChannelHandlerAdapter</span><span class="o">()</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">channelActive</span><span class="o">(</span><span class="nc">ChannelHandlerContext</span> <span class="n">ctx</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">Exception</span> <span class="o">{</span>
<span class="kd">super</span><span class="o">.</span><span class="na">channelActive</span><span class="o">(</span><span class="n">ctx</span><span class="o">);</span>
<span class="n">clientChannel</span> <span class="o">=</span> <span class="n">ctx</span><span class="o">;</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">channelRead</span><span class="o">(</span><span class="nc">ChannelHandlerContext</span> <span class="n">ctx</span><span class="o">,</span> <span class="nc">Object</span> <span class="n">msg</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">Exception</span> <span class="o">{</span>
<span class="n">serverChannel</span><span class="o">.</span><span class="na">writeAndFlush</span><span class="o">(</span><span class="n">msg</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">);</span>
</code></pre></div></div>
用netty做传奇2服务器 网络部分
2016-02-25T00:00:00-06:00
http://www.zhaoxiaodan.com/java/用Netty做传奇2服务器-网络部分
<p>私服Delphi版本的源码地址: <a href="https://github.com/pangliang/EGameOfMir2">EGameOfMir2</a></p>
<p>仿造Netty版本项目地址: <a href="https://github.com/pangliang/MirServer-Netty">MirServer-Netty</a></p>
<h2 id="原版结构">原版结构</h2>
<p>原版服务器一共分为如下模块,</p>
<ul>
<li>登录门服</li>
<li>登录服</li>
<li>角色门服</li>
<li>角色服</li>
<li>游戏服</li>
<li>数据库服务器</li>
</ul>
<p>两个门服实际上并不做任何业务处理, 只是当做前端跟用户做连接, 然后转发封包给后面的登录服和角色服. 感觉就有点像方向代理的感觉; 可能开发这个服务器的时候还没有成熟的负载均衡技术吧, 自己开发; 我的版本不打算用这个结构, 简单为主;</p>
<h2 id="封包结构">封包结构</h2>
<p>反服务器开发肯定首先是封包的结构. 传奇的封包都是由’#’开始并且以’!’结束的; 用netty的LogginHandler打出来就是这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> +-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 23 31 3c 3c 3c 3c 3c 49 40 43 3c 3c 3c 3c 3c 3c |#1<<<<<I@C<<<<<<|
|00000010| 3c 3c 48 4f 44 6f 47 6f 40 6e 48 6c 21 |<<HODoGo@nHl! |
+--------+-------------------------------------------------+----------------+
</code></pre></div></div>
<p>确实看不懂, 但是实际上他是经过类base64转码的. 个人考虑可能是为了避免’内容’部分出现#!这两个字符吧. 解码函数在源码的<code class="language-plaintext highlighter-rouge">EDcode.pas</code>里, 解码之后:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> +-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 23 31 00 00 00 00 d1 07 00 00 00 00 00 00 31 32 |#1............12|
|00000010| 33 2f 31 32 00 00 00 00 00 00 00 00 21 |3/12........! |
+--------+-------------------------------------------------+----------------+
</code></pre></div></div>
<p>经过分析源码知道, 封包构造是在<code class="language-plaintext highlighter-rouge">MakeDefaultMsg</code>函数, 然后使用它的地方都类似这个样子:</p>
<pre><code class="language-delphi">function MakeDefaultMsg(wIdent: Word; nRecog: Integer; wParam, wTag, wSeries: Word): TDefaultMessage;
begin
Result.Recog := nRecog;
Result.Ident := wIdent;
Result.Param := wParam;
Result.Tag := wTag;
Result.Series := wSeries;
end;
SendGateMsg(
UserInfo.Socket, UserInfo.sSockIndex,
EncodeMessage(DefMsg)
+ EncodeString(sSelGateIP + '/' + IntToStr(nSelGatePort) + '/' + IntToStr(UserInfo.nSessionID))
);
</code></pre>
<p>那知道了, 封包就是一个转码之后的’defualmsg’ 和 ‘内容’; 那么实际defaultmsg上可以理解为<code class="language-plaintext highlighter-rouge">协议头</code>, 整个封包就是这个结构:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+-----------------------------------------------------------------------------------------+
| 0 | 1 | 2 3 4 5 | 6 7 | 8 9 | 10 11 | 12 13 | 14 ..... n -1 | n |
+-----------------------------------------------------------------------------------------+
| # | header | body | ! |
+-----------------------------------------------------------------------------------------+
| # |index| p0 |protocol| p1 | p2 | p3 | body | ! |
+-----------------------------------------------------------------------------------------+
</code></pre></div></div>
<p>但是麻烦的是, index这个是客户端发来的封包才有的, 表示封包的序号, 防止中间被恶意插包作假</p>
<h2 id="netty封包编解码">Netty封包编解码</h2>
<p>服务器开发封包首先就是注意分包粘包问题, 可喜的是netty已经帮我们做了很多工作; 既然传奇的封包是’!’结尾, 我们就可以用一个<code class="language-plaintext highlighter-rouge">DelimiterBasedFrameDecoder</code>进行拆包. 粘包用!符自然就分开了, 分包问题没收到!不算.</p>
<p>拆完包之后就是解码, 参考VC版代码写个<code class="language-plaintext highlighter-rouge">Bit6Decoder</code>的<code class="language-plaintext highlighter-rouge">MessageToMessageDecoder</code>的解码器,解码之后再放回到ByteBuf中
,然后再用一个<code class="language-plaintext highlighter-rouge">PacketDecoder</code>把字节码转成java对象</p>
<p>那么在netty的pipeline也就是类似这个样子:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ch</span><span class="o">.</span><span class="na">pipeline</span><span class="o">().</span><span class="na">addLast</span><span class="o">(</span>
<span class="c1">//编码</span>
<span class="k">new</span> <span class="nf">DelimiterBasedFrameDecoder</span><span class="o">(</span><span class="no">REQUEST_MAX_FRAME_LENGTH</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="nc">Unpooled</span><span class="o">.</span><span class="na">wrappedBuffer</span><span class="o">(</span><span class="k">new</span> <span class="kt">byte</span><span class="o">[]{</span><span class="sc">'!'</span><span class="o">})),</span>
<span class="k">new</span> <span class="nf">ClientPacketBit6Decoder</span><span class="o">(),</span>
<span class="k">new</span> <span class="nf">ClientPacketDecoder</span><span class="o">(</span><span class="nc">ClientPackets</span><span class="o">.</span><span class="na">class</span><span class="o">.</span><span class="na">getCanonicalName</span><span class="o">()),</span>
<span class="c1">//解码</span>
<span class="k">new</span> <span class="nf">PacketBit6Encoder</span><span class="o">(),</span>
<span class="k">new</span> <span class="nf">PacketEncoder</span><span class="o">(),</span>
<span class="o">);</span>
</code></pre></div></div>
<h2 id="封包对象映射">封包对象映射</h2>
<p>转成java对象的时候, 使用了一个枚举类进行<code class="language-plaintext highlighter-rouge">协议id</code>-><code class="language-plaintext highlighter-rouge">封包对象类名</code>的映射, 这样我知道id使用<code class="language-plaintext highlighter-rouge">Class.forName</code>就自然load到封包对象的class, 再newInstance()就可以拼装了; 那么多的封包就不用一个个写映射了, 代码就像这样:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protected</span> <span class="nc">Packet</span> <span class="nf">decodePacket</span><span class="o">(</span><span class="kt">short</span> <span class="n">protocolId</span><span class="o">,</span> <span class="nc">ByteBuf</span> <span class="n">in</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">Exception</span> <span class="o">{</span>
<span class="nc">Protocol</span> <span class="n">protocol</span> <span class="o">=</span> <span class="nc">Protocol</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">protocolId</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="kc">null</span> <span class="o">==</span> <span class="n">protocol</span><span class="o">)</span> <span class="o">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">Exception</span><span class="o">(</span><span class="s">"unknow protocol id:"</span> <span class="o">+</span> <span class="n">protocolId</span><span class="o">);</span>
<span class="o">}</span>
<span class="nc">Class</span><span class="o"><?</span> <span class="kd">extends</span> <span class="nc">Packet</span><span class="o">></span> <span class="n">packetClass</span><span class="o">;</span>
<span class="k">try</span> <span class="o">{</span>
<span class="n">packetClass</span> <span class="o">=</span> <span class="o">(</span><span class="nc">Class</span><span class="o"><?</span> <span class="kd">extends</span> <span class="nc">Packet</span><span class="o">>)</span> <span class="nc">Class</span><span class="o">.</span><span class="na">forName</span><span class="o">(</span><span class="n">packetPackageName</span> <span class="o">+</span> <span class="s">"$"</span> <span class="o">+</span> <span class="n">protocol</span><span class="o">.</span><span class="na">name</span><span class="o">());</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">ClassNotFoundException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
<span class="n">packetClass</span> <span class="o">=</span> <span class="nc">Packet</span><span class="o">.</span><span class="na">class</span><span class="o">;</span>
<span class="o">}</span>
<span class="nc">Packet</span> <span class="n">packet</span> <span class="o">=</span> <span class="n">packetClass</span><span class="o">.</span><span class="na">newInstance</span><span class="o">();</span>
<span class="n">packet</span><span class="o">.</span><span class="na">readPacket</span><span class="o">(</span><span class="n">in</span><span class="o">);</span>
<span class="n">packet</span><span class="o">.</span><span class="na">protocol</span> <span class="o">=</span> <span class="nc">Protocol</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">protocolId</span><span class="o">);</span>
<span class="k">return</span> <span class="n">packet</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<p>不过这里之前有个纠结的地方, 就是这个<code class="language-plaintext highlighter-rouge">装配</code>, 到底是放到Packet里让packet自己去read自己装配自己, 还是说我应该在Decoder里装配; 因为按照netty的设计, Decoder就是干装配这件事情的. Packet就是一个<code class="language-plaintext highlighter-rouge">简单</code>的POJO;</p>
<p>但是放到packet里自己read也有好处, 因为如果封包格式并不<code class="language-plaintext highlighter-rouge">都是标准</code>的, 比如传奇这里的body部分, 有些是<code class="language-plaintext highlighter-rouge">loginId/pwd</code>,有些是<code class="language-plaintext highlighter-rouge">loginId/charName</code>, 或者不能<code class="language-plaintext highlighter-rouge">反推</code>的比如是json格式, 那用Decoder的话, 它也干不了这个事情, 因为if else 太多了;</p>
<p>所以这里选用packet自己读自己装配的方式; 不过这部分的代码进行了两次数据拷贝, 生产服有优化的空间. 简单实现先; 而且把拆包, 解码, 分开来也是为了方便使用log打印出每一步来进行封包查看, 就像上面的封包输出的那样子</p>
<h2 id="处理器映射">处理器映射</h2>
<p>封包处理器同样的, 通过Protocol的name来映射; 交给一个Dispatcher来分发, 代码就像这样:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">channelRead</span><span class="o">(</span><span class="nc">ChannelHandlerContext</span> <span class="n">ctx</span><span class="o">,</span> <span class="nc">Object</span> <span class="n">pakcet</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">Exception</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(!(</span><span class="n">pakcet</span> <span class="k">instanceof</span> <span class="nc">Packet</span><span class="o">))</span> <span class="o">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">Exception</span><span class="o">(</span><span class="s">"Recv msg is not instance of Packet"</span><span class="o">);</span>
<span class="o">}</span>
<span class="nc">String</span> <span class="n">protocolName</span> <span class="o">=</span> <span class="n">pakcet</span><span class="o">.</span><span class="na">getClass</span><span class="o">().</span><span class="na">getSimpleName</span><span class="o">();</span> <span class="c1">//Packet 就是通过 protocol id 反射出来的 name</span>
<span class="nc">Class</span><span class="o"><?</span> <span class="kd">extends</span> <span class="nc">Handler</span><span class="o">></span> <span class="n">handlerClass</span><span class="o">;</span>
<span class="k">try</span> <span class="o">{</span>
<span class="n">handlerClass</span> <span class="o">=</span> <span class="o">(</span><span class="nc">Class</span><span class="o"><?</span> <span class="kd">extends</span> <span class="nc">Handler</span><span class="o">>)</span> <span class="nc">Class</span><span class="o">.</span><span class="na">forName</span><span class="o">(</span><span class="n">handlerPackageName</span> <span class="o">+</span> <span class="s">"."</span> <span class="o">+</span> <span class="n">protocolName</span> <span class="o">+</span> <span class="s">"Handler"</span><span class="o">);</span>
<span class="o">}</span><span class="k">catch</span><span class="o">(</span><span class="nc">ClassNotFoundException</span> <span class="n">e</span><span class="o">)</span>
<span class="o">{</span>
<span class="n">handlerClass</span> <span class="o">=</span> <span class="nc">Handler</span><span class="o">.</span><span class="na">class</span><span class="o">;</span>
<span class="o">}</span>
<span class="nc">Handler</span> <span class="n">handler</span> <span class="o">=</span> <span class="n">handlerClass</span><span class="o">.</span><span class="na">newInstance</span><span class="o">();</span>
<span class="n">handler</span><span class="o">.</span><span class="na">exce</span><span class="o">(</span><span class="n">ctx</span><span class="o">,</span> <span class="o">(</span><span class="nc">Packet</span><span class="o">)</span> <span class="n">pakcet</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>后来我在Protocol里又加了个eventName 来让Packet和Handler的名字复合java的命名规则; 而让Protocol枚举的名字保持跟Delphi的一致,类似<code class="language-plaintext highlighter-rouge">SM_ID_NOTFOUND</code>,方便参考源码, 也能根据前缀SM和CM区分是client的包还是server的包)</p>
<p>那么这样映射完之后, 我只要这个构造Decoder和dispatcher的时候传入不同的包名, 我就能自动映射到不同模块的包定义中; 这个逻辑可以重用到上面那一堆 门服 和业务处理服中; 所以被我放到了<code class="language-plaintext highlighter-rouge">Core</code>这个模块; 而这些服模块, 就只需要定义<code class="language-plaintext highlighter-rouge">封包类</code>和<code class="language-plaintext highlighter-rouge">封包处理类</code>即可</p>
<p>各个模块的netty的pipeline就像这样:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ********************** 登录服务器</span>
<span class="c1">//编码</span>
<span class="k">new</span> <span class="nf">DelimiterBasedFrameDecoder</span><span class="o">(</span><span class="no">REQUEST_MAX_FRAME_LENGTH</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="nc">Unpooled</span><span class="o">.</span><span class="na">wrappedBuffer</span><span class="o">(</span><span class="k">new</span> <span class="kt">byte</span><span class="o">[]{</span><span class="sc">'!'</span><span class="o">})),</span>
<span class="k">new</span> <span class="nf">ClientPacketBit6Decoder</span><span class="o">(),</span>
<span class="k">new</span> <span class="nf">ClientPacketDecoder</span><span class="o">(</span><span class="s">"loginserver.clientpackets"</span><span class="o">),</span>
<span class="c1">//解码</span>
<span class="k">new</span> <span class="nf">PacketBit6Encoder</span><span class="o">(),</span>
<span class="k">new</span> <span class="nf">PacketEncoder</span><span class="o">(),</span>
<span class="c1">//分包分发</span>
<span class="k">new</span> <span class="nf">PacketDispatcher</span><span class="o">(</span><span class="s">"loginserver.handlers"</span><span class="o">),</span>
<span class="c1">// ********************** 游戏服务器</span>
<span class="c1">//编码</span>
<span class="k">new</span> <span class="nf">DelimiterBasedFrameDecoder</span><span class="o">(</span><span class="no">REQUEST_MAX_FRAME_LENGTH</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="nc">Unpooled</span><span class="o">.</span><span class="na">wrappedBuffer</span><span class="o">(</span><span class="k">new</span> <span class="kt">byte</span><span class="o">[]{</span><span class="sc">'!'</span><span class="o">})),</span>
<span class="k">new</span> <span class="nf">ClientPacketBit6Decoder</span><span class="o">(),</span>
<span class="k">new</span> <span class="nf">ClientPacketDecoder</span><span class="o">(</span><span class="s">"gameserver.clientpackets"</span><span class="o">),</span>
<span class="c1">//解码</span>
<span class="k">new</span> <span class="nf">PacketBit6Encoder</span><span class="o">(),</span>
<span class="k">new</span> <span class="nf">PacketEncoder</span><span class="o">(),</span>
<span class="c1">//分包分发</span>
<span class="k">new</span> <span class="nf">PacketDispatcher</span><span class="o">(</span><span class="s">"gameserver.handlers"</span><span class="o">),</span>
</code></pre></div></div>
<p>然后假如要写客户端都是可以用的, 反过来就可以了, 就比如<code class="language-plaintext highlighter-rouge">MockClient</code>模块里的</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//编码</span>
<span class="k">new</span> <span class="nf">DelimiterBasedFrameDecoder</span><span class="o">(</span><span class="mi">2048</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="nc">Unpooled</span><span class="o">.</span><span class="na">wrappedBuffer</span><span class="o">(</span><span class="k">new</span> <span class="kt">byte</span><span class="o">[]{</span><span class="sc">'!'</span><span class="o">})),</span>
<span class="k">new</span> <span class="nf">PacketBit6Decoder</span><span class="o">(),</span>
<span class="k">new</span> <span class="nf">PacketDecoder</span><span class="o">(</span><span class="nc">ServerPackets</span><span class="o">.</span><span class="na">class</span><span class="o">.</span><span class="na">getName</span><span class="o">()),</span>
<span class="c1">//解码</span>
<span class="k">new</span> <span class="nf">ClientPacketBit6Encoder</span><span class="o">(),</span>
<span class="k">new</span> <span class="nf">PacketEncoder</span><span class="o">(),</span>
</code></pre></div></div>
Android Hook(2) Java2java
2015-08-18T00:00:00-05:00
http://www.zhaoxiaodan.com/android/Android-Hook(2)-java2java
<p>之前学习了如何做一个简单android的函数勾子, 而这个勾子是用native 的函数去hook java函数, 现在来学习如何封装让他可以实现java hook java</p>
<p>不过不管怎么说, 这里已经不算是<code class="language-plaintext highlighter-rouge">原理</code>了, 因为<code class="language-plaintext highlighter-rouge">原理</code>就是<code class="language-plaintext highlighter-rouge">改accessFlags并设置nativeFunc</code>, 实际的hook 函数还是个native函数, 所以说这个是用这个<code class="language-plaintext highlighter-rouge">原理</code>来封装</p>
<p>我们一般要hook一个方法, 有可能希望在三个时间点进行处理:</p>
<ul>
<li>原方法执行前</li>
<li>替换原方法</li>
<li>原方法执行后</li>
</ul>
<p>先来做个最简单的, 在原方法执行之前 执行, 那么就需要如下的Handler 用来hook 原函数, 并且这个java层的hook 可以获得原函数所在类的对象实例和所有参数值</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">Handler</span>
<span class="o">{</span>
<span class="cm">/**
* 在原方法执行之前执行
* @param params 原方法参数
*/</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">before</span><span class="o">(</span><span class="nc">Object</span> <span class="n">instance</span><span class="o">,</span> <span class="nc">Object</span><span class="o">[]</span> <span class="n">params</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>所以在jni 函数hook 里将原函数的nativeFunc 修改为 forwardToHander, 然后forwardToHander里 解析原函数的参数值等信息之后, 再调用 Handler的before方法, 将原函数所在类实例和参数值传递给handler的before回调方法</p>
<p>具体代码:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include <jni.h>
#include "log.h"
#include "Dalvik.h"
</span>
<span class="cm">/**
* dvmCallMethod 需要的是 ArrayObject*, 而 nativeFunction 传入的参数是 const u4*
* 做个转换
*/</span>
<span class="n">ArrayObject</span><span class="o">*</span> <span class="nf">argsToArrayObject</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">argsTypeDesc</span><span class="p">,</span> <span class="k">const</span> <span class="n">u4</span><span class="o">*</span> <span class="n">args</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">//全部参数都转成Object型</span>
<span class="n">ClassObject</span><span class="o">*</span> <span class="n">objectArrayClass</span> <span class="o">=</span> <span class="n">dvmFindArrayClass</span><span class="p">(</span><span class="s">"[Ljava/lang/Object;"</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
<span class="c1">// 参数个数, 类型描述符都是一个字符, 所以 argsTypeDesc 字符串长度就是参数个数</span>
<span class="kt">int</span> <span class="n">argsSize</span> <span class="o">=</span> <span class="n">strlen</span><span class="p">(</span><span class="n">argsTypeDesc</span><span class="p">);</span>
<span class="c1">// 分配空间</span>
<span class="n">ArrayObject</span><span class="o">*</span> <span class="n">argArray</span> <span class="o">=</span> <span class="n">dvmAllocArrayByClass</span><span class="p">(</span><span class="n">objectArrayClass</span><span class="p">,</span><span class="n">argsSize</span> <span class="p">,</span><span class="n">ALLOC_DEFAULT</span><span class="p">);</span>
<span class="c1">// 挨个赋值</span>
<span class="c1">// 因为 args 并不是一个真正意义上的"数组", 当long 或者 double 形, 会占用 args[i]和args[i+1]</span>
<span class="c1">// 所以需要多一个下标来指向args内存</span>
<span class="kt">int</span> <span class="n">pArgs</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span><span class="n">i</span><span class="o"><</span><span class="n">argsSize</span><span class="p">;</span><span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// 当前参数类型描述符</span>
<span class="k">const</span> <span class="kt">char</span> <span class="n">descChar</span> <span class="o">=</span> <span class="n">argsTypeDesc</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
<span class="n">JValue</span> <span class="n">value</span><span class="p">;</span>
<span class="n">Object</span><span class="o">*</span> <span class="n">obj</span><span class="p">;</span>
<span class="k">switch</span><span class="p">(</span><span class="n">descChar</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">case</span> <span class="sc">'Z'</span><span class="p">:</span>
<span class="k">case</span> <span class="sc">'C'</span><span class="p">:</span>
<span class="k">case</span> <span class="sc">'F'</span><span class="p">:</span>
<span class="k">case</span> <span class="sc">'B'</span><span class="p">:</span>
<span class="k">case</span> <span class="sc">'S'</span><span class="p">:</span>
<span class="k">case</span> <span class="sc">'I'</span><span class="p">:</span>
<span class="c1">//如果是基本数据类型, 则使用java的"自动装配", 也就是 int 转 Integer, long 转 Long, 这样就都转成Object了</span>
<span class="n">value</span><span class="p">.</span><span class="n">i</span> <span class="o">=</span> <span class="n">args</span><span class="p">[</span><span class="n">pArgs</span><span class="o">++</span><span class="p">];</span>
<span class="n">obj</span> <span class="o">=</span> <span class="p">(</span><span class="n">Object</span><span class="o">*</span><span class="p">)</span> <span class="n">dvmBoxPrimitive</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">dvmFindPrimitiveClass</span><span class="p">(</span><span class="n">descChar</span><span class="p">));</span>
<span class="n">dvmReleaseTrackedAlloc</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">dvmThreadSelf</span><span class="p">());</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="sc">'D'</span><span class="p">:</span>
<span class="k">case</span> <span class="sc">'J'</span><span class="p">:</span>
<span class="c1">// 基本数据类型中, double 和 long 占用两个字节, 所以 pArgs 需要 +2</span>
<span class="n">value</span><span class="p">.</span><span class="n">j</span> <span class="o">=</span> <span class="n">dvmGetArgLong</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="n">pArgs</span><span class="o">++</span><span class="p">);</span>
<span class="n">pArgs</span><span class="o">++</span><span class="p">;</span>
<span class="n">obj</span> <span class="o">=</span> <span class="p">(</span><span class="n">Object</span><span class="o">*</span><span class="p">)</span> <span class="n">dvmBoxPrimitive</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">dvmFindPrimitiveClass</span><span class="p">(</span><span class="n">descChar</span><span class="p">));</span>
<span class="n">dvmReleaseTrackedAlloc</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">dvmThreadSelf</span><span class="p">());</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="sc">'['</span><span class="p">:</span>
<span class="k">case</span> <span class="sc">'L'</span><span class="p">:</span>
<span class="n">obj</span> <span class="o">=</span> <span class="p">(</span><span class="n">Object</span><span class="o">*</span><span class="p">)</span> <span class="n">args</span><span class="p">[</span><span class="n">pArgs</span><span class="o">++</span><span class="p">];</span>
<span class="k">break</span><span class="p">;</span>
<span class="nl">default:</span>
<span class="n">LOGE</span><span class="p">(</span><span class="s">"Unknown method signature description character: %c</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">descChar</span><span class="p">);</span>
<span class="n">obj</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// 塞到ArrayObject 中</span>
<span class="n">dvmSetObjectArrayElement</span><span class="p">(</span><span class="n">argArray</span><span class="p">,</span><span class="n">i</span><span class="p">,</span><span class="n">obj</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">argArray</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/**
* 被hook的函数统一调用这个native方法, 由他来装配之后跳转到Handler
*/</span>
<span class="k">static</span> <span class="kt">void</span> <span class="nf">forwardToHander</span><span class="p">(</span><span class="k">const</span> <span class="n">u4</span><span class="o">*</span> <span class="n">args</span><span class="p">,</span> <span class="n">JValue</span><span class="o">*</span> <span class="n">pResult</span><span class="p">,</span>
<span class="k">const</span> <span class="n">Method</span><span class="o">*</span> <span class="n">method</span><span class="p">,</span> <span class="k">struct</span> <span class="n">Thread</span><span class="o">*</span> <span class="n">self</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 之前放进去的 "附加参数"</span>
<span class="n">Object</span><span class="o">*</span> <span class="n">handlerObject</span> <span class="o">=</span> <span class="n">const_cast</span><span class="o"><</span><span class="n">Object</span><span class="o">*></span><span class="p">(</span><span class="n">reinterpret_cast</span><span class="o"><</span><span class="k">const</span> <span class="n">Object</span><span class="o">*></span><span class="p">(</span><span class="n">method</span><span class="o">-></span><span class="n">insns</span><span class="p">));</span>
<span class="c1">// 在原方法执行之前, 我们是要调用 Handler 的 before 这个hook</span>
<span class="n">Method</span><span class="o">*</span> <span class="n">handlerBeforeMethod</span> <span class="o">=</span> <span class="n">dvmFindInterfaceMethodHierByDescriptor</span><span class="p">(</span><span class="n">handlerObject</span><span class="o">-></span><span class="n">clazz</span><span class="p">,</span><span class="s">"before"</span><span class="p">,</span><span class="s">"(Ljava/lang/Object;[Ljava/lang/Object;)V"</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span><span class="n">nullptr</span> <span class="o">!=</span> <span class="n">handlerBeforeMethod</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">LOGD</span><span class="p">(</span><span class="s">"handlerBeforeMethod name:%s,desc:%s"</span><span class="p">,</span><span class="n">handlerBeforeMethod</span><span class="o">-></span><span class="n">name</span><span class="p">,</span><span class="n">handlerBeforeMethod</span><span class="o">-></span><span class="n">shorty</span><span class="p">);</span>
<span class="n">JValue</span><span class="o">*</span> <span class="n">result</span> <span class="o">=</span> <span class="n">new</span> <span class="n">JValue</span><span class="p">();</span>
<span class="c1">// method->shorty[0] 表示返回值类型, 比如"ILJ" 说明原函数有一个Object型参数和一个long型参数, 返回值为 整形</span>
<span class="c1">// 注意自动装配类型, int long double, 如果是Integer, Long, Double, 则描述符为Ljava/lang/Integer,Ljava/lang/Long和Ljava/lang/Double</span>
<span class="k">const</span> <span class="kt">char</span> <span class="n">returnTypeDesc</span> <span class="o">=</span> <span class="n">method</span><span class="o">-></span><span class="n">shorty</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">argsTypeDesc</span> <span class="o">=</span> <span class="o">&</span><span class="p">(</span><span class="n">method</span><span class="o">-></span><span class="n">shorty</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>
<span class="c1">// 转一下参数格式</span>
<span class="n">ArrayObject</span><span class="o">*</span> <span class="n">argsArray</span> <span class="p">;</span>
<span class="n">Object</span><span class="o">*</span> <span class="n">originalInstance</span><span class="p">;</span>
<span class="c1">// 同样的, 如果原函数不是static 的, args[0] 函数是所在类的实例</span>
<span class="k">if</span><span class="p">(</span><span class="n">dvmIsStaticMethod</span><span class="p">(</span><span class="n">method</span><span class="p">))</span>
<span class="p">{</span>
<span class="n">argsArray</span> <span class="o">=</span> <span class="n">argsToArrayObject</span><span class="p">(</span><span class="n">argsTypeDesc</span><span class="p">,</span><span class="o">&</span><span class="p">(</span><span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]));</span>
<span class="n">originalInstance</span> <span class="o">=</span> <span class="n">nullptr</span><span class="p">;</span>
<span class="p">}</span><span class="k">else</span><span class="p">{</span>
<span class="n">argsArray</span> <span class="o">=</span> <span class="n">argsToArrayObject</span><span class="p">(</span><span class="n">argsTypeDesc</span><span class="p">,</span><span class="o">&</span><span class="p">(</span><span class="n">args</span><span class="p">[</span><span class="mi">1</span><span class="p">]));</span>
<span class="n">originalInstance</span> <span class="o">=</span> <span class="p">(</span><span class="n">Object</span><span class="o">*</span><span class="p">)(</span><span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
<span class="p">}</span>
<span class="n">dvmCallMethod</span><span class="p">(</span><span class="n">self</span><span class="p">,</span><span class="n">handlerBeforeMethod</span><span class="p">,</span> <span class="n">handlerObject</span><span class="p">,</span> <span class="n">result</span><span class="p">,</span> <span class="n">originalInstance</span> <span class="p">,</span><span class="n">argsArray</span><span class="p">);</span>
<span class="p">}</span><span class="k">else</span><span class="p">{</span>
<span class="n">LOGE</span><span class="p">(</span><span class="s">"method no found...."</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// before 前置调用先不修改原返回值了</span>
<span class="n">pResult</span><span class="o">-></span><span class="n">l</span> <span class="o">=</span> <span class="n">nullptr</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/**
* 设置hook, 将原函数调用转发给nativeFunc forwardToHander 进行处理
*/</span>
<span class="k">extern</span> <span class="s">"C"</span> <span class="n">JNIEXPORT</span> <span class="kt">void</span> <span class="n">JNICALL</span>
<span class="nf">Java_com_zhaoxiaodan_hookdemo_Demo2_hook</span><span class="p">(</span><span class="n">JNIEnv</span> <span class="o">*</span><span class="n">env</span><span class="p">,</span> <span class="n">jobject</span> <span class="n">instance</span><span class="p">,</span> <span class="n">jobject</span> <span class="n">clazzToHook</span><span class="p">,</span>
<span class="n">jstring</span> <span class="n">methodName_</span><span class="p">,</span> <span class="n">jstring</span> <span class="n">methodSig_</span><span class="p">,</span>
<span class="n">jobject</span> <span class="n">handler</span><span class="p">)</span> <span class="p">{</span>
<span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">methodName</span> <span class="o">=</span> <span class="n">env</span><span class="o">-></span><span class="n">GetStringUTFChars</span><span class="p">(</span><span class="n">methodName_</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">methodSig</span> <span class="o">=</span> <span class="n">env</span><span class="o">-></span><span class="n">GetStringUTFChars</span><span class="p">(</span><span class="n">methodSig_</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="n">jmethodID</span> <span class="n">methodIDToHook</span> <span class="o">=</span> <span class="n">env</span><span class="o">-></span><span class="n">GetMethodID</span><span class="p">((</span><span class="n">jclass</span><span class="p">)</span> <span class="n">clazzToHook</span><span class="p">,</span><span class="n">methodName</span><span class="p">,</span><span class="n">methodSig</span><span class="p">);</span>
<span class="c1">// 找不到有可能是个static</span>
<span class="k">if</span><span class="p">(</span><span class="n">nullptr</span> <span class="o">==</span> <span class="n">methodIDToHook</span><span class="p">){</span>
<span class="n">env</span><span class="o">-></span><span class="n">ExceptionClear</span><span class="p">();</span>
<span class="n">methodIDToHook</span> <span class="o">=</span> <span class="n">env</span><span class="o">-></span><span class="n">GetStaticMethodID</span><span class="p">((</span><span class="n">jclass</span><span class="p">)</span> <span class="n">clazzToHook</span><span class="p">,</span><span class="n">methodName</span><span class="p">,</span><span class="n">methodSig</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">if</span><span class="p">(</span><span class="n">methodIDToHook</span> <span class="o">!=</span> <span class="n">nullptr</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">Method</span><span class="o">*</span> <span class="n">method</span> <span class="o">=</span> <span class="n">reinterpret_cast</span><span class="o"><</span><span class="n">Method</span><span class="o">*></span><span class="p">(</span><span class="n">methodIDToHook</span><span class="p">);</span>
<span class="n">LOGD</span><span class="p">(</span><span class="s">"hook function %s, args desc:%s"</span><span class="p">,</span><span class="n">method</span><span class="o">-></span><span class="n">name</span><span class="p">,</span><span class="n">method</span><span class="o">-></span><span class="n">shorty</span><span class="p">);</span>
<span class="n">SET_METHOD_FLAG</span><span class="p">(</span><span class="n">method</span><span class="p">,</span> <span class="n">ACC_NATIVE</span><span class="p">);</span>
<span class="c1">//转发</span>
<span class="n">method</span><span class="o">-></span><span class="n">nativeFunc</span> <span class="o">=</span> <span class="o">&</span><span class="n">forwardToHander</span><span class="p">;</span>
<span class="n">method</span><span class="o">-></span><span class="n">registersSize</span><span class="o">=</span><span class="n">method</span><span class="o">-></span><span class="n">insSize</span><span class="p">;</span>
<span class="n">method</span><span class="o">-></span><span class="n">outsSize</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span>
<span class="c1">// "附加参数" 功能</span>
<span class="c1">// 在hook 的时候传进来的 handler 实现实例, 我们放到Method 结构体的insns 属性中</span>
<span class="c1">// 到时候dvm 调用 nativeFunc 的时候会带回到 nativeFunc 中, 我们就能取到了</span>
<span class="n">method</span><span class="o">-></span><span class="n">insns</span> <span class="o">=</span> <span class="n">reinterpret_cast</span><span class="o"><</span><span class="k">const</span> <span class="n">u2</span><span class="o">*></span><span class="p">(</span><span class="n">dvmDecodeIndirectRef</span><span class="p">(</span><span class="n">dvmThreadSelf</span><span class="p">(),</span><span class="n">handler</span><span class="p">));</span>
<span class="p">}</span>
<span class="n">env</span><span class="o">-></span><span class="n">ReleaseStringUTFChars</span><span class="p">(</span><span class="n">methodName_</span><span class="p">,</span> <span class="n">methodName</span><span class="p">);</span>
<span class="n">env</span><span class="o">-></span><span class="n">ReleaseStringUTFChars</span><span class="p">(</span><span class="n">methodSig_</span><span class="p">,</span> <span class="n">methodSig</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>使用代码为:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Demo2</span>
<span class="o">{</span>
<span class="nc">String</span> <span class="no">TAG</span> <span class="o">=</span> <span class="s">"===[hookdemo]==="</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">interface</span> <span class="nc">Handler</span>
<span class="o">{</span>
<span class="cm">/**
* 在原方法执行之前执行
* @param params 原方法参数
*/</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">before</span><span class="o">(</span><span class="nc">Object</span> <span class="n">instance</span><span class="o">,</span> <span class="nc">Object</span><span class="o">[]</span> <span class="n">params</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">test</span><span class="o">(</span><span class="nc">String</span> <span class="n">param1</span><span class="o">,</span> <span class="kt">long</span> <span class="n">param2</span><span class="o">,</span> <span class="kt">int</span><span class="o">[]</span> <span class="n">param3</span><span class="o">)</span>
<span class="o">{</span>
<span class="k">return</span> <span class="n">param1</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="nc">String</span> <span class="nf">staticTest</span><span class="o">(</span><span class="nc">String</span> <span class="n">param1</span><span class="o">,</span> <span class="nc">Long</span> <span class="n">param2</span><span class="o">,</span> <span class="kt">int</span><span class="o">[]</span> <span class="n">param3</span><span class="o">)</span>
<span class="o">{</span>
<span class="k">return</span> <span class="n">param1</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">demo</span><span class="o">()</span>
<span class="o">{</span>
<span class="nc">String</span> <span class="n">param1</span> <span class="o">=</span> <span class="s">"param1"</span><span class="o">;</span>
<span class="cm">/**
* 设置hook
* hook 住Demo2 这个类的 test 方法
* 实现一个Handler类来实现 回调函数 before
*/</span>
<span class="n">hook</span><span class="o">(</span><span class="nc">Demo2</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="s">"test"</span><span class="o">,</span> <span class="s">"(Ljava/lang/String;J[I)Ljava/lang/String;"</span><span class="o">,</span> <span class="k">new</span> <span class="nc">Handler</span><span class="o">()</span>
<span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">before</span><span class="o">(</span><span class="nc">Object</span> <span class="n">instance</span><span class="o">,</span> <span class="nc">Object</span><span class="o">[]</span> <span class="n">params</span><span class="o">)</span>
<span class="o">{</span>
<span class="nc">Log</span><span class="o">.</span><span class="na">d</span><span class="o">(</span><span class="no">TAG</span><span class="o">,</span> <span class="s">"===========Handler before,param len: "</span><span class="o">+</span><span class="n">params</span><span class="o">.</span><span class="na">length</span><span class="o">+</span><span class="s">",instance:"</span><span class="o">+</span><span class="n">instance</span><span class="o">);</span>
<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">params</span><span class="o">.</span><span class="na">length</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span>
<span class="o">{</span>
<span class="nc">Log</span><span class="o">.</span><span class="na">d</span><span class="o">(</span><span class="no">TAG</span><span class="o">,</span> <span class="s">"p"</span> <span class="o">+</span> <span class="n">i</span> <span class="o">+</span> <span class="s">"="</span> <span class="o">+</span> <span class="n">params</span><span class="o">[</span><span class="n">i</span><span class="o">]);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">});</span>
<span class="nc">Log</span><span class="o">.</span><span class="na">d</span><span class="o">(</span><span class="no">TAG</span><span class="o">,</span> <span class="s">"===========call test"</span> <span class="o">+</span> <span class="k">this</span><span class="o">.</span><span class="na">test</span><span class="o">(</span><span class="n">param1</span><span class="o">,</span><span class="mi">999L</span><span class="o">,</span><span class="k">new</span> <span class="kt">int</span><span class="o">[]{</span><span class="mi">1</span><span class="o">,</span><span class="mi">2</span><span class="o">,</span><span class="mi">3</span><span class="o">}));</span>
<span class="cm">/**
* 测试静态函数的hook, 则回调函数参数中 instance 为 null
*/</span>
<span class="n">hook</span><span class="o">(</span><span class="nc">Demo2</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="s">"staticTest"</span><span class="o">,</span> <span class="s">"(Ljava/lang/String;Ljava/lang/Long;[I)Ljava/lang/String;"</span><span class="o">,</span> <span class="k">new</span> <span class="nc">Handler</span><span class="o">()</span>
<span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">before</span><span class="o">(</span><span class="nc">Object</span> <span class="n">instance</span><span class="o">,</span> <span class="nc">Object</span><span class="o">[]</span> <span class="n">params</span><span class="o">)</span>
<span class="o">{</span>
<span class="nc">Log</span><span class="o">.</span><span class="na">d</span><span class="o">(</span><span class="no">TAG</span><span class="o">,</span> <span class="s">"===========Handler before,param len: "</span><span class="o">+</span><span class="n">params</span><span class="o">.</span><span class="na">length</span><span class="o">+</span><span class="s">",instance:"</span><span class="o">+</span><span class="n">instance</span><span class="o">);</span>
<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">params</span><span class="o">.</span><span class="na">length</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span>
<span class="o">{</span>
<span class="nc">Log</span><span class="o">.</span><span class="na">d</span><span class="o">(</span><span class="no">TAG</span><span class="o">,</span> <span class="s">"p"</span> <span class="o">+</span> <span class="n">i</span> <span class="o">+</span> <span class="s">"="</span> <span class="o">+</span> <span class="n">params</span><span class="o">[</span><span class="n">i</span><span class="o">]);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">});</span>
<span class="nc">Log</span><span class="o">.</span><span class="na">d</span><span class="o">(</span><span class="no">TAG</span><span class="o">,</span> <span class="s">"===========call test"</span> <span class="o">+</span> <span class="k">this</span><span class="o">.</span><span class="na">staticTest</span><span class="o">(</span><span class="n">param1</span><span class="o">,</span> <span class="mi">999L</span><span class="o">,</span> <span class="k">new</span> <span class="kt">int</span><span class="o">[]{</span><span class="mi">1</span><span class="o">,</span> <span class="mi">2</span><span class="o">,</span> <span class="mi">3</span><span class="o">}));</span>
<span class="o">}</span>
<span class="cm">/**
* hook 函数, 是个jni 函数
* @param clazzToHook
* @param methodName
* @param methodSig
* @param handler
*/</span>
<span class="kd">private</span> <span class="kd">native</span> <span class="kt">void</span> <span class="nf">hook</span><span class="o">(</span><span class="nc">Class</span><span class="o"><?></span> <span class="n">clazzToHook</span><span class="o">,</span> <span class="nc">String</span> <span class="n">methodName</span><span class="o">,</span> <span class="nc">String</span> <span class="n">methodSig</span><span class="o">,</span> <span class="nc">Handler</span> <span class="n">handler</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>但是这个封装其实也不好, 只是学习如何在nativeFunc 通过dvm 调用回java层代码而已</p>
<p>它并没有真正的转发处理, 还是native来调用具体的方法; 也就是说如果hooker 需要做其他逻辑的话, 比如, 我是不是设置了before是不是有replace之类的, native代码就多了</p>
<p>最好的就是, 我把原函数改掉, 然后获取信息, 然后转发给一个java层处理器, 告诉它, 被hook的是这个函数, 对象是这个, 参数是这些, 然后勾子是这个, 就可以了, Handler 勾子的调用都由这个java层处理器来做, 而不是natviceFunc里, native里不需要做太多逻辑</p>
<p>具体实现就不在做了, 因为<a href="https://github.com/alibaba/dexposed">alibaba/dexposed</a>项目已经做的很好了, 具体可以看它的实现</p>
<p>我自己也做了些注释:</p>
<blockquote>
<p><a href="https://github.com/alibaba/dexposed">pangliang/dexposed</a></p>
</blockquote>
Android Hook(1) Dexposed原理
2015-08-14T00:00:00-05:00
http://www.zhaoxiaodan.com/android/Android-Hook(1)-dexposed原理
<p><a href="https://github.com/alibaba/dexposed">dexposed</a> 这个项目相当不错, 之前就想着怎么动态替换jvm中的代码, 一直没有思路; 现在好好学习一下</p>
<h2 id="准备源码库">准备源码库</h2>
<p>因为<code class="language-plaintext highlighter-rouge">dexposed</code>其实是用了dvm和art调用class的方式来做的, 而dvm和art的头文件什么的在android源码中, 所以下一份源码, 具体办法见上一个博文: <a href="http://www.zhaoxiaodan.com/android/%E5%87%86%E5%A4%87android%E6%BA%90%E7%A0%81%E5%BA%93.html">准备android源码库</a></p>
<h2 id="最简单的hook">最简单的hook</h2>
<p>Demo1中有个test函数, 在调用hook之前正常返回”11111”;
调用hook之后, 却返回”newTestMethod”, 被我们给<code class="language-plaintext highlighter-rouge">修改</code>了</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Demo1</span>
<span class="o">{</span>
<span class="nc">String</span> <span class="no">TAG</span> <span class="o">=</span> <span class="s">"===[hookdemo]==="</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="nc">String</span> <span class="nf">staticTest</span><span class="o">(</span><span class="nc">String</span> <span class="n">param1</span><span class="o">)</span>
<span class="o">{</span>
<span class="k">return</span> <span class="s">"staticTest"</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">test</span><span class="o">(</span><span class="nc">String</span> <span class="n">param1</span><span class="o">)</span>
<span class="o">{</span>
<span class="k">return</span> <span class="s">"11111"</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">demo</span><span class="o">()</span>
<span class="o">{</span>
<span class="nc">String</span> <span class="n">param1</span> <span class="o">=</span> <span class="s">"param1"</span><span class="o">;</span>
<span class="nc">Log</span><span class="o">.</span><span class="na">d</span><span class="o">(</span><span class="no">TAG</span><span class="o">,</span> <span class="s">"===========before hook test:"</span> <span class="o">+</span> <span class="k">this</span><span class="o">.</span><span class="na">test</span><span class="o">(</span><span class="n">param1</span><span class="o">));</span>
<span class="n">hook</span><span class="o">(</span><span class="nc">Demo1</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="s">"test"</span><span class="o">,</span> <span class="s">"(Ljava/lang/String;)Ljava/lang/String;"</span><span class="o">);</span>
<span class="nc">Log</span><span class="o">.</span><span class="na">d</span><span class="o">(</span><span class="no">TAG</span><span class="o">,</span> <span class="s">"===========after hook test:"</span> <span class="o">+</span> <span class="k">this</span><span class="o">.</span><span class="na">test</span><span class="o">(</span><span class="n">param1</span><span class="o">));</span>
<span class="nc">Log</span><span class="o">.</span><span class="na">d</span><span class="o">(</span><span class="no">TAG</span><span class="o">,</span> <span class="s">"===========before hook staticTest:"</span> <span class="o">+</span> <span class="k">this</span><span class="o">.</span><span class="na">staticTest</span><span class="o">(</span><span class="n">param1</span><span class="o">));</span>
<span class="n">hook</span><span class="o">(</span><span class="nc">Demo1</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="s">"staticTest"</span><span class="o">,</span> <span class="s">"(Ljava/lang/String;)Ljava/lang/String;"</span><span class="o">);</span>
<span class="nc">Log</span><span class="o">.</span><span class="na">d</span><span class="o">(</span><span class="no">TAG</span><span class="o">,</span> <span class="s">"===========after hook staticTest:"</span> <span class="o">+</span> <span class="k">this</span><span class="o">.</span><span class="na">staticTest</span><span class="o">(</span><span class="n">param1</span><span class="o">));</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="kd">native</span> <span class="kt">void</span> <span class="nf">hook</span><span class="o">(</span><span class="nc">Class</span><span class="o"><?></span> <span class="n">clazzToHook</span><span class="o">,</span> <span class="nc">String</span> <span class="n">methodName</span><span class="o">,</span> <span class="nc">String</span> <span class="n">methodSig</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>ndk 中的部分</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include <jni.h>
#include "log.h"
#include "Dalvik.h"
</span>
<span class="k">static</span> <span class="kt">void</span> <span class="nf">showMethodInfo</span><span class="p">(</span><span class="k">const</span> <span class="n">Method</span><span class="o">*</span> <span class="n">method</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">//看看method的各个属性都是啥:</span>
<span class="n">LOGD</span><span class="p">(</span><span class="s">"accessFlags:%d"</span><span class="p">,</span><span class="n">method</span><span class="o">-></span><span class="n">accessFlags</span><span class="p">);</span>
<span class="n">LOGD</span><span class="p">(</span><span class="s">"clazz->descriptor:%s"</span><span class="p">,</span><span class="n">method</span><span class="o">-></span><span class="n">clazz</span><span class="o">-></span><span class="n">descriptor</span><span class="p">);</span>
<span class="n">LOGD</span><span class="p">(</span><span class="s">"clazz->sourceFile:%s"</span><span class="p">,</span><span class="n">method</span><span class="o">-></span><span class="n">clazz</span><span class="o">-></span><span class="n">sourceFile</span><span class="p">);</span>
<span class="n">LOGD</span><span class="p">(</span><span class="s">"methodIndex:%d"</span><span class="p">,</span><span class="n">method</span><span class="o">-></span><span class="n">methodIndex</span><span class="p">);</span>
<span class="n">LOGD</span><span class="p">(</span><span class="s">"name:%s"</span><span class="p">,</span><span class="n">method</span><span class="o">-></span><span class="n">name</span><span class="p">);</span>
<span class="n">LOGD</span><span class="p">(</span><span class="s">"shorty:%s"</span><span class="p">,</span><span class="n">method</span><span class="o">-></span><span class="n">shorty</span><span class="p">);</span>
<span class="p">}</span>
<span class="cm">/**
* 使用jni GetMethodID 方法获取jmethodID 强制转为 Method 的hook 方法 示例
*/</span>
<span class="k">static</span> <span class="kt">void</span> <span class="nf">newTestMethod</span><span class="p">(</span><span class="k">const</span> <span class="n">u4</span><span class="o">*</span> <span class="n">args</span><span class="p">,</span> <span class="n">JValue</span><span class="o">*</span> <span class="n">pResult</span><span class="p">,</span>
<span class="k">const</span> <span class="n">Method</span><span class="o">*</span> <span class="n">method</span><span class="p">,</span> <span class="k">struct</span> <span class="n">Thread</span><span class="o">*</span> <span class="n">self</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// args 是原来函数的参数数组, 原来test函数只有一个String型参数</span>
<span class="c1">// 并且要注意, 如果是不是static函数, 下标0 是函数所在类的实例obj</span>
<span class="c1">// 在dvm中Object, jni 中的jobject 和 java 中的 Object类 都不是同一个东西</span>
<span class="c1">// String类对应StringObject</span>
<span class="c1">// 取出参数打印出来看看</span>
<span class="n">StringObject</span><span class="o">*</span> <span class="n">param1</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="k">if</span><span class="p">(</span><span class="n">dvmIsStaticMethod</span><span class="p">(</span><span class="n">method</span><span class="p">))</span>
<span class="n">param1</span> <span class="o">=</span> <span class="p">(</span><span class="n">StringObject</span><span class="o">*</span><span class="p">)</span><span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="k">else</span>
<span class="n">param1</span> <span class="o">=</span> <span class="p">(</span><span class="n">StringObject</span><span class="o">*</span><span class="p">)</span><span class="n">args</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
<span class="n">LOGD</span><span class="p">(</span><span class="s">"param1:%s"</span><span class="p">,</span><span class="n">dvmCreateCstrFromString</span><span class="p">(</span><span class="n">param1</span><span class="p">));</span>
<span class="c1">//JValue 是个union ,要返回int 就 pResult->i=1; 返回Object对象就 pResult->l = ojb;</span>
<span class="c1">// 但是, 在dvm中的Object, jni 中的jobject 和 java 中的 Object类 都不是同一个东西</span>
<span class="c1">// 所以, 我们这里使用dvm的函数来创建一个StringObject*</span>
<span class="n">pResult</span><span class="o">-></span><span class="n">l</span> <span class="o">=</span> <span class="n">dvmCreateStringFromCstr</span><span class="p">(</span><span class="s">"newTestMethod"</span><span class="p">);</span>
<span class="c1">// 一般情况应该使用宏 : RETURN_XXX(result);</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">extern</span> <span class="s">"C"</span> <span class="n">JNIEXPORT</span> <span class="kt">void</span> <span class="n">JNICALL</span>
<span class="nf">Java_com_zhaoxiaodan_hookdemo_Demo1_hook</span><span class="p">(</span><span class="n">JNIEnv</span> <span class="o">*</span><span class="n">env</span><span class="p">,</span> <span class="n">jobject</span> <span class="n">instance</span><span class="p">,</span> <span class="n">jobject</span> <span class="n">clazzToHook</span><span class="p">,</span>
<span class="n">jstring</span> <span class="n">methodName_</span><span class="p">,</span> <span class="n">jstring</span> <span class="n">methodSig_</span><span class="p">)</span> <span class="p">{</span>
<span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">methodName</span> <span class="o">=</span> <span class="n">env</span><span class="o">-></span><span class="n">GetStringUTFChars</span><span class="p">(</span><span class="n">methodName_</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">methodSig</span> <span class="o">=</span> <span class="n">env</span><span class="o">-></span><span class="n">GetStringUTFChars</span><span class="p">(</span><span class="n">methodSig_</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="n">jmethodID</span> <span class="n">methodIDToHook</span> <span class="o">=</span> <span class="n">env</span><span class="o">-></span><span class="n">GetMethodID</span><span class="p">((</span><span class="n">jclass</span><span class="p">)</span> <span class="n">clazzToHook</span><span class="p">,</span><span class="n">methodName</span><span class="p">,</span><span class="n">methodSig</span><span class="p">);</span>
<span class="c1">// 找不到有可能是个static</span>
<span class="k">if</span><span class="p">(</span><span class="n">nullptr</span> <span class="o">==</span> <span class="n">methodIDToHook</span><span class="p">){</span>
<span class="n">env</span><span class="o">-></span><span class="n">ExceptionClear</span><span class="p">();</span>
<span class="n">methodIDToHook</span> <span class="o">=</span> <span class="n">env</span><span class="o">-></span><span class="n">GetStaticMethodID</span><span class="p">((</span><span class="n">jclass</span><span class="p">)</span> <span class="n">clazzToHook</span><span class="p">,</span><span class="n">methodName</span><span class="p">,</span><span class="n">methodSig</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">if</span><span class="p">(</span><span class="n">methodIDToHook</span> <span class="o">!=</span> <span class="n">nullptr</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">//主要在这里替换</span>
<span class="c1">//jmethodID 在dvm里实际上就是个Method 结构体</span>
<span class="n">Method</span><span class="o">*</span> <span class="n">method</span> <span class="o">=</span> <span class="p">(</span><span class="n">Method</span><span class="o">*</span><span class="p">)</span> <span class="n">methodIDToHook</span><span class="p">;</span>
<span class="c1">//看看method的各个属性都是啥:</span>
<span class="n">showMethodInfo</span><span class="p">(</span><span class="n">method</span><span class="p">);</span>
<span class="c1">//设置Method 的 accessFlags 为 枚举型</span>
<span class="c1">// ACC_NATIVE 表示 这个method 切换成了一个native 方法</span>
<span class="c1">// 这个枚举 在 dalvik/libdex/DexFile.h</span>
<span class="c1">// 类似:</span>
<span class="c1">// ACC_PUBLIC = 0x00000001, // class, field, method, ic</span>
<span class="c1">// ACC_PRIVATE = 0x00000002, // field, method, ic</span>
<span class="c1">// ACC_PROTECTED = 0x00000004, // field, method, ic</span>
<span class="n">SET_METHOD_FLAG</span><span class="p">(</span><span class="n">method</span><span class="p">,</span> <span class="n">ACC_NATIVE</span><span class="p">);</span>
<span class="c1">//既然是一个native方法, 那就把 nativeFunc 指针指向我们的hook, 用来替换test的新方法</span>
<span class="n">method</span><span class="o">-></span><span class="n">nativeFunc</span> <span class="o">=</span> <span class="o">&</span><span class="n">newTestMethod</span><span class="p">;</span>
<span class="c1">// registersSize是函数栈大小, insSize是参数占用大小</span>
<span class="c1">// 如果是native方法, 就没有额外开销了</span>
<span class="c1">// 所有开销就是参数占用, 所以把它设置成跟参数占用空间</span>
<span class="n">method</span><span class="o">-></span><span class="n">registersSize</span><span class="o">=</span><span class="n">method</span><span class="o">-></span><span class="n">insSize</span><span class="p">;</span>
<span class="c1">//未知</span>
<span class="n">method</span><span class="o">-></span><span class="n">outsSize</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">env</span><span class="o">-></span><span class="n">ReleaseStringUTFChars</span><span class="p">(</span><span class="n">methodName_</span><span class="p">,</span> <span class="n">methodName</span><span class="p">);</span>
<span class="n">env</span><span class="o">-></span><span class="n">ReleaseStringUTFChars</span><span class="p">(</span><span class="n">methodSig_</span><span class="p">,</span> <span class="n">methodSig</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>运行之后得到:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/===[hookdemo]===﹕ ===========before hook test:11111
/[---hookdemo---]﹕ accessFlags:1
/[---hookdemo---]﹕ clazz->descriptor:Lcom/zhaoxiaodan/hookdemo/MainActivity;
/[---hookdemo---]﹕ clazz->sourceFile:MainActivity.java
/[---hookdemo---]﹕ methodIndex:334
/[---hookdemo---]﹕ name:test
/[---hookdemo---]﹕ shorty:LL
/[---hookdemo---]﹕ param1:param1
/===[hookdemo]===﹕ ===========after hook test:newTestMethod
/===[hookdemo]===﹕ ===========before hook staticTest:staticTest
/dalvikvm﹕ GetMethodID: not returning static method Lcom/zhaoxiaodan/hookdemo/MainActivity;.staticTest (Ljava/lang/String;)Ljava/lang/String;
/[---hookdemo---]﹕ accessFlags:9
/[---hookdemo---]﹕ clazz->descriptor:Lcom/zhaoxiaodan/hookdemo/MainActivity;
/[---hookdemo---]﹕ clazz->sourceFile:MainActivity.java
/[---hookdemo---]﹕ methodIndex:0
/[---hookdemo---]﹕ name:staticTest
/[---hookdemo---]﹕ shorty:LL
/[---hookdemo---]﹕ param1:param1
/===[hookdemo]===﹕ ===========after hook staticTest:newTestMethod
</code></pre></div></div>
<p>原理就是, Method结构体表示了一个java层函数, 而其中的accessFlags属性如果是ACC_NATIVE , dvm在call 原java层函数的时候, 则会转向调用 属性nativeFunc 所指向的函数</p>
<p>所以我们把不是native的java层函数的accessFlags强制改为ACC_NATIVE, 然后把nativeFunc 指向我们的新函数, 则完成了方法的修改</p>
<p>只不过, 这里使用native方法替换了java层的原方法</p>
<h2 id="参考文章">参考文章</h2>
<ul>
<li><a href="http://blog.csdn.net/u013234805/article/details/24796503">编译屏障和内存屏障</a> : 需要设置的 ANDROID_SMP 是啥 ?</li>
<li><a href="http://blog.csdn.net/luoshengyang/article/details/8923483">Dalvik虚拟机JNI方法的注册过程分析</a></li>
</ul>
准备android源码库
2015-08-13T00:00:00-05:00
http://www.zhaoxiaodan.com/android/准备android源码库
<h1 id="1-准备repo">1. 准备repo</h1>
<p>由于google被墙, 可以全面使用国内大学的源来弄</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl https://storage.googleapis.com/git-repo-downloads/repo <span class="o">></span> ./repo
</code></pre></div></div>
<p>修改REPO_URL为清华源:</p>
<blockquote>
<p>REPO_URL = “https://gerrit-googlesource.lug.ustc.edu.cn/git-repo”</p>
</blockquote>
<h1 id="2-准备代码库">2. 准备代码库</h1>
<h2 id="初始化">初始化</h2>
<p>以<code class="language-plaintext highlighter-rouge">android-4.2.2_r1.2</code> 版本为例, 以后需要切换版本只需要重复<code class="language-plaintext highlighter-rouge">init</code>和<code class="language-plaintext highlighter-rouge">sync</code> 即可</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./repo init <span class="nt">-u</span> git://mirrors.ustc.edu.cn/aosp/platform/manifest <span class="nt">-b</span> android-4.2.2_r1.2
</code></pre></div></div>
<h2 id="同步项目">同步项目</h2>
<p>依赖如下几个项目:</p>
<ul>
<li>art</li>
<li>bionic</li>
<li>dalvik</li>
<li>external/gtest</li>
<li>external/libcxx</li>
<li>external/stlport</li>
<li>external/valgrind</li>
<li>frameworks/base</li>
<li>frameworks/native</li>
<li>libnativehelper</li>
<li>system/core</li>
</ul>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./repo <span class="nb">sync</span> <span class="nt">-j4</span> art <span class="se">\</span>
bionic <span class="se">\</span>
dalvik <span class="se">\</span>
external/gtest <span class="se">\</span>
external/libcxx <span class="se">\</span>
external/stlport <span class="se">\</span>
external/valgrind <span class="se">\</span>
frameworks/base <span class="se">\</span>
frameworks/native <span class="se">\</span>
libnativehelper <span class="se">\</span>
system/core
</code></pre></div></div>
<h2 id="切换版本">切换版本</h2>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./repo init <span class="nt">-u</span> git://aosp.tuna.tsinghua.edu.cn/android/platform/manifest <span class="nt">-b</span> 版本
<span class="c"># 同步当前已下载的模块</span>
./repo <span class="nb">sync</span> <span class="nt">-j4</span> <span class="sb">`</span>./repo list | <span class="nb">awk</span> <span class="s1">'{print $3}'</span>
</code></pre></div></div>
加解密函数在php,java和c#上的适配
2015-08-06T00:00:00-05:00
http://www.zhaoxiaodan.com/java/php/加解密函数在php,java和c#上的适配
<p>一般加密解密使用通用的加解密算法, 但是各个语言的使用方法也不同, 有时候搞来搞去的, 发现php加出来的java又解不了, 最近看了下</p>
<p>其实一个加密api的使用主要有三个部分, 只要各个语言的三种模式都设置相同, 那么就能相互加解密成功, 这三个部分是:</p>
<blockquote>
<ol>
<li>算法</li>
<li>块模式</li>
<li>填充模式</li>
</ol>
</blockquote>
<p>三种语言的使用加解密库代码:</p>
<h3 id="java">java</h3>
<p>由<code class="language-plaintext highlighter-rouge">Cipher.getInstance()</code> 函数直接设定并获得加密类</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kd">static</span> <span class="nc">String</span> <span class="nf">decrypt</span><span class="o">(</span><span class="nc">String</span> <span class="n">value</span><span class="o">,</span><span class="nc">String</span> <span class="n">key</span><span class="o">)</span> <span class="o">{</span>
<span class="k">try</span><span class="o">{</span>
<span class="nc">SecretKeySpec</span> <span class="n">keySpec</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">SecretKeySpec</span><span class="o">(</span><span class="n">key</span><span class="o">.</span><span class="na">getBytes</span><span class="o">(),</span><span class="s">"TripleDES"</span><span class="o">);</span>
<span class="nc">Cipher</span> <span class="n">cipher</span> <span class="o">=</span> <span class="nc">Cipher</span><span class="o">.</span><span class="na">getInstance</span><span class="o">(</span><span class="s">"TripleDES/ECB/NoPadding"</span><span class="o">);</span> <span class="c1">// 算法/块模式/填充模式</span>
<span class="n">cipher</span><span class="o">.</span><span class="na">init</span><span class="o">(</span><span class="nc">Cipher</span><span class="o">.</span><span class="na">DECRYPT_MODE</span><span class="o">,</span> <span class="n">keySpec</span><span class="o">);</span>
<span class="kt">byte</span><span class="o">[]</span> <span class="n">decryptedByte</span><span class="o">=</span><span class="n">cipher</span><span class="o">.</span><span class="na">doFinal</span><span class="o">(</span><span class="nc">Base64</span><span class="o">.</span><span class="na">getDecoder</span><span class="o">().</span><span class="na">decode</span><span class="o">(</span><span class="n">value</span><span class="o">.</span><span class="na">getBytes</span><span class="o">()));</span>
<span class="k">return</span> <span class="k">new</span> <span class="nf">String</span><span class="o">(</span><span class="n">decryptedByte</span><span class="o">);</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
<span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
<span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<h3 id="c--的">c# 的</h3>
<p>由<code class="language-plaintext highlighter-rouge">XXXXXXCryptoServiceProvider</code> 类提供加密算法, 他的<code class="language-plaintext highlighter-rouge">Mode</code>属性设置模式,<code class="language-plaintext highlighter-rouge">Padding</code>属性设置填充模式</p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">static</span> <span class="kt">string</span> <span class="nf">DESDeCode</span><span class="p">(</span><span class="kt">string</span> <span class="n">pToDecrypt</span><span class="p">,</span> <span class="kt">string</span> <span class="n">sKey</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">TripleDESCryptoServiceProvider</span> <span class="n">tdes</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">TripleDESCryptoServiceProvider</span><span class="p">();</span> <span class="c1">//算法</span>
<span class="kt">byte</span><span class="p">[]</span> <span class="n">inputByteArray</span> <span class="p">=</span> <span class="n">Convert</span><span class="p">.</span><span class="nf">FromBase64String</span><span class="p">(</span><span class="n">pToDecrypt</span><span class="p">);</span>
<span class="n">tdes</span><span class="p">.</span><span class="n">Key</span> <span class="p">=</span> <span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="nf">GetBytes</span><span class="p">(</span><span class="n">sKey</span><span class="p">);</span>
<span class="n">tdes</span><span class="p">.</span><span class="n">Mode</span> <span class="p">=</span> <span class="n">CipherMode</span><span class="p">.</span><span class="n">ECB</span><span class="p">;</span> <span class="c1">//块模式</span>
<span class="n">tdes</span><span class="p">.</span><span class="n">Padding</span> <span class="p">=</span> <span class="n">PaddingMode</span><span class="p">.</span><span class="n">Zeros</span><span class="p">;</span> <span class="c1">//填充模式</span>
<span class="n">ICryptoTransform</span> <span class="n">cTransform</span> <span class="p">=</span> <span class="n">tdes</span><span class="p">.</span><span class="nf">CreateDecryptor</span><span class="p">();</span>
<span class="kt">byte</span><span class="p">[]</span> <span class="n">resultArray</span> <span class="p">=</span> <span class="n">cTransform</span><span class="p">.</span><span class="nf">TransformFinalBlock</span><span class="p">(</span><span class="n">inputByteArray</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">inputByteArray</span><span class="p">.</span><span class="n">Length</span><span class="p">);</span>
<span class="kt">string</span> <span class="n">str</span> <span class="p">=</span> <span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="nf">GetString</span><span class="p">(</span><span class="n">resultArray</span><span class="p">);</span>
<span class="k">return</span> <span class="n">str</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="php">php</h3>
<p>php最简单, 一个函数就搞定, 第一个参数设置算法, 第四个参数设置模式; php默认的填充模式是<code class="language-plaintext highlighter-rouge">Zeros</code>, 也就是填充0</p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?php</span>
<span class="nv">$text</span> <span class="o">=</span> <span class="nb">mcrypt_decrypt</span><span class="p">(</span>
<span class="nx">MCRYPT_TRIPLEDES</span><span class="p">,</span> <span class="c1">//指定算法</span>
<span class="nv">$KEY</span><span class="p">,</span><span class="nv">$cryptStr</span><span class="p">,</span>
<span class="nx">MCRYPT_MODE_ECB</span> <span class="c1">//块模式</span>
<span class="p">);</span>
</code></pre></div></div>
<h2 id="对应关系示例">对应关系示例</h2>
<table>
<thead>
<tr>
<th>php</th>
<th>java</th>
<th>c#</th>
</tr>
</thead>
<tbody>
<tr>
<td>MCRYPT_TRIPLEDES, MCRYPT_MODE_ECB</td>
<td>“TripleDES/ECB/NoPadding”</td>
<td>TripleDESCryptoServiceProvider, CipherMode.ECB, PaddingMode.Zeros</td>
</tr>
<tr>
<td>MCRYPT_TRIPLEDES, MCRYPT_MODE_CBC</td>
<td>“TripleDES/CBC/NoPadding”</td>
<td>TripleDESCryptoServiceProvider, CipherMode.CBC, PaddingMode.Zeros</td>
</tr>
</tbody>
</table>
<h2 id="cbc-和-ecb">CBC 和 ECB</h2>
<p>加密实际上就是把要加密的明文按位数分块, 然后再把密码也分块, 把明文块和密码块输入到加密算法中计算得到密文块的过程; 而CBC ECB指定的是明文块和密码块的输入方式</p>
<p>ECB模式就是简单的对应, 明文块0对应密码块0, 输入到算法中得到密文块0, 用图说明:</p>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2015/20150806061831.png" alt="" /></p>
<p>CBC模式, 它还需要一个叫iv的<code class="language-plaintext highlighter-rouge">变量</code>, 明文块0先和iv计算, 然后对应密码块0 , 输入到算法中得到密文块0, 密文块1则是通过明文块1和上一步的结果, 也就是密文块0计算, 再和密码块1输入到算法中得到</p>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2015/20150806062428.png" alt="" /></p>
<h2 id="java-可用算法块模式填充模式列表">java 可用[算法/块模式/填充模式]列表</h2>
<ul>
<li>AES/CBC/NoPadding (128)</li>
<li>AES/CBC/PKCS5Padding (128)</li>
<li>AES/ECB/NoPadding (128)</li>
<li>AES/ECB/PKCS5Padding (128)</li>
<li>DES/CBC/NoPadding (56)</li>
<li>DES/CBC/PKCS5Padding (56)</li>
<li>DES/ECB/NoPadding (56)</li>
<li>DES/ECB/PKCS5Padding (56)</li>
<li>DESede/CBC/NoPadding (168)</li>
<li>DESede/CBC/PKCS5Padding (168)</li>
<li>DESede/ECB/NoPadding (168)</li>
<li>DESede/ECB/PKCS5Padding (168)</li>
<li>RSA/ECB/PKCS1Padding (1024, 2048)</li>
<li>RSA/ECB/OAEPWithSHA-1AndMGF1Padding (1024, 2048)</li>
<li>RSA/ECB/OAEPWithSHA-256AndMGF1Padding (1024, 2048)</li>
</ul>
<h2 id="php的算法列表">php的算法列表</h2>
<ul>
<li>define (‘MCRYPT_ENCRYPT’, 0);</li>
<li>define (‘MCRYPT_DECRYPT’, 1);</li>
<li>define (‘MCRYPT_DEV_RANDOM’, 0);</li>
<li>define (‘MCRYPT_DEV_URANDOM’, 1);</li>
<li>define (‘MCRYPT_RAND’, 2);</li>
<li>define (‘MCRYPT_3DES’, “tripledes”);</li>
<li>define (‘MCRYPT_ARCFOUR_IV’, “arcfour-iv”);</li>
<li>define (‘MCRYPT_ARCFOUR’, “arcfour”);</li>
<li>define (‘MCRYPT_BLOWFISH’, “blowfish”);</li>
<li>define (‘MCRYPT_BLOWFISH_COMPAT’, “blowfish-compat”);</li>
<li>define (‘MCRYPT_CAST_128’, “cast-128”);</li>
<li>define (‘MCRYPT_CAST_256’, “cast-256”);</li>
<li>define (‘MCRYPT_CRYPT’, “crypt”);</li>
<li>define (‘MCRYPT_DES’, “des”);</li>
<li>define (‘MCRYPT_ENIGNA’, “crypt”);</li>
<li>define (‘MCRYPT_GOST’, “gost”);</li>
<li>define (‘MCRYPT_LOKI97’, “loki97”);</li>
<li>define (‘MCRYPT_PANAMA’, “panama”);</li>
<li>define (‘MCRYPT_RC2’, “rc2”);</li>
<li>define (‘MCRYPT_RIJNDAEL_128’, “rijndael-128”);</li>
<li>define (‘MCRYPT_RIJNDAEL_192’, “rijndael-192”);</li>
<li>define (‘MCRYPT_RIJNDAEL_256’, “rijndael-256”);</li>
<li>define (‘MCRYPT_SAFER64’, “safer-sk64”);</li>
<li>define (‘MCRYPT_SAFER128’, “safer-sk128”);</li>
<li>define (‘MCRYPT_SAFERPLUS’, “saferplus”);</li>
<li>define (‘MCRYPT_SERPENT’, “serpent”);</li>
<li>define (‘MCRYPT_THREEWAY’, “threeway”);</li>
<li>define (‘MCRYPT_TRIPLEDES’, “tripledes”);</li>
<li>define (‘MCRYPT_TWOFISH’, “twofish”);</li>
<li>define (‘MCRYPT_WAKE’, “wake”);</li>
<li>define (‘MCRYPT_XTEA’, “xtea”);</li>
<li>define (‘MCRYPT_IDEA’, “idea”);</li>
<li>define (‘MCRYPT_MARS’, “mars”);</li>
<li>define (‘MCRYPT_RC6’, “rc6”);</li>
<li>define (‘MCRYPT_SKIPJACK’, “skipjack”);</li>
</ul>
<h2 id="php的块模式列表">php的块模式列表</h2>
<ul>
<li>define (‘MCRYPT_MODE_CBC’, “cbc”);</li>
<li>define (‘MCRYPT_MODE_CFB’, “cfb”);</li>
<li>define (‘MCRYPT_MODE_ECB’, “ecb”);</li>
<li>define (‘MCRYPT_MODE_NOFB’, “nofb”);</li>
<li>define (‘MCRYPT_MODE_OFB’, “ofb”);</li>
<li>define (‘MCRYPT_MODE_STREAM’, “stream”);</li>
</ul>
<h2 id="c的块模式列表">c#的块模式列表</h2>
<ul>
<li>CBC</li>
<li>CFB</li>
<li>CTS</li>
<li>ECB</li>
<li>OFB</li>
</ul>
<h2 id="c的填充模式列表">c#的填充模式列表</h2>
<ul>
<li>ANSIX923</li>
<li>ISO10126</li>
<li>None</li>
<li>PKCS7</li>
<li>Zeros</li>
</ul>
华为,酷派手机无法adb调试
2015-07-30T00:00:00-05:00
http://www.zhaoxiaodan.com/android/华为,酷派手机无法adb调试
<p>mac系统也没华为和酷派的驱动啥的, 搜索了一下, 华为的手机是自己还有个所谓的<code class="language-plaintext highlighter-rouge">usb模式</code>; 默认是个自有模式, 跟google原版不一样; 需要修改回<code class="language-plaintext highlighter-rouge">google模式</code>才行</p>
<p>具体办法是:</p>
<p>进入手机的拨号, 输入<code class="language-plaintext highlighter-rouge">*#*#2846579#*#*</code>进入工程模式, 然后选择<code class="language-plaintext highlighter-rouge">ProjectMenu</code>-><code class="language-plaintext highlighter-rouge">后台设置</code>-><code class="language-plaintext highlighter-rouge">use端口设置</code> 改成<code class="language-plaintext highlighter-rouge">GOOGLE模式</code></p>
<p>然后重启手机</p>
<p>如果还是看不到, 需要在命令行执行:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># android update adb</span>
</code></pre></div></div>
<p>也就是网上说的添加<code class="language-plaintext highlighter-rouge">VendorId</code>到<code class="language-plaintext highlighter-rouge">~/.android/adb_usb.ini</code>里的办法, 但是文件里明说了不要修改, 应该使用<code class="language-plaintext highlighter-rouge">android update adb</code></p>
<p>反正我是运行了然后重启adb服务就好了</p>
缩小ndk库的大小
2015-07-29T00:00:00-05:00
http://www.zhaoxiaodan.com/android/缩小ndk库的大小
<p>搞了个sdk的native层, 也就简单几行jni调用, 读取文件,加载类啥的. 编译完发现居然有370多k. 作为一个游戏的sdk, 人家游戏都才几M, 一个sdk要400k, 怎么能忍</p>
<p>google了下, 找到这个文章:<a href="http://www.cnblogs.com/octave/p/4454205.html">Android NDK 如何缩减库的大小</a></p>
<p>说是iostream 会大幅增加大小,查了下代码, 只有读取文件这一个功能有用到iostream</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span><span class="o">*</span> <span class="n">FileUtils</span><span class="o">::</span><span class="n">readFileData</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&</span> <span class="n">filename</span><span class="p">,</span> <span class="kt">ssize_t</span> <span class="o">*</span><span class="n">size</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">ifstream</span> <span class="n">in</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ifstream</span><span class="o">::</span><span class="n">ate</span> <span class="o">|</span> <span class="n">std</span><span class="o">::</span><span class="n">ifstream</span><span class="o">::</span><span class="n">binary</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">in</span><span class="p">.</span><span class="n">is_open</span><span class="p">())</span>
<span class="p">{</span>
<span class="n">throw</span> <span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">"file "</span><span class="o">+</span><span class="n">filename</span><span class="o">+</span><span class="s">" open error"</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="n">fileSize</span> <span class="o">=</span> <span class="n">in</span><span class="p">.</span><span class="n">tellg</span><span class="p">();</span>
<span class="kt">void</span><span class="o">*</span> <span class="n">data</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">fileSize</span><span class="p">);</span>
<span class="n">in</span><span class="p">.</span><span class="n">seekg</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ifstream</span><span class="o">::</span><span class="n">beg</span><span class="p">);</span>
<span class="n">in</span><span class="p">.</span><span class="n">read</span><span class="p">((</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="n">data</span><span class="p">,</span> <span class="n">fileSize</span><span class="p">);</span>
<span class="n">in</span><span class="p">.</span><span class="n">close</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">size</span><span class="p">)</span>
<span class="p">{</span>
<span class="o">*</span><span class="n">size</span> <span class="o">=</span> <span class="n">fileSize</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">data</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>当然嘛, 不用流也可以读文件的, 代码改为:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span><span class="o">*</span> <span class="n">FileUtils</span><span class="o">::</span><span class="n">readFileData</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&</span> <span class="n">filename</span><span class="p">,</span> <span class="kt">ssize_t</span> <span class="o">*</span><span class="n">size</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">FILE</span><span class="o">*</span> <span class="n">file</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="n">filename</span><span class="p">.</span><span class="n">c_str</span><span class="p">(),</span> <span class="s">"rb"</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">file</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">throw</span> <span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">"file "</span><span class="o">+</span><span class="n">filename</span><span class="o">+</span><span class="s">" open error"</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">struct</span> <span class="n">stat</span> <span class="n">info</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">stat</span><span class="p">(</span><span class="n">filename</span><span class="p">.</span><span class="n">c_str</span><span class="p">(),</span> <span class="o">&</span><span class="n">info</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">throw</span> <span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">"get "</span><span class="o">+</span><span class="n">filename</span><span class="o">+</span><span class="s">" stat error"</span><span class="p">);</span>
<span class="kt">int</span> <span class="n">fileSize</span> <span class="o">=</span> <span class="n">info</span><span class="p">.</span><span class="n">st_size</span><span class="p">;</span>
<span class="kt">void</span><span class="o">*</span> <span class="n">data</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">fileSize</span><span class="p">);</span>
<span class="n">fread</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">void</span><span class="p">),</span> <span class="n">fileSize</span><span class="p">,</span> <span class="n">file</span><span class="p">);</span>
<span class="n">fclose</span><span class="p">(</span><span class="n">file</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">size</span><span class="p">)</span>
<span class="p">{</span>
<span class="o">*</span><span class="n">size</span> <span class="o">=</span> <span class="n">fileSize</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">data</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>再编译, 库的大小缩小到了97k</p>
利用ssh_tunnel访问服务器环境的内网服务
2015-07-10T00:00:00-05:00
http://www.zhaoxiaodan.com/%E5%85%B6%E4%BB%96/利用ssh_tunnel访问服务器环境的内网服务
<p>项目最初觉得没有必要生产环境开发环境这么搞, 直接用rds做开发库了; 阿里云的数据库后台登录又必须先登录阿里云帐号; 不适合把帐号给多个人</p>
<p>google了下, 网上有如下几种方案</p>
<ul>
<li>用tengine做反向代理</li>
<li>用mysql-proxy做代理</li>
<li>用iptable做封包转发</li>
<li>利用ssh tunnel</li>
</ul>
<p>其实前面三种, 跟直接把mysql开出公网来没啥区别, 只不过稍微灵活一点, 用的时候开开, 不用关掉;
但是万一<code class="language-plaintext highlighter-rouge">忘记</code>关了呢? 而且<code class="language-plaintext highlighter-rouge">控制权</code>还是在服务器管理者手里;</p>
<p>一般都给开发发放ssh权限, 利用ssh tunnel还是方便, 够<code class="language-plaintext highlighter-rouge">临时</code>, 连不连又都是开发自己选择;</p>
<p>使用命令:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh <span class="nt">-N</span> <span class="nt">-L</span> 本地端口号:rds实例地址:rds实例端口号 ecs用户名@ecsip
</code></pre></div></div>
<p>ssh tunnel的意思就是说, 建立一个本地端口, 把这个本地端口的数据包通过ssh服务发到<code class="language-plaintext highlighter-rouge">ecsip</code>这个服务器, 再通过服务器上的ssh服务转发给指定的<code class="language-plaintext highlighter-rouge">rds实例地址:rds实例端口号</code>, 那也就打开了一个<code class="language-plaintext highlighter-rouge">本地->ECS服务器->RDS</code>的通道了</p>
<p>这个东西的优势在于, 我只需要保证ssh服务的健壮, 就可以了;</p>
<p>同样的, 这个方式还适合其他的不便于<code class="language-plaintext highlighter-rouge">让公网可直接访问</code>的内网服务, 比如redis什么的</p>
<p>##注意</p>
<p>ssh服务必须打开<code class="language-plaintext highlighter-rouge">GatewayPorts</code>选项, 具体办法是修改<code class="language-plaintext highlighter-rouge">/etc/ssh/sshd_config</code>配置文件, 添加一行</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>GatewayPorts yes
</code></pre></div></div>
Linux登录欢迎信息
2015-07-02T00:00:00-05:00
http://www.zhaoxiaodan.com/%E5%85%B6%E4%BB%96/linux登录欢迎信息
<p>服务器多了, 有时候登录的对不对都不知道; 显示这种信息是不是很酷?</p>
<p>普通linux 是直接修改<code class="language-plaintext highlighter-rouge">/etc/motd</code>文件
ubuntu是<code class="language-plaintext highlighter-rouge">/etc/update-motd.d/</code>目录中, 按序号执行的bash脚本, 比如<code class="language-plaintext highlighter-rouge">01-pangliang</code>就是我自定义的, 注意需要把这个文件加上可执行权限:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>➜ ~ ll /etc/update-motd.d/
total 24
drwxr-xr-x 2 root root 4096 Jul 2 15:58 <span class="nb">.</span>
drwxr-xr-x 107 root root 4096 Jun 26 10:59 ..
<span class="nt">-rwxr-xr-x</span> 1 root root 1118 Jul 2 15:52 00-header
<span class="nt">-rw-r--r--</span> 1 root root 1439 Jul 2 15:58 01-pangliang <<span class="nt">--</span>没有可执行权限无效
<span class="nt">-rwxr-xr-x</span> 1 root root 1359 Sep 27 2014 10-help-text
<span class="nt">-rwxr-xr-x</span> 1 root root 299 Apr 12 2014 91-release-upgrade
</code></pre></div></div>
<p>然后运行下面命令解析生成:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>run-parts /etc/update-motd.d
</code></pre></div></div>
<p>整个<code class="language-plaintext highlighter-rouge">01-pangliang</code>文件内容:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>
<span class="nb">echo</span> <span class="s2">"</span><span class="se">\0</span><span class="s2">33[33m"</span>
<span class="nb">echo</span> <span class="s2">"
_ _ ___
| | | | (___)
___ | |__ _____ ____ _____ __| | ____ _____ _____ ____ _____ ____ _
/___)| _ </span><span class="se">\ </span><span class="s2">(____ | / ___)| ___ | / _ | / ___)| ___ |(____ || </span><span class="se">\(</span><span class="s2">_____)/ _ | | |
|___ || | | |/ ___ || | | ____|( (_| || | | ____|/ ___ || | | | ( (_| | _| |_
(___/ |_| |_|</span><span class="se">\_</span><span class="s2">____||_| |_____) </span><span class="se">\_</span><span class="s2">___||_| |_____)</span><span class="se">\_</span><span class="s2">____||_|_|_| </span><span class="se">\_</span><span class="s2">__ |(_____)
(_____|
"</span>
<span class="nb">cat</span> <span class="o"><<</span><span class="no">EOF</span><span class="sh">
_oo0oo_
088888880
88" . "88
(| -_- |)
0</span><span class="se">\ </span><span class="sh">= /0
___/'---'</span><span class="se">\_</span><span class="sh">__
.' </span><span class="se">\\\\</span><span class="sh">| |// '.
/ </span><span class="se">\\\\</span><span class="sh">||| : |||// </span><span class="se">\\</span><span class="sh">
/_ ||||| -:- |||||- </span><span class="se">\\</span><span class="sh">
| | </span><span class="se">\\\\\\</span><span class="sh"> - /// | |
| </span><span class="se">\_</span><span class="sh">| ''</span><span class="se">\-</span><span class="sh">--/'' |_/ |
</span><span class="se">\ </span><span class="sh"> .-</span><span class="se">\_</span><span class="sh">_ '-' __/-. /
___'. .' /--.--</span><span class="se">\ </span><span class="sh"> '. .'___
."" '< '.___</span><span class="se">\_</span><span class="sh"><|>_/___.' >' "".
| | : '- </span><span class="se">\'</span><span class="sh">.;'</span><span class="se">\ </span><span class="sh">_ /';.'/ - ' : | |
</span><span class="se">\ </span><span class="sh"> </span><span class="se">\ </span><span class="sh">'_. </span><span class="se">\_</span><span class="sh"> __</span><span class="se">\ </span><span class="sh">/__ _/ .-' / /
====='-.____'.___ </span><span class="se">\_</span><span class="sh">____/___.-'____.-'=====
'=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
佛祖保佑 iii 永不死机
</span><span class="no">
EOF
</span><span class="nb">echo</span> <span class="s2">"</span><span class="se">\0</span><span class="s2">33[0m"</span>
</code></pre></div></div>
<p>文字生成ASCII图的网站推荐:</p>
<ul>
<li><a href="http://www.network-science.de/ascii/">ascii</a> 使用<code class="language-plaintext highlighter-rouge">rounded</code>字体</li>
</ul>
Eclipse转androidstudio
2015-06-12T00:00:00-05:00
http://www.zhaoxiaodan.com/android/eclipse转AndroidStudio
<p>最近搞ndk比较多, eclipse的ndk还算不错, 但是cdt有点傻傻的, 我本来加了c++11的特性, 配置好了build也没有任何问题, cdt偏偏说认不出<code class="language-plaintext highlighter-rouge">std::thread</code>之类的</p>
<p>而且google官方已经把adt改成android studio了, adt基于eclipse, 而android studio基于IntelliJ IDEA. 一直都觉得idea 比eclipse用起来舒服, 就因为需要搞android一直没有换; 得了, 现在可以换过去了</p>
<h2 id="ndk配置">ndk配置</h2>
<ul>
<li>local.properties</li>
</ul>
<blockquote>
<p>ndk.dir=/server/android-ndk-r10e</p>
</blockquote>
<ul>
<li>build.gradle</li>
</ul>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>android {
...
defaultConfig {
...
ndk {
moduleName "android_anti_debug"
cFlags "-std=c++11 -pthread -fexceptions -frtti"
ldLibs "log", "z", "android", "atomic"
stl "gnustl_static"
abiFilters "x86", "armeabi"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
</code></pre></div></div>
<h2 id="编译慢">编译慢</h2>
<p>AndroidStudio -> Preferences -> BuildTools -> Gradle -> 勾选<code class="language-plaintext highlighter-rouge">Offline work</code></p>
Jni反反调试
2015-06-12T00:00:00-05:00
http://www.zhaoxiaodan.com/java/android/Jni反反调试
<p>之前又研究下怎么<code class="language-plaintext highlighter-rouge">反调试</code>, 该到道高一尺魔高一丈的时候了, 再来研究下怎么<code class="language-plaintext highlighter-rouge">反反调试</code></p>
<p>还是用之前的项目: <a href="https://github.com/pangliang/android-anti-debug">android-anti-debug</a></p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">anti_debug</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">LOGD</span><span class="p">(</span><span class="s">"call ptrace ....... "</span><span class="p">);</span>
<span class="n">ptrace</span><span class="p">(</span><span class="n">PTRACE_TRACEME</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">jint</span> <span class="nf">JNI_OnLoad</span><span class="p">(</span><span class="n">JavaVM</span><span class="o">*</span> <span class="n">vm</span><span class="p">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">reserved</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">anti_debug</span><span class="p">();</span>
<span class="n">JNIEnv</span><span class="o">*</span> <span class="n">env</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">vm</span><span class="o">-></span><span class="n">GetEnv</span><span class="p">(</span><span class="k">reinterpret_cast</span><span class="o"><</span><span class="kt">void</span><span class="o">**></span><span class="p">(</span><span class="o">&</span><span class="n">env</span><span class="p">),</span> <span class="n">JNI_VERSION_1_6</span><span class="p">)</span> <span class="o">!=</span> <span class="n">JNI_OK</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">JNI_VERSION_1_6</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>编译好之后用ida打开, 因为程序简单, 很容易就找到入口点<code class="language-plaintext highlighter-rouge">JNI_OnLoad</code>:</p>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2015/20150612085335.png" alt="" /></p>
<p>然后在<code class="language-plaintext highlighter-rouge">000004b5</code>处找到我们的关键调用<code class="language-plaintext highlighter-rouge">anti_debug()</code>,那我们只要让他不要再调用这个地方, 就不会被<code class="language-plaintext highlighter-rouge">反调试</code>了</p>
<p>那么这个<code class="language-plaintext highlighter-rouge">call _Z10anti_debugv</code>汇编指令一共5个byte: 1byte的指令, 4byte的目标地址, 那么用二进制编辑器把<code class="language-plaintext highlighter-rouge">000004b5</code> 到<code class="language-plaintext highlighter-rouge">000004b9</code> 这5个byte用<code class="language-plaintext highlighter-rouge">空指令</code>(二进制0x90)替换掉, 就不会被运行了, 就像这样:</p>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2015/20150612090903.png" alt="" /></p>
<p>然后再用ida打开验证下:</p>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2015/20150612091057.png" alt="" /></p>
<p>好了, 重新生成一下apk包, 再次运行, 不会print出”call ptrace ……. “, 也可以被<code class="language-plaintext highlighter-rouge">attach</code>了</p>
<p>下次估计又得是<code class="language-plaintext highlighter-rouge">道高一尺</code>的时候, 既然<code class="language-plaintext highlighter-rouge">反反调试</code>依赖<code class="language-plaintext highlighter-rouge">反编译</code>, 那看看能不能<code class="language-plaintext highlighter-rouge">反反编译</code></p>
<p>##备注</p>
<ul>
<li>用vim修改, 打开后用<code class="language-plaintext highlighter-rouge">:%!xxd</code>进入二进制, 修改后用<code class="language-plaintext highlighter-rouge">:%!xxd -r</code> 返回再保存</li>
<li>其实可以用Hopper Disassembler v3来反编译和修改: <code class="language-plaintext highlighter-rouge">Modify</code> -> <code class="language-plaintext highlighter-rouge">Assemble Instruction</code> 直接输入<code class="language-plaintext highlighter-rouge">nop</code>即可</li>
<li>修改文件, 最好是修改<code class="language-plaintext highlighter-rouge">obj</code>目录里的.so文件, 因为每次运行的时候还会走一次make, 修然没有 make, 但是会把obj里的.so<code class="language-plaintext highlighter-rouge">install</code>到<code class="language-plaintext highlighter-rouge">libs</code>里</li>
</ul>
Android Native反调试
2015-06-11T00:00:00-05:00
http://www.zhaoxiaodan.com/java/android/android-native反调试
<h2 id="思考">思考</h2>
<p>之前研究了下如何调试和尝试反一个别人加密的东西, 所以现在的体会就是:</p>
<blockquote>
<p>其实重点不是你如何加密, 重点是如何不让别人知道你怎么加密的</p>
</blockquote>
<p>因为像这种<code class="language-plaintext highlighter-rouge">自己加密的资源运行的时候自己解密之后拿来用</code>的程序, 我甚至根本不用关心你到底怎么加密, 加密算法是啥, 我只需要知道, 你解密完了之后, 那个资源的内存块在哪, 写个dumper就全拿到了;</p>
<p>加密并不能防止被破解, 只是增加破解的难度和门槛, 加密解密是一个相互博弈的过程</p>
<p>把资源加个密, 那么对于那些技术比较初级, 又想简单拷贝的人, 他们就拿你没办法了; 但是对于那些懂一点原理懂一点IDA的人, 并没有什么卵用</p>
<p>那么好, 现在我要研究一下, 怎么样再增加那么一点点门槛</p>
<p>之前解密的一个重要前提就是调试, 所以, 最直接想到的增加的门槛就是<code class="language-plaintext highlighter-rouge">反调试</code>.</p>
<h2 id="测试">测试</h2>
<p>先建个android 工程, 并加入native支持</p>
<blockquote>
<p><a href="https://github.com/pangliang/android-anti-debug">android-anti-debug</a></p>
</blockquote>
<p>先打出pid和父pid出来看看</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include <jni.h>
#include <stdio.h>
#include <sys/ptrace.h>
#include <unistd.h>
</span>
<span class="cp">#include "log.h"
</span>
<span class="kt">void</span> <span class="nf">anti_debug</span><span class="p">()</span>
<span class="p">{</span>
<span class="kt">int</span> <span class="n">pid</span> <span class="o">=</span> <span class="n">getpid</span><span class="p">();</span>
<span class="kt">int</span> <span class="n">ppid</span> <span class="o">=</span> <span class="n">getppid</span><span class="p">();</span>
<span class="n">LOGD</span><span class="p">(</span><span class="s">"pid:%d,ppid:%d"</span><span class="p">,</span><span class="n">pid</span><span class="p">,</span><span class="n">ppid</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">jint</span> <span class="nf">JNI_OnLoad</span><span class="p">(</span><span class="n">JavaVM</span><span class="o">*</span> <span class="n">vm</span><span class="p">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">reserved</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">anti_debug</span><span class="p">();</span>
<span class="n">JNIEnv</span><span class="o">*</span> <span class="n">env</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">vm</span><span class="o">-></span><span class="n">GetEnv</span><span class="p">(</span><span class="k">reinterpret_cast</span><span class="o"><</span><span class="kt">void</span><span class="o">**></span><span class="p">(</span><span class="o">&</span><span class="n">env</span><span class="p">),</span> <span class="n">JNI_VERSION_1_6</span><span class="p">)</span> <span class="o">!=</span> <span class="n">JNI_OK</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">JNI_VERSION_1_6</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>06-11 06:42:24.411: D/[android-anti-debug](1451): pid:1451,ppid:192
</code></pre></div></div>
<p>pid 192 是:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root 192 1 492204 38400 ffffffff b756598c S zygote
</code></pre></div></div>
<blockquote>
<p>在Android系统中,所有的应用程序进程以及系统服务进程SystemServer都是由Zygote进程孕育(fork)出来的,这也许就是为什么要把它称为Zygote(受精卵)的原因吧</p>
<p>具体参见:<a href="http://blog.csdn.net/luoshengyang/article/details/6768304">Android系统进程Zygote启动过程的源代码分析</a></p>
</blockquote>
<p>看下 <code class="language-plaintext highlighter-rouge">/proc/1451/stat</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@mx3:/data/local/tmp # cat /proc/17204//status
Name: ndroidantidebug
State: S (sleeping)
Tgid: 17204
Pid: 17204
PPid: 2146
TracerPid: 0
Uid: 10058 10058 10058 10058
Gid: 10058 10058 10058 10058
FDSize: 256
...
</code></pre></div></div>
<p>用gdbserver 去attach一下看看发生什么:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@mx3:/data/local/tmp # ps |grep blog
u0_a58 30678 2146 907884 59720 ffffffff 4005778c S com.zhaoxiaodan.blog.androidantidebug
root@mx3:/data/local/tmp # ./gdbserver --attach 127.0.0.1:1234 30678
Attached; pid = 30678
Listening on port 1234
root@mx3:/data/local/tmp # cat /proc/17204//status
Name: ndroidantidebug
State: t (tracing stop)
Tgid: 17204
Pid: 17204
PPid: 2146
TracerPid: 20337
Uid: 10058 10058 10058 10058
Gid: 10058 10058 10058 10058
</code></pre></div></div>
<p>发现<code class="language-plaintext highlighter-rouge">TracerPid</code>行由0 变为了 20337</p>
<p>ida这些调试工具其实都是使用<code class="language-plaintext highlighter-rouge">ptrace</code>进行的, ptrace有一个很重要的特定:</p>
<blockquote>
<p>一个进程只能被一个进程调试。</p>
</blockquote>
<p>所以, 最简单的办法就是在<code class="language-plaintext highlighter-rouge">JNI_OnLoad</code>里直接<code class="language-plaintext highlighter-rouge">ptrace(PTRACE_TRACEME, 0, 0, 0);</code></p>
<h2 id="方法1-直接ptraceptrace_traceme-0-0-0">方法1, 直接<code class="language-plaintext highlighter-rouge">ptrace(PTRACE_TRACEME, 0, 0, 0);</code></h2>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include <jni.h>
#include <stdio.h>
#include <sys/ptrace.h>
#include <unistd.h>
</span>
<span class="cp">#include "log.h"
</span>
<span class="kt">void</span> <span class="nf">anti_debug</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">ptrace</span><span class="p">(</span><span class="n">PTRACE_TRACEME</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">jint</span> <span class="nf">JNI_OnLoad</span><span class="p">(</span><span class="n">JavaVM</span><span class="o">*</span> <span class="n">vm</span><span class="p">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">reserved</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">anti_debug</span><span class="p">();</span>
<span class="n">JNIEnv</span><span class="o">*</span> <span class="n">env</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">vm</span><span class="o">-></span><span class="n">GetEnv</span><span class="p">(</span><span class="k">reinterpret_cast</span><span class="o"><</span><span class="kt">void</span><span class="o">**></span><span class="p">(</span><span class="o">&</span><span class="n">env</span><span class="p">),</span> <span class="n">JNI_VERSION_1_6</span><span class="p">)</span> <span class="o">!=</span> <span class="n">JNI_OK</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">JNI_VERSION_1_6</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>然后再用gdbserver 去attach:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@mx3:/data/local/tmp # ./gdbserver --attach 127.0.0.1:1234 31092
Cannot attach to lwp 31092: Operation not permitted (1)
Exiting
</code></pre></div></div>
<p>好了, 挂不上了</p>
<p>但是这种方法, 用反编译打开, 很容易就找到调用<code class="language-plaintext highlighter-rouge">ptrace</code>的地方, 不知道修改下<code class="language-plaintext highlighter-rouge">汇编指令</code>(比如改为nilnil), 就跳过这个调用了</p>
<h2 id="方法2-暗桩">方法2, 暗桩</h2>
<p>根据上面说的<code class="language-plaintext highlighter-rouge">/proc/$pid/status</code>中<code class="language-plaintext highlighter-rouge">TracerPid</code>行显示调试程序的<code class="language-plaintext highlighter-rouge">pid</code>的原理, 可以写一个方法检查下这个值, 如果!=0就退出程序</p>
<p>检查函数如下:</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">be_attached_check</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">try</span>
<span class="p">{</span>
<span class="k">const</span> <span class="kt">int</span> <span class="n">bufsize</span> <span class="o">=</span> <span class="mi">1024</span><span class="p">;</span>
<span class="kt">char</span> <span class="n">filename</span><span class="p">[</span><span class="n">bufsize</span><span class="p">];</span>
<span class="kt">char</span> <span class="n">line</span><span class="p">[</span><span class="n">bufsize</span><span class="p">];</span>
<span class="kt">int</span> <span class="n">pid</span> <span class="o">=</span> <span class="n">getpid</span><span class="p">();</span>
<span class="n">sprintf</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="s">"/proc/%d/status"</span><span class="p">,</span> <span class="n">pid</span><span class="p">);</span>
<span class="kt">FILE</span><span class="o">*</span> <span class="n">fd</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="s">"r"</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">fd</span> <span class="o">!=</span> <span class="nb">nullptr</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">while</span> <span class="p">(</span><span class="n">fgets</span><span class="p">(</span><span class="n">line</span><span class="p">,</span> <span class="n">bufsize</span><span class="p">,</span> <span class="n">fd</span><span class="p">))</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">strncmp</span><span class="p">(</span><span class="n">line</span><span class="p">,</span> <span class="s">"TracerPid"</span><span class="p">,</span> <span class="mi">9</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">int</span> <span class="n">statue</span> <span class="o">=</span> <span class="n">atoi</span><span class="p">(</span><span class="o">&</span><span class="n">line</span><span class="p">[</span><span class="mi">10</span><span class="p">]);</span>
<span class="n">LOGD</span><span class="p">(</span><span class="s">"%s"</span><span class="p">,</span> <span class="n">line</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">statue</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">LOGD</span><span class="p">(</span><span class="s">"be attached !! kill %d"</span><span class="p">,</span> <span class="n">pid</span><span class="p">);</span>
<span class="n">fclose</span><span class="p">(</span><span class="n">fd</span><span class="p">);</span>
<span class="kt">int</span> <span class="n">ret</span> <span class="o">=</span> <span class="n">kill</span><span class="p">(</span><span class="n">pid</span><span class="p">,</span> <span class="n">SIGKILL</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">fclose</span><span class="p">(</span><span class="n">fd</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span>
<span class="p">{</span>
<span class="n">LOGD</span><span class="p">(</span><span class="s">"open %s fail..."</span><span class="p">,</span> <span class="n">filename</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">catch</span> <span class="p">(...)</span>
<span class="p">{</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>可以把这个函数搞成一个宏, 然后写个程序随机的把这个宏插入到源码的各个地方, 随着代码的不断执行, 会遇到各个这样的检查点</p>
<p>其实也没什么卵用, 只不过桩子多了, 你拔起来就麻烦点咯</p>
<p>下面这个只是用线程模拟检查的过程:</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include "android-anti-debug.h"
#include <string>
#include <sys/ptrace.h>
#include <unistd.h>
#include <stdlib.h>
#include <chrono>
#include <thread>
#include "log.h"
</span>
<span class="kt">void</span> <span class="nf">be_attached_check</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">try</span>
<span class="p">{</span>
<span class="k">const</span> <span class="kt">int</span> <span class="n">bufsize</span> <span class="o">=</span> <span class="mi">1024</span><span class="p">;</span>
<span class="kt">char</span> <span class="n">filename</span><span class="p">[</span><span class="n">bufsize</span><span class="p">];</span>
<span class="kt">char</span> <span class="n">line</span><span class="p">[</span><span class="n">bufsize</span><span class="p">];</span>
<span class="kt">int</span> <span class="n">pid</span> <span class="o">=</span> <span class="n">getpid</span><span class="p">();</span>
<span class="n">sprintf</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="s">"/proc/%d/status"</span><span class="p">,</span> <span class="n">pid</span><span class="p">);</span>
<span class="kt">FILE</span><span class="o">*</span> <span class="n">fd</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="s">"r"</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">fd</span> <span class="o">!=</span> <span class="nb">nullptr</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">while</span> <span class="p">(</span><span class="n">fgets</span><span class="p">(</span><span class="n">line</span><span class="p">,</span> <span class="n">bufsize</span><span class="p">,</span> <span class="n">fd</span><span class="p">))</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">strncmp</span><span class="p">(</span><span class="n">line</span><span class="p">,</span> <span class="s">"TracerPid"</span><span class="p">,</span> <span class="mi">9</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">int</span> <span class="n">statue</span> <span class="o">=</span> <span class="n">atoi</span><span class="p">(</span><span class="o">&</span><span class="n">line</span><span class="p">[</span><span class="mi">10</span><span class="p">]);</span>
<span class="n">LOGD</span><span class="p">(</span><span class="s">"%s"</span><span class="p">,</span> <span class="n">line</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">statue</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">LOGD</span><span class="p">(</span><span class="s">"be attached !! kill %d"</span><span class="p">,</span> <span class="n">pid</span><span class="p">);</span>
<span class="n">fclose</span><span class="p">(</span><span class="n">fd</span><span class="p">);</span>
<span class="kt">int</span> <span class="n">ret</span> <span class="o">=</span> <span class="n">kill</span><span class="p">(</span><span class="n">pid</span><span class="p">,</span> <span class="n">SIGKILL</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">fclose</span><span class="p">(</span><span class="n">fd</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span>
<span class="p">{</span>
<span class="n">LOGD</span><span class="p">(</span><span class="s">"open %s fail..."</span><span class="p">,</span> <span class="n">filename</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">catch</span> <span class="p">(...)</span>
<span class="p">{</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">//检查线程, 每秒检查一下</span>
<span class="kt">void</span> <span class="nf">thread_task</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">while</span> <span class="p">(</span><span class="nb">true</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">LOGD</span><span class="p">(</span><span class="s">"start be_attached_check..."</span><span class="p">);</span>
<span class="n">be_attached_check</span><span class="p">();</span>
<span class="n">std</span><span class="o">::</span><span class="n">this_thread</span><span class="o">::</span><span class="n">sleep_for</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">chrono</span><span class="o">::</span><span class="n">seconds</span><span class="p">(</span><span class="n">n</span><span class="p">));</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">anti_debug</span><span class="p">()</span>
<span class="p">{</span>
<span class="c1">// ptrace(PTRACE_TRACEME, 0, 0, 0);</span>
<span class="k">auto</span> <span class="n">checkThread</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="kr">thread</span><span class="p">(</span><span class="n">thread_task</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
<span class="n">checkThread</span><span class="p">.</span><span class="n">detach</span><span class="p">();</span>
<span class="p">}</span>
<span class="n">jint</span> <span class="nf">JNI_OnLoad</span><span class="p">(</span><span class="n">JavaVM</span><span class="o">*</span> <span class="n">vm</span><span class="p">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">reserved</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">anti_debug</span><span class="p">();</span>
<span class="n">JNIEnv</span><span class="o">*</span> <span class="n">env</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">vm</span><span class="o">-></span><span class="n">GetEnv</span><span class="p">(</span><span class="k">reinterpret_cast</span><span class="o"><</span><span class="kt">void</span><span class="o">**></span><span class="p">(</span><span class="o">&</span><span class="n">env</span><span class="p">),</span> <span class="n">JNI_VERSION_1_6</span><span class="p">)</span> <span class="o">!=</span> <span class="n">JNI_OK</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">JNI_VERSION_1_6</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
Githubpage居然把百度蜘蛛给屏蔽了
2015-06-05T00:00:00-05:00
http://www.zhaoxiaodan.com/%E5%85%B6%E4%BB%96/GithubPage居然把百度蜘蛛给屏蔽了
<p>虽然咱的博客也就是简单的做个笔记, 但是还是奇怪于, 访问统计中百度的来源少的可怜, 基本没有; 一直也没管; 因为我就是做笔记, 给自己看的;</p>
<p>今天群里聊天, 才知道原来github 把百度爬虫给屏蔽了.</p>
<p>好吧, 那怎么办?</p>
<p>###使用国内的gitcafe 做个镜像</p>
<ul>
<li>注册个gitcafe帐号,最好跟github用户名一致</li>
<li>gitcafe创建一个跟用户名一样的项目</li>
<li>修改原来github page的git配置, 添加多一个remote, 名字叫<code class="language-plaintext highlighter-rouge">gitcafe</code>, 注意修改为自己的用户名和项目名</li>
</ul>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#原来的master只关联github remote
[branch "master"]
remote = github
merge = refs/heads/master
#添加gitcafe remote
[remote "gitcafe"]
url = https://gitcafe.com/pangliang/pangliang.git
fetch = +refs/heads/*:refs/remotes/gitcafe/*
</code></pre></div></div>
<ul>
<li>把本地的<code class="language-plaintext highlighter-rouge">master</code>分支push到gitcafe的<code class="language-plaintext highlighter-rouge">gitcafe-pages</code>分支</li>
</ul>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git push gitcafe master:gitcafe-pages
</code></pre></div></div>
<ul>
<li>修改gitcafe上的cname</li>
<li>把域名添加多几个A记录, 按<code class="language-plaintext highlighter-rouge">海外</code>区分, <code class="language-plaintext highlighter-rouge">海外</code>的就走github好了, 国内走gitcafe, 然后单独再加个给baidu的索引, 就像这样:</li>
</ul>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2015/20150605053529.png" alt="" /></p>
<h3 id="效果">效果</h3>
<p>在国内访问, 就自动走了gitcafe的ip, 就像这样:</p>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2015/20150605054151.png" alt="" /></p>
<h3 id="百度也能抓去成功了">百度也能抓去成功了</h3>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2015/20150605055448.png" alt="" /></p>
<h3 id="怎么检查github-page-是否对">怎么检查github page 是否对?</h3>
<p>人在国内, 怎么检查, 两个办法, 把dns指向8.8.8.8, 或者添加hosts强制指向<code class="language-plaintext highlighter-rouge">192.30.252.153</code></p>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2015/20150605054917.png" alt="" /></p>
苹果蓝牙sdk没有回调问题
2015-06-03T00:00:00-05:00
http://www.zhaoxiaodan.com/%E5%85%B6%E4%BB%96/苹果蓝牙sdk没有回调问题
<p>最近也就小米手环蓝牙协议, android版本完成了一小半. 开始尝试ios/mac版本</p>
<p>发现一个挺坑的问题, 一个简单的测试程序一般这么写:</p>
<pre><code class="language-Objective-C">
@interface AppDelegate()
@property (atomic,strong) BlutoothIO* io;
@end
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
CBCentralManager* central = [[CBCentralManager alloc]initWithDelegate:self queue:nil options:nil];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
}
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
NSLog(@"centralManagerDidUpdateState:%ld",central.state);
if (central.state == CBCentralManagerStatePoweredOn) {
NSLog(@"scanForPeripheralsWithServices");
[central scanForPeripheralsWithServices:nil
options:nil];
}
}
@end
</code></pre>
<p>然后你会发现, 什么都没发生, <code class="language-plaintext highlighter-rouge">centralManagerDidUpdateState</code>函数完全没有被调用. 各种google, 才知道结果;</p>
<p>为啥? 因为object-c会自动回收, <code class="language-plaintext highlighter-rouge">applicationDidFinishLaunching</code> 里的 <code class="language-plaintext highlighter-rouge">central</code> 在函数结束后就被析构掉了, 自然也就不会有回调发生!</p>
<p>同理,修改之后的程序:</p>
<pre><code class="language-Objective-C">- (id)init
{
if ((self = [super init]))
{
self.central = [[CBCentralManager alloc]initWithDelegate:self queue:nil options:nil];
}
return self;
}
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
NSLog(@"centralManagerDidUpdateState:%ld",central.state);
if (central.state == CBCentralManagerStatePoweredOn) {
NSLog(@"scanForPeripheralsWithServices");
[central scanForPeripheralsWithServices:nil
options:nil];
}
}
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(@"Peripheral connected: %@",peripheral.name);
[peripheral discoverServices:nil];
}
- (void)centralManager:(CBCentralManager *)central
didFailToConnectPeripheral:(CBPeripheral *)peripheral
error:(NSError *)error{
NSLog(@"didFailToConnectPeripheral: %@, error:%@",peripheral.name,error);
}
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *) advertisementData
RSSI:(NSNumber *)RSSI {
NSLog(@"Discovered %@", peripheral.name);
[central stopScan];
peripheral.delegate = self;
[central connectPeripheral:peripheral options:nil];
}
- (void)peripheralDidUpdateRSSI:(CBPeripheral *)peripheral
error:(NSError *)error{
NSLog(@"peripheralDidUpdateRSSI %@", peripheral.RSSI);
}
</code></pre>
<p>在<code class="language-plaintext highlighter-rouge">didDiscoverPeripheral</code>回调之后, 发现了设备<code class="language-plaintext highlighter-rouge">peripheral</code>, 然后去做<code class="language-plaintext highlighter-rouge">connectPeripheral</code>, 不管<code class="language-plaintext highlighter-rouge">didConnectPeripheral</code>还是<code class="language-plaintext highlighter-rouge">didFailToConnectPeripheral</code>也都没有被回调, 为啥?</p>
<p>因为<code class="language-plaintext highlighter-rouge">didDiscoverPeripheral</code>函数中传进来的<code class="language-plaintext highlighter-rouge">peripheral</code> 再函数之后同样被释放了!</p>
<p>解决办法就是找个数组之类的什么东西hold住他, 比如:</p>
<pre><code class="language-Objective-C">@property (strong,nonatomic) NSMutableArray *peripherals;
-(void) centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
[self.peripherals addObject:peripheral];
[central connectPeripheral:peripheral options:nil];
}
</code></pre>
<p>吐槽!! 第一个问题<code class="language-plaintext highlighter-rouge">central</code>没有hold住还好理解, 你<code class="language-plaintext highlighter-rouge">CBCentralManager</code>在<code class="language-plaintext highlighter-rouge">DiscoverPeripheral</code>了之后传过来的<code class="language-plaintext highlighter-rouge">peripheral</code>,你<code class="language-plaintext highlighter-rouge">CBCentralManager</code>都不hold住, 还做什么Manager…</p>
<p>参考:</p>
<ul>
<li><a href="http://stackoverflow.com/questions/26377470/ios-corebluetooth-centralmanagerdidconnectperipheral-didfailtoconnectperiph">iOS CoreBluetooth : centralManager:didConnectPeripheral / didFailToConnectPeripheral: not getting called</a></li>
</ul>
小米手环sdk安卓版
2015-05-27T00:00:00-05:00
http://www.zhaoxiaodan.com/android/小米手环sdk安卓版
<p>先放SDK地址:</p>
<p><a href="https://github.com/pangliang/miband-sdk-android">miband-sdk-android</a></p>
<p>重点参考了两个东西:</p>
<ul>
<li><a href="https://github.com/UgoRaffaele/xiaomi-miband-android">UgoRaffaele/xiaomi-miband-android</a></li>
<li><a href="http://allmydroids.blogspot.de/2014/12/xiaomi-mi-band-ble-protocol-reverse.html">xiaomi-mi-band-ble-protocol</a></li>
</ul>
<p>这两个东西帮了大忙, 基本上<code class="language-plaintext highlighter-rouge">特征</code>和用途, 几个数据结构的格式基本都有了, 剩下的就是怎么使用的问题</p>
<p>经过三天的不断调试, 终于折腾出小米手环的sdk 安卓版本, 实现功能有:</p>
<ul>
<li>设置用户信息</li>
<li>获取实时步数通知</li>
<li>震动手环</li>
<li>设置led颜色</li>
<li>获取电池信息</li>
<li>获取信号强度RSSI值信息</li>
</ul>
<p>但是还有几个问题没有解决:</p>
<ul>
<li>手里只有一个手环, 不知道两个手环同时出现的时候在<code class="language-plaintext highlighter-rouge">startLeScan</code>怎么区分</li>
<li>使用SDK, 每次连接之后总要去做UserInfo的设置, 如果不设置, 大部分<code class="language-plaintext highlighter-rouge">写</code>操作都不可用. 但是记得<code class="language-plaintext highlighter-rouge">小米运动</code>每次重连并不需要<code class="language-plaintext highlighter-rouge">拍一下</code></li>
<li>纠结于api的使用方式, 因为android蓝牙sdk本身就是异步方式, 所以现在sdk也设计为异步回调方式; 但是感觉不是很好用, 特别是在想要<code class="language-plaintext highlighter-rouge">连续写</code>的时候, 必须等一个<code class="language-plaintext highlighter-rouge">写的回调</code>才能继续写, 那就是callback里套callback… 很难看, 解决办法可以使用写队列, 但是貌似<code class="language-plaintext highlighter-rouge">BluetoothGattCallback</code>的<code class="language-plaintext highlighter-rouge">onXXXX</code>方法并没有requestId什么的去对应是那次写; 所以考虑下一般用锁改成同步调用方式好了, 每次读写调用必须等<code class="language-plaintext highlighter-rouge">BluetoothGattCallback</code>中有结果后才返回</li>
</ul>
通过lescan获取rssi实现蓝牙测距
2015-05-22T00:00:00-05:00
http://www.zhaoxiaodan.com/android/通过leScan获取rssi实现蓝牙测距
<p>领导送了个小米的手环, 看到如果是小米手机, 支持手环免密码解锁手机. 但是现在用的是mx3, android4.4的, 不支持. 于是就想知道, 它怎么实现的</p>
<p>google之, 蓝牙4.0有个叫低功耗<code class="language-plaintext highlighter-rouge">bluetooth.le</code>的协议, android api里有个<code class="language-plaintext highlighter-rouge">leScan()</code>方法, 可以对周边的低功耗蓝牙设备进行扫描, 扫描时, 就会返回信号强度<code class="language-plaintext highlighter-rouge">RSSI</code>的值; 如下面的代码, 就可以每3-5秒钟返回一次结果</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="kd">private</span> <span class="nc">BluetoothAdapter</span><span class="o">.</span><span class="na">LeScanCallback</span> <span class="n">mLeScanCallback</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">BluetoothAdapter</span><span class="o">.</span><span class="na">LeScanCallback</span><span class="o">()</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">onLeScan</span><span class="o">(</span><span class="kd">final</span> <span class="nc">BluetoothDevice</span> <span class="n">device</span><span class="o">,</span> <span class="kd">final</span> <span class="kt">int</span> <span class="n">rssi</span><span class="o">,</span><span class="kd">final</span> <span class="kt">byte</span><span class="o">[]</span> <span class="n">scanRecord</span><span class="o">)</span>
<span class="o">{</span>
<span class="nc">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="no">TAG</span><span class="o">,</span> <span class="s">"name:"</span><span class="o">+</span><span class="n">device</span><span class="o">.</span><span class="na">getName</span><span class="o">()</span>
<span class="o">+</span><span class="s">",add:"</span><span class="o">+</span><span class="n">device</span><span class="o">.</span><span class="na">getAddress</span><span class="o">()</span>
<span class="o">+</span><span class="s">",type:"</span><span class="o">+</span><span class="n">device</span><span class="o">.</span><span class="na">getType</span><span class="o">()</span>
<span class="o">+</span><span class="s">",bondState:"</span><span class="o">+</span><span class="n">device</span><span class="o">.</span><span class="na">getBondState</span><span class="o">()</span>
<span class="o">+</span><span class="s">",rssi:"</span><span class="o">+</span><span class="n">rssi</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">};</span>
<span class="nc">BluetoothAdapter</span> <span class="n">adapter</span> <span class="o">=</span> <span class="nc">BluetoothAdapter</span><span class="o">.</span><span class="na">getDefaultAdapter</span><span class="o">();</span>
<span class="n">adapter</span><span class="o">.</span><span class="na">startLeScan</span><span class="o">(</span><span class="n">mLeScanCallback</span><span class="o">);</span>
</code></pre></div></div>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>05-22 12:41:42.450: I/<span class="o">==[</span>shouhuantest]<span class="o">==(</span>6174<span class="o">)</span>: name:MI,add:88:0F:10:80:A2:4A,type:2,bondState:12,rssi:-59
05-22 12:41:45.220: I/<span class="o">==[</span>shouhuantest]<span class="o">==(</span>6174<span class="o">)</span>: name:MI,add:88:0F:10:80:A2:4A,type:2,bondState:12,rssi:-60
05-22 12:41:46.880: I/<span class="o">==[</span>shouhuantest]<span class="o">==(</span>6174<span class="o">)</span>: name:MI,add:88:0F:10:80:A2:4A,type:2,bondState:12,rssi:-61
05-22 12:41:47.985: I/<span class="o">==[</span>shouhuantest]<span class="o">==(</span>6174<span class="o">)</span>: name:MI,add:88:0F:10:80:A2:4A,type:2,bondState:12,rssi:-58
05-22 12:41:50.185: I/<span class="o">==[</span>shouhuantest]<span class="o">==(</span>6174<span class="o">)</span>: name:MI,add:88:0F:10:80:A2:4A,type:2,bondState:12,rssi:-60
05-22 12:41:51.835: I/<span class="o">==[</span>shouhuantest]<span class="o">==(</span>6174<span class="o">)</span>: name:MI,add:88:0F:10:80:A2:4A,type:2,bondState:12,rssi:-68
05-22 12:41:53.495: I/<span class="o">==[</span>shouhuantest]<span class="o">==(</span>6174<span class="o">)</span>: name:MI,add:88:0F:10:80:A2:4A,type:2,bondState:12,rssi:-80
05-22 12:41:57.335: I/<span class="o">==[</span>shouhuantest]<span class="o">==(</span>6174<span class="o">)</span>: name:MI,add:88:0F:10:80:A2:4A,type:2,bondState:12,rssi:-81
05-22 12:41:58.995: I/<span class="o">==[</span>shouhuantest]<span class="o">==(</span>6174<span class="o">)</span>: name:MI,add:88:0F:10:80:A2:4A,type:2,bondState:12,rssi:-75
05-22 12:41:59.775: I/<span class="o">==[</span>shouhuantest]<span class="o">==(</span>6174<span class="o">)</span>: name:MI,add:88:0F:10:80:A2:4A,type:2,bondState:12,rssi:-73
05-22 12:42:00.885: I/<span class="o">==[</span>shouhuantest]<span class="o">==(</span>6174<span class="o">)</span>: name:MI,add:88:0F:10:80:A2:4A,type:2,bondState:12,rssi:-79
05-22 12:42:02.550: I/<span class="o">==[</span>shouhuantest]<span class="o">==(</span>6174<span class="o">)</span>: name:MI,add:88:0F:10:80:A2:4A,type:2,bondState:12,rssi:-77
05-22 12:42:05.310: I/<span class="o">==[</span>shouhuantest]<span class="o">==(</span>6174<span class="o">)</span>: name:MI,add:88:0F:10:80:A2:4A,type:2,bondState:12,rssi:-78
05-22 12:42:07.510: I/<span class="o">==[</span>shouhuantest]<span class="o">==(</span>6174<span class="o">)</span>: name:MI,add:88:0F:10:80:A2:4A,type:2,bondState:12,rssi:-76
</code></pre></div></div>
<p>把手环贴在手机上, 基本rssi值在-60左右, 离开一米, 差不多-80左右</p>
<p>但是这个结果频率太低了, 3-5秒一次, 你还得取个3-5次做个平均值吧; 那这个延迟用来做解锁太坑了;</p>
<p>android API level 21 也就是android 5.0 还多了一个 <code class="language-plaintext highlighter-rouge">android.bluetooth.le</code>包, 里面有个<code class="language-plaintext highlighter-rouge">BluetoothLeScanner.startScan(List<ScanFilter> filters, ScanSettings settings, ScanCallback callback)</code>, 支持在扫描的时候设置过滤器, 和扫描设置什么的;</p>
<p>看到<code class="language-plaintext highlighter-rouge">ScanSettings</code>还有<code class="language-plaintext highlighter-rouge">getReportDelayMillis()</code>, 看来还可以设置扫描的频率; 手头没有android5.0, 没法测试了…</p>
<h2 id="5月27日更新">5月27日更新</h2>
<p>之前也不是很清楚蓝牙的api, 其实这个不应该这么来用</p>
<p><code class="language-plaintext highlighter-rouge">BluetoothGatt</code>里有个`readRemoteRssi()’函数, 每次读取都会获取新的RSSI值, 所以要监控的话起个线程不停读这个函数就好了</p>
<p>简单使用方法:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//实现个gattcallback</span>
<span class="nc">BluetoothGattCallback</span> <span class="n">gattCallback</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">BluetoothGattCallback</span><span class="o">()</span>
<span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">onConnectionStateChange</span><span class="o">(</span><span class="nc">BluetoothGatt</span> <span class="n">gatt</span><span class="o">,</span> <span class="kt">int</span> <span class="n">status</span><span class="o">,</span> <span class="kt">int</span> <span class="n">newState</span><span class="o">)</span>
<span class="o">{</span>
<span class="c1">//设备连接状态改变会回调这个函数</span>
<span class="kd">super</span><span class="o">.</span><span class="na">onConnectionStateChange</span><span class="o">(</span><span class="n">gatt</span><span class="o">,</span> <span class="n">status</span><span class="o">,</span> <span class="n">newState</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">newState</span> <span class="o">==</span> <span class="nc">BluetoothProfile</span><span class="o">.</span><span class="na">STATE_CONNECTED</span><span class="o">)</span>
<span class="o">{</span>
<span class="c1">//连接成功, 可以把这个gatt 保存起来, 需要读rssi的时候就</span>
<span class="n">gatt</span><span class="o">.</span><span class="na">readRemoteRssi</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">onReadRemoteRssi</span><span class="o">(</span><span class="nc">BluetoothGatt</span> <span class="n">gatt</span><span class="o">,</span> <span class="kt">int</span> <span class="n">rssi</span><span class="o">,</span> <span class="kt">int</span> <span class="n">status</span><span class="o">)</span>
<span class="o">{</span>
<span class="kd">super</span><span class="o">.</span><span class="na">onReadRemoteRssi</span><span class="o">(</span><span class="n">gatt</span><span class="o">,</span> <span class="n">rssi</span><span class="o">,</span> <span class="n">status</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="nc">BluetoothGatt</span><span class="o">.</span><span class="na">GATT_SUCCESS</span> <span class="o">==</span> <span class="n">status</span><span class="o">)</span>
<span class="o">{</span>
<span class="c1">//读取成功, rssi就是新的值</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="c1">//开启扫描</span>
<span class="nc">BluetoothAdapter</span> <span class="n">adapter</span> <span class="o">=</span> <span class="nc">BluetoothAdapter</span><span class="o">.</span><span class="na">getDefaultAdapter</span><span class="o">();</span>
<span class="n">adapter</span><span class="o">.</span><span class="na">startLeScan</span><span class="o">(</span><span class="k">new</span> <span class="nc">LeScanCallback</span><span class="o">()</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">onLeScan</span><span class="o">(</span><span class="kd">final</span> <span class="nc">BluetoothDevice</span> <span class="n">device</span><span class="o">,</span> <span class="kd">final</span> <span class="kt">int</span> <span class="n">rssi</span><span class="o">,</span>
<span class="kd">final</span> <span class="kt">byte</span><span class="o">[]</span> <span class="n">scanRecord</span><span class="o">)</span>
<span class="o">{</span>
<span class="c1">//扫到设备, 停止扫描</span>
<span class="nc">BluetoothAdapter</span> <span class="n">adapter</span> <span class="o">=</span> <span class="nc">BluetoothAdapter</span><span class="o">.</span><span class="na">getDefaultAdapter</span><span class="o">();</span>
<span class="n">adapter</span><span class="o">.</span><span class="na">stopLeScan</span><span class="o">(</span><span class="k">this</span><span class="o">);</span>
<span class="c1">//设备连接上gatt</span>
<span class="n">device</span><span class="o">.</span><span class="na">connectGatt</span><span class="o">(</span><span class="n">context</span><span class="o">,</span> <span class="kc">false</span><span class="o">,</span> <span class="n">gattCallback</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">});</span>
</code></pre></div></div>
小米手环蓝牙协议研究
2015-05-22T00:00:00-05:00
http://www.zhaoxiaodan.com/android/小米手环蓝牙协议研究
<p>上篇文章<a href="/其他/通过leScan获取rssi实现蓝牙测距.html">通过leScan获取rssi实现蓝牙测距</a>, 利用这个可以做很多好玩的东西, 比如手机防丢. 那这样当手机和手环离开一定距离时, 手机和手环都发出警报.</p>
<p>手机的好做, 发出声音, 震动, 什么的; 但是手环想要震动, 小米没有给API啊, google了一会也没有结果</p>
<p>好吧, 非暴力不合作.</p>
<p>下载<code class="language-plaintext highlighter-rouge">小米运动_1.4.641_1058.apk</code>, 然后利用<a href="https://github.com/pangliang/apk_hack.git">apk_hack</a> :</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./decompile_apk.sh 小米运动_1.4.641_1058.apk
</code></pre></div></div>
<p>导入到eclipse 进行debug…</p>
<p>eclipse崩溃? 难道这个app还做了反反编译, 反debug? 不至于吧? 各种google, 后来发现, 每次debug, eclipse右下角的内存显示都不停涨, 涨涨到512m就挂了; 好吧, 想起来eclipse的最大内存是512m, 估计是这个编译要太多内存了</p>
<p>修改eclipse目录下的<code class="language-plaintext highlighter-rouge">eclipse.ini</code>文件中的<code class="language-plaintext highlighter-rouge">-Xmx</code>参数到2048m; 正常运行了, 看到最大内存跳到700多m</p>
<p>好了, 正常debug了, 发现每次按查找手环, debug都会打印出:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>05-22 20:07:31.878: D/BluetoothGatt(20195): writeCharacteristic() - uuid: 0000ff05-0000-1000-8000-00805f9b34fb
05-22 20:07:32.528: D/BluetoothGatt(20195): onCharacteristicWrite() - Device=88:0F:10:80:A2:4A UUID=0000ff05-0000-1000-8000-00805f9b34fb Status=0
05-22 20:07:32.533: D/BluetoothGatt(20195): readCharacteristic() - uuid: 0000ff0c-0000-1000-8000-00805f9b34fb
05-22 20:07:32.538: D/BluetoothGatt(20195): onNotify() - Device=88:0F:10:80:A2:4A UUID=0000ff03-0000-1000-8000-00805f9b34fb
05-22 20:07:33.773: D/BluetoothGatt(20195): onCharacteristicRead() - Device=88:0F:10:80:A2:4A UUID=0000ff0c-0000-1000-8000-00805f9b34fb Status=0
</code></pre></div></div>
<p>哦? <code class="language-plaintext highlighter-rouge">BluetoothGatt</code>, google下, 这个是低功耗蓝牙的连接方式, 简单说就是每个硬件设备会包含很多<code class="language-plaintext highlighter-rouge">服务</code>, 不同的服务按照<code class="language-plaintext highlighter-rouge">UUID</code>来区分. 也就是说, 现在这个<code class="language-plaintext highlighter-rouge">让手环震动</code>是通过<code class="language-plaintext highlighter-rouge">UUID=0000ff05-0000-1000-8000-00805f9b34fb</code> 这个服务进行的</p>
<p>然后顺着这个UUID, google到了下面这些:</p>
<ul>
<li><a href="https://bitbucket.org/OscarAcena/mibanda/issue/5/get-handle-values-from-mili-service">Get handle values from MILI service charcteristics</a></li>
<li><a href="https://github.com/paulgavrikov/xiaomi-miband-android">xiaomi-miband-android</a></li>
<li><a href="http://allmydroids.blogspot.de/2014/12/xiaomi-mi-band-ble-protocol-reverse.html">xiaomi-mi-band-ble-protocol</a></li>
</ul>
<p>在<code class="language-plaintext highlighter-rouge">xiaomi-mi-band-ble-protocol</code>这个文章里已经收集了不少<code class="language-plaintext highlighter-rouge">协议</code>和<code class="language-plaintext highlighter-rouge">特征UUID</code></p>
<p>加上参考<a href="https://github.com/UgoRaffaele/xiaomi-miband-android">UgoRaffaele/xiaomi-miband-android</a>, 基本上, UserInfo, 灯颜色, 电池信息 的数据结构都已经有了</p>
<p>但是google到的所有结果都只是实现<code class="language-plaintext highlighter-rouge">读取</code>功能, 震动什么的, 看来得自己实现了</p>
<p>分析了一下<code class="language-plaintext highlighter-rouge">连接</code>手环时的过程:</p>
<ul>
<li>先开启ff03 这个notify, 因为写入UserInfo的结果通过这个获得</li>
<li>然后开启实时步数notify</li>
<li>然后是ACTIVITY_DATA notify</li>
<li>电量notify</li>
<li>然后写入UserInfo完成</li>
</ul>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
//开启notify notify
05-26 16:41:26.622: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: setCharacteristicNotification<span class="o">()</span> - uuid: 0000ff03-0000-1000-8000-00805f9b34fb <span class="nb">enable</span>: <span class="nb">true
</span>05-26 16:41:26.637: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: writeDescriptor<span class="o">()</span> - uuid: 00002902-0000-1000-8000-00805f9b34fb
05-26 16:41:26.732: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onDescriptorWrite<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff03-0000-1000-8000-00805f9b34fb
//开启 REALTIME_STEPS notify
05-26 16:41:26.752: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: setCharacteristicNotification<span class="o">()</span> - uuid: 0000ff06-0000-1000-8000-00805f9b34fb <span class="nb">enable</span>: <span class="nb">true
</span>05-26 16:41:26.762: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: writeDescriptor<span class="o">()</span> - uuid: 00002902-0000-1000-8000-00805f9b34fb
05-26 16:41:26.827: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onDescriptorWrite<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff06-0000-1000-8000-00805f9b34fb
//开启 ACTIVITY_DATA notify
05-26 16:41:26.837: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: setCharacteristicNotification<span class="o">()</span> - uuid: 0000ff07-0000-1000-8000-00805f9b34fb <span class="nb">enable</span>: <span class="nb">true
</span>05-26 16:41:26.847: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: writeDescriptor<span class="o">()</span> - uuid: 00002902-0000-1000-8000-00805f9b34fb
05-26 16:41:26.922: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onDescriptorWrite<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff07-0000-1000-8000-00805f9b34fb
//开启 电量 notify
05-26 16:41:26.932: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: setCharacteristicNotification<span class="o">()</span> - uuid: 0000ff0c-0000-1000-8000-00805f9b34fb <span class="nb">enable</span>: <span class="nb">true
</span>05-26 16:41:26.942: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: writeDescriptor<span class="o">()</span> - uuid: 00002902-0000-1000-8000-00805f9b34fb
05-26 16:41:27.022: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onDescriptorWrite<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff0c-0000-1000-8000-00805f9b34fb
//开启 SENSOR_DATA notify
05-26 16:41:27.047: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: setCharacteristicNotification<span class="o">()</span> - uuid: 0000ff0e-0000-1000-8000-00805f9b34fb <span class="nb">enable</span>: <span class="nb">true
</span>05-26 16:41:27.057: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: writeDescriptor<span class="o">()</span> - uuid: 00002902-0000-1000-8000-00805f9b34fb
05-26 16:41:27.172: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onDescriptorWrite<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff0e-0000-1000-8000-00805f9b34fb
//读取 REALTIME_STEPS
05-26 16:41:33.167: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: readCharacteristic<span class="o">()</span> - uuid: 0000ff06-0000-1000-8000-00805f9b34fb
05-26 16:41:33.267: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onCharacteristicRead<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff06-0000-1000-8000-00805f9b34fb <span class="nv">Status</span><span class="o">=</span>0
05-26 16:41:33.292: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: writeCharacteristic<span class="o">()</span> - uuid: 0000ff05-0000-1000-8000-00805f9b34fb
05-26 16:41:33.357: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onCharacteristicWrite<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff05-0000-1000-8000-00805f9b34fb <span class="nv">Status</span><span class="o">=</span>0
//写UserInfo, <span class="nv">value</span><span class="o">=[</span>25, 113, 53, 1, 1, 32, <span class="nt">-83</span>, 55, 1, <span class="nt">-24</span>, <span class="nt">-125</span>, <span class="nt">-106</span>, <span class="nt">-26</span>, <span class="nt">-94</span>, <span class="nt">-127</span>, 0, 0, 0, 0, 35]
05-26 16:41:33.837: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: writeCharacteristic<span class="o">()</span> - uuid: 0000ff04-0000-1000-8000-00805f9b34fb
05-26 16:41:33.947: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onNotify<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff03-0000-1000-8000-00805f9b34fb
05-26 16:41:34.007: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onCharacteristicWrite<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff04-0000-1000-8000-00805f9b34fb <span class="nv">Status</span><span class="o">=</span>0
//写入时间 <span class="nv">value</span><span class="o">=[</span>15, 4, 26, 17, 6, 58, <span class="nt">-1</span>, <span class="nt">-1</span>, <span class="nt">-1</span>, <span class="nt">-1</span>, <span class="nt">-1</span>, <span class="nt">-1</span><span class="o">]</span>
05-26 16:41:36.162: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: writeCharacteristic<span class="o">()</span> - uuid: 0000ff0a-0000-1000-8000-00805f9b34fb
05-26 16:41:36.247: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onCharacteristicWrite<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff0a-0000-1000-8000-00805f9b34fb <span class="nv">Status</span><span class="o">=</span>0
//读取时间
05-26 16:41:36.282: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: readCharacteristic<span class="o">()</span> - uuid: 0000ff0a-0000-1000-8000-00805f9b34fb
05-26 16:41:36.382: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onCharacteristicRead<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff0a-0000-1000-8000-00805f9b34fb <span class="nv">Status</span><span class="o">=</span>0
//读取电池
05-26 16:41:36.487: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: readCharacteristic<span class="o">()</span> - uuid: 0000ff0c-0000-1000-8000-00805f9b34fb
05-26 16:41:36.577: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onCharacteristicRead<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff0c-0000-1000-8000-00805f9b34fb <span class="nv">Status</span><span class="o">=</span>0
//读取 device_info
05-26 16:41:36.707: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: readCharacteristic<span class="o">()</span> - uuid: 0000ff01-0000-1000-8000-00805f9b34fb
05-26 16:41:36.772: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onCharacteristicRead<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff01-0000-1000-8000-00805f9b34fb <span class="nv">Status</span><span class="o">=</span>0
//读取 LE_PARAMS
05-26 16:41:37.302: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: readCharacteristic<span class="o">()</span> - uuid: 0000ff09-0000-1000-8000-00805f9b34fb
05-26 16:41:37.407: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onCharacteristicRead<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff09-0000-1000-8000-00805f9b34fb <span class="nv">Status</span><span class="o">=</span>0
05-26 16:41:37.892: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: readCharacteristic<span class="o">()</span> - uuid: 0000ff09-0000-1000-8000-00805f9b34fb
05-26 16:41:37.987: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onCharacteristicRead<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff09-0000-1000-8000-00805f9b34fb <span class="nv">Status</span><span class="o">=</span>0
05-26 16:41:38.022: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: writeCharacteristic<span class="o">()</span> - uuid: 0000ff05-0000-1000-8000-00805f9b34fb
05-26 16:41:38.187: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onNotify<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff07-0000-1000-8000-00805f9b34fb
05-26 16:41:38.212: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onNotify<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff07-0000-1000-8000-00805f9b34fb
05-26 16:41:38.267: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onNotify<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff07-0000-1000-8000-00805f9b34fb
05-26 16:41:38.332: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onCharacteristicWrite<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff05-0000-1000-8000-00805f9b34fb <span class="nv">Status</span><span class="o">=</span>0
05-26 16:41:38.352: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onNotify<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff07-0000-1000-8000-00805f9b34fb
05-26 16:41:38.427: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onNotify<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff07-0000-1000-8000-00805f9b34fb
05-26 16:41:38.462: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onNotify<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff07-0000-1000-8000-00805f9b34fb
// <span class="nv">value</span><span class="o">=[</span>4, 0, 1, 15, 4, 26, 7, 0, 0, 0, 31]
05-26 16:41:38.797: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: writeCharacteristic<span class="o">()</span> - uuid: 0000ff05-0000-1000-8000-00805f9b34fb
05-26 16:41:38.867: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onCharacteristicWrite<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff05-0000-1000-8000-00805f9b34fb <span class="nv">Status</span><span class="o">=</span>0
// <span class="nv">value</span><span class="o">=[</span>4, 2, 0, 15, 4, 21, 8, 0, 29, 0, 31]
05-26 16:41:38.932: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: writeCharacteristic<span class="o">()</span> - uuid: 0000ff05-0000-1000-8000-00805f9b34fb
05-26 16:41:39.012: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onCharacteristicWrite<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff05-0000-1000-8000-00805f9b34fb <span class="nv">Status</span><span class="o">=</span>0
05-26 16:41:43.392: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: writeCharacteristic<span class="o">()</span> - uuid: 0000ff05-0000-1000-8000-00805f9b34fb
05-26 16:41:43.507: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onNotify<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff03-0000-1000-8000-00805f9b34fb
05-26 16:41:43.522: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onCharacteristicWrite<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff05-0000-1000-8000-00805f9b34fb <span class="nv">Status</span><span class="o">=</span>0
05-26 16:41:56.712: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: readCharacteristic<span class="o">()</span> - uuid: 0000ff06-0000-1000-8000-00805f9b34fb
05-26 16:41:56.862: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onCharacteristicRead<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff06-0000-1000-8000-00805f9b34fb <span class="nv">Status</span><span class="o">=</span>0
05-26 16:41:56.897: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: writeCharacteristic<span class="o">()</span> - uuid: 0000ff05-0000-1000-8000-00805f9b34fb
05-26 16:41:57.082: D/BluetoothGatt<span class="o">(</span>31147<span class="o">)</span>: onCharacteristicWrite<span class="o">()</span> - <span class="nv">Device</span><span class="o">=</span>88:0F:10:80:A2:4A <span class="nv">UUID</span><span class="o">=</span>0000ff05-0000-1000-8000-00805f9b34fb <span class="nv">Status</span><span class="o">=</span>0
</code></pre></div></div>
<p>结合decomplier, 几个数据结构:</p>
<p>UserInfo实现在<code class="language-plaintext highlighter-rouge">com.xiaomi.hm.health.bt.profile.IMiLiProfile$UserInfo</code></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="n">com</span><span class="o">.</span><span class="na">xiaomi</span><span class="o">.</span><span class="na">hm</span><span class="o">.</span><span class="na">health</span><span class="o">.</span><span class="na">bt</span><span class="o">.</span><span class="na">profile</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">android.os.Parcel</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">android.os.Parcelable</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">android.os.Parcelable.Creator</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">final</span> <span class="kd">class</span> <span class="nc">IMiLiProfile</span><span class="n">$UserInfo</span>
<span class="kd">implements</span> <span class="nc">Parcelable</span>
<span class="o">{</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">Parcelable</span><span class="o">.</span><span class="na">Creator</span><span class="o"><</span><span class="nc">UserInfo</span><span class="o">></span> <span class="no">CREATOR</span> <span class="o">=</span> <span class="k">new</span> <span class="n">k</span><span class="o">();</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">byte</span> <span class="n">a</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">byte</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">byte</span> <span class="n">c</span> <span class="o">=</span> <span class="mi">2</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">UserInfo</span> <span class="n">k</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">UserInfo</span><span class="o">(</span><span class="mi">170420175</span><span class="o">,</span> <span class="o">(</span><span class="kt">byte</span><span class="o">)</span><span class="mi">0</span><span class="o">,</span> <span class="o">(</span><span class="kt">byte</span><span class="o">)</span><span class="mi">23</span><span class="o">,</span> <span class="o">(</span><span class="kt">byte</span><span class="o">)-</span><span class="mi">88</span><span class="o">,</span> <span class="o">(</span><span class="kt">byte</span><span class="o">)</span><span class="mi">50</span><span class="o">,</span> <span class="s">""</span><span class="o">.</span><span class="na">getBytes</span><span class="o">());</span>
<span class="kd">public</span> <span class="kd">final</span> <span class="kt">int</span> <span class="n">d</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">final</span> <span class="kt">byte</span> <span class="n">e</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">final</span> <span class="kt">byte</span> <span class="n">f</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">final</span> <span class="kt">byte</span> <span class="n">g</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">final</span> <span class="kt">byte</span> <span class="n">h</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">final</span> <span class="kt">byte</span><span class="o">[]</span> <span class="n">i</span><span class="o">;</span>
<span class="kd">public</span> <span class="kt">byte</span> <span class="n">j</span><span class="o">;</span>
<span class="kd">public</span> <span class="nc">IMiLiProfile</span><span class="n">$UserInfo</span><span class="o">(</span><span class="kt">int</span> <span class="n">paramInt</span><span class="o">,</span> <span class="kt">byte</span> <span class="n">paramByte1</span><span class="o">,</span> <span class="kt">byte</span> <span class="n">paramByte2</span><span class="o">,</span> <span class="kt">byte</span> <span class="n">paramByte3</span><span class="o">,</span> <span class="kt">byte</span> <span class="n">paramByte4</span><span class="o">,</span> <span class="kt">byte</span> <span class="n">paramByte5</span><span class="o">,</span> <span class="kt">byte</span><span class="o">[]</span> <span class="n">paramArrayOfByte</span><span class="o">)</span>
<span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">d</span> <span class="o">=</span> <span class="n">paramInt</span><span class="o">;</span>
<span class="k">this</span><span class="o">.</span><span class="na">e</span> <span class="o">=</span> <span class="n">paramByte1</span><span class="o">;</span>
<span class="k">this</span><span class="o">.</span><span class="na">f</span> <span class="o">=</span> <span class="n">paramByte2</span><span class="o">;</span>
<span class="k">this</span><span class="o">.</span><span class="na">g</span> <span class="o">=</span> <span class="n">paramByte3</span><span class="o">;</span>
<span class="k">this</span><span class="o">.</span><span class="na">h</span> <span class="o">=</span> <span class="n">paramByte4</span><span class="o">;</span>
<span class="k">this</span><span class="o">.</span><span class="na">i</span> <span class="o">=</span> <span class="n">paramArrayOfByte</span><span class="o">;</span>
<span class="k">this</span><span class="o">.</span><span class="na">j</span> <span class="o">=</span> <span class="n">paramByte5</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="nc">IMiLiProfile</span><span class="n">$UserInfo</span><span class="o">(</span><span class="kt">int</span> <span class="n">paramInt</span><span class="o">,</span> <span class="kt">byte</span> <span class="n">paramByte1</span><span class="o">,</span> <span class="kt">byte</span> <span class="n">paramByte2</span><span class="o">,</span> <span class="kt">byte</span> <span class="n">paramByte3</span><span class="o">,</span> <span class="kt">byte</span> <span class="n">paramByte4</span><span class="o">,</span> <span class="kt">byte</span><span class="o">[]</span> <span class="n">paramArrayOfByte</span><span class="o">)</span>
<span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">d</span> <span class="o">=</span> <span class="n">paramInt</span><span class="o">;</span>
<span class="k">this</span><span class="o">.</span><span class="na">e</span> <span class="o">=</span> <span class="n">paramByte1</span><span class="o">;</span>
<span class="k">this</span><span class="o">.</span><span class="na">f</span> <span class="o">=</span> <span class="n">paramByte2</span><span class="o">;</span>
<span class="k">this</span><span class="o">.</span><span class="na">g</span> <span class="o">=</span> <span class="n">paramByte3</span><span class="o">;</span>
<span class="k">this</span><span class="o">.</span><span class="na">h</span> <span class="o">=</span> <span class="n">paramByte4</span><span class="o">;</span>
<span class="k">this</span><span class="o">.</span><span class="na">i</span> <span class="o">=</span> <span class="n">paramArrayOfByte</span><span class="o">;</span>
<span class="k">this</span><span class="o">.</span><span class="na">j</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">int</span> <span class="nf">describeContents</span><span class="o">()</span>
<span class="o">{</span>
<span class="k">return</span> <span class="mi">0</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">toString</span><span class="o">()</span>
<span class="o">{</span>
<span class="nc">StringBuilder</span> <span class="n">localStringBuilder1</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">StringBuilder</span><span class="o">(</span><span class="mi">128</span><span class="o">);</span>
<span class="n">localStringBuilder1</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"[[[ "</span> <span class="o">+</span> <span class="n">getClass</span><span class="o">().</span><span class="na">getSimpleName</span><span class="o">()</span> <span class="o">+</span> <span class="s">" ]]]"</span><span class="o">);</span>
<span class="n">localStringBuilder1</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"\n uid: "</span> <span class="o">+</span> <span class="k">this</span><span class="o">.</span><span class="na">d</span><span class="o">);</span>
<span class="nc">StringBuilder</span> <span class="n">localStringBuilder2</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">StringBuilder</span><span class="o">().</span><span class="na">append</span><span class="o">(</span><span class="s">"\n gender: "</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">e</span> <span class="o">==</span> <span class="mi">0</span><span class="o">);</span>
<span class="k">for</span> <span class="o">(</span><span class="nc">String</span> <span class="n">str</span> <span class="o">=</span> <span class="s">"female"</span><span class="o">;</span> <span class="o">;</span> <span class="n">str</span> <span class="o">=</span> <span class="s">"male"</span><span class="o">)</span>
<span class="o">{</span>
<span class="n">localStringBuilder1</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">str</span><span class="o">);</span>
<span class="n">localStringBuilder1</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"\n age: "</span> <span class="o">+</span> <span class="k">this</span><span class="o">.</span><span class="na">f</span> <span class="o">+</span> <span class="s">"yrs"</span><span class="o">);</span>
<span class="n">localStringBuilder1</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"\n height: "</span> <span class="o">+</span> <span class="o">(</span><span class="mh">0xFF</span> <span class="o">&</span> <span class="k">this</span><span class="o">.</span><span class="na">g</span><span class="o">)</span> <span class="o">+</span> <span class="s">"cm"</span><span class="o">);</span>
<span class="n">localStringBuilder1</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"\n weight: "</span> <span class="o">+</span> <span class="o">(</span><span class="mh">0xFF</span> <span class="o">&</span> <span class="k">this</span><span class="o">.</span><span class="na">h</span><span class="o">)</span> <span class="o">+</span> <span class="s">"kg"</span><span class="o">);</span>
<span class="n">localStringBuilder1</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"\n alias: "</span> <span class="o">+</span> <span class="k">new</span> <span class="nc">String</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">i</span><span class="o">));</span>
<span class="n">localStringBuilder1</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"\n type: "</span> <span class="o">+</span> <span class="k">this</span><span class="o">.</span><span class="na">j</span><span class="o">);</span>
<span class="k">return</span> <span class="n">localStringBuilder1</span><span class="o">.</span><span class="na">toString</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">writeToParcel</span><span class="o">(</span><span class="nc">Parcel</span> <span class="n">paramParcel</span><span class="o">,</span> <span class="kt">int</span> <span class="n">paramInt</span><span class="o">)</span>
<span class="o">{</span>
<span class="n">paramParcel</span><span class="o">.</span><span class="na">writeInt</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">d</span><span class="o">);</span>
<span class="n">paramParcel</span><span class="o">.</span><span class="na">writeByte</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">e</span><span class="o">);</span>
<span class="n">paramParcel</span><span class="o">.</span><span class="na">writeByte</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">f</span><span class="o">);</span>
<span class="n">paramParcel</span><span class="o">.</span><span class="na">writeByte</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">g</span><span class="o">);</span>
<span class="n">paramParcel</span><span class="o">.</span><span class="na">writeByte</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">h</span><span class="o">);</span>
<span class="n">paramParcel</span><span class="o">.</span><span class="na">writeString</span><span class="o">(</span><span class="k">new</span> <span class="nc">String</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">i</span><span class="o">));</span>
<span class="n">paramParcel</span><span class="o">.</span><span class="na">writeByte</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">j</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>电池信息在<code class="language-plaintext highlighter-rouge">com.xiaomi.hm.health.bt.profile.d</code></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="n">com</span><span class="o">.</span><span class="na">xiaomi</span><span class="o">.</span><span class="na">hm</span><span class="o">.</span><span class="na">health</span><span class="o">.</span><span class="na">bt</span><span class="o">.</span><span class="na">profile</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.text.DateFormat</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.Calendar</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">final</span> <span class="kd">class</span> <span class="nc">d</span>
<span class="o">{</span>
<span class="kd">public</span> <span class="kd">final</span> <span class="kt">int</span> <span class="n">a</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">final</span> <span class="nc">Calendar</span> <span class="n">b</span><span class="o">;</span>
<span class="kd">public</span> <span class="kt">int</span> <span class="n">c</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">final</span> <span class="kt">int</span> <span class="n">d</span><span class="o">;</span>
<span class="kd">public</span> <span class="nf">d</span><span class="o">(</span><span class="kt">int</span> <span class="n">paramInt1</span><span class="o">,</span> <span class="nc">Calendar</span> <span class="n">paramCalendar</span><span class="o">,</span> <span class="kt">int</span> <span class="n">paramInt2</span><span class="o">,</span> <span class="kt">int</span> <span class="n">paramInt3</span><span class="o">)</span>
<span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">a</span> <span class="o">=</span> <span class="n">paramInt1</span><span class="o">;</span>
<span class="k">this</span><span class="o">.</span><span class="na">b</span> <span class="o">=</span> <span class="n">paramCalendar</span><span class="o">;</span>
<span class="k">this</span><span class="o">.</span><span class="na">c</span> <span class="o">=</span> <span class="n">paramInt2</span><span class="o">;</span>
<span class="k">this</span><span class="o">.</span><span class="na">d</span> <span class="o">=</span> <span class="n">paramInt3</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">toString</span><span class="o">()</span>
<span class="o">{</span>
<span class="nc">StringBuilder</span> <span class="n">localStringBuilder</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">StringBuilder</span><span class="o">(</span><span class="mi">128</span><span class="o">);</span>
<span class="n">localStringBuilder</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"[[[ "</span> <span class="o">+</span> <span class="n">getClass</span><span class="o">().</span><span class="na">getSimpleName</span><span class="o">()</span> <span class="o">+</span> <span class="s">" ]]]"</span><span class="o">);</span>
<span class="n">localStringBuilder</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"\n level: "</span> <span class="o">+</span> <span class="k">this</span><span class="o">.</span><span class="na">a</span> <span class="o">+</span> <span class="s">"%"</span><span class="o">);</span>
<span class="n">localStringBuilder</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"\n lastCharge: "</span> <span class="o">+</span> <span class="nc">DateFormat</span><span class="o">.</span><span class="na">getDateTimeInstance</span><span class="o">().</span><span class="na">format</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">b</span><span class="o">.</span><span class="na">getTime</span><span class="o">()));</span>
<span class="n">localStringBuilder</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"\n charges: "</span> <span class="o">+</span> <span class="k">this</span><span class="o">.</span><span class="na">c</span><span class="o">);</span>
<span class="n">localStringBuilder</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"\n status: "</span> <span class="o">+</span> <span class="nc">Integer</span><span class="o">.</span><span class="na">toHexString</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">d</span><span class="o">));</span>
<span class="k">return</span> <span class="n">localStringBuilder</span><span class="o">.</span><span class="na">toString</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
Android锁屏或切到桌面导致activity的ondestroy被调用
2015-05-14T00:00:00-05:00
http://www.zhaoxiaodan.com/android/android锁屏或切到桌面导致Activity的onDestroy被调用
<p>这次做个移动MM的支付SDK, 提交测试总是打回来说:</p>
<blockquote>
<p>启动该应用,触发任意计费点,在支付界面进行切换后台或锁屏解锁操作,返回后应用报错退出</p>
</blockquote>
<p>查来查去, 发现每次切到桌面, sdk总是又被初始化一次; 而sdk初始话是在Activity的onCreate() 中进行的; 那也就说Activity在切Home的时候被onDestroy()了</p>
<p>于是去检查 Cocos2dxActivity 的各个onXXX方法, 没见啥异常</p>
<p>参考<a href="http://blog.csdn.net/imdxt1986/article/details/7339711">横屏切换竖屏Activity的生命周期及configChanges</a></p>
<ul>
<li>
<p>不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次</p>
</li>
<li>
<p>设置Activity的android:configChanges=”orientation”时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次</p>
</li>
<li>
<p>设置Activity的android:configChanges=”keyboardHidden”时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次</p>
</li>
<li>
<table>
<tbody>
<tr>
<td>设置Activity的android:configChanges=”orientation</td>
<td>screenSize</td>
<td>keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法</td>
</tr>
</tbody>
</table>
</li>
</ul>
<p>于是设置之, 提交</p>
针对cocos2d图片资源自定义加密的解密
2015-05-13T00:00:00-05:00
http://www.zhaoxiaodan.com/cocos2dx/针对cocos2d图片资源自定义加密的解密
<p>发现某游戏其assets目录的图片打开是雪花, 而且用二进制打开查看文件, 找魔数啊什么的, 都没发定义, 是新的文件格式? 还是被加密了?</p>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2015/20150513055351.png" alt="" /></p>
<p>google到这篇文章 <a href="http://blog.csdn.net/ynnmnm/article/details/44921337">破解TexturePacker加密资源 - 使用IDA</a>才想起来, 可以使用ida进行反编译.(调试后发现, 该游戏并不是使用TexturePacker提供的加密方式)</p>
<p>###在哪里解密的?</p>
<p>虽然游戏没有源码, 但是cocos2dx是有源码的, 参考进行调试, 还是蛮简单的, 而且记得一句话, 不管你怎么加密, 最后你在内存里, 肯定有一份解密之后的数据.</p>
<p>好, 按这个思路, 那我首先看, 这个解密之后的数据到底放在哪里?</p>
<p>根据以前的经验, cocos2dx的图片资源加载, 是在<code class="language-plaintext highlighter-rouge">Image::initWithImageData(const unsigned char * data, ssize_t dataLen)</code> 函数, 按照函数名称顾名思义:用数据初始化Image, 好了, 在ida 给这个函数下个断点, 先看看这个传进来的data</p>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2015/20150513043533.png" alt="" /></p>
<p>可以看到, data这个内存地址是<code class="language-plaintext highlighter-rouge">debug148:797363E8</code>, 而且这个地址的内容跟上面打开看到的完全不一样, 再再而且, 前4个字节就是png文件的魔数!<code class="language-plaintext highlighter-rouge">.png</code></p>
<p>那也就是说, 图片文件是在被读入内存之后, 在被初始化之前就解密了;</p>
<p>那图片在哪里被读入?</p>
<p>cocos2dx的图片资源加载, 一般都是从<code class="language-plaintext highlighter-rouge">TextureCache::addImage(const std::string &path)</code> 这个函数开始的, 参考源码</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Texture2D</span> <span class="o">*</span> <span class="n">TextureCache</span><span class="o">::</span><span class="n">addImage</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="o">&</span><span class="n">path</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">//cache中是否已经有这个图片</span>
<span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">_textures</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">fullpath</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span> <span class="n">it</span> <span class="o">!=</span> <span class="n">_textures</span><span class="p">.</span><span class="n">end</span><span class="p">()</span> <span class="p">)</span>
<span class="n">texture</span> <span class="o">=</span> <span class="n">it</span><span class="o">-></span><span class="n">second</span><span class="p">;</span>
<span class="c1">//没有</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span> <span class="n">texture</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">do</span>
<span class="p">{</span>
<span class="n">image</span> <span class="o">=</span> <span class="k">new</span> <span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">nothrow</span><span class="p">)</span> <span class="n">Image</span><span class="p">();</span>
<span class="n">CC_BREAK_IF</span><span class="p">(</span><span class="nb">nullptr</span> <span class="o">==</span> <span class="n">image</span><span class="p">);</span>
<span class="c1">//初始化image</span>
<span class="kt">bool</span> <span class="n">bRet</span> <span class="o">=</span> <span class="n">image</span><span class="o">-></span><span class="n">initWithImageFile</span><span class="p">(</span><span class="n">fullpath</span><span class="p">);</span>
<span class="n">CC_BREAK_IF</span><span class="p">(</span><span class="o">!</span><span class="n">bRet</span><span class="p">);</span>
<span class="n">texture</span> <span class="o">=</span> <span class="k">new</span> <span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">nothrow</span><span class="p">)</span> <span class="n">Texture2D</span><span class="p">();</span>
<span class="c1">//再用image初始化texture</span>
<span class="k">if</span><span class="p">(</span> <span class="n">texture</span> <span class="o">&&</span> <span class="n">texture</span><span class="o">-></span><span class="n">initWithImage</span><span class="p">(</span><span class="n">image</span><span class="p">)</span> <span class="p">)</span>
<span class="p">{</span>
<span class="c1">//hold 住</span>
<span class="n">_textures</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span> <span class="n">std</span><span class="o">::</span><span class="n">make_pair</span><span class="p">(</span><span class="n">fullpath</span><span class="p">,</span> <span class="n">texture</span><span class="p">)</span> <span class="p">);</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="n">CCLOG</span><span class="p">(</span><span class="s">"cocos2d: Couldn't create texture for file:%s in TextureCache"</span><span class="p">,</span> <span class="n">path</span><span class="p">.</span><span class="n">c_str</span><span class="p">());</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">CC_SAFE_RELEASE</span><span class="p">(</span><span class="n">image</span><span class="p">);</span>
<span class="k">return</span> <span class="n">texture</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>然后追到</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">bool</span> <span class="n">Image</span><span class="o">::</span><span class="n">initWithImageFile</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&</span> <span class="n">path</span><span class="p">)</span>
<span class="p">{</span>
<span class="p">...</span>
<span class="c1">//从文件读入数据</span>
<span class="n">Data</span> <span class="n">data</span> <span class="o">=</span> <span class="n">FileUtils</span><span class="o">::</span><span class="n">getInstance</span><span class="p">()</span><span class="o">-></span><span class="n">getDataFromFile</span><span class="p">(</span><span class="n">_filePath</span><span class="p">);</span>
<span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>
<p>其实也就是<code class="language-plaintext highlighter-rouge">CCFileUtils.cpp</code>中的<code class="language-plaintext highlighter-rouge">Data FileUtils::getDataFromFile(const std::string& filename)</code></p>
<p>这个<code class="language-plaintext highlighter-rouge">CCFileUtils.cpp</code>是平台相关的, 每个平台有每个平台的具体实现. android的版本是<code class="language-plaintext highlighter-rouge">CCFileUtils-android.cpp</code>文件</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Data</span> <span class="n">FileUtilsAndroid</span><span class="o">::</span><span class="n">getData</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&</span> <span class="n">filename</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">forString</span><span class="p">)</span>
<span class="p">{</span>
<span class="p">...</span>
<span class="c1">//上面一大堆, 主要就是先判断这个filename是绝对目录还是assets目录, //如果是assets目录则用AssetManager 来打开并读取,再做些分配内存什么的事情</span>
<span class="c1">//</span>
<span class="kt">int</span> <span class="n">bytesread</span> <span class="o">=</span> <span class="n">AAsset_read</span><span class="p">(</span><span class="n">asset</span><span class="p">,</span> <span class="p">(</span><span class="kt">void</span><span class="o">*</span><span class="p">)</span><span class="n">data</span><span class="p">,</span> <span class="n">fileSize</span><span class="p">);</span>
<span class="n">size</span> <span class="o">=</span> <span class="n">bytesread</span><span class="p">;</span>
<span class="n">AAsset_close</span><span class="p">(</span><span class="n">asset</span><span class="p">);</span>
<span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>
<p>在ida中找到相应的代码查看</p>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2015/20150513054201.png" alt="" /></p>
<p>发现在<code class="language-plaintext highlighter-rouge">AAsset_read</code> 之后多了一个<code class="language-plaintext highlighter-rouge">FileUtils::xxxxxxxxx</code>函数的调用, 并且,<code class="language-plaintext highlighter-rouge">v10</code>这个指针指向的就是文件读入后的内存, 传给了这个函数.</p>
<p>进到函数看一眼:</p>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2015/20150513054635.png" alt="" /></p>
<p>哦? 内存每4个字节位与一个数再存回去? 得了, 基本是解密函数无疑. 验证下</p>
<p>在115行也就是<code class="language-plaintext highlighter-rouge">AAsset_read</code>之后下个断</p>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2015/20150513055523.png" alt="" /></p>
<p>看到文件被读入内存, 并且内存中的数据跟文件是一致的</p>
<p>再在<code class="language-plaintext highlighter-rouge">FileUtils::xxxxxxxxx</code>之后下个断</p>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2015/20150513055730.png" alt="" /></p>
<p>看到, v10 这个内存变化了, 出现了png的魔数<code class="language-plaintext highlighter-rouge">.png</code></p>
<p>那么<code class="language-plaintext highlighter-rouge">FileUtils::xxxxxxxxx</code>函数就是解密函数无疑</p>
<p>###知道解密算法了, 怎么把图还原出来?</p>
<p>很简单, 照着这个函数写一个c函数, 读文件进内存, 调这个函数解密, 就可以了</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include <stdio.h>
#include <stdlib.h>
</span><span class="kt">void</span> <span class="nf">xxxxx</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span> <span class="n">fileData</span><span class="p">,</span> <span class="kt">int</span> <span class="n">fileSize</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">int</span> <span class="n">v5</span><span class="p">,</span><span class="n">v6</span><span class="p">,</span><span class="n">v7</span><span class="p">,</span><span class="n">v8</span><span class="p">,</span><span class="n">v9</span><span class="p">,</span><span class="n">i</span><span class="p">;</span>
<span class="n">v5</span><span class="o">=</span><span class="mh">0x00000000</span><span class="p">;</span>
<span class="n">v6</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span>
<span class="k">while</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">v7</span><span class="o">=</span><span class="n">v6</span><span class="o">+</span><span class="n">v5</span><span class="p">;</span>
<span class="k">if</span><span class="p">(</span> <span class="n">v6</span> <span class="o">>=</span> <span class="n">fileSize</span> <span class="o">/</span> <span class="mi">4</span><span class="p">)</span>
<span class="k">break</span><span class="p">;</span>
<span class="n">v8</span><span class="o">=</span> <span class="mi">4</span> <span class="o">*</span> <span class="n">v6</span><span class="p">;</span>
<span class="n">v9</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">int</span> <span class="o">*</span><span class="p">)</span><span class="o">&</span><span class="n">fileData</span><span class="p">[</span><span class="mi">4</span> <span class="o">*</span> <span class="n">v6</span><span class="o">++</span><span class="p">];</span>
<span class="o">*</span><span class="p">(</span><span class="kt">int</span> <span class="o">*</span><span class="p">)</span><span class="o">&</span><span class="n">fileData</span><span class="p">[</span><span class="n">v8</span><span class="p">]</span> <span class="o">=</span> <span class="n">v7</span> <span class="o">^</span> <span class="n">v9</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">for</span> <span class="p">(</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="p">;</span> <span class="o">*</span><span class="p">(</span><span class="o">&</span><span class="n">fileData</span><span class="p">[</span><span class="n">fileSize</span><span class="p">]</span> <span class="o">+</span> <span class="n">i</span><span class="p">)</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="o">&</span><span class="n">fileData</span><span class="p">[</span><span class="n">fileSize</span><span class="p">]</span> <span class="o">+</span> <span class="n">i</span><span class="p">)</span> <span class="o">^</span> <span class="mh">0xCC</span> <span class="p">)</span>
<span class="p">{</span>
<span class="o">--</span><span class="n">i</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span> <span class="o">~</span><span class="n">i</span> <span class="o">>=</span> <span class="n">fileSize</span> <span class="o">%</span> <span class="mi">4</span> <span class="p">)</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span><span class="kt">char</span><span class="o">**</span> <span class="n">args</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span><span class="p">(</span><span class="n">argc</span> <span class="o"><=</span> <span class="mi">2</span><span class="p">){</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"paramters wrong</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">char</span><span class="o">*</span> <span class="n">filePath</span> <span class="o">=</span> <span class="n">args</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
<span class="kt">char</span><span class="o">*</span> <span class="n">savePath</span> <span class="o">=</span> <span class="n">args</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"%s => %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="n">filePath</span><span class="p">,</span><span class="n">savePath</span><span class="p">);</span>
<span class="kt">FILE</span> <span class="o">*</span><span class="n">fp</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="n">filePath</span><span class="p">,</span> <span class="s">"r"</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">fp</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"read file %s fail !!</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="n">filePath</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">fseek</span><span class="p">(</span><span class="n">fp</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="n">SEEK_END</span><span class="p">);</span>
<span class="kt">size_t</span> <span class="n">size</span> <span class="o">=</span> <span class="n">ftell</span><span class="p">(</span><span class="n">fp</span><span class="p">);</span>
<span class="n">fseek</span><span class="p">(</span><span class="n">fp</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="n">SEEK_SET</span><span class="p">);</span>
<span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span> <span class="n">buffer</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="p">)</span> <span class="o">*</span> <span class="n">size</span><span class="p">);</span>
<span class="kt">size_t</span> <span class="n">readsize</span> <span class="o">=</span> <span class="n">fread</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="p">),</span> <span class="n">size</span><span class="p">,</span> <span class="n">fp</span><span class="p">);</span>
<span class="n">fclose</span><span class="p">(</span><span class="n">fp</span><span class="p">);</span>
<span class="n">xxxxx</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span><span class="n">readsize</span><span class="p">);</span>
<span class="kt">FILE</span> <span class="o">*</span><span class="n">pFile</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="n">savePath</span><span class="p">,</span> <span class="s">"w"</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">pFile</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"write file %s fail !!</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="n">savePath</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">fwrite</span> <span class="p">(</span><span class="n">buffer</span> <span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">char</span><span class="p">),</span> <span class="n">readsize</span><span class="p">,</span> <span class="n">pFile</span><span class="p">);</span>
<span class="n">fclose</span> <span class="p">(</span><span class="n">pFile</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
使用ida调试android的c程序
2015-05-08T00:00:00-05:00
http://www.zhaoxiaodan.com/android/使用IDA调试android的c程序
<h2 id="普通调试">普通调试</h2>
<ol>
<li>用ida读入libcocos2dcpp.so</li>
<li>将ida目录下的<code class="language-plaintext highlighter-rouge">dbgsrv/android_server</code>拷贝到android手机</li>
<li><code class="language-plaintext highlighter-rouge">adb shell</code>进入, 并以root运行<code class="language-plaintext highlighter-rouge">android_server</code></li>
<li>手机运行app, ida使用<code class="language-plaintext highlighter-rouge">Remote ARM Linux/Android debugger</code>, 并attach对应的进程</li>
<li>下断点调试即可</li>
</ol>
<h2 id="app在attach之前不运行">app在attach之前不运行</h2>
<p>一般先运行app, 这个时候比如lib就会被加载并运行, 想断点某些入口点怎么办?</p>
<ol>
<li>在adb的shell中使用<code class="language-plaintext highlighter-rouge">am start -D -n 包名/Activity类名</code>运行app, 这个时候app会显示<code class="language-plaintext highlighter-rouge">waiting debugger to attach</code></li>
<li>ida慢慢attach;在想要断的地方下好断子 (这个attach不是上面的attach)</li>
<li>使用 <code class="language-plaintext highlighter-rouge">jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700</code> 真正attach, 此时游戏运行</li>
</ol>
<h2 id="attach之后只看到一个进程">attach之后只看到一个进程:</h2>
<p><code class="language-plaintext highlighter-rouge">android_server</code>没有用root运行, 或者shell并不是root, 比如魅族mx3, 虽然有root权限, 但是shell不是真root</p>
<h2 id="魅族mx3获得shell-root">魅族MX3获得shell root</h2>
<p>mx3的rom默认可以开启<code class="language-plaintext highlighter-rouge">系统权限</code>, 但是使用shell还是无法切换到root用户; 解决办法是安装<code class="language-plaintext highlighter-rouge">SuperSU</code>软件</p>
<h2 id="放在sdcard-里不能运行">放在/sdcard 里不能运行</h2>
<p>sdcard 默认会格式化为FAT32, 所以在这个里面的所有文件都没有-x 权限, 解决办法就是放到<code class="language-plaintext highlighter-rouge">/data/local/tmp</code>目录下</p>
<h2 id="mac怎么调试">mac怎么调试</h2>
<p>在Paralle 跑win8, win8 跑 ida, ida是通过ip去attach的, 那就得让win8 连到<code class="language-plaintext highlighter-rouge">外界</code>能<code class="language-plaintext highlighter-rouge">看到</code>的网络, 虚拟网卡使用<code class="language-plaintext highlighter-rouge">桥接</code>, 接到比如公司wifi上</p>
<p>如果用真机, 真机也连到wifi上就行了</p>
<p>如果是Genymotion, VitrualBox里的虚拟网卡同样设置成<code class="language-plaintext highlighter-rouge">桥接</code>, 连到wifi, 那win8和Genymotion 就在同一个网络了</p>
<p>然后在路由器把ip固定住</p>
利用七牛管理工具将截图直接上传
2015-04-07T00:00:00-05:00
http://www.zhaoxiaodan.com/%E5%85%B6%E4%BB%96/利用七牛管理工具将截图直接上传
<p>之前做了一个基于 alfred 的workflow用来管理github page 博客文章; 还实现了将截图直接保存到文件的功能;
但是网站嘛, 肯定就会用到cdn图床, 于是想到了使用七牛;</p>
<p>七牛提供了sdk可以定制开发, 但是也提供了一个命令行工具, 就不重复开发轮子了;</p>
<p>workflow地址:</p>
<blockquote>
<p><a href="https://github.com/liang8305/gp_manager">https://github.com/liang8305/gp_manager</a></p>
</blockquote>
<h2 id="使用前">使用前</h2>
<ol>
<li>配置</li>
</ol>
<p>使用 <code class="language-plaintext highlighter-rouge">gp config</code> 命令打开配置文件, 设置 <code class="language-plaintext highlighter-rouge">qiniu_bucket</code>, 表示你想使用的七牛bucket</p>
<ol>
<li>qiniu 登录</li>
</ol>
<p>使用 <code class="language-plaintext highlighter-rouge">gp qinniulogin 用户名 密码</code> 登录七牛</p>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2015/20150407112144.png" alt="image" /></p>
<p>##使用方式</p>
<ol>
<li>使用系统, 或者QQ截图进行截图, 比如快捷键: <code class="language-plaintext highlighter-rouge">Shift + Cmmand + Control + 4</code></li>
<li>使用 <code class="language-plaintext highlighter-rouge">gp paste2qiniu</code> 命令
<img src="http://qiniucdn.zhaoxiaodan.com/2015/20150407112323.png" alt="image" /></li>
<li>gp_manager 就会将你的截图上传到七牛你所指定的bucket里, 并将图片的url复制到剪切板</li>
</ol>
买个美国区阿里云ecs来做vpn
2015-04-02T00:00:00-05:00
http://www.zhaoxiaodan.com/%E5%85%B6%E4%BB%96/买个美国区阿里云ECS来做VPN
<p>这个GFW发威了, 很烦. 一直用红杏吧还行, 但是只能在浏览器用; 手机啥的都用不了. 最近想刷刷twitter, 搞个虚机吧;</p>
<p>在阿里云控制台购买机器, 发现美国区域的, 每天10点开抢, 每天限量20台. 好吧, 现在9点55分…</p>
<p>抢到了, 1核1G, 1m贷款, 75/月, 付款… 创建成功</p>
<p>等等, 是不是应该用按流量收费才好? 1m 贷款太慢了吧? 按量付费的, 45/月, 然后0.5/G流量</p>
<p>那也就是说我用60G流量才和固定带宽价格一样. 60G, 恩, 应该够了吧</p>
<p>换一下吧, 但是每天限量20台, 应该没了吧? 试试, 购买相同配置, 修改流量为按量付费, 峰值10M, 下订单, ok , 支付成功</p>
<p>唉? 不是20台么, 发现, 使用<code class="language-plaintext highlighter-rouge">购买相同配置</code>, 在选择区域那默认就勾选了美国区, 但是如果你点出去, 就点不回来了, 不知道这个是bug, 还是故意就没卡的那么死</p>
<ol>
<li>登录虚机</li>
<li>创建账户</li>
<li>账户加入sudo</li>
<li>本地<code class="language-plaintext highlighter-rouge">~/.ssh/id_rsa.pub</code>内容拷贝到虚机<code class="language-plaintext highlighter-rouge">~/.ssh/authorized_keys</code>做免密码登录</li>
<li>apt-get update</li>
</ol>
<p>哦? ubuntu 14.04镜像已经改为aliyun的apt镜像了;
好, 装git zsh oh-my-zsh, 靠, 10kb, 怎么回事?</p>
<p>ping下<code class="language-plaintext highlighter-rouge">mirrors.aliyun.com</code>, 好嘛, 是中国区的ip, 能不慢嘛</p>
<p>改回官方源吧</p>
<ol>
<li>装git, zsh , oh-my-zsh</li>
<li>
<p>安装docker</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> $ sudo apt-get install apt-transport-https
$ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9
$ sudo bash -c "echo deb https://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list"
$ sudo apt-get update
$ sudo apt-get install lxc-docker
</code></pre></div> </div>
</li>
<li>
<p>做pptp的dockerfile, 具体在这里: <a href="https://github.com/liang8305/my-dockerfile/blob/master/pptp">pptp dockerfile</a></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> FROM ubuntu:14.04
MAINTAINER pangliang "418094911@qq.com"
RUN apt-get update \
&& apt-get install -y supervisor pptpd iptables
RUN sed -i 's/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/g' /etc/sysctl.conf \
#&& sed -i 's/exit 0/iptables -t nat -A POSTROUTING -o eth0 -s 192.168.0.0\/24 -j MASQUERADE\n exit 0/g' /etc/rc.local \
&& echo "done"
ADD pptpd.conf /etc/pptpd.conf
ADD pptpd-options /etc/ppp/pptpd-options
ADD run.sh /run.sh
#timezone
RUN echo "Asia/Shanghai" >/etc/timezone \
&& dpkg-reconfigure --frontend noninteractive tzdata
#supervisor
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
CMD ["/usr/bin/supervisord"]
</code></pre></div> </div>
</li>
</ol>
<p>网上其实有很多安装教程了, 步奏也很简单, 安装<code class="language-plaintext highlighter-rouge">pptpd</code>, 和<code class="language-plaintext highlighter-rouge">iptables</code>, 然后设置 <code class="language-plaintext highlighter-rouge">net.ipv4.ip_forward=1</code>允许中转, 然后利用iptables 将子网段的数据转发给 真实网卡</p>
Aliyun安装docker无法启动
2015-04-02T00:00:00-05:00
http://www.zhaoxiaodan.com/%E5%85%B6%E4%BB%96/aliyun安装docker无法启动
<p>很简单, 因为阿里云服务器默认占用了172.16.x.x 网段, 删掉这个路由表即可</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo route del -net 172.16.0.0 netmask 255.240.0.0
</code></pre></div></div>
如何克隆sd卡的内容
2015-03-25T00:00:00-05:00
http://www.zhaoxiaodan.com/%E5%85%B6%E4%BB%96/如何克隆SD卡的内容
<p>群里有车友的虎子地图还是D2的卡, 要去4S升级还要几百块. 想想, 用D3的卡复制一个不行么; 试了一下, 直接拷贝卡内的文件, 是不行的; 需要dd做克隆</p>
<p>##step1. 定位你的SDCard</p>
<p>命令行输入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>diskutil list
</code></pre></div></div>
<p>显示如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/dev/disk0
#: TYPE NAME SIZE IDENTIFIER
0: GUID_partition_scheme *251.0 GB disk0
1: EFI EFI 209.7 MB disk0s1
2: Apple_HFS Macintosh HD 90.7 GB disk0s2
3: Apple_HFS d 34.9 GB disk0s3
4: Apple_HFS f 65.6 GB disk0s4
5: Apple_HFS g 59.1 GB disk0s5
/dev/disk1
#: TYPE NAME SIZE IDENTIFIER
0: Apple_partition_scheme *14.2 MB disk1
1: Apple_partition_map 32.3 KB disk1s1
2: Apple_HFS Flash Player 14.2 MB disk1s2
/dev/disk2
#: TYPE NAME SIZE IDENTIFIER
0: Apple_partition_scheme *16.1 MB disk2
1: Apple_partition_map 32.3 KB disk2s1
2: Apple_HFS Flash Player 16.0 MB disk2s2
/dev/disk3
#: TYPE NAME SIZE IDENTIFIER
0: FDisk_partition_scheme *31.9 GB disk3
</code></pre></div></div>
<p>那么你的sdcard就是盘符disk3</p>
<p>##step2. 用原卡制作镜像</p>
<p>命令行输入, 制作镜像文件:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo dd if=/dev/rdisk3 of=/Users/liangwei/Downloads/yihud3.dmg bs=1m
</code></pre></div></div>
<p>##step3. 格式化新卡</p>
<p>把元卡拿出来, 把新卡放进去, 然后同step1, 找到新卡盘符, 然后命令行:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>diskutil unmountDisk /dev/disk3
sudo newfs_msdos -F 32 /dev/disk3
</code></pre></div></div>
<p>##step4. 烧写新卡</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo dd if=/Users/liangwei/Downloads/yihud3.dmg of=/dev/rdisk3 bs=1m
</code></pre></div></div>
<p>树莓派的镜像卡也是这样烧写, 但是格式化的时候需要格式化为 FAT16:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo newfs_msdos -F 16 /dev/disk3
</code></pre></div></div>
调整mac启动分区大小
2015-03-18T00:00:00-05:00
http://www.zhaoxiaodan.com/mac/调整Mac启动分区大小
<p>刚给mac的磁盘进行了彻底的清理, 但是空间感觉还是不够用, 而其他盘其实还有很多空间又用不上, 于是想着干脆调一下空间大小得了;</p>
<p>打开磁盘工具一看傻眼了, 启动分区不能调整大小; 后来goole了一下, 其实不能调整大小不是因为是启动分区, 而是因为, 这个分区后面有一个隐藏分区:<code class="language-plaintext highlighter-rouge">Recovery HD</code>; 只要把这个分区删除就可以了;</p>
<p>##删除Recovery HD</p>
<ol>
<li>
<p>找到的分区id: <code class="language-plaintext highlighter-rouge">diskutil list</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> # diskutil list
/dev/disk0
#: TYPE NAME SIZE IDENTIFIER
0: GUID_partition_scheme *251.0 GB disk0
1: EFI EFI 209.7 MB disk0s1
2: Apple_HFS Macintosh HD 62.5 GB disk0s2
3: Apple_Boot Recovery HD 650.0 MB disk0s3
4: Apple_HFS d 62.5 GB disk0s4
5: Apple_HFS f 65.6 GB disk0s5
6: Apple_HFS g 59.1 GB disk0s6
/dev/disk1
#: TYPE NAME SIZE IDENTIFIER
0: Apple_partition_scheme *10.7 GB disk1
1: Apple_partition_map 32.3 KB disk1s1
2: Apple_HFS Unity Download Assis... 10.7 GB disk1s2
</code></pre></div> </div>
</li>
<li>
<p>删除分区</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> # diskutil eraseVolume HFS+ Blank /dev/disk0s3
</code></pre></div> </div>
</li>
<li>
<p>跟主分区合并</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> # diskutil mergePartitions HFS+ "Macintosh HD" disk0s2 disk0s3
</code></pre></div> </div>
</li>
</ol>
<p>好了, 现在就可以到磁盘工具调整大小了</p>
清理xcode磁盘空间
2015-03-18T00:00:00-05:00
http://www.zhaoxiaodan.com/mac/清理Xcode磁盘空间
<p>##移除对旧设备的支持</p>
<p>影响:可重新生成;再连接旧设备调试时,会重新自动生成。我移除了4.3.2, 5.0, 5.1等版本的设备支持。</p>
<p>路径:~/Library/Developer/Xcode/iOS DeviceSupport</p>
<p>##移除旧版本的模拟器支持</p>
<p>影响:不可恢复;如果需要旧版本的模拟器,就需要重新下载了。我移除了4.3.2, 5.0, 5.1等旧版本的模拟器。</p>
<p>路径:~/Library/Application Support/iPhone Simulator</p>
<p>##移除模拟器的临时文件</p>
<p>影响:可重新生成;如果需要保留较新版本的模拟器,但tmp文件夹很大。放心删吧,tmp文件夹里的内容是不重要的。在iOS Device中,存储空间不足时,tmp文件夹是可能被清空的。</p>
<p>路径:~/Library/Application Support/iPhone Simulator/6.1/tmp (以iOS Simulator 6.1为例)</p>
<p>##移除模拟器中安装的Apps</p>
<p>影响:不可恢复;对应的模拟器中安装的Apps被清空了,如果不需要就删了吧。</p>
<p>路径:~/Library/Application Support/iPhone Simulator/6.1/Applications (以iOS Simulator 6.1为例)</p>
<p>##移除Archives</p>
<p>影响:不可恢复;Adhoc或者App Store版本会被删除。建议备份dSYM文件夹</p>
<p>路径:~/Library/Developer/Xcode/Archives</p>
<p>##移除DerivedData</p>
<p>影响:可重新生成;会删除build生成的项目索引、build输出以及日志。重新打开项目时会重新生成,大的项目会耗费一些时间。</p>
<p>路径:~/Library/Developer/Xcode/DerivedData</p>
<p>##移除旧的Docsets</p>
<p>影响:不可恢复;将删除旧的Docsets文档</p>
<p>路径:~/Library/Developer/Shared/Documentation/DocSets</p>
游戏资源二次压缩
2015-03-04T00:00:00-06:00
http://www.zhaoxiaodan.com/cocos2dx/游戏资源二次压缩
<p>因为渠道的要求关系, 之前的游戏包过大, 需要再减小将近一半的体积; 再不去修改代码的情况下, 想到的首先是将图片大小直接缩小一半, 再在引擎中全部放大出来的办法;</p>
<p>###放大</p>
<p>就是在cocos2d-x 的贴图缓存加载的时候进行放大, 具体位置是的<code class="language-plaintext highlighter-rouge">CCTextureCache.cpp</code> 的<code class="language-plaintext highlighter-rouge">CCTexture2D * CCTextureCache::addImage(const char * path)</code>方法中:</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">CCTexture2D</span> <span class="o">*</span> <span class="n">CCTextureCache</span><span class="o">::</span><span class="n">addImage</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span> <span class="n">path</span><span class="p">)</span>
<span class="p">{</span>
<span class="p">......</span>
<span class="n">pImage</span> <span class="o">=</span> <span class="k">new</span> <span class="n">CCImage</span><span class="p">();</span>
<span class="n">CC_BREAK_IF</span><span class="p">(</span><span class="nb">NULL</span> <span class="o">==</span> <span class="n">pImage</span><span class="p">);</span>
<span class="kt">bool</span> <span class="n">bRet</span> <span class="o">=</span> <span class="n">pImage</span><span class="o">-></span><span class="n">initWithImageFile</span><span class="p">(</span><span class="n">fullpath</span><span class="p">.</span><span class="n">c_str</span><span class="p">(),</span> <span class="n">eImageFormat</span><span class="p">);</span>
<span class="n">CC_BREAK_IF</span><span class="p">(</span><span class="o">!</span><span class="n">bRet</span><span class="p">);</span>
<span class="c1">// pImage加载完文件内容之后, 对pImage统一放大</span>
<span class="n">CCTexture2D</span> <span class="o">*</span> <span class="n">pcOriTexture</span> <span class="o">=</span> <span class="k">new</span> <span class="n">CCTexture2D</span><span class="p">();</span>
<span class="n">pcOriTexture</span><span class="o">-></span><span class="n">initWithImage</span><span class="p">(</span><span class="n">pImage</span><span class="p">);</span>
<span class="n">pcOriTexture</span><span class="o">-></span><span class="n">autorelease</span><span class="p">();</span>
<span class="n">pcOriTexture</span><span class="o">-></span><span class="n">setAliasTexParameters</span><span class="p">();</span>
<span class="kt">int</span> <span class="n">w</span> <span class="o">=</span> <span class="n">pcOriTexture</span><span class="o">-></span><span class="n">getPixelsWide</span><span class="p">()</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">h</span> <span class="o">=</span> <span class="n">pcOriTexture</span><span class="o">-></span><span class="n">getPixelsHigh</span><span class="p">()</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span>
<span class="n">CCRenderTexture</span> <span class="o">*</span> <span class="n">pcTempRenderTex</span> <span class="o">=</span> <span class="n">CCRenderTexture</span><span class="o">::</span><span class="n">create</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">);</span>
<span class="n">pcTempRenderTex</span><span class="o">-></span><span class="n">beginWithClear</span><span class="p">(</span><span class="mf">0.0</span><span class="n">f</span><span class="p">,</span> <span class="mf">0.0</span><span class="n">f</span><span class="p">,</span> <span class="mf">0.0</span><span class="n">f</span><span class="p">,</span> <span class="mf">0.0</span><span class="n">f</span><span class="p">);</span>
<span class="n">pcOriTexture</span><span class="o">-></span><span class="n">drawInRect</span><span class="p">(</span><span class="n">CCRectMake</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">));</span>
<span class="n">pcTempRenderTex</span><span class="o">-></span><span class="n">end</span><span class="p">();</span>
<span class="n">pImage</span> <span class="o">=</span> <span class="n">pcTempRenderTex</span><span class="o">-></span><span class="n">newCCImage</span><span class="p">();</span>
<span class="c1">// 统一放大end</span>
<span class="c1">//原来的加载, 但是pImage 已经放大过了</span>
<span class="n">texture</span> <span class="o">=</span> <span class="k">new</span> <span class="n">CCTexture2D</span><span class="p">();</span>
<span class="k">if</span><span class="p">(</span> <span class="n">texture</span> <span class="o">&&</span>
<span class="n">texture</span><span class="o">-></span><span class="n">initWithImage</span><span class="p">(</span><span class="n">pImage</span><span class="p">)</span> <span class="p">)</span>
<span class="p">{</span>
<span class="p">......</span>
<span class="p">}</span>
</code></pre></div></div>
<p>###源图片压缩</p>
<p>使用<code class="language-plaintext highlighter-rouge">ImageMagick</code>工具的<code class="language-plaintext highlighter-rouge">convert</code>进行缩小, 再用<code class="language-plaintext highlighter-rouge">ImageAlpha.app</code> 进行压缩</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="nv">imgfiles</span><span class="o">=</span><span class="sb">`</span>find res <span class="nt">-name</span> <span class="s2">"*.png"</span><span class="sb">`</span>
<span class="nv">num</span><span class="o">=</span>0
<span class="k">for </span>file <span class="k">in</span> <span class="nv">$imgfiles</span>
<span class="k">do
</span><span class="nv">num</span><span class="o">=</span><span class="k">$((</span><span class="nv">$num</span><span class="o">+</span><span class="m">1</span><span class="k">))</span>
<span class="k">done
</span><span class="nb">echo</span> <span class="s2">"开始压缩...共</span><span class="k">${</span><span class="nv">num</span><span class="k">}</span><span class="s2">个文件..."</span>
<span class="nv">index</span><span class="o">=</span>0
<span class="k">for </span>file <span class="k">in</span> <span class="nv">$imgfiles</span>
<span class="k">do
</span><span class="nv">target</span><span class="o">=</span><span class="k">${</span><span class="nv">file</span><span class="p">/res/res_resize</span><span class="k">}</span>
<span class="nv">index</span><span class="o">=</span><span class="k">$((</span><span class="nv">$index</span><span class="o">+</span><span class="m">1</span><span class="k">))</span>
<span class="nb">echo</span> <span class="s2">"</span><span class="k">${</span><span class="nv">index</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">num</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">file</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">target</span><span class="k">}</span><span class="s2">"</span>
convert <span class="nt">-resize</span> 50% <span class="nv">$file</span> <span class="nv">$target</span>
/Applications/ImageAlpha.app/Contents/Resources/pngnq <span class="nt">-f</span> <span class="nt">-e</span> .png <span class="nt">-s</span> 1 <span class="nt">-u15</span> <span class="nt">-Q</span> 10 <span class="nt">-T</span> 15 <span class="nv">$target</span>
<span class="k">done</span>
</code></pre></div></div>
Eclipse中web项目的依赖部署
2015-02-25T00:00:00-06:00
http://www.zhaoxiaodan.com/java/eclipse中web项目的依赖部署
<p>WEB工程有引用其它JAVA工程中的类,在TOMCAT运行时,找不到依赖工程中的JAVA类.
比如工程A引用工程B, 一般情况下是把工程B编译成jar包然后放到<code class="language-plaintext highlighter-rouge">WEB-INF/lib</code>目录下
但是开发的时候一方面不能热部署, 另一个查看源码时也无法跳转</p>
<p>解决办法是:</p>
<ol>
<li>解决编译依赖: 项目<code class="language-plaintext highlighter-rouge">Properties</code>的<code class="language-plaintext highlighter-rouge">Java Build Path</code>中加入引用的<code class="language-plaintext highlighter-rouge">Project</code></li>
<li>解决部署依赖: 项目<code class="language-plaintext highlighter-rouge">Properties</code>的<code class="language-plaintext highlighter-rouge">Deloyment Assmbly</code>中加入引用的<code class="language-plaintext highlighter-rouge">Project</code></li>
</ol>
批量创建渠道包脚本
2015-02-05T00:00:00-06:00
http://www.zhaoxiaodan.com/android/批量创建渠道包脚本
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="nv">channel_start</span><span class="o">=</span>101
<span class="nv">channel_end</span><span class="o">=</span>110
<span class="nv">apk_name_prefix</span><span class="o">=</span>sxlm_release_v2.0.0_lequsdk_tysf
<span class="nv">target_dir</span><span class="o">=</span>/Users/liangwei/Downloads
<span class="k">for</span> <span class="o">((</span> <span class="nv">channel</span><span class="o">=</span><span class="nv">$channel_start</span><span class="p">;</span>channel<<span class="o">=</span><span class="nv">$channel_end</span><span class="p">;</span> channel++ <span class="o">))</span>
<span class="k">do
</span><span class="nb">echo</span> <span class="s2">"==========创建渠道包,渠道号:</span><span class="nv">$channel</span><span class="s2"> ..."</span>
<span class="nb">echo</span> <span class="s2">"替换渠道配置文件 AndroidManifest.xml "</span>
<span class="nb">sed</span> <span class="nt">-ig</span> <span class="s2">"s/android</span><span class="se">\:</span><span class="s2">name=</span><span class="se">\"</span><span class="s2">channel</span><span class="se">\"</span><span class="s2"> android</span><span class="se">\:</span><span class="s2">value=</span><span class="se">\"</span><span class="s2">sxlm_</span><span class="se">\(</span><span class="s2">[0-9]*</span><span class="se">\)\"</span><span class="s2">/android</span><span class="se">\:</span><span class="s2">name=</span><span class="se">\"</span><span class="s2">channel</span><span class="se">\"</span><span class="s2"> android</span><span class="se">\:</span><span class="s2">value=</span><span class="se">\"</span><span class="s2">sxlm_</span><span class="nv">$channel</span><span class="se">\"</span><span class="s2">/g"</span> AndroidManifest.xml
<span class="nv">check</span><span class="o">=</span><span class="sb">`</span><span class="nb">grep</span> <span class="s2">"android:name=</span><span class="se">\"</span><span class="s2">channel</span><span class="se">\"</span><span class="s2"> android:value=</span><span class="se">\"</span><span class="s2">sxlm_</span><span class="nv">$channel</span><span class="se">\"</span><span class="s2">"</span> AndroidManifest.xml<span class="sb">`</span>
<span class="k">if</span> <span class="o">[[</span> <span class="nt">-z</span> <span class="nv">$check</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
</span><span class="nb">echo</span> <span class="s2">"AndroidManifest.xml 无法替换所指定的渠道"</span>
<span class="nb">exit
</span><span class="k">fi
</span><span class="nb">echo</span> <span class="s2">"替换渠道配置文件 ../scripts/app/GameConfig.lua "</span>
<span class="nb">sed</span> <span class="nt">-ig</span> <span class="s2">"s/GAME_CHANNEL=</span><span class="se">\(</span><span class="s2">[0-9]*</span><span class="se">\)</span><span class="s2">/GAME_CHANNEL=</span><span class="nv">$channel</span><span class="s2">/g"</span> ../scripts/app/GameConfig.lua
<span class="nv">check</span><span class="o">=</span><span class="sb">`</span><span class="nb">grep</span> <span class="s2">"GAME_CHANNEL=</span><span class="nv">$channel</span><span class="s2">"</span> ../scripts/app/GameConfig.lua<span class="sb">`</span>
<span class="k">if</span> <span class="o">[[</span> <span class="nt">-z</span> <span class="nv">$check</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
</span><span class="nb">echo</span> <span class="s2">"../scripts/app/GameConfig.lua 无法替换所指定的渠道"</span>
<span class="nb">exit
</span><span class="k">fi
</span><span class="nb">echo</span> <span class="s2">"编译..."</span>
./build_native.sh <span class="o">></span>/dev/null
<span class="nb">echo</span> <span class="s2">"ant 打包..."</span>
ant release <span class="o">></span>/dev/null
<span class="nb">echo</span> <span class="s2">"拷贝文件..."</span>
<span class="nb">cp </span>bin/Sxlm-release.apk <span class="s2">"</span><span class="nv">$target_dir</span><span class="s2">/</span><span class="nv">$apk_name_prefix</span><span class="s2">""_channel</span><span class="nv">$channel</span><span class="s2">.apk"</span>
<span class="k">done</span>
</code></pre></div></div>
又开始刷机取root
2015-01-20T00:00:00-06:00
http://www.zhaoxiaodan.com/android/又开始刷机取root
<p>有个项目, 需要root的手机, 手头没有, 只好找了台公司的HTC one</p>
<h2 id="1-解锁">1. 解锁</h2>
<p>音量键下+电源键 进hboot, 发现没unlocker, 直接到官网根据提示就能解锁:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>http://www.htcdev.com/bootloader/unlock-instructions
</code></pre></div></div>
<h2 id="2-刷recovery">2. 刷recovery</h2>
<p>下了个 <code class="language-plaintext highlighter-rouge">openrecovery-twrp-2.7.0.0-m7.img</code> , 手机重启到fastboot usb下,使用刷机</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ fastboot flash recovery openrecovery-twrp-2.7.0.0-m7.img
</code></pre></div></div>
<p>然后~!! 悲剧了, 进recovery黑屏, google之, 原来还需要对应国际版电信版联通版, 我的是联通双卡版, 好吧, 重新下了个 <code class="language-plaintext highlighter-rouge">openrecovery-twrp-2.8.4.0-m7cdug.img</code>, 刷进去, OK</p>
<h2 id="3-刷room">3. 刷room</h2>
<p>rom也一样, 需要联通版本… gfan找个下载, 刷进去OK</p>
Docker使用
2015-01-12T00:00:00-06:00
http://www.zhaoxiaodan.com/%E5%85%B6%E4%BB%96/docker使用
<ol>
<li>
<p>通过Docker源安装最新版本</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span><span class="nb">sudo </span>apt-get <span class="nb">install </span>apt-transport-https
<span class="nv">$ </span><span class="nb">sudo </span>apt-key adv <span class="nt">--keyserver</span> hkp://keyserver.ubuntu.com:80 <span class="nt">--recv-keys</span> 36A1D7869245C8950F966E92D8576A8BA88D21E9
<span class="nv">$ </span><span class="nb">sudo </span>bash <span class="nt">-c</span> <span class="s2">"echo deb https://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list"</span>
<span class="nv">$ </span><span class="nb">sudo </span>apt-get update
<span class="nv">$ </span><span class="nb">sudo </span>apt-get <span class="nb">install </span>lxc-docker
</code></pre></div> </div>
</li>
<li>
<p>添加用户到docker组</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span><span class="nb">sudo </span>usermod <span class="nt">-aG</span> docker liangwei
</code></pre></div> </div>
</li>
<li>
<p>阿里云Docker镜像库</p>
<p>http://help.aliyun.com/view/11108189_13857376.html?spm=0.0.0.0.I64grx</p>
</li>
<li>
<p>添加CA检查忽略:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span><span class="nb">sudo </span>vi /etc/default/docker
<span class="c">#文件中添加</span>
<span class="nv">DOCKER_OPTS</span><span class="o">=</span><span class="s2">"--insecure-registry registry.mirrors.aliyuncs.com"</span>
</code></pre></div> </div>
</li>
<li>
<p>搭建私有的registry</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span>docker run <span class="nt">-d</span> <span class="nt">-e</span> <span class="nv">STORAGE_PATH</span><span class="o">=</span>/tmp/registry <span class="nt">-v</span> /data/docker/registry:/tmp/registry <span class="nt">-p</span> 127.0.0.1:5000:5000 registry.mirrors.aliyuncs.com/library/registry
</code></pre></div> </div>
</li>
</ol>
终于到了纠结名字的时候
2014-12-13T00:00:00-06:00
http://www.zhaoxiaodan.com/%E5%85%B6%E4%BB%96/终于到了纠结名字的时候
<p>西西也快满月了, 到了该纠结取什么名字的时候. 我并不太迷信, 但是觉得中华文字博大精深, 各种组合都差不多, 很难选出一个明显优于其他组合的, 自己非常喜欢的名字. 我想既然是中国人, 可以先使用老的讲究: 五行八字. 根据这个圈定一个选字的范围</p>
<p>但是这玩意吧, 又有各种说法; 各种说法一致的是, 根据生辰查询出八字. 既然是查询出来的, 通过生辰得到八字在各种说法都是一致的. 不一致的是, 到底喜用的五行是啥, 计算方法不一样</p>
<p>一种说法简单的数八字中各五行的个数, 少的那种就是喜用的; 个人感觉这种太不像样了</p>
<p>另外一种是看月份对日子是相生还是相克;</p>
<p>首先看日干是什么五行, 也可以说这个人是什么命; 比如西西生于<code class="language-plaintext highlighter-rouge">丁酉</code>日, 日主为<code class="language-plaintext highlighter-rouge">火</code>也就是<code class="language-plaintext highlighter-rouge">火</code>命. 然后看生于哪个月,这个月的五行是有助于日主的, 还是有碍的; 西西生于<code class="language-plaintext highlighter-rouge">乙亥</code>月, 此季节水旺,木相,金休,土囚,火死; 那么对于<code class="language-plaintext highlighter-rouge">火命</code>的人是相克的. 叫做<code class="language-plaintext highlighter-rouge">日主身弱</code>, 宜补同类五行助之; 也可以理解为一个<code class="language-plaintext highlighter-rouge">火</code>命的人生于冬天, 当然是有点受不了的, 需要辅助. 那么<code class="language-plaintext highlighter-rouge">火</code>当然也就是补<code class="language-plaintext highlighter-rouge">木</code>了, 所以<code class="language-plaintext highlighter-rouge">木</code>就是最喜的五行, 当然也可以直接补<code class="language-plaintext highlighter-rouge">火</code>, <code class="language-plaintext highlighter-rouge">火</code>就是次喜的五行</p>
<p>这样就得到名字的五行最好为<code class="language-plaintext highlighter-rouge">木 - 火</code>.</p>
<p>然后马年宝宝, 宜有草字头的字; 恰好, 基本草字头的字都是<code class="language-plaintext highlighter-rouge">木</code>属性, 那中间的字就是草字头的. 比如<code class="language-plaintext highlighter-rouge">芷</code>,<code class="language-plaintext highlighter-rouge">茹</code>,<code class="language-plaintext highlighter-rouge">艺</code>; 然后还适合含有<code class="language-plaintext highlighter-rouge">辰</code>的字, 意喻<code class="language-plaintext highlighter-rouge">龙马精神</code></p>
<p>得了! 梁艺晨!</p>
<p>至于一个字, 是什么五行, 也是很多说法, 按音韵按笔画按偏旁按字义. 个人倾向于按字义.</p>
<h2 id="艺">艺</h2>
<p>根据甲骨文, 艺字的造字本义:种植草木. 则艺字五行属<code class="language-plaintext highlighter-rouge">木</code></p>
<p><img width="700px" src="http://qiniucdn.zhaoxiaodan.com/2014/20141213122038.png" /></p>
<h2 id="晨">晨</h2>
<p>字义是描述太阳的 , 五行自然为<code class="language-plaintext highlighter-rouge">火</code></p>
<p><img width="700px" src="http://qiniucdn.zhaoxiaodan.com/2014/20141213122414.png" /></p>
Userdefault在mac中找不到
2014-12-12T00:00:00-06:00
http://www.zhaoxiaodan.com/cocos2dx/UserDefault在mac中找不到
<p>现在用cocos2d-x v3 版本, 用CCUserDefault 存储一些值, 在mac电脑使用PrebuildRuntimeLua.app 进行测试的时候发现怎么都找不到 UserDefault.xml 这个文件</p>
<p>google和查询代码之后发现, v3版本 ios 和mac 版本的UserDefault 使用了 apple 原生的 NSUserDefault, 那么存储文件就变成了:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~/Library/Preferences/org.cocos2dx.PrebuiltRuntimeLua.plist
</code></pre></div></div>
<p>好了, 删掉这个文件, 重启app 发现, 还是能取到删除文件之前存进去的值, 奇葩哦!</p>
<p>再google, 原来mac还会有一个<code class="language-plaintext highlighter-rouge">UserDefault</code>服务, 临时存着这些<code class="language-plaintext highlighter-rouge">Preferences</code> 配置, 有点类似cache层一样; 当有需要读取这些配置就会每次都去读文件, 服务直接发回值; 所以清除他们就是删除文件之后还要执行命令:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>defaults read org.cocos2dx.PrebuiltRuntimeLua
</code></pre></div></div>
<p>实际上这是让<code class="language-plaintext highlighter-rouge">UserDefault</code>服务读取<code class="language-plaintext highlighter-rouge">org.cocos2dx.PrebuiltRuntimeLua</code>, 但是文件已经删掉了, 那么自然就清空了</p>
<p>如果是自己编译了app, 那么文件名和<code class="language-plaintext highlighter-rouge">key</code>就是你的<code class="language-plaintext highlighter-rouge">Bundle Identifier</code>, 例如是<code class="language-plaintext highlighter-rouge">com.example.game.helloworld</code>, 那么, 文件是:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~/Library/Preferences/com.example.game.helloworld.plist
</code></pre></div></div>
<p>重新读取的domain 是:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>defaults read com.example.game.helloworld
</code></pre></div></div>
Cocos2d X视频播放添加跳过按钮
2014-11-17T00:00:00-06:00
http://www.zhaoxiaodan.com/android/cocos2d-x视频播放添加跳过按钮
<p>之前做了个cocos2dx的视频播放扩展<a href="https://github.com/liang8305/cocos2dx_videoview_extends">cocos2dx_videoview_extends</a>, 一直都是点击屏幕就跳过视频, 这样玩家容易误操作. 一般的方法是添加一个”跳过”按钮</p>
<p>##Android
google之后发现, SurfaceView 是没法在添加一个子view的, 也就是说没办法 SurfaceView.addView(View)</p>
<p>但是我们的SurfaceView是添加到Activity 获取到的ViewGroup里的, 那我们能否添加另外一个View到这个ViewGroup里, 并且层次在SurfaceView之上呢? 经过试验是可以的.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="nc">VideoView</span> <span class="n">videoView</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">VideoView</span><span class="o">(</span><span class="n">instance</span><span class="o">);</span>
<span class="n">videoView</span><span class="o">.</span><span class="na">setLuaOnFinishCallback</span><span class="o">(</span><span class="n">luaCallback</span><span class="o">);</span>
<span class="k">try</span> <span class="o">{</span>
<span class="nc">AssetFileDescriptor</span> <span class="n">afd</span> <span class="o">=</span> <span class="n">instance</span><span class="o">.</span><span class="na">getAssets</span><span class="o">().</span><span class="na">openFd</span><span class="o">(</span>
<span class="n">name</span><span class="o">);</span>
<span class="n">videoView</span><span class="o">.</span><span class="na">setVideo</span><span class="o">(</span><span class="n">afd</span><span class="o">);</span>
<span class="kd">final</span> <span class="nc">ViewGroup</span> <span class="n">group</span> <span class="o">=</span> <span class="o">(</span><span class="nc">ViewGroup</span><span class="o">)</span> <span class="n">instance</span>
<span class="o">.</span><span class="na">getWindow</span><span class="o">().</span><span class="na">getDecorView</span><span class="o">();</span>
<span class="n">group</span><span class="o">.</span><span class="na">addView</span><span class="o">(</span><span class="n">videoView</span><span class="o">);</span>
<span class="n">videoView</span><span class="o">.</span><span class="na">setZOrderMediaOverlay</span><span class="o">(</span><span class="kc">true</span><span class="o">);</span>
<span class="nc">TextView</span> <span class="n">skipButton</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">TextView</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">appActivity</span><span class="o">);</span>
<span class="n">skipButton</span><span class="o">.</span><span class="na">setText</span><span class="o">(</span><span class="s">"跳过 >>"</span><span class="o">);</span>
<span class="n">skipButton</span><span class="o">.</span><span class="na">setTextColor</span><span class="o">(</span><span class="nc">Color</span><span class="o">.</span><span class="na">argb</span><span class="o">(</span><span class="mi">180</span><span class="o">,</span> <span class="mi">255</span><span class="o">,</span> <span class="mi">255</span><span class="o">,</span> <span class="mi">255</span><span class="o">));</span>
<span class="n">skipButton</span><span class="o">.</span><span class="na">setTextSize</span><span class="o">(</span><span class="mi">20</span><span class="o">);</span>
<span class="n">group</span><span class="o">.</span><span class="na">addView</span><span class="o">(</span><span class="n">skipButton</span><span class="o">);</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
<span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
<span class="n">videoView</span><span class="o">.</span><span class="na">onVideoFinish</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>
<p>于是重构一下, 把button添加到videoView中管理, 因为需要统一将videoView和button在播放结束时移除</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nf">VideoView</span><span class="o">(</span><span class="nc">AppActivity</span> <span class="n">appActivity</span><span class="o">)</span> <span class="o">{</span>
<span class="kd">super</span><span class="o">(</span><span class="n">appActivity</span><span class="o">);</span>
<span class="k">this</span><span class="o">.</span><span class="na">appActivity</span> <span class="o">=</span> <span class="n">appActivity</span><span class="o">;</span>
<span class="nc">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="no">TAG</span><span class="o">,</span> <span class="s">"new VideoView"</span><span class="o">);</span>
<span class="kd">final</span> <span class="nc">SurfaceHolder</span> <span class="n">holder</span> <span class="o">=</span> <span class="n">getHolder</span><span class="o">();</span>
<span class="n">holder</span><span class="o">.</span><span class="na">addCallback</span><span class="o">(</span><span class="k">this</span><span class="o">);</span>
<span class="k">this</span><span class="o">.</span><span class="na">addSkipButton</span><span class="o">();</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="kt">void</span> <span class="nf">addSkipButton</span><span class="o">(){</span>
<span class="n">skipButton</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">TextView</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">appActivity</span><span class="o">);</span>
<span class="n">skipButton</span><span class="o">.</span><span class="na">setText</span><span class="o">(</span><span class="s">"跳过 >>"</span><span class="o">);</span>
<span class="n">skipButton</span><span class="o">.</span><span class="na">setTextColor</span><span class="o">(</span><span class="nc">Color</span><span class="o">.</span><span class="na">argb</span><span class="o">(</span><span class="mi">180</span><span class="o">,</span> <span class="mi">255</span><span class="o">,</span> <span class="mi">255</span><span class="o">,</span> <span class="mi">255</span><span class="o">));</span>
<span class="n">skipButton</span><span class="o">.</span><span class="na">setTextSize</span><span class="o">(</span><span class="mi">20</span><span class="o">);</span>
<span class="n">skipButton</span><span class="o">.</span><span class="na">setOnClickListener</span><span class="o">(</span><span class="k">new</span> <span class="nc">View</span><span class="o">.</span><span class="na">OnClickListener</span><span class="o">()</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">onClick</span><span class="o">(</span><span class="nc">View</span> <span class="n">v</span><span class="o">)</span> <span class="o">{</span>
<span class="o">....</span>
<span class="o">}</span>
<span class="o">});</span>
<span class="c1">//获取ViewGroup 并添加button</span>
<span class="o">((</span><span class="nc">ViewGroup</span><span class="o">)</span> <span class="k">this</span><span class="o">.</span><span class="na">appActivity</span><span class="o">.</span><span class="na">getWindow</span><span class="o">().</span><span class="na">getDecorView</span><span class="o">()).</span><span class="na">addView</span><span class="o">(</span>
<span class="n">skipButton</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>运行代码之后奇怪了, button并没有显示出来? debug之后发现, 是因为, 原来ViewGroup先添加了videoView 再添加button, 那么button自然在videoView 之上; 修改完之后, 顺序颠倒了!</p>
<p>那么可以在surfaceCreated 回调函数添加代码:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">skipButton</span><span class="o">.</span><span class="na">bringToFront</span><span class="o">();</span>
</code></pre></div></div>
<p>还有个问题, 就是现在添加的button是在左上角, 如何做定位? 利用 LayoutParams, 查看Cocos2dxActivity源码, 发现其使用的是FrameLayout, 那么好, 利用其进行定位:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">FrameLayout</span><span class="o">.</span><span class="na">LayoutParams</span> <span class="n">params</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FrameLayout</span><span class="o">.</span><span class="na">LayoutParams</span><span class="o">(</span>
<span class="n">skipButton</span><span class="o">.</span><span class="na">getWidth</span><span class="o">(),</span> <span class="n">skipButton</span><span class="o">.</span><span class="na">getHeight</span><span class="o">());</span>
<span class="n">params</span><span class="o">.</span><span class="na">leftMargin</span> <span class="o">=</span> <span class="o">(</span><span class="kt">int</span><span class="o">)</span> <span class="o">(</span><span class="n">getWidth</span><span class="o">()</span> <span class="o">*</span> <span class="mf">0.8</span><span class="o">);</span>
<span class="n">params</span><span class="o">.</span><span class="na">topMargin</span> <span class="o">=</span> <span class="o">(</span><span class="kt">int</span><span class="o">)</span> <span class="o">(</span><span class="n">getHeight</span><span class="o">()</span> <span class="o">*</span> <span class="mf">0.86</span><span class="o">);</span>
<span class="n">skipButton</span><span class="o">.</span><span class="na">setLayoutParams</span><span class="o">(</span><span class="n">params</span><span class="o">);</span>
</code></pre></div></div>
<p>##iOS</p>
<p>iOS则简单的多, 因为播放器的view 是可以 addSubview 添加子view的, 于是:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">UIButton</span> <span class="o">*</span><span class="n">button</span> <span class="o">=</span> <span class="p">[</span><span class="n">UIButton</span> <span class="n">buttonWithType</span><span class="o">:</span><span class="n">UIButtonTypeRoundedRect</span><span class="p">];</span>
<span class="p">[</span><span class="n">button</span> <span class="n">addTarget</span><span class="o">:</span><span class="n">self</span> <span class="n">action</span><span class="o">:</span><span class="err">@</span><span class="n">selector</span><span class="p">(</span><span class="n">handleTap</span><span class="o">:</span><span class="p">)</span> <span class="n">forControlEvents</span><span class="o">:</span><span class="n">UIControlEventTouchUpInside</span><span class="p">];</span>
<span class="p">[</span><span class="n">button</span> <span class="n">setTitle</span><span class="o">:</span><span class="err">@</span><span class="s">"Skip >>"</span> <span class="n">forState</span><span class="o">:</span><span class="n">UIControlStateNormal</span><span class="p">];</span>
<span class="n">button</span><span class="p">.</span><span class="n">frame</span> <span class="o">=</span> <span class="n">CGRectMake</span><span class="p">(</span><span class="n">player</span><span class="p">.</span><span class="n">view</span><span class="p">.</span><span class="n">frame</span><span class="p">.</span><span class="n">size</span><span class="p">.</span><span class="n">width</span> <span class="o">-</span> <span class="mi">200</span><span class="p">,</span> <span class="n">player</span><span class="p">.</span><span class="n">view</span><span class="p">.</span><span class="n">frame</span><span class="p">.</span><span class="n">size</span><span class="p">.</span><span class="n">height</span> <span class="o">-</span> <span class="mi">50</span><span class="p">,</span> <span class="mi">160</span><span class="p">.</span><span class="mi">0</span><span class="p">,</span> <span class="mi">40</span><span class="p">.</span><span class="mi">0</span><span class="p">);</span>
<span class="p">[</span><span class="n">player</span><span class="p">.</span><span class="n">view</span> <span class="n">addSubview</span><span class="o">:</span><span class="n">button</span><span class="p">];</span>
</code></pre></div></div>
让你的github贡献图表变成3d
2014-11-07T00:00:00-06:00
http://www.zhaoxiaodan.com/%E5%85%B6%E4%BB%96/让你的github贡献图表变成3D
<p>在网上看到一个github页的贡献图标, 是3D的, 够炫
google了下, 是一个大神做了个chrome的插件:</p>
<blockquote>
<p><a href="https://github.com/jasonlong/isometric-contributions">isometric-contributions</a></p>
</blockquote>
<p>使用之后具体效果是这样的:</p>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2014/201411071918.png" width="400px" /></p>
<p>是够傻酷炫的了, 以后可以考虑移植一下, 做成签名档, 博客挂件什么的;</p>
Ios审核各种拒
2014-11-07T00:00:00-06:00
http://www.zhaoxiaodan.com/ios/ios审核各种拒
<p>Ios游戏审核又悲剧了, 这次是离线数据的问题, 全文:</p>
<blockquote>
<p>Reasons</p>
<p>2.23: Apps must follow the iOS Data Storage Guidelines or they will be rejected
—– 2.23 —–</p>
<p>We found that your app does not follow the iOS Data Storage Guidelines, which is required per the App Store Review Guidelines.</p>
<p>In particular, we found that on launch and/or content download, your app stores 2.29 MB. To check how much data your app is storing:</p>
<ul>
<li>Install and launch your app</li>
<li>Go to Settings > iCloud > Storage & Backup > Manage Storage</li>
<li>If necessary, tap “Show all apps”</li>
<li>Check your app’s storage</li>
</ul>
<p>The iOS Data Storage Guidelines indicate that only content that the user creates using your app, e.g., documents, new files, edits, etc., should be backed up by iCloud.</p>
<p>Temporary files used by your app should only be stored in the /tmp directory; please remember to delete the files stored in this location when the user exits the app.</p>
<p>Data that can be recreated but must persist for proper functioning of your app - or because customers expect it to be available for offline use - should be marked with the “do not back up” attribute. For NSURL objects, add the NSURLIsExcludedFromBackupKey attribute to prevent the corresponding file from being backed up. For CFURLRef objects, use the corresponding kCFURLIsExcludedFromBackupKey attribute.</p>
<p>For more information, please see Technical Q&A 1719: How do I prevent files from being backed up to iCloud and iTunes?.</p>
<p>It is necessary to revise your app to meet the requirements of the iOS Data Storage Guidelines.</p>
<p>For discrete code-level questions, you may wish to consult with Apple Developer Technical Support. When the DTS engineer follows up with you, please be ready to provide:</p>
<ul>
<li>complete details of your rejection issue(s)</li>
<li>screenshots</li>
<li>steps to reproduce the issue(s)</li>
<li>symbolicated crash logs - if your issue results in a crash log</li>
</ul>
<p>If you have difficulty reproducing a reported issue, please try testing the workflow as described in Technical Q&A QA1764: How to reproduce bugs reported against App Store submissions.</p>
</blockquote>
<p>说的很清楚了, 说的很明白了, 把离线数据加上离线的属性</p>
<p>如果在doc目录下确实有很大的文件或文件夹而又不方便移到cache目录,则可以对这个文件或文件夹调用</p>
<blockquote>
<ul>
<li>(BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL</li>
</ul>
</blockquote>
<p>这个方法在苹果的在线文档里有。</p>
Cocoside修改lua文件自动reload子模块无效
2014-11-06T00:00:00-06:00
http://www.zhaoxiaodan.com/cocos2dx/CocosIDE修改lua文件自动reload子模块无效
<p>##问题
<code class="language-plaintext highlighter-rouge">Cocos Code IDE</code>在lua开发debug的时候, 如果修改了文件会自动reload, 这个功能能方便;</p>
<p>但是如果main引用的文件A又引用了文件B, 当B修改的时候虽然触发reload, 但是B却没有生效</p>
<p>##排查
追了下runtime源码, 是<code class="language-plaintext highlighter-rouge">Cocos Code IDE</code>在保存文件<code class="language-plaintext highlighter-rouge">xxxx.lua</code>的时候会给app发送个socket命令: <code class="language-plaintext highlighter-rouge">reload xxxx.lua</code>, 并且同时<code class="language-plaintext highlighter-rouge">reload main.lua</code> ; 也就是说 引用 <code class="language-plaintext highlighter-rouge">xxxx.lua</code>的文件A并没有被reload</p>
<p>##临时解决办法</p>
<ol>
<li>
<p>引用的时候统一使用 <code class="language-plaintext highlighter-rouge">require("src/xxxx")</code> 格式</p>
</li>
<li>
<p>在main.lua 的main函数加入如下代码, 把<code class="language-plaintext highlighter-rouge">src</code>目录下的文件重新reload一遍</p>
</li>
</ol>
<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">function</span> <span class="nf">main</span><span class="p">()</span>
<span class="k">for</span> <span class="n">filename</span><span class="p">,</span><span class="n">v</span> <span class="k">in</span> <span class="nb">pairs</span><span class="p">(</span><span class="nb">package.loaded</span><span class="p">)</span> <span class="k">do</span>
<span class="k">if</span> <span class="nb">string.find</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span><span class="s2">"src/"</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span> <span class="ow">and</span> <span class="nb">string.find</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span><span class="s2">"src/main"</span><span class="p">)</span> <span class="o">~=</span> <span class="mi">1</span> <span class="k">then</span>
<span class="c1">--卸载旧文件</span>
<span class="nb">package.loaded</span><span class="p">[</span><span class="n">filename</span><span class="p">]</span> <span class="o">=</span> <span class="kc">nil</span>
<span class="nb">require</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="c1">-- source</span>
<span class="k">end</span>
</code></pre></div></div>
阿里云备案真是给力
2014-10-30T00:00:00-05:00
http://www.zhaoxiaodan.com/%E5%85%B6%E4%BB%96/阿里云备案真是给力
<p>阿里云的ecs的确很好用, 但是一直就觉得, 只要是搭建在阿里云的域名网站都需要备案, 不然就直接咔嚓阻断, 这个的确太坑了.</p>
<p>这次迫不得及做为本站的域名做备案, 体验了一把, 感觉还真是快捷方便. 需要的资料, 比如身份证正反面扫描件, 本来就有, 直接填好两个表格后上传. 再下载一个表格打印签名后拍照. 再申请个幕布自己用单反拍照, 裁成800x600 上传. 搞定…</p>
<p>主要是:足不出户!!而且大厂就是不一样, 快递的幕布过来都是顺风, 昨天填单子, 然后申请幕布, 今天收到幕布拍照上传, 当天直接就提交管局了;</p>
<p>相对国外VPS, 阿里云需要备案的确是个死肋. 阿里云自己也知道, 当然会把这个备案的体验做的方便快捷. 不然他们就没法混了</p>
<p>阿里云做到了!</p>
使用alfred Workflow来新建和管理博文
2014-10-28T00:00:00-05:00
http://www.zhaoxiaodan.com/%E5%85%B6%E4%BB%96/使用alfred-workflow来新建和管理博文
<h1 id="gp_manager">gp_manager</h1>
<p>github page manager, alfred workflow</p>
<p>##Feature</p>
<ul>
<li>new post file use default templet</li>
<li>edit post file by search keyword</li>
<li>edit templet</li>
</ul>
<p>##How to install</p>
<ol>
<li>download the last release zip: <a href="https://github.com/liang8305/gp_manager/releases/download/v1.0/gp_manager.alfredworkflow.zip">gp_manager.alfredworkflow.zip</a></li>
<li>unzip <code class="language-plaintext highlighter-rouge">gp_manager.alfredworkflow.zip</code> you will get <code class="language-plaintext highlighter-rouge">gp_manager.alfredworkflow</code></li>
<li>double click the <code class="language-plaintext highlighter-rouge">gp_manager.alfredworkflow</code> file , alfred will open and install it</li>
</ol>
<p>##How to use</p>
<p>alfred keyword is <code class="language-plaintext highlighter-rouge">gp</code></p>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2015/20150513034730.png" alt="7da330f5-e6fb-4a48-835b-e7caf2bfb001" /></p>
<p>##Edit default templet:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gp templet *search*
</code></pre></div></div>
<p>##New post file:</p>
<p>it will create new post file, filename format <code class="language-plaintext highlighter-rouge">{yyyy-mm-dd}-{title}.md</code></p>
gp new title
<p>it will create new post file in dir <code class="language-plaintext highlighter-rouge">{dirname}</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gp new dirname/title
</code></pre></div></div>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2015/20150513034807.png" alt="9c63d7ed-19e3-4cdc-b4b1-fa879d4b601d" /></p>
<p>##Edit post file</p>
<p>it will search the file</p>
gp edit <em>search</em>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2015/20150513034819.png" alt="70f992ae-3a6d-4e10-9230-074506cbbb0b" /></p>
Cocos2dx项目移植wp8小记
2014-10-10T00:00:00-05:00
http://www.zhaoxiaodan.com/cocos2dx/cocos2dx项目移植WP8小记
<p>项目开始要移植wp8了, 用vs2012编译cocos2dx出win的项目, 遇到各种坑, 记录下:</p>
<h2 id="多处理器编译">多处理器编译</h2>
<p>这个肯定是要开的, 不管linux, mac 来编ios还是android, 多核同时编肯定快</p>
<p>开启方法:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>项目属性 -> C/C++ -> 常规 -> 多处理器编译 -> 是(/MP)
</code></pre></div></div>
<h2 id="最小重新生成">最小重新生成</h2>
<p>这个东西, 就像是标准c的编译, 会判断生成的.o文件和源文件的修改时间, 如果没有修改就不会再重新编译.o;</p>
<p>开启方法:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>项目属性 -> C/C++ -> 代码生成 -> 启用最小重新生成 -> 是(/Gm)
</code></pre></div></div>
<h2 id="然后发现坑爹了">然后发现坑爹了</h2>
<p>选了(/MP) 就不能(/Gm), 怎么办? 经过测试, 可以先多处理器完全编译一次, 比如lua项目可以先多线程编一次cocos2dx的库,然后再改成(/Gm)</p>
<h2 id="error-c1033-无法打开程序数据库">error c1033 无法打开程序数据库</h2>
<p>(/Gm)的必要选项是<code class="language-plaintext highlighter-rouge">调试信息格式</code>选(/ZI), 选了编译又报错<code class="language-plaintext highlighter-rouge">error c1033 无法打开程序数据库</code>, 得了, 貌似死循环了</p>
<p>经过各种检查发现, 其实是文件系统的问题</p>
<p>我是用PB装的虚拟win7, cocos2dx项目本来是在mac系统, 共享并映射到win7里用vs2012打开的</p>
<p>结果吧, 以前mac那个盘格式化的时候选了<code class="language-plaintext highlighter-rouge">区分大小写</code>, 就悲剧了</p>
<p>重新倒腾了一遍, 把盘格式化为不区分大小写就解决了</p>
Utf8字符串在lua的截取和字数统计
2014-08-23T00:00:00-05:00
http://www.zhaoxiaodan.com/%E5%85%B6%E4%BB%96/UTF8字符串在lua的截取和字数统计
<h2 id="需求">需求</h2>
<p>按字面个数来截取</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>函数(字符串, 开始位置, 截取长度)
utf8sub("你好1世界哈哈",2,5) = 好1世界哈
utf8sub("1你好1世界哈哈",2,5) = 你好1世界
utf8sub("你好世界1哈哈",1,5) = 你好世界1
utf8sub("12345678",3,5) = 34567
utf8sub("øpø你好pix",2,5) = pø你好p
</code></pre></div></div>
<h2 id="错误方法">错误方法</h2>
<p>网上找了一些算法, 都不太正确; 要么就是乱码, 要么就是只考虑了4 byte 中文的情况, 不够全面</p>
<ol>
<li>
<p>string.sub(s,1,截取长度*4)</p>
<p>网上很多直接使用”<code class="language-plaintext highlighter-rouge">""string.sub(s,1,截取长度*4)</code>“是肯定不对的, 因为如果中英文混合的字符串, 例如<code class="language-plaintext highlighter-rouge">你好1世界</code>的字符长度分别是<code class="language-plaintext highlighter-rouge">4,4,1,4,4</code>, 如果截取4个字, 4*4=4+4+1+4+3, 那<code class="language-plaintext highlighter-rouge">世界</code>的<code class="language-plaintext highlighter-rouge">界</code>字将会被取前3个byte, 就会出现乱码</p>
</li>
<li>
<p>if byte>128 then index = index + 4</p>
</li>
</ol>
<h2 id="问题关键">问题关键</h2>
<ol>
<li>utf8字符是变长字符</li>
<li>字符长度有规律</li>
</ol>
<h2 id="utf-8字符规律">UTF-8字符规律</h2>
<p>字符串的首个byte表示了该utf8字符的长度</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0xxxxxxx - 1 byte
110yxxxx - 192, 2 byte
1110yyyy - 225, 3 byte
11110zzz - 240, 4 byte
</code></pre></div></div>
<h2 id="各种正确算法">各种正确算法</h2>
<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">-- 判断utf8字符byte长度</span>
<span class="c1">-- 0xxxxxxx - 1 byte</span>
<span class="c1">-- 110yxxxx - 192, 2 byte</span>
<span class="c1">-- 1110yyyy - 225, 3 byte</span>
<span class="c1">-- 11110zzz - 240, 4 byte</span>
<span class="kd">local</span> <span class="k">function</span> <span class="nf">chsize</span><span class="p">(</span><span class="n">char</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">char</span> <span class="k">then</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"not char"</span><span class="p">)</span>
<span class="k">return</span> <span class="mi">0</span>
<span class="k">elseif</span> <span class="n">char</span> <span class="o">></span> <span class="mi">240</span> <span class="k">then</span>
<span class="k">return</span> <span class="mi">4</span>
<span class="k">elseif</span> <span class="n">char</span> <span class="o">></span> <span class="mi">225</span> <span class="k">then</span>
<span class="k">return</span> <span class="mi">3</span>
<span class="k">elseif</span> <span class="n">char</span> <span class="o">></span> <span class="mi">192</span> <span class="k">then</span>
<span class="k">return</span> <span class="mi">2</span>
<span class="k">else</span>
<span class="k">return</span> <span class="mi">1</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="c1">-- 计算utf8字符串字符数, 各种字符都按一个字符计算</span>
<span class="c1">-- 例如utf8len("1你好") => 3</span>
<span class="k">function</span> <span class="nf">utf8len</span><span class="p">(</span><span class="n">str</span><span class="p">)</span>
<span class="kd">local</span> <span class="n">len</span> <span class="o">=</span> <span class="mi">0</span>
<span class="kd">local</span> <span class="n">currentIndex</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">while</span> <span class="n">currentIndex</span> <span class="o"><=</span> <span class="o">#</span><span class="n">str</span> <span class="k">do</span>
<span class="kd">local</span> <span class="n">char</span> <span class="o">=</span> <span class="nb">string.byte</span><span class="p">(</span><span class="n">str</span><span class="p">,</span> <span class="n">currentIndex</span><span class="p">)</span>
<span class="n">currentIndex</span> <span class="o">=</span> <span class="n">currentIndex</span> <span class="o">+</span> <span class="n">chsize</span><span class="p">(</span><span class="n">char</span><span class="p">)</span>
<span class="n">len</span> <span class="o">=</span> <span class="n">len</span> <span class="o">+</span><span class="mi">1</span>
<span class="k">end</span>
<span class="k">return</span> <span class="n">len</span>
<span class="k">end</span>
<span class="c1">-- 截取utf8 字符串</span>
<span class="c1">-- str: 要截取的字符串</span>
<span class="c1">-- startChar: 开始字符下标,从1开始</span>
<span class="c1">-- numChars: 要截取的字符长度</span>
<span class="k">function</span> <span class="nf">utf8sub</span><span class="p">(</span><span class="n">str</span><span class="p">,</span> <span class="n">startChar</span><span class="p">,</span> <span class="n">numChars</span><span class="p">)</span>
<span class="kd">local</span> <span class="n">startIndex</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">while</span> <span class="n">startChar</span> <span class="o">></span> <span class="mi">1</span> <span class="k">do</span>
<span class="kd">local</span> <span class="n">char</span> <span class="o">=</span> <span class="nb">string.byte</span><span class="p">(</span><span class="n">str</span><span class="p">,</span> <span class="n">startIndex</span><span class="p">)</span>
<span class="n">startIndex</span> <span class="o">=</span> <span class="n">startIndex</span> <span class="o">+</span> <span class="n">chsize</span><span class="p">(</span><span class="n">char</span><span class="p">)</span>
<span class="n">startChar</span> <span class="o">=</span> <span class="n">startChar</span> <span class="o">-</span> <span class="mi">1</span>
<span class="k">end</span>
<span class="kd">local</span> <span class="n">currentIndex</span> <span class="o">=</span> <span class="n">startIndex</span>
<span class="k">while</span> <span class="n">numChars</span> <span class="o">></span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">currentIndex</span> <span class="o"><=</span> <span class="o">#</span><span class="n">str</span> <span class="k">do</span>
<span class="kd">local</span> <span class="n">char</span> <span class="o">=</span> <span class="nb">string.byte</span><span class="p">(</span><span class="n">str</span><span class="p">,</span> <span class="n">currentIndex</span><span class="p">)</span>
<span class="n">currentIndex</span> <span class="o">=</span> <span class="n">currentIndex</span> <span class="o">+</span> <span class="n">chsize</span><span class="p">(</span><span class="n">char</span><span class="p">)</span>
<span class="n">numChars</span> <span class="o">=</span> <span class="n">numChars</span> <span class="o">-</span><span class="mi">1</span>
<span class="k">end</span>
<span class="k">return</span> <span class="n">str</span><span class="p">:</span><span class="n">sub</span><span class="p">(</span><span class="n">startIndex</span><span class="p">,</span> <span class="n">currentIndex</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
<span class="k">end</span>
<span class="c1">-- 自测</span>
<span class="k">function</span> <span class="nf">test</span><span class="p">()</span>
<span class="c1">-- test utf8len</span>
<span class="nb">assert</span><span class="p">(</span><span class="n">utf8len</span><span class="p">(</span><span class="s2">"你好1世界哈哈"</span><span class="p">)</span> <span class="o">==</span> <span class="mi">7</span><span class="p">)</span>
<span class="nb">assert</span><span class="p">(</span><span class="n">utf8len</span><span class="p">(</span><span class="s2">"你好世界1哈哈 "</span><span class="p">)</span> <span class="o">==</span> <span class="mi">8</span><span class="p">)</span>
<span class="nb">assert</span><span class="p">(</span><span class="n">utf8len</span><span class="p">(</span><span class="s2">" 你好世 界1哈哈"</span><span class="p">)</span> <span class="o">==</span> <span class="mi">9</span><span class="p">)</span>
<span class="nb">assert</span><span class="p">(</span><span class="n">utf8len</span><span class="p">(</span><span class="s2">"12345678"</span><span class="p">)</span> <span class="o">==</span> <span class="mi">8</span><span class="p">)</span>
<span class="nb">assert</span><span class="p">(</span><span class="n">utf8len</span><span class="p">(</span><span class="s2">"øpø你好pix"</span><span class="p">)</span> <span class="o">==</span> <span class="mi">8</span><span class="p">)</span>
<span class="c1">-- test utf8sub</span>
<span class="nb">assert</span><span class="p">(</span><span class="n">utf8sub</span><span class="p">(</span><span class="s2">"你好1世界哈哈"</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="s2">"好1世界哈"</span><span class="p">)</span>
<span class="nb">assert</span><span class="p">(</span><span class="n">utf8sub</span><span class="p">(</span><span class="s2">"1你好1世界哈哈"</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="s2">"你好1世界"</span><span class="p">)</span>
<span class="nb">assert</span><span class="p">(</span><span class="n">utf8sub</span><span class="p">(</span><span class="s2">" 你好1世界 哈哈"</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">6</span><span class="p">)</span> <span class="o">==</span> <span class="s2">"你好1世界 "</span><span class="p">)</span>
<span class="nb">assert</span><span class="p">(</span><span class="n">utf8sub</span><span class="p">(</span><span class="s2">"你好世界1哈哈"</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="s2">"你好世界1"</span><span class="p">)</span>
<span class="nb">assert</span><span class="p">(</span><span class="n">utf8sub</span><span class="p">(</span><span class="s2">"12345678"</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="s2">"34567"</span><span class="p">)</span>
<span class="nb">assert</span><span class="p">(</span><span class="n">utf8sub</span><span class="p">(</span><span class="s2">"øpø你好pix"</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="s2">"pø你好p"</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"all test succ"</span><span class="p">)</span>
<span class="k">end</span>
<span class="n">test</span><span class="p">()</span>
</code></pre></div></div>
如何配置githubpage的二级域名
2014-07-24T00:00:00-05:00
http://www.zhaoxiaodan.com/%E5%85%B6%E4%BB%96/如何配置GithubPage的二级域名
<p><img width="650px" src="http://qiniucdn.zhaoxiaodan.com/2014/20140724000000.png" alt="..." /></p>
<p>#Github Page种类</p>
<ol>
<li>
<p><code class="language-plaintext highlighter-rouge">UserPage</code>: 用户的整个站点, 这个是最出github支持的类型, 创建一个形如username.github.com的项目就可以</p>
</li>
<li>
<p><code class="language-plaintext highlighter-rouge">ProjectPage</code>: 用户创建出来的项目也可以创建站点, 创建一个项目后, 在建立一个名叫<code class="language-plaintext highlighter-rouge">gh-pages</code>的branch, 这个branch里的文件就是page的站点文件</p>
</li>
</ol>
<p>#UserPage默认域名</p>
<p>用户站点的默认域名是<code class="language-plaintext highlighter-rouge">username.github.io</code>, 比如笔者的站点就是<code class="language-plaintext highlighter-rouge">liang8305.github.io</code></p>
<p>#ProjectPage默认域名</p>
<p>项目的默认域名, 是使用UserPage域名加上二级目录实现的, 比如笔者有个项目叫<code class="language-plaintext highlighter-rouge">cydia</code>, 那么该项目的站点就是访问 <code class="language-plaintext highlighter-rouge">liang8305.github.io/cydia</code></p>
<p>#UserPage自定义域名</p>
<p>我有自己的域名, 如何绑定到UserPage? 比如用<code class="language-plaintext highlighter-rouge">www.zhaoxiaodan.com</code>替代<code class="language-plaintext highlighter-rouge">liang8305.github.io</code>他是使用CNAME技术来实现的</p>
<p>具体步骤:</p>
<ol>
<li>
<p>去域名注册商那里, 做一个<code class="language-plaintext highlighter-rouge">CNAME指向</code>, 将<code class="language-plaintext highlighter-rouge">www.zhaoxiaodan.com</code> 指向 <code class="language-plaintext highlighter-rouge">liang8305.github.io</code>,</p>
</li>
<li>
<p>在<code class="language-plaintext highlighter-rouge">liang8305/liang8305.github.com</code>这个项目(也就是page项目)根目录下建一个<code class="language-plaintext highlighter-rouge">CNAME</code>文件, 里面填写<code class="language-plaintext highlighter-rouge">www.zhaoxiaodan.com</code>, 然后提交到仓库;</p>
</li>
<li>
<p>等10分钟</p>
</li>
</ol>
<blockquote>
<p>CNAME指向之后, 当浏览器访问<code class="language-plaintext highlighter-rouge">www.zhaoxiaodan.com</code>的时候浏览器就知道<code class="language-plaintext highlighter-rouge">实际上</code>是访问<code class="language-plaintext highlighter-rouge">liang8305.github.io</code><br />
添加CNAME 文件之后, 当GithubPage服务器接收到访问<code class="language-plaintext highlighter-rouge">www.zhaoxiaodan.com</code>的http请求, 就知道, 对应的是这个工程了</p>
</blockquote>
<p>#ProjectPage自定义域名</p>
<p>比如用<code class="language-plaintext highlighter-rouge">cydia.zhaoxiaodan.com</code>替代<code class="language-plaintext highlighter-rouge">liang8305.github.io/cydia</code></p>
<ol>
<li>
<p>同样的, 去域名注册商那里, 做一个<code class="language-plaintext highlighter-rouge">CNAME指向</code>, 将<code class="language-plaintext highlighter-rouge">cydia.zhaoxiaodan.com</code> 指向 <code class="language-plaintext highlighter-rouge">liang8305.github.io</code>, 如果以后会有很多二级域名都指过来, 其实可以做一个模糊二级指过来, 比如<code class="language-plaintext highlighter-rouge">*.zhaoxiaodan.com</code></p>
</li>
<li>
<p>在<code class="language-plaintext highlighter-rouge">liang8305/cydia</code>这个项目(也就是page项目)根目录下建一个<code class="language-plaintext highlighter-rouge">CNAME</code>文件, 里面填写<code class="language-plaintext highlighter-rouge">cydia.zhaoxiaodan.com</code>, 然后提交到仓库;</p>
</li>
<li>
<p>等10分钟</p>
</li>
</ol>
Cocos2dx视频播放插件,支持lua调用
2014-07-21T00:00:00-05:00
http://www.zhaoxiaodan.com/cocos2dx/cocos2dx视频播放插件,支持lua调用
<p>###git:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://github.com/liang8305/cocos2dx_videoview_extends
</code></pre></div></div>
<p>###添加步骤:</p>
<ol>
<li>
<p>VideoView.h VideoView.cpp 放到c++ 的Classes 目录</p>
</li>
<li>
<p>修改jni 的 Android.mk 添加VideoView.cpp 编译</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> LOCAL_SRC_FILES := ... \
../../Classes/VideoView.cpp \
...
</code></pre></div> </div>
</li>
<li>
<p>AppDelegate.cpp 添加lua 扩展</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> #include "VideoView.h"
bool AppDelegate::applicationDidFinishLaunching()
{
...
auto engine = LuaEngine::getInstance();
ScriptEngineManager::getInstance()->setScriptEngine(engine);
//添加lua 扩展
lua_State* L = engine->getLuaStack()->getLuaState();
tolua_videoview_extension_open(L);
if (engine->executeScriptFile("src/main.lua")) {
return false;
}
return true;
}
</code></pre></div> </div>
</li>
<li>
<p>cn.sharedream.game.VideoView.java 放到android java src 目录</p>
</li>
<li>
<p>在lua中使用</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> -- 播放完之后的回调函数
local function videoFinish()
print("================videoFinish")
--恢复游戏, 和原来的声音
cc.Director:getInstance():startAnimation();
cc.SimpleAudioEngine:getInstance():pauseMusic();
end
-- 原游戏, 声音暂停
cc.Director:getInstance():stopAnimation();
cc.SimpleAudioEngine:getInstance():pauseMusic();
VideoView:play("res/video2.mp4",videoFinish)
</code></pre></div> </div>
</li>
</ol>
使用ds5调试取不到devices的解决办法
2014-01-20T00:00:00-06:00
http://www.zhaoxiaodan.com/android/使用DS5调试取不到Devices的解决办法
<p>##问题</p>
<p>想用DS-5来做cocos2d-x的android native app 调试, 结果设置DS-5 debugger的时候总是取不到devices;
并且看到eclipse的ERROR窗口报错”A DS-5 script encountered an error”, 点开看详情:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="code"><pre> <span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
<span class="n">File</span> <span class="s">"C:</span><span class="err">\</span><span class="s">Program Files (x86)</span><span class="err">\</span><span class="s">eclipse</span><span class="err">\</span><span class="s">plugins</span><span class="err">\</span><span class="s">com.arm.debug.configdatabase.data.community_5.17.0.20131213_202909</span><span class="err">\</span><span class="s">Boards</span><span class="err">\</span><span class="s">Android Application Debug</span><span class="err">\</span><span class="s">Native Application Library Debug</span><span class="err">\</span><span class="s">getDevices.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">46</span><span class="p">,</span> <span class="ow">in</span> <span class="o"><</span><span class="n">module</span><span class="o">></span>
<span class="n">devices</span> <span class="o">=</span> <span class="n">adb_common</span><span class="o">.</span><span class="n">get_devices</span><span class="p">()</span>
<span class="n">File</span> <span class="s">"C:</span><span class="err">\</span><span class="s">Program Files (x86)</span><span class="err">\</span><span class="s">eclipse</span><span class="err">\</span><span class="s">plugins</span><span class="err">\</span><span class="s">com.arm.debug.configdatabase.data.community_5.17.0.20131213_202909</span><span class="err">\</span><span class="s">Boards</span><span class="err">\</span><span class="s">Android Application Debug</span><span class="err">\</span><span class="s">Native Application Library Debug</span><span class="se">\a</span><span class="s">db_common.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">125</span><span class="p">,</span> <span class="ow">in</span> <span class="n">get_devices</span>
<span class="n">adb_start_server</span><span class="p">()</span>
<span class="n">File</span> <span class="s">"C:</span><span class="err">\</span><span class="s">Program Files (x86)</span><span class="err">\</span><span class="s">eclipse</span><span class="err">\</span><span class="s">plugins</span><span class="err">\</span><span class="s">com.arm.debug.configdatabase.data.community_5.17.0.20131213_202909</span><span class="err">\</span><span class="s">Boards</span><span class="err">\</span><span class="s">Android Application Debug</span><span class="err">\</span><span class="s">Native Application Library Debug</span><span class="se">\a</span><span class="s">db_common.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">111</span><span class="p">,</span> <span class="ow">in</span> <span class="n">adb_start_server</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">subprocess</span><span class="o">.</span><span class="n">Popen</span><span class="p">(</span><span class="s">"adb start-server"</span><span class="p">,</span> <span class="n">shell</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">stdin</span><span class="o">=</span><span class="n">PIPE</span><span class="p">,</span> <span class="n">stdout</span><span class="o">=</span><span class="n">PIPE</span><span class="p">,</span> <span class="n">stderr</span><span class="o">=</span><span class="n">PIPE</span><span class="p">)</span>
<span class="n">File</span> <span class="s">"__pyclasspath__/Lib/subprocess.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">751</span><span class="p">,</span> <span class="ow">in</span> <span class="n">__init__</span>
<span class="n">File</span> <span class="s">"__pyclasspath__/Lib/subprocess.py"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1236</span><span class="p">,</span> <span class="ow">in</span> <span class="n">_execute_child</span>
<span class="nb">TypeError</span><span class="p">:</span> <span class="n">unsupported</span> <span class="n">operand</span> <span class="nb">type</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="k">for</span> <span class="o">+</span><span class="p">:</span> <span class="s">'NoneType'</span> <span class="ow">and</span> <span class="s">'list'</span>
<span class="n">at</span> <span class="n">org</span><span class="o">.</span><span class="n">python</span><span class="o">.</span><span class="n">core</span><span class="o">.</span><span class="n">PyException</span><span class="o">.</span><span class="n">fillInStackTrace</span><span class="p">(</span><span class="n">PyException</span><span class="o">.</span><span class="n">java</span><span class="p">:</span><span class="mi">70</span><span class="p">)</span>
<span class="n">at</span> <span class="n">java</span><span class="o">.</span><span class="n">lang</span><span class="o">.</span><span class="n">Throwable</span><span class="o">.<</span><span class="n">init</span><span class="o">></span><span class="p">(</span><span class="n">Throwable</span><span class="o">.</span><span class="n">java</span><span class="p">:</span><span class="mi">181</span><span class="p">)</span>
<span class="n">at</span> <span class="n">java</span><span class="o">.</span><span class="n">lang</span><span class="o">.</span><span class="nb">Exception</span><span class="o">.<</span><span class="n">init</span><span class="o">></span><span class="p">(</span><span class="nb">Exception</span><span class="o">.</span><span class="n">java</span><span class="p">:</span><span class="mi">29</span><span class="p">)</span>
<span class="n">at</span> <span class="n">java</span><span class="o">.</span><span class="n">lang</span><span class="o">.</span><span class="n">RuntimeException</span><span class="o">.<</span><span class="n">init</span><span class="o">></span><span class="p">(</span><span class="n">RuntimeException</span><span class="o">.</span><span class="n">java</span><span class="p">:</span><span class="mi">32</span><span class="p">)</span>
<span class="n">at</span> <span class="n">org</span><span class="o">.</span><span class="n">python</span><span class="o">.</span><span class="n">core</span><span class="o">.</span><span class="n">PyException</span><span class="o">.<</span><span class="n">init</span><span class="o">></span><span class="p">(</span><span class="n">PyException</span><span class="o">.</span><span class="n">java</span><span class="p">:</span><span class="mi">46</span><span class="p">)</span>
<span class="n">at</span> <span class="n">org</span><span class="o">.</span><span class="n">python</span><span class="o">.</span><span class="n">core</span><span class="o">.</span><span class="n">PyException</span><span class="o">.<</span><span class="n">init</span><span class="o">></span><span class="p">(</span><span class="n">PyException</span><span class="o">.</span><span class="n">java</span><span class="p">:</span><span class="mi">43</span><span class="p">)</span>
<span class="o">.....</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>总之就是subprocess.py里有null point了;</p>
<p>##解决办法:</p>
<p>修改”EXPLISE_HOME\plugins\com.arm.debug.configdatabase.data.community_5.17.0.20131213_202909\Boards\Android Application Debug\Native Application Library Debug\adb_common.py”文件
形如</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>subprocess.Popen("adb start-server", shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
</code></pre></div></div>
<p>的都要修改为</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>subprocess.Popen("adb start-server", shell=False, stdin=PIPE, stdout=PIPE, stderr=PIPE)
</code></pre></div></div>
Android纯原生nativeactivity机制解析
2013-12-02T00:00:00-06:00
http://www.zhaoxiaodan.com/android/Android纯原生NativeActivity机制解析
<p>##NativeActivity 机制</p>
<p>刚使用cocos2dx V3的最大感受就是没有 Cocos2dxActivity 这个java了…为啥? 就是cocos2dx V3 是真正的Native了;</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="code"><pre><span class="kd">public</span> <span class="kd">class</span> <span class="nc">testgame</span> <span class="kd">extends</span> <span class="nc">Cocos2dxActivity</span> <span class="o">{</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">onCreate</span><span class="o">(</span><span class="nc">Bundle</span> <span class="n">savedInstanceState</span><span class="o">)</span> <span class="o">{</span>
<span class="kd">super</span><span class="o">.</span><span class="na">onCreate</span><span class="o">(</span><span class="n">savedInstanceState</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="nc">Cocos2dxGLSurfaceView</span> <span class="nf">onCreateView</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nf">LuaGLSurfaceView</span><span class="o">(</span><span class="k">this</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">static</span> <span class="o">{</span>
<span class="nc">System</span><span class="o">.</span><span class="na">loadLibrary</span><span class="o">(</span><span class="s">"cocos2dlua"</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>相对的, 是android原来编写应用的方式: JavaActivity; <br />
而NativeActivity是Android SDK提供的编写Native应用的辅助类, 它帮助管理Android 框架和应用之间的事件通信; <br />
查看 $NDK_ROOT/platforms/android-14/arch-x86/usr/include/android/native_activity.h,这个是NativeActivity的机制说明</p>
<!-- more -->
<p>要编写一个NativeActivity, 你可以使用最原始的方法:</p>
<ol>
<li>
<p>实现NativeActivity创建和注册方法:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>typedef void ANativeActivity_createFunc(ANativeActivity* activity,
void* savedState, size_t savedStateSize);
//默认的ANativeActivity_createFunc
extern ANativeActivity_createFunc ANativeActivity_onCreate;
</code></pre></div> </div>
</li>
<li>
<p>设置callback</p>
</li>
</ol>
<figure class="highlight"><pre><code class="language-cpp" data-lang="cpp"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
</pre></td><td class="code"><pre><span class="k">typedef</span> <span class="k">struct</span> <span class="n">ANativeActivity</span> <span class="p">{</span>
<span class="k">struct</span> <span class="n">ANativeActivityCallbacks</span><span class="o">*</span> <span class="n">callbacks</span><span class="p">;</span>
<span class="p">......</span>
<span class="p">}</span> <span class="n">ANativeActivity</span><span class="p">;</span>
<span class="k">typedef</span> <span class="k">struct</span> <span class="n">ANativeActivityCallbacks</span> <span class="p">{</span>
<span class="cm">/**
* 当NativeActivity 启动时回调. 就像原来 JAVAActivity.onStart()一样
*/</span>
<span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">onStart</span><span class="p">)(</span><span class="n">ANativeActivity</span><span class="o">*</span> <span class="n">activity</span><span class="p">);</span>
<span class="c1">//类似的还有</span>
<span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">onResume</span><span class="p">)(</span><span class="n">ANativeActivity</span><span class="o">*</span> <span class="n">activity</span><span class="p">);</span>
<span class="kt">void</span><span class="o">*</span> <span class="p">(</span><span class="o">*</span><span class="n">onSaveInstanceState</span><span class="p">)(</span><span class="n">ANativeActivity</span><span class="o">*</span> <span class="n">activity</span><span class="p">,</span> <span class="kt">size_t</span><span class="o">*</span> <span class="n">outSize</span><span class="p">);</span>
<span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">onPause</span><span class="p">)(</span><span class="n">ANativeActivity</span><span class="o">*</span> <span class="n">activity</span><span class="p">);</span>
<span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">onStop</span><span class="p">)(</span><span class="n">ANativeActivity</span><span class="o">*</span> <span class="n">activity</span><span class="p">);</span>
<span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">onDestroy</span><span class="p">)(</span><span class="n">ANativeActivity</span><span class="o">*</span> <span class="n">activity</span><span class="p">);</span>
<span class="p">......</span>
<span class="p">}</span> <span class="n">ANativeActivityCallbacks</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>一个简单的 NativeActivity就像这样:</p>
<figure class="highlight"><pre><code class="language-cpp" data-lang="cpp"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="code"><pre><span class="k">static</span> <span class="kt">void</span> <span class="nf">onStart</span><span class="p">(</span><span class="n">ANativeActivity</span><span class="o">*</span> <span class="n">activity</span><span class="p">)</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"hello world"</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">static</span> <span class="kt">void</span> <span class="nf">onDestroy</span><span class="p">(</span><span class="n">ANativeActivity</span><span class="o">*</span> <span class="n">activity</span><span class="p">)</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"bye..."</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">//实现入口点函数, 就像main一样</span>
<span class="kt">void</span> <span class="nf">ANativeActivity_onCreate</span><span class="p">(</span><span class="n">ANativeActivity</span><span class="o">*</span> <span class="n">activity</span><span class="p">,</span><span class="kt">void</span><span class="o">*</span> <span class="n">savedState</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">savedStateSize</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">//实现设置回调函数</span>
<span class="n">activity</span><span class="o">-></span><span class="n">callbacks</span><span class="o">-></span><span class="n">onDestroy</span> <span class="o">=</span> <span class="n">onDestroy</span><span class="p">;</span>
<span class="n">activity</span><span class="o">-></span><span class="n">callbacks</span><span class="o">-></span><span class="n">onStart</span> <span class="o">=</span> <span class="n">onStart</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>就什么简单: ANativeActivity_onCreate就相当于main函数, 然后作为app它有生命周期和,你就需要监听一下生命周期事件, 完事!</p>
<p>##android_native_app_glue
编写Native应用有两种方式,</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1. 自己实现native_activity.h中的回调函数;
2. 利用android_native_app_glue
</code></pre></div></div>
<p>android_native_app_glue它将input事件单独起了一个线程,将实现使用pipe管道进行流水处理, 避免事件处理造成的ui hang住 <br />
cocos2dx就是使用第二种</p>
<p>$NDK_ROOT/sources/android/native_app_glue/android_native_app_glue.c</p>
<figure class="highlight"><pre><code class="language-cpp" data-lang="cpp"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
</pre></td><td class="code"><pre><span class="c1">//实现入口点回调函数</span>
<span class="kt">void</span> <span class="nf">ANativeActivity_onCreate</span><span class="p">(</span><span class="n">ANativeActivity</span><span class="o">*</span> <span class="n">activity</span><span class="p">,</span>
<span class="kt">void</span><span class="o">*</span> <span class="n">savedState</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">savedStateSize</span><span class="p">)</span> <span class="p">{</span>
<span class="n">LOGV</span><span class="p">(</span><span class="s">"Creating: %p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">activity</span><span class="p">);</span>
<span class="c1">//设置activity回调函数</span>
<span class="n">activity</span><span class="o">-></span><span class="n">callbacks</span><span class="o">-></span><span class="n">onDestroy</span> <span class="o">=</span> <span class="n">onDestroy</span><span class="p">;</span>
<span class="n">activity</span><span class="o">-></span><span class="n">callbacks</span><span class="o">-></span><span class="n">onStart</span> <span class="o">=</span> <span class="n">onStart</span><span class="p">;</span>
<span class="p">......</span>
<span class="n">activity</span><span class="o">-></span><span class="n">callbacks</span><span class="o">-></span><span class="n">onInputQueueCreated</span> <span class="o">=</span> <span class="n">onInputQueueCreated</span><span class="p">;</span>
<span class="n">activity</span><span class="o">-></span><span class="n">callbacks</span><span class="o">-></span><span class="n">onInputQueueDestroyed</span> <span class="o">=</span> <span class="n">onInputQueueDestroyed</span><span class="p">;</span>
<span class="c1">//生成android_app</span>
<span class="n">activity</span><span class="o">-></span><span class="n">instance</span> <span class="o">=</span> <span class="n">android_app_create</span><span class="p">(</span><span class="n">activity</span><span class="p">,</span> <span class="n">savedState</span><span class="p">,</span> <span class="n">savedStateSize</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">static</span> <span class="k">struct</span> <span class="n">android_app</span><span class="o">*</span> <span class="nf">android_app_create</span><span class="p">(</span><span class="n">ANativeActivity</span><span class="o">*</span> <span class="n">activity</span><span class="p">,</span>
<span class="kt">void</span><span class="o">*</span> <span class="n">savedState</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">savedStateSize</span><span class="p">)</span> <span class="p">{</span>
<span class="p">......</span>
<span class="c1">//声明事件管道</span>
<span class="n">android_app</span><span class="o">-></span><span class="n">msgread</span> <span class="o">=</span> <span class="n">msgpipe</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="n">android_app</span><span class="o">-></span><span class="n">msgwrite</span> <span class="o">=</span> <span class="n">msgpipe</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
<span class="c1">//在新线程中让app_entry</span>
<span class="n">pthread_attr_t</span> <span class="n">attr</span><span class="p">;</span>
<span class="n">pthread_attr_init</span><span class="p">(</span><span class="o">&</span><span class="n">attr</span><span class="p">);</span>
<span class="n">pthread_attr_setdetachstate</span><span class="p">(</span><span class="o">&</span><span class="n">attr</span><span class="p">,</span> <span class="n">PTHREAD_CREATE_DETACHED</span><span class="p">);</span>
<span class="n">pthread_create</span><span class="p">(</span><span class="o">&</span><span class="n">android_app</span><span class="o">-></span><span class="kr">thread</span><span class="p">,</span> <span class="o">&</span><span class="n">attr</span><span class="p">,</span> <span class="n">android_app_entry</span><span class="p">,</span> <span class="n">android_app</span><span class="p">);</span>
<span class="p">......</span>
<span class="p">}</span>
<span class="k">static</span> <span class="kt">void</span><span class="o">*</span> <span class="nf">android_app_entry</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">param</span><span class="p">)</span> <span class="p">{</span>
<span class="k">struct</span> <span class="n">android_app</span><span class="o">*</span> <span class="n">android_app</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">android_app</span><span class="o">*</span><span class="p">)</span><span class="n">param</span><span class="p">;</span>
<span class="n">android_app</span><span class="o">-></span><span class="n">config</span> <span class="o">=</span> <span class="n">AConfiguration_new</span><span class="p">();</span>
<span class="n">AConfiguration_fromAssetManager</span><span class="p">(</span><span class="n">android_app</span><span class="o">-></span><span class="n">config</span><span class="p">,</span> <span class="n">android_app</span><span class="o">-></span><span class="n">activity</span><span class="o">-></span><span class="n">assetManager</span><span class="p">);</span>
<span class="n">print_cur_config</span><span class="p">(</span><span class="n">android_app</span><span class="p">);</span>
<span class="c1">//设置消息"循环器"来源</span>
<span class="n">android_app</span><span class="o">-></span><span class="n">cmdPollSource</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">LOOPER_ID_MAIN</span><span class="p">;</span>
<span class="n">android_app</span><span class="o">-></span><span class="n">cmdPollSource</span><span class="p">.</span><span class="n">app</span> <span class="o">=</span> <span class="n">android_app</span><span class="p">;</span>
<span class="c1">//设置消息辅助类的事件处理回调</span>
<span class="n">android_app</span><span class="o">-></span><span class="n">cmdPollSource</span><span class="p">.</span><span class="n">process</span> <span class="o">=</span> <span class="n">process_cmd</span><span class="p">;</span>
<span class="cm">/**
* 这里使用了ALooper辅助类, 实现源码暂时找不到, 推测就是会监听刚才声明的`事件管道`的读取端
* 当有消息来的时候就调用cmdPollSource.process指定的回调函数process_cmd
*/</span>
<span class="n">ALooper</span><span class="o">*</span> <span class="n">looper</span> <span class="o">=</span> <span class="n">ALooper_prepare</span><span class="p">(</span><span class="n">ALOOPER_PREPARE_ALLOW_NON_CALLBACKS</span><span class="p">);</span>
<span class="n">ALooper_addFd</span><span class="p">(</span><span class="n">looper</span><span class="p">,</span> <span class="n">android_app</span><span class="o">-></span><span class="n">msgread</span><span class="p">,</span> <span class="n">LOOPER_ID_MAIN</span><span class="p">,</span> <span class="n">ALOOPER_EVENT_INPUT</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span>
<span class="o">&</span><span class="n">android_app</span><span class="o">-></span><span class="n">cmdPollSource</span><span class="p">);</span>
<span class="n">android_app</span><span class="o">-></span><span class="n">looper</span> <span class="o">=</span> <span class="n">looper</span><span class="p">;</span>
<span class="c1">//给个标志给主线程, 让主线程知道我们已经初始化完毕, 可以继续下一步了</span>
<span class="n">pthread_mutex_lock</span><span class="p">(</span><span class="o">&</span><span class="n">android_app</span><span class="o">-></span><span class="n">mutex</span><span class="p">);</span>
<span class="n">android_app</span><span class="o">-></span><span class="n">running</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="n">pthread_cond_broadcast</span><span class="p">(</span><span class="o">&</span><span class="n">android_app</span><span class="o">-></span><span class="n">cond</span><span class="p">);</span>
<span class="n">pthread_mutex_unlock</span><span class="p">(</span><span class="o">&</span><span class="n">android_app</span><span class="o">-></span><span class="n">mutex</span><span class="p">);</span>
<span class="c1">//调用app实现的android_main</span>
<span class="n">android_main</span><span class="p">(</span><span class="n">android_app</span><span class="p">);</span>
<span class="n">android_app_destroy</span><span class="p">(</span><span class="n">android_app</span><span class="p">);</span>
<span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">static</span> <span class="kt">void</span> <span class="nf">process_cmd</span><span class="p">(</span><span class="k">struct</span> <span class="n">android_app</span><span class="o">*</span> <span class="n">app</span><span class="p">,</span> <span class="k">struct</span> <span class="n">android_poll_source</span><span class="o">*</span> <span class="n">source</span><span class="p">)</span> <span class="p">{</span>
<span class="kt">int8_t</span> <span class="n">cmd</span> <span class="o">=</span> <span class="n">android_app_read_cmd</span><span class="p">(</span><span class="n">app</span><span class="p">);</span>
<span class="n">android_app_pre_exec_cmd</span><span class="p">(</span><span class="n">app</span><span class="p">,</span> <span class="n">cmd</span><span class="p">);</span>
<span class="c1">//调用app实现的onAppCmd</span>
<span class="k">if</span> <span class="p">(</span><span class="n">app</span><span class="o">-></span><span class="n">onAppCmd</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="n">app</span><span class="o">-></span><span class="n">onAppCmd</span><span class="p">(</span><span class="n">app</span><span class="p">,</span> <span class="n">cmd</span><span class="p">);</span>
<span class="n">android_app_post_exec_cmd</span><span class="p">(</span><span class="n">app</span><span class="p">,</span> <span class="n">cmd</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>##为什么</p>
<p>按照原来Java Activity的方式, JavaActivity里去load一下jni的.so库,然后JavaActivity.onCreate()里再调用jni里的onCreate()之类的东西; <br />
然后JavaActivity.onTouchXXX 什么的时间都要通过jni转一下, 编写很麻烦, 对于jni这种中转依赖的很重; <br />
而使用NativeActivity, 就完全是Native的; 所有onXXX()都是直接C++里实现就好;</p>
Cocos2dx源码阅读 启动流程
2013-11-30T00:00:00-06:00
http://www.zhaoxiaodan.com/cocos2dx/cocos2dx源码阅读-启动流程
<p>##我对名词的定义</p>
<ol>
<li>app: 系统层面的应用,例如相对android就是整个一个apk程序</li>
<li>引擎: cocos2dx</li>
<li>porject: cocos2dx 里的一个project</li>
</ol>
<p>##Android</p>
<ol>
<li>
<p>AndroidManifest.xml 指定启动Activity为 <code class="language-plaintext highlighter-rouge">android.app.NativeActivity</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><activity android:name="android.app.NativeActivity"
android:label="@string/app_name"
android:screenOrientation="landscape"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:configChanges="orientation|screenSize|smallestScreenSize">
//设定回调的.so库
<meta-data android:name="android.app.lib_name"
android:value="cocos2dcpp" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</code></pre></div> </div>
</li>
<li>
<p>NativeActivity机制约定,Activity启动后会找AndroidManifest.xml文件中的<code class="language-plaintext highlighter-rouge">android.app.lib_name</code>配置项设定的(在cocos2d引擎就是是<code class="language-plaintext highlighter-rouge">cocos2dcpp</code>)这个.so,
并回调这个.so实现的<code class="language-plaintext highlighter-rouge">android_main</code> 函数;将控制权交回给APP应用 <a href="/2013-12-02-Android纯原生NativeActivity机制解析.html]">更多相关阅读</a></p>
</li>
<li>
<p>引擎在 <code class="language-plaintext highlighter-rouge">$COCOS2DX_ROOT/cocos/2d/platform/android/nativeactivity.cpp</code> 实现android_main回调函数,此时程序进入引擎层 <br />
引擎做一系列初始化,监听AppCmd事件, 当事件为<code class="language-plaintext highlighter-rouge">APP_CMD_INIT_WINDOW</code>时回调project实现的 <code class="language-plaintext highlighter-rouge">cocos_android_app_init</code>, 让porject(cocos2d范围的应用)得以初始化;</p>
</li>
</ol>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">android_main</span><span class="o">(</span><span class="n">struct</span> <span class="n">android_app</span><span class="o">*</span> <span class="n">state</span><span class="o">)</span> <span class="o">{</span>
<span class="o">......</span>
<span class="c1">//设置app事件回调函数</span>
<span class="n">state</span><span class="o">-></span><span class="n">onAppCmd</span> <span class="o">=</span> <span class="n">engine_handle_cmd</span><span class="o">;</span>
<span class="c1">//设置input事件回调函数</span>
<span class="n">state</span><span class="o">-></span><span class="n">onInputEvent</span> <span class="o">=</span> <span class="n">engine_handle_input</span><span class="o">;</span>
<span class="n">engine</span><span class="o">.</span><span class="na">app</span> <span class="o">=</span> <span class="n">state</span><span class="o">;</span>
<span class="c1">// Prepare to monitor accelerometer</span>
<span class="n">engine</span><span class="o">.</span><span class="na">sensorManager</span> <span class="o">=</span> <span class="n">ASensorManager_getInstance</span><span class="o">();</span>
<span class="o">......</span>
<span class="o">}</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> static void engine_handle_cmd(struct android_app* app, int32_t cmd) {
struct engine* engine = (struct engine*)app->userData;
switch (cmd) {
case APP_CMD_SAVE_STATE:
......
case APP_CMD_INIT_WINDOW: //程序初始化
if (engine->app->window != NULL) {
cocos_dimensions d = engine_init_display(engine);
if ((d.w > 0) &&
(d.h > 0)) {
......
// 初始化事件, 进行引擎初始化
cocos_init(d, app);
}
}
break;
}
}
static void cocos_init(cocos_dimensions d, struct android_app* app) {
......
if (!cocos2d::Director::getInstance()->getOpenGLView())
{
cocos2d::EGLView *view = cocos2d::EGLView::getInstance();
view->setFrameSize(d.w, d.h);
//调用project实现的初始化方法,也就说,project实现cocos2d::Application , 见下节点
cocos_android_app_init(app);
//引擎run, 也就是project run
cocos2d::Application::getInstance()->run();
}
......
}
</code></pre></div></div>
<ol>
<li>
<p>游戏目录的<code class="language-plaintext highlighter-rouge">jni/helloxxx/main.cpp</code> 实现 cocos_android_app_init, 生成 AppDelegate</p>
<p>void cocos_android_app_init (struct android_app* app) {</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> /**
* AppDelegate 继承自cocos2d::Application, cocos2dx里很多地方使用的都是'单例'模式;
* 上面的 cocos2d::Application::getInstance()->run(); AppDelegate也就run起来了
*/
AppDelegate *pAppDelegate = new AppDelegate();
</code></pre></div> </div>
<p>}</p>
</li>
<li>
<p>$PROJECT/Classes目录下, AppDelegate 也是初始化一遍, 将scence场景 交给Director导演进行run</p>
<p>bool AppDelegate::applicationDidFinishLaunching() {</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ......
// 生成我自己的场景
auto scene = HelloWorld::createScene();
// 让框架帮我把场景跑(管理)起来;
director->runWithScene(scene);
</code></pre></div> </div>
<p>}</p>
</li>
<li>
<p>这个时候,也就进入到了project的场景当中</p>
</li>
</ol>
使用systemtap检查php代码
2013-08-08T00:00:00-05:00
http://www.zhaoxiaodan.com/lnmp/使用systemtap检查php代码
<h1 id="需要带debug的php">需要带debug的php</h1>
<ul>
<li>
<p>根据上一篇文章安装好systemtap, 并补装DTrace support:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> sudo apt-get install systemtap-sdt-dev
</code></pre></div> </div>
</li>
<li>
<p>看下ubuntu 当前的php是哪个版本, 方便装php扩展, 我的是5.4.9, 到时候down源码也down这个版本的源码;</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> $ sudo apt-get install php5-cli
$ php -v
PHP 5.4.9-4ubuntu2.2 (cli) (built: Jul 15 2013 18:23:35)
Copyright (c) 1997-2012 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2012 Zend Technologies
</code></pre></div> </div>
</li>
<li>
<p>下载php源码并编译之, configure的php.ini直接使用ubuntu自带的:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> $ git clone git://github.com/php/php-src php-src
$ cd php-src
$ git checkout PHP-5.4
//或者 到 https://github.com/php/php-src/tree/PHP-5.4 下载 zip包
$ ./buildconf --force
$ ./configure --disable-all --enable-dtrace --with-config-file-path=/etc/php5/cli --with-config-file-scan-dir=/etc/php5/cli/conf.d/
$ make
</code></pre></div> </div>
</li>
<li>
<p>尝试一下并看下php-cli都有哪些函数</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> $stap -l 'process.provider("*").mark("*")' -c '/media/win_f/php-src/sapi/cli/php -i'
process("/media/win_f/php-src/sapi/cli/php").provider("php").mark("compile__file__entry")
process("/media/win_f/php-src/sapi/cli/php").provider("php").mark("compile__file__return")
process("/media/win_f/php-src/sapi/cli/php").provider("php").mark("error")
process("/media/win_f/php-src/sapi/cli/php").provider("php").mark("exception__caught")
process("/media/win_f/php-src/sapi/cli/php").provider("php").mark("exception__thrown")
process("/media/win_f/php-src/sapi/cli/php").provider("php").mark("execute__entry")
process("/media/win_f/php-src/sapi/cli/php").provider("php").mark("execute__return")
process("/media/win_f/php-src/sapi/cli/php").provider("php").mark("function__entry")
process("/media/win_f/php-src/sapi/cli/php").provider("php").mark("function__return")
process("/media/win_f/php-src/sapi/cli/php").provider("php").mark("request__shutdown")
process("/media/win_f/php-src/sapi/cli/php").provider("php").mark("request__startup")
</code></pre></div> </div>
</li>
</ul>
Ubuntu13.04安装使用systemtap
2013-08-08T00:00:00-05:00
http://www.zhaoxiaodan.com/lnmp/ubuntu13.04安装使用systemtap
<h1 id="添加包含dbgsym版本包的源">添加包含dbgsym版本包的源:</h1>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> 为12.04添加apt-get源:
deb http://ddebs.ubuntu.com/ precise main restricted universe multiverse
deb http://ddebs.ubuntu.com/ precise-security main restricted universe multiverse
deb http://ddebs.ubuntu.com/ precise-updates main restricted universe multiverse
deb http://ddebs.ubuntu.com/ precise-proposed main restricted universe multiverse
为12.10添加apt-get源:
deb http://ddebs.ubuntu.com/ quantal main restricted universe multiverse
deb http://ddebs.ubuntu.com/ quantal-security main restricted universe multiverse
deb http://ddebs.ubuntu.com/ quantal-updates main restricted universe multiverse
deb http://ddebs.ubuntu.com/ quantal-proposed main restricted universe multiverse
为13.04添加apt-get源:
sudo tee /etc/apt/sources.list.d/ddebs.list << EOF
deb http://ddebs.ubuntu.com/ raring main restricted universe multiverse
deb http://ddebs.ubuntu.com/ raring-security main restricted universe multiverse
deb http://ddebs.ubuntu.com/ raring-updates main restricted universe multiverse
deb http://ddebs.ubuntu.com/ raring-proposed main restricted universe multiverse
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys ECDCAD72428D7C01
sudo apt-get update
</code></pre></div></div>
<h1 id="安装内核">安装内核</h1>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> sudo apt-get install linux-image-$(uname -r)-dbgsym
</code></pre></div></div>
<h1 id="安装systemtap">安装systemtap</h1>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt-get install systemtap
</code></pre></div></div>
<h1 id="使用">使用</h1>
<ul>
<li>
<p>准备一个简单的c程序:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> void hello(int r)
{
int haha = 100;
printf("kljasldjfksajdkfjds\n");
}
int main(int argc,void* argv)
{
while(1){
hello(23423423);
sleep(1);
}
exit(0);
}
</code></pre></div> </div>
</li>
<li>
<p>带debug 编译:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> gcc -g hello.c
</code></pre></div> </div>
</li>
<li>
<p>运行:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> stap -e 'probe process("a.out").function("*")
{
printf("%s(%d) %s(%s)\n", execname(), pid(),ppfunc(), $$parms$$)
}' -c '/home/liangwei/a.out'
</code></pre></div> </div>
</li>
<li>
<p>结果:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> kljasldjfksajdkfjds
a.out(23959) main(argc=0x0 argv=0x7fff5b4ed450)
a.out(23959) hello(r=0x0)
kljasldjfksajdkfjds
a.out(23959) hello(r=0x0)
kljasldjfksajdkfjds
a.out(23959) hello(r=0x16569bf)
kljasldjfksajdkfjds
a.out(23959) hello(r=0x16569bf)
kljasldjfksajdkfjds
a.out(23959) hello(r=0x16569bf)
kljasldjfksajdkfjds
a.out(23959) hello(r=0x16569bf)
kljasldjfksajdkfjds
a.out(23959) hello(r=0x16569bf)
kljasldjfksajdkfjds
a.out(23959) hello(r=0x16569bf)
</code></pre></div> </div>
</li>
</ul>
Linux网络编程练习及各种io模型性能测试
2013-07-26T00:00:00-05:00
http://www.zhaoxiaodan.com/lnmp/linux网络编程练习及各种IO模型性能测试
<h1 id="git地址">GIT地址</h1>
<p><a href="https://github.com/liang8305/network_performance_test">https://github.com/liang8305/network_performance_test</a></p>
<h1 id="block-io">Block IO</h1>
<p>首先使用最基本的阻塞IO来写;
运行 block_io_server 和 block_io_client</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>...
conn:1016
conn:1017
conn:1018
conn:1019
conn:1020
Server Accept Failed!
</code></pre></div></div>
<p>每次都是到1020个连接就没办法再增加了! 1020? 1024? 马上想到是打开文件数限制,于是增加到65500;</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo vim /etc/security/limits.conf
文件尾追加
* hard nofile 65500
* soft nofile 65500
</code></pre></div></div>
<p>根据limits.conf 的注释, 追加的4个参数的含义分别为:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Each line describes a limit for a user in the form:
<domain> <type> <item> <value>
<domain>: 可以是用户,也可以是组,要用@group这样的语法,也可以是通配符如*%
- an user name
- a group name, with @group syntax
- the wildcard *, for default entry
- the wildcard %, can be also used with %group syntax,
for maxlogin limit
- NOTE: group and wildcard limits are not applied to root.
To apply a limit to the root user, <domain> must be
the literal username root.
<type> : - "hard" for enforcing hard limits
<item>: 第三列, 设置的项目, nofile 为 最大打开文件数
- core - limits the core file size (KB)
- nofile - max number of open files
...
</code></pre></div></div>
<p>重启系统后验证, 显示65500 则设置成功;</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>%ulimit -n
65500
</code></pre></div></div>
<p>之后运行, 基本做到10000个连接; server就挂掉了 <br />
不过现在是单机连单机的情况;</p>
<h1 id="发现的问题-创建线程的坑">发现的问题: 创建线程的坑</h1>
<p>网上很多多线程socket示例都是使用如下逻辑:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>//创建socket
循环{
int socket_fd = socket(PF_INET, SOCK_STREAM, 0);
connect(socket_fd, ....);
//创建线程,并把socket句柄传给线程
pthread_create(&tid, NULL, loop, (void *) &socket_fd)
}
//然后在线程的function中
void * loop(void *arg) {
int socket = *((int *) arg);
...
}
</code></pre></div></div>
<p>但是实际上这样是错误的; 会有并发问题; <br />
因为 int socket_fd 看起来每次循环都”声明”了一个变量; 但是在c语言中,每个变量只声明一次, 分配一次地址; <br />
添加一行代码验证,</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>int socket_fd = socket(PF_INET, SOCK_STREAM, 0);
printf("socket_fd:%d, add:%d\n",socket_fd,&socket_fd);
</code></pre></div></div>
<p>可看到不同句柄的地址都是一致的</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>...
socket_fd 5, add:2665480
socket_fd 6, add:2665480
socket_fd 7, add:2665480
...
</code></pre></div></div>
<p>那么对于同一个内存的数据的读取和取出是在不同线程中, 则会有并发问题;
就比如生成socket1之后创建线程A, 线程A并没有马上获得cpu, 然后主线程又 生成了一个socket2,再创建线程B, 此时, 线程A和B取出来的socket值都将是2</p>
<p>可以验证, 在线程中加个睡眠:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>void * loop(void *arg) {
sleep(1);
int socket = *((int *) arg);
printf("thread get socket%d\n",socket);
...
}
</code></pre></div></div>
给macos设置ramdisk加速缓存文件夹
2013-06-13T00:00:00-05:00
http://www.zhaoxiaodan.com/mac/给MacOS设置RAMDISK加速缓存文件夹
<p>OS X 系统主要存放应用程序缓存的目录,也就是 ~/Library/Caches</p>
<p>删除 ~/Library/Caches 这个目录</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo rm -rf ~/Library/Caches
</code></pre></div></div>
<p>把ramdisk链接到cache目录:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ln -s /Volumes/RamDisk/ ~/Library/Caches
</code></pre></div></div>
<p>编写创建ramdisk脚本,保存为<code class="language-plaintext highlighter-rouge">/sbin/create_ramdisk.sh</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/bin/bash
if ! test -e /Volumes/Ramdisk ; then
diskutil erasevolume HFS+ RamDisk `hdiutil attach -nomount ram://2097152`
fi
</code></pre></div></div>
<p>设置开机加载创建脚本:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo vi /System/Library/LaunchDaemons/ramdisk.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Disabled</key>
<false/>
<key>Label</key>
<string>com.liangwei.tools</string>
<key>ProgramArguments</key>
<string>/sbin/create_ramdisk.sh</string>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
</code></pre></div></div>
Macos开机启动设置
2013-06-13T00:00:00-05:00
http://www.zhaoxiaodan.com/mac/MacOS开机启动设置
<p>在macos中是没有像linux一样的 init.d; 而是用了似乎”更高级”的<code class="language-plaintext highlighter-rouge">launchctl</code></p>
<p>launchctl使用plist来配置启动程序, plist文件分别放在4个地方:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/System/Library/LaunchDaemons
/System/Library/LaunchAgents
~/Library/LaunchDaemons
~/Library/LaunchAgents
</code></pre></div></div>
<p>前两个是系统使用的部分,后两个是用户使用的;LaunchDaemons是不登录也加载,LaunchAgents只会在登录时才加载</p>
<p>plist的内容类似:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Disabled</key>
<false/>
<key>Label</key>
<string>com.liangwei.tools</string>
<key>ProgramArguments</key>
<string>/sbin/create_ramdisk.sh</string>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
</code></pre></div></div>
<p>plist脚本中定义的属性以及具体的含义,可以参看苹果官方网站的说明,地址为:<a href="https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man5/launchd.plist.5.html">launchd.plist(5) Mac OS X Manual Page</a></p>
通过php魔术函数__call构造简单的数据库访问类dao
2013-05-21T00:00:00-05:00
http://www.zhaoxiaodan.com/lnmp/通过php魔术函数__call构造简单的数据库访问类DAO
<figure class="highlight"><pre><code class="language-php" data-lang="php"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
</pre></td><td class="code"><pre><span class="cp"><?php</span>
<span class="kd">class</span> <span class="nc">DAO</span>
<span class="p">{</span>
<span class="k">private</span> <span class="nv">$sqls</span><span class="o">=</span><span class="k">array</span><span class="p">(</span>
<span class="s1">'getActionMoSucc'</span><span class="o">=></span><span class="s1">'select * from xxxx where action=$0'</span><span class="p">,</span>
<span class="p">);</span>
<span class="k">function</span> <span class="nf">__call</span><span class="p">(</span><span class="nv">$method</span><span class="p">,</span><span class="nv">$args</span><span class="p">)</span>
<span class="p">{</span>
<span class="nb">var_dump</span><span class="p">(</span><span class="nv">$method</span><span class="p">);</span>
<span class="nb">var_dump</span><span class="p">(</span><span class="nv">$args</span><span class="p">);</span>
<span class="nv">$key</span><span class="o">=</span><span class="nb">substr</span><span class="p">(</span><span class="nv">$method</span><span class="p">,</span><span class="mi">3</span><span class="p">)</span><span class="o">.</span><span class="nb">implode</span><span class="p">(</span><span class="s2">""</span><span class="p">,</span><span class="nv">$args</span><span class="p">);</span>
<span class="nb">var_dump</span><span class="p">(</span><span class="nv">$key</span><span class="p">);</span>
<span class="c1">//if($value=$redis->get($key))</span>
<span class="c1">// return $value;</span>
<span class="c1">//else</span>
<span class="nv">$this</span><span class="o">-></span><span class="na">getFromMysql</span><span class="p">(</span><span class="nv">$method</span><span class="p">,</span><span class="nv">$args</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">function</span> <span class="nf">getFromMysql</span><span class="p">(</span><span class="nv">$key</span><span class="p">,</span><span class="nv">$args</span><span class="p">)</span>
<span class="p">{</span>
<span class="nv">$sql</span><span class="o">=</span><span class="nv">$this</span><span class="o">-></span><span class="na">sqls</span><span class="p">[</span><span class="nv">$key</span><span class="p">];</span>
<span class="k">foreach</span><span class="p">(</span><span class="nv">$args</span> <span class="k">as</span> <span class="nv">$k</span><span class="o">=></span><span class="nv">$v</span><span class="p">)</span>
<span class="nv">$sql</span><span class="o">=</span><span class="nb">str_replace</span><span class="p">(</span><span class="s2">"</span><span class="se">\$</span><span class="nv">$k</span><span class="s2">"</span><span class="p">,</span><span class="nv">$v</span><span class="p">,</span><span class="nv">$sql</span><span class="p">);</span>
<span class="nb">var_dump</span><span class="p">(</span><span class="nv">$sql</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nv">$counter</span><span class="o">=</span><span class="k">new</span> <span class="nx">DAO</span><span class="p">();</span>
<span class="nv">$action</span><span class="o">=</span><span class="s2">"TEST"</span><span class="p">;</span>
<span class="nv">$counter</span><span class="o">-></span><span class="na">getActionMoSucc</span><span class="p">(</span><span class="nv">$action</span><span class="p">);</span>
<span class="cp">?></span>
</pre></td></tr></tbody></table></code></pre></figure>
如何在macos上编译同时支持32bit和64bit的universal库文件
2013-03-02T00:00:00-06:00
http://www.zhaoxiaodan.com/ios/如何在MacOS上编译同时支持32bit和64bit的universal库文件
<p>原来总是依赖homebrew, 然后这个东西总是会吧-arch i386 -arch x86_64给你去掉, 无法编译出universal的包
还不如自己下载包编译, 主要就是注意有些包使用了汇编代码(加速), 去掉这个功能或者分开编译即可</p>
<p>###含有ABI的编译</p>
<p>通过指定分别CFLAGS,CXXFLAGS,LDFLAGS 为-arch i386 或者 -arch x86_64, 并且将ABI对应设置为’32’ 或 ‘64’
分别编译出32bit和64bit版本, 并把两个版本的库文件分别保存 <br />
然后通过lipo工具将两个版本的库合成一个, 命令类似’lipo -create 32bit.dylib 64bit.dylib -output universal.dylib’</p>
<p>###编译工具的不同</p>
<p>普通编译时有遇到’llvm-gcc-4.2: -E, -S, -save-temps and -M options are not allowed with multiple -arch flags’错误,
也就是说这个gcc不支持通知编译多个arch<br />
看下<code class="language-plaintext highlighter-rouge">llvm-gcc-4.2 -v</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Using built-in specs.
Target: i686-apple-darwin11
Configured with: /private/var/tmp/llvmgcc42/llvmgcc42-2336.11~148/src/configure --disable-checking --enable-werror --prefix=/Applications/Xcode.app/Contents/Developer/usr/llvm-gcc-4.2 --mandir=/share/man --enable-languages=c,objc,c++,obj-c++ --program-prefix=llvm- --program-transform-name=/^[cg][^.-]*$/s/$/-4.2/ --with-slibdir=/usr/lib --build=i686-apple-darwin11 --enable-llvm=/private/var/tmp/llvmgcc42/llvmgcc42-2336.11~148/dst-llvmCore/Developer/usr/local --program-prefix=i686-apple-darwin11- --host=x86_64-apple-darwin11 --target=i686-apple-darwin11 --with-gxx-include-dir=/usr/include/c++/4.2.1
Thread model: posix
gcc version 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)
</code></pre></div></div>
<p>看到这里有个’Target: i686-apple-darwin11’,明显, 这个编译器是i686平台的</p>
<p>再看<code class="language-plaintext highlighter-rouge">clang -v</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Apple LLVM version 4.2 (clang-425.0.24) (based on LLVM 3.2svn)
Target: x86_64-apple-darwin12.2.0
Thread model: posix
</code></pre></div></div>
<p>看到<code class="language-plaintext highlighter-rouge">Target: x86_64-apple-darwin12.2.0</code>, 也就是是个64位的编译器, 能支持多个arch; 通过configure中加<code class="language-plaintext highlighter-rouge">CC=clang CXX=clang++</code>指定</p>
<p>###LIBS</p>
<p>如果某个库使用了第三方库, 且需要指定需要链接的库, 比如</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./configure libabcd_CFLAGS=/usr/local/ libabcd_LIBS=/usr/local/
</code></pre></div></div>
<p>那么就需要<code class="language-plaintext highlighter-rouge">LIBS="-labcd"</code> 告诉编译器去哪里找这个库</p>
<h3 id="libimobiledevie-编译示例">libimobiledevie 编译示例</h3>
<ol>
<li>
<p>gmp</p>
<p>需要分i386 和 x86_64 分别编译, 然后lipo合并</p>
<p>1) i386</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> make build-i386
cd build-i386
../configure CFLAGS='-arch i386' LDFLAGS='-arch i386' CXXFLAG='-arch i386' --prefix=/usr/local ABI=32
make -j 4
sudo make install
cd ..
#改名, 以备后面使用
sudo mv /usr/local/lib/libgmp.10.dylib /usr/local/lib/libgmp.10.i386.dylib
sudo mv /usr/local/lib/libgmp.a /usr/local/lib/libgmp.i386.a
</code></pre></div> </div>
<p>2) x86_64</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> make build-x84_64
cd build-x84_64
../configure CFLAGS='-arch x86_64' LDFLAGS='-arch x86_64' CXXFLAG='-arch x86_64' --prefix=/usr/local ABI=64
make -j 4
sudo make install
cd ..
#改名, 以备后面使用
sudo mv /usr/local/lib/libgmp.10.dylib /usr/local/lib/libgmp.10.x84_64.dylib
sudo mv /usr/local/lib/libgmp.a /usr/local/lib/libgmp.x84_64.a
</code></pre></div> </div>
<p>3) 生成universal的包</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> cd /usr/local/lib
sudo lipo -create libgmp.10.i386.dylib libgmp.10.x84_64.dylib -output libgmp.10.dylib
sudo lipo -create libgmp.i386.a libgmp.x84_64.a -output libgmp.a
</code></pre></div> </div>
<p>4)验证下</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> lipo -info libgmp.dylib libgmp.a
Architectures in the fat file: libgmp.dylib are: i386 x86_64
Architectures in the fat file: libgmp.a are: i386 x86_64
</code></pre></div> </div>
<p>5) 修改头文件</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> sudo vi /usr/local/include/gmp.h
#if defined(__i386__)
#define __GMP_HAVE_HOST_CPU_FAMILY_power 0
#define __GMP_HAVE_HOST_CPU_FAMILY_powerpc 0
#define GMP_LIMB_BITS 32
#define GMP_NAIL_BITS 0
#elif defined(__x86_64__)
#define __GMP_HAVE_HOST_CPU_FAMILY_power 0
#define __GMP_HAVE_HOST_CPU_FAMILY_powerpc 0
#define GMP_LIMB_BITS 64
#define GMP_NAIL_BITS 0
#elif defined(__ppc__)
#define __GMP_HAVE_HOST_CPU_FAMILY_power 0
#define __GMP_HAVE_HOST_CPU_FAMILY_powerpc 1
#define GMP_LIMB_BITS 32
#define GMP_NAIL_BITS 0
#elif defined(__powerpc64__)
#define __GMP_HAVE_HOST_CPU_FAMILY_power 0
#define __GMP_HAVE_HOST_CPU_FAMILY_powerpc 1
#define GMP_LIMB_BITS 64
#define GMP_NAIL_BITS 0
#else
#error Unsupported architecture
#endif
</code></pre></div> </div>
</li>
</ol>
<p>2.nettle, 同上分别编译后合并</p>
<p>3.pll-ket, configure时直接 CFLAGS=’-arch i386 -arch x86_64’ CXXFLAGS…. 编译universal</p>
<p>4.libimobiledevice</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>export PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/local/lib/pkgconfig
./configure --disable-dependency-tracking --prefix=/usr/local/ --without-cython CFLAGS='-arch i386 -arch x86_64' LDFLAGS='-arch i386 -arch x86_64' CXXFLAG='-arch i386 -arch x86_64' CC=clang CXX=clang++ LIBS='-lplist -lusbmuxd -lcrypto -lssl'
</code></pre></div></div>
Macos动态库加载分析
2013-02-05T00:00:00-06:00
http://www.zhaoxiaodan.com/mac/ios/macos动态库加载分析
<p>最近折腾的NPAPI插件需要用到第三方动态库, 总是加载不成功, 经过各种google, 终于明白:</p>
<p>##dylib_id 动态库标识
在java里, 加载jar库或者class文件都是已路径进行的, 而dylib也一样</p>
<p>每个dylib有一个id属性,这个值<code class="language-plaintext highlighter-rouge">形式上</code>是一个unix路径, 但其实它仅仅是一个id<br />
这个id作为库的一个名称, 提供给<code class="language-plaintext highlighter-rouge">连接者</code>使用, 例如我有一个libimobiledevice.dylib 库:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>//查看
liangmatoMacBook-Pro:MacOS liangwei$ otool -L ./libimobiledevice.dylib
./libimobiledevice.dylib:
/usr/local/lib/libimobiledevice.3.dylib (compatibility version 4.0.0, current version 4.1.0)
/usr/local/lib/libplist.1.1.8.dylib (compatibility version 1.0.0, current version 1.1.8)
</code></pre></div></div>
<p>第一行的<code class="language-plaintext highlighter-rouge">/usr/local/lib/libimobiledevice.3.dylib</code> 就是这个库的id,
不管这个dylib文件命名是什么, 也不管它放置在哪里, 当你的程序编译ld它的时候, 都是以id作为标识进行连接的<br />
(当然, 一般情况下, 库安装路径跟id是相同的)<br />
比如, 你有个程序A, 用到了libimobiledevice.dylib库, 库文件放在/workspace/test/libs下, 当你编译完之后, 查看A</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>liangmatoMacBook-Pro:MacOS liangwei$ otool -L ./A
./A:
/usr/local/lib/libimobiledevice.3.dylib (compatibility version 4.0.0, current version 4.1.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)
</code></pre></div></div>
<p>因为A是个可执行程序, 所以, 第一行开始的内容就是使用到的库,<br />
那么它使用的是以<code class="language-plaintext highlighter-rouge">/usr/local/lib/libimobiledevice.3.dylib</code>为id的库<br />
这里的连接值是库的id, 而不是它的路径<code class="language-plaintext highlighter-rouge">/workspace/test/libs/libimobiledevice.3.dylib</code></p>
<p>##DYLD_LIBRARY_PATH 动态库搜索路径
运行程序A, A使用了<code class="language-plaintext highlighter-rouge">/usr/local/lib/libimobiledevice.3.dylib</code>, 那么系统如何知道去哪里读取这个文件? <br />
系统首先直接去<code class="language-plaintext highlighter-rouge">/usr/local/lib/libimobiledevice.3.dylib</code>路径去读取, 如果有则加载, <br />
如果没有, 系统以文件名<code class="language-plaintext highlighter-rouge">libimobiledevice.3.dylib</code>去设定好的目录搜索的,默认的, 这个目录是<code class="language-plaintext highlighter-rouge">/usr/lib</code>. <br />
你也可以进行设置, 添加其他一些搜索路径. 让系统在<code class="language-plaintext highlighter-rouge">/usr/lib</code>找不到之后再搜索这些目录 <br />
而这个设置, 正是<code class="language-plaintext highlighter-rouge">DYLD_LIBRARY_PATH</code> 环境变量:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>export DYLD_LIBRARY_PATH=/另一个libs的存放路径/:/另一个libs的存放路径2/
</code></pre></div></div>
<p>系统搜索完默认的<code class="language-plaintext highlighter-rouge">/usr/lib</code>之后会依次搜索DYLD_LIBRARY_PATH指定的目录
如果把文件libimobiledevice.3.dylib改名, 会提示搜索不到, A将无法运行</p>
<p>##如何修改A所需库路径为程序所在目录下?
有一个变量叫<code class="language-plaintext highlighter-rouge">@loader_path</code>, 就是只可执行程序当前所在目录. 于是我们修改A对libimobiledevice.3.dylib的依赖</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>install_name_tool -change /usr/local/lib/libimobiledevice.3.dylib @loader_path/libimobiledevice.3.dylib A
</code></pre></div></div>
<p>再查看</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>liangmatoMacBook-Pro:MacOS liangwei$ otool -L ./A
./A:
@loader_path/libimobiledevice.3.dylib (compatibility version 4.0.0, current version 4.1.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)
</code></pre></div></div>
<p>那么此时如果A在目录/workspace/test/A, 运行时, 会直接去/workspace/test/ 加载libimobiledevice.3.dylib</p>
<p>发现A还是无法运行, 发现libimobiledevice.3.dylib 依赖libplist.1.1.8.dylib ,<br />
同样, 把libimobiledevice.3.dylib 对libplist.1.1.8.dylib的也修改为当前目录</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>install_name_tool -change /usr/local/lib/libplist.1.1.8.dylib @loader_path/libplist.1.1.8.dylib libimobiledevice.3.dylib
</code></pre></div></div>
<p>然后把libplist.1.1.8.dylib也拷贝到A所在目录下即可</p>
<p>##如何让A编译出来直接连接的是@loader_path下的库?
修改他们的id为<code class="language-plaintext highlighter-rouge">@loader_path/xxx</code>, 如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>install_name_tool -id @loader_path/libimobiledevice.3.dylib libimobiledevice.3.dylib
</code></pre></div></div>
<p>使用修改过的dylib文件进行重新编译, A直接就依赖<code class="language-plaintext highlighter-rouge">@loader_path/libimobiledevice.3.dylib</code> 而不再是<code class="language-plaintext highlighter-rouge">/usr/local/lib/libimobiledevice.3.dylib</code></p>
<p>##其他</p>
<ol>
<li>
<p>loader_path 支持相对路径, 如:</p>
<p>/workspace
…/libs
……/libimobiledevice.3.dylib
……/libplist.1.1.8.dylib
…/bin
……/A</p>
</li>
</ol>
<p>那么A对<code class="language-plaintext highlighter-rouge">libimobiledevice.3.dylib</code>的依赖可以改为<code class="language-plaintext highlighter-rouge">@loader_path/../libs/libimobiledevice.3.dylib</code></p>
<p>##完整例子</p>
<p>工程目录:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/workspace
A.c
.../libs
....../libimobiledevice.3.dylib
....../libplist.1.1.8.dylib
.../bin
....../A
A 依赖 libimobiledevice.3.dylib, libusbmuxd.2.dylib, libplist.1.dylib
libimobiledevice.3.dylib 依赖 libusbmuxd.2.dylib, libplist.1.dylib
libusbmuxd.2.dylib 依赖 libplist.1.dylib
libplist.1.dylib 无依赖
</code></pre></div></div>
<p>1) 下载新的库 <br />
2) 分别修改 libimobiledevice.3.dylib, libusbmuxd.2.dylib, libplist.1.dylib 的id为 @loader_path/文件名 <br />
3) 分别修改 dylib对其他dylib的依赖为 @loader_path/文件名 <br />
4) Xcode 中<code class="language-plaintext highlighter-rouge">Build Phases</code>-><code class="language-plaintext highlighter-rouge">Link Binary With Libraries</code>, 添加libs目录下的三个库 <br />
5) Xcode 中<code class="language-plaintext highlighter-rouge">Build Phases</code>-><code class="language-plaintext highlighter-rouge">Add Build Phase</code>-><code class="language-plaintext highlighter-rouge">Add Copy Files</code>, 修改<code class="language-plaintext highlighter-rouge">Destination</code>为<code class="language-plaintext highlighter-rouge">Executables</code>, 并添加libs目录下的三个库 <br />
6) 编译即可</p>
<p>进入bin目录查看得到:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>liangmatoMacBook-Pro:libs liangwei$ otool -L /workspace/bin/*
/workspace/bin/A:
@loader_path/libplist.1.dylib (compatibility version 1.0.0, current version 1.1.8)
@loader_path/libusbmuxd.2.dylib (compatibility version 2.0.0, current version 1.0.8)
@loader_path/libimobiledevice.dylib (compatibility version 4.0.0, current version 4.1.0)
/System/Library/Frameworks/WebKit.framework/Versions/A/WebKit (compatibility version 1.0.0, current version 536.26.9)
/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (compatibility version 1.0.0, current version 19.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)
/workspace/bin/libimobiledevice.dylib:
@loader_path/libimobiledevice.dylib (compatibility version 4.0.0, current version 4.1.0)
@loader_path/libplist.1.dylib (compatibility version 1.0.0, current version 1.1.8)
@loader_path/libusbmuxd.2.dylib (compatibility version 2.0.0, current version 1.0.8)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.3.11)
/usr/lib/libssl.0.9.7.dylib (compatibility version 0.9.7, current version 0.9.7)
/usr/lib/libcrypto.0.9.7.dylib (compatibility version 0.9.7, current version 0.9.7)
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
/workspace/bin/libplist.1.dylib:
@loader_path/libplist.1.dylib (compatibility version 1.0.0, current version 1.1.8)
/usr/lib/libxml2.2.dylib (compatibility version 9.0.0, current version 9.16.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.3.11)
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
/workspace/bin/libusbmuxd.2.dylib:
@loader_path/libusbmuxd.2.dylib (compatibility version 2.0.0, current version 1.0.8)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.3.11)
@loader_path/libplist.1.dylib (compatibility version 1.0.0, current version 1.1.8)
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
</code></pre></div></div>
Ituns与iphone的通信协议usbmuxd解析
2013-02-03T00:00:00-06:00
http://www.zhaoxiaodan.com/ios/ituns与iphone的通信协议usbmuxd解析
<p>最开始研究与iphone通信, 都会想当然的google下usb协议, 必经iphone是通过usb线连接到电脑. 其实不然, iTunes是通过TCP协议与iPhone通信的</p>
<p>##usbmuxd</p>
<p>iTunes使用一种叫”usbmux”的东西与iphone通信, 这个东西提供了一个USB - TCP的转换服务. <br />
这个服务在Mac端是由<code class="language-plaintext highlighter-rouge">/System/Library/PrivateFrameworks/MobileDevice.framework/Resources/usbmuxd</code> 提供的, 当然, 开机自动启动. <br />
它创建了一个Unix Domain Socket 在 <code class="language-plaintext highlighter-rouge">/var/run/usbmuxd</code>. usbmuxd服务程序监控iPhone在USB口上的连接, 当它监控到iPhone以<code class="language-plaintext highlighter-rouge">用户模式</code>连接到USB,
(相对的是<code class="language-plaintext highlighter-rouge">recovery</code>模式), usbmuxd服务程序就会连接到这个<code class="language-plaintext highlighter-rouge">/var/run/usbmuxd</code>的TCP端口, 并开始成为一个USB - TCP <code class="language-plaintext highlighter-rouge">请求</code>转发器</p>
<p>那么,如果想编写个第三方程序与iphone进行通信,实现类似iTunes的功能, 你的程序可以通过usbmuxd! 建立一个TCP连接到<code class="language-plaintext highlighter-rouge">/var/run/usbmuxd</code>端口,
根据协议发送对应的请求包, usbmuxd服务会将请求转发到USB的iPhone上</p>
<p>##lockdownd协议</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>//协议头
struct usbmux_header {
u32 length; // 消息长度,包括头部
u32 version; // 协议版本号
u32 type; // 消息类型,请求,响应,握手,等
u32 tag; // 消息编号, 用来对应响应
char payload; //请求体
};
//头部中的type类型
enum {
usbmux_result = 1,
usbmux_connect = 2,
usbmux_hello = 3,
usbmux_payload = 8,
};
</code></pre></div></div>
<p>##监听</p>
<p>知道了iTunes使用的协议, 那么有没有办法看看iTunes都发了些什么包? 有个简单的办法就是使用<code class="language-plaintext highlighter-rouge">socat</code>, 类似:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo mv /var/run/usbmuxd /var/run/usbmuxx
sudo socat -t100 -x -v UNIX-LISTEN:/var/run/usbmuxd,mode=777,reuseaddr,fork UNIX-CONNECT:/var/run/usbmuxx
</code></pre></div></div>
<p>##包示例:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> 2013/02/04 00:07:19.567563 length=483 from=0 to=482
e3 01 00 00 01 00 00 00 08 00 00 00 02 00 00 00 ................
3c 3f 78 6d 6c 20 76 65 72 73 69 6f 6e 3d 22 31 <?xml version="1
2e 30 22 20 65 6e 63 6f 64 69 6e 67 3d 22 55 54 .0" encoding="UT
46 2d 38 22 3f 3e 0a F-8"?>.
3c 21 44 4f 43 54 59 50 45 20 70 6c 69 73 74 20 <!DOCTYPE plist
50 55 42 4c 49 43 20 22 2d 2f 2f 41 70 70 6c 65 PUBLIC "-//Apple
2f 2f 44 54 44 20 50 4c 49 53 54 20 31 2e 30 2f //DTD PLIST 1.0/
2f 45 4e 22 20 22 68 74 74 70 3a 2f 2f 77 77 77 /EN" "http://www
2e 61 70 70 6c 65 2e 63 6f 6d 2f 44 54 44 73 2f .apple.com/DTDs/
50 72 6f 70 65 72 74 79 4c 69 73 74 2d 31 2e 30 PropertyList-1.0
2e 64 74 64 22 3e 0a .dtd">.
3c 70 6c 69 73 74 20 76 65 72 73 69 6f 6e 3d 22 <plist version="
31 2e 30 22 3e 0a 1.0">.
3c 64 69 63 74 3e 0a <dict>.
09 3c 6b 65 79 3e 42 75 6e 64 6c 65 49 44 3c 2f .<key>BundleID</
6b 65 79 3e 0a key>.
09 3c 73 74 72 69 6e 67 3e 63 6f 6d 2e 61 70 70 .<string>com.app
6c 65 2e 69 54 75 6e 65 73 48 65 6c 70 65 72 3c le.iTunesHelper<
2f 73 74 72 69 6e 67 3e 0a /string>.
09 3c 6b 65 79 3e 43 6c 69 65 6e 74 56 65 72 73 .<key>ClientVers
69 6f 6e 53 74 72 69 6e 67 3c 2f 6b 65 79 3e 0a ionString</key>.
09 3c 73 74 72 69 6e 67 3e 75 73 62 6d 75 78 64 .<string>usbmuxd
2d 32 39 36 2e 33 3c 2f 73 74 72 69 6e 67 3e 0a -296.3</string>.
09 3c 6b 65 79 3e 4d 65 73 73 61 67 65 54 79 70 .<key>MessageTyp
65 3c 2f 6b 65 79 3e 0a e</key>.
09 3c 73 74 72 69 6e 67 3e 4c 69 73 74 65 6e 3c .<string>Listen<
2f 73 74 72 69 6e 67 3e 0a /string>.
09 3c 6b 65 79 3e 50 72 6f 67 4e 61 6d 65 3c 2f .<key>ProgName</
6b 65 79 3e 0a key>.
09 3c 73 74 72 69 6e 67 3e 69 54 75 6e 65 73 48 .<string>iTunesH
65 6c 70 65 72 3c 2f 73 74 72 69 6e 67 3e 0a elper</string>.
09 3c 6b 65 79 3e 6b 4c 69 62 55 53 42 4d 75 78 .<key>kLibUSBMux
56 65 72 73 69 6f 6e 3c 2f 6b 65 79 3e 0a Version</key>.
09 3c 69 6e 74 65 67 65 72 3e 33 3c 2f 69 6e 74 .<integer>3</int
65 67 65 72 3e 0a eger>.
3c 2f 64 69 63 74 3e 0a </dict>.
3c 2f 70 6c 69 73 74 3e 0a </plist>.
--
< 2013/02/04 00:07:19.570319 length=294 from=0 to=293
26 01 00 00 01 00 00 00 08 00 00 00 02 00 00 00 &...............
3c 3f 78 6d 6c 20 76 65 72 73 69 6f 6e 3d 22 31 <?xml version="1
2e 30 22 20 65 6e 63 6f 64 69 6e 67 3d 22 55 54 .0" encoding="UT
46 2d 38 22 3f 3e 0a F-8"?>.
3c 21 44 4f 43 54 59 50 45 20 70 6c 69 73 74 20 <!DOCTYPE plist
50 55 42 4c 49 43 20 22 2d 2f 2f 41 70 70 6c 65 PUBLIC "-//Apple
2f 2f 44 54 44 20 50 4c 49 53 54 20 31 2e 30 2f //DTD PLIST 1.0/
2f 45 4e 22 20 22 68 74 74 70 3a 2f 2f 77 77 77 /EN" "http://www
2e 61 70 70 6c 65 2e 63 6f 6d 2f 44 54 44 73 2f .apple.com/DTDs/
50 72 6f 70 65 72 74 79 4c 69 73 74 2d 31 2e 30 PropertyList-1.0
2e 64 74 64 22 3e 0a .dtd">.
3c 70 6c 69 73 74 20 76 65 72 73 69 6f 6e 3d 22 <plist version="
31 2e 30 22 3e 0a 1.0">.
3c 64 69 63 74 3e 0a <dict>.
09 3c 6b 65 79 3e 4d 65 73 73 61 67 65 54 79 70 .<key>MessageTyp
65 3c 2f 6b 65 79 3e 0a e</key>.
09 3c 73 74 72 69 6e 67 3e 52 65 73 75 6c 74 3c .<string>Result<
2f 73 74 72 69 6e 67 3e 0a /string>.
09 3c 6b 65 79 3e 4e 75 6d 62 65 72 3c 2f 6b 65 .<key>Number</ke
79 3e 0a y>.
09 3c 69 6e 74 65 67 65 72 3e 30 3c 2f 69 6e 74 .<integer>0</int
65 67 65 72 3e 0a eger>.
3c 2f 64 69 63 74 3e 0a </dict>.
3c 2f 70 6c 69 73 74 3e 0a </plist>.
--
</code></pre></div></div>
<p>第一个iTunes向<code class="language-plaintext highlighter-rouge">/var/run/usbmuxd</code>发的请求包, 第一行 <code class="language-plaintext highlighter-rouge">e3 01 00 00 01 00 00 00 08 00 00 00 02 00 00 00</code>是头部
<code class="language-plaintext highlighter-rouge">e3 01 00 00</code> 即0x01e3 = 483, 包长度</p>
<p>头部后面紧跟的是payload, 也就是xml格式的请求内容</p>
Chrome高级npapi插件开发
2013-01-31T00:00:00-06:00
http://www.zhaoxiaodan.com/%E5%85%B6%E4%BB%96/chrome高级NPAPI插件开发
<p>##注意事项</p>
<p>Chrome22之后貌似完全放弃10.5 Carbon之类的支持,所以Event Model需要进行设置,否则NPAPI插件在Chrome22下无法加载。NPP_New函数中进行如此设置</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>browser->setvalue(instance,
NPPVpluginEventModel,
(void *)NPEventModelCocoa);
</code></pre></div></div>
<p>文档没注意看,折腾死了,用xcode新建的库工程添加了MIME类型, 编译出来的库浏览器还是不加载</p>
<p>经过各种检查, 终于发现, 原来需要注意一下两点:</p>
<p>Info.plist</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The plugin communicates its MIME and filename extension information using the Info.plist file, which is packaged in the plugin bundle. The plugin also communicates its bundle type in that file, under the key CFBundlePackageType; the type is 'BRPL'. If the type isn't an NPAPI plugin type, the bundle won't load as an NPAPI plugin. You can always just use 'BRPL'.
</code></pre></div></div>
<p>XP_MACOSX</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>It's important to define the GCC preprocessor definition XP_MACOSX to 1; this is used by the NPAPI headers to build properly on Mac OS X. If you don't define it, they won't be interpreted correctly. This is easy to miss in the sample project's build settings.
</code></pre></div></div>
<p>Architectures</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>must be 'Standard(32/64-bit Intel)
</code></pre></div></div>
<p>Symbol visibility</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Symbol visibility is a common problem for people trying to get NPAPI plugins working. Some symbols must be visible as standard C symbols so the browser can find them, which means they need to be prefixed by an underscore, and must not have the C++ obfuscation that is generated by the C++ compiler.
</code></pre></div></div>
<p>The three symbols that must always be visible are:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>NP_Initialize()
NP_GetEntryPoints()
NP_Shutdown()
</code></pre></div></div>
<p>The sample plugin is written entirely in C, using a standard Xcode build configuration, so by default all of its symbols are C-style and visible.</p>
<p>If you want to implement your plugin in C++ or Objective-C++, you need to tell the compiler to export them in C format by using extern “C” in the header, like this:</p>
<p>#pragma GCC visibility push(default)</p>
<p>extern “C” {
NPError NP_Initialize(NPNetscapeFuncs *browserFuncs);
NPError NP_GetEntryPoints(NPPluginFuncs *pluginFuncs);
void NP_Shutdown(void);
}</p>
<p>#pragma GCC visibility pop
You can check to be sure your symbols are visible and in standard C format by using the nm utility provided among the Mac OS X developer tools:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nm BasicPlugin
</code></pre></div></div>
<p>…
00000810 T _NP_GetEntryPoints
000007fa T _NP_Initialize
000008a0 T _NP_Shutdown</p>
<p>简单的说就是</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Info.plist
CFBundlePackageType的值必须为BRPL
XP_MACOSX
xcode项目中 BuildSettings-> Preprocesssing Macros 必须设置 XP_MACOSX=1
</code></pre></div></div>
Hardcode的密码破解有多难
2013-01-16T00:00:00-06:00
http://www.zhaoxiaodan.com/cocos2dx/hardcode的密码破解有多难
<p><a href="/ios/cocos2d-x载入lua的过程">这篇文章</a>中实现了lua文件的简单加解密功能,<br />
但是感觉密码直接写在全局变量中很容易被人拿到,<br />
但是究竟有多难, 自己尝试了一把</p>
<p>相关代码片段为:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>static const char *getF (lua_State *L, void *ud, size_t *size) {
...
//将文件读入LoadF结构体的buff内存, 并将实际读到的长度赋予size,
*size = fread(lf->buff, 1, sizeof(lf->buff), lf->f);
//将buff内容解密
*size = aes256_decrypt(key, lf->buff,*size);
return (*size > 0) ? lf->buff : NULL;
}
</code></pre></div></div>
<p>反编译执行文件, 找到<code class="language-plaintext highlighter-rouge">getF</code>函数,一看</p>
<p><img src="http://qiniucdn.zhaoxiaodan.com/2013/201301131.png" alt="反编译" /></p>
<p>我的密码刺裸裸的展现出来了</p>
<p>##疑问:</p>
<ol>
<li>有密码, 没有S盒的情况下, 是否能解密?</li>
<li>有密码, 有执行文件, 是否能把执行文件转换成lib, 另外写个程序调用<code class="language-plaintext highlighter-rouge">aes256_decrypt</code> 方法进行解密? (也就相当于有了S盒)</li>
</ol>
<p>如果以上两个都能实现, 那这个加密也就骗骗非技术人员罢了…</p>
如何将app打包成deb包供越狱iphone在cydia源上下载安装
2013-01-15T00:00:00-06:00
http://www.zhaoxiaodan.com/ios/如何将app打包成deb包供越狱iphone在cydia源上下载安装
<p>##修改编译选项重新打包</p>
<p>在工程的<code class="language-plaintext highlighter-rouge">Build Settings -> Code Signing -> Code Signing Identity</code> 选项, 将 Debug 和 Release 下的 <code class="language-plaintext highlighter-rouge">Any iOS SDK</code> 都设置为 <code class="language-plaintext highlighter-rouge">Don't Code Sign</code><br />
然后在重新<code class="language-plaintext highlighter-rouge">Archive</code></p>
<p>##准备目录</p>
<p>创建一个目录用来打包,如tmp,tmp下建DEBIAN和Applications两个目录, DEBIAN下建一个文本文件control <br />
tmp目录结构如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-DEBIAN
---control
-Applications
</code></pre></div></div>
<p>control文件就是打包时的配置文件,它也会作为deb包的配置被打包到包中, <br />
文件例子:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Package: com.sharedream.game
Name: 游戏测试
Version: 0.1-1
Description: 游戏测试游戏,开发中...
Section: 游戏
Depends: firmware (>= 4.3)
Priority: optional
Architecture: iphoneos-arm
Author: liangwei <http://weibo.com/iamliangwei>
Homepage: http://weibo.com/iamliangwei
Icon: file:///Applications/game.app/Icon.png
Maintainer: liangwei <http://weibo.com/iamliangwei>
</code></pre></div></div>
<p>然后将xcode打包出来的.app文件整个拷贝到Applications目录下, <br />
结构如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-DEBIAN
---control
-Applications
---game.app
</code></pre></div></div>
<p>##打包</p>
<p>退出至tmp的上层目录</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dpkg-deb -b tmp game.deb
</code></pre></div></div>
<p>看到如下几行就是打包完成了.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>warning, `com.sull.sample/DEBIAN/control' contains user-defined field `Name'
warning, `com.sull.sample/DEBIAN/control' contains user-defined field `Author'
warning, `com.sull.sample/DEBIAN/control' contains user-defined field `Sponsor'
dpkg-deb: ignoring 3 warnings about the control file(s)
</code></pre></div></div>
<p>拷贝到cydia源中, 重新扫描包生成Packages列表文件, 并压缩成Packages.bz2就可以啦</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dpkg-scanpackages -m debs >Packages
bzip2 -zkf Packages
</code></pre></div></div>
<p>“contains ununderstood data member data.tar.xz” 的安装错误</p>
<p>是因为自从1.17.0版本的dpkg-deb开始, 默认使用xz格式来压缩data.tar文件
但是,cydia在ios提供的dpkg是1.14版本, 还没有支持xz这种压缩格式
所以我们需要设置”-Zgzip”参数给dpkg-deb 进行打包, 类似命令:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dpkg-deb -Zgzip -b tmp game.deb
</code></pre></div></div>
Cocos2d X载入lua的过程
2013-01-13T00:00:00-06:00
http://www.zhaoxiaodan.com/cocos2dx/cocos2d-x载入lua的过程
<p>入口点在AppDelegate.cpp中:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>//类CCLuaEngine 实现了 cocos2d-x的脚本引擎接口
CCScriptEngineProtocol* pEngine = CCLuaEngine::engine();
//manager中设置脚本引擎
CCScriptEngineManager::sharedManager()->setScriptEngine(pEngine);
//通过相对路径获取绝对路径
string path = CCFileUtils::sharedFileUtils()->fullPathFromRelativePath("script/Main.lua");
//把文件所在目录添加至lua文件搜索路径
pEngine->addSearchPath(path.substr(0, path.find_last_of("/")).c_str());
//运行指定的文件
pEngine->executeScriptFile(path.c_str());
</code></pre></div></div>
<p>cocos2d-x是可以支持多种脚本引擎的, <code class="language-plaintext highlighter-rouge">CCScriptEngineProtocol</code>是脚本引擎接口定义 <br />
其LUA版本的实现在<code class="language-plaintext highlighter-rouge">libs/lua/cocos2dx_support/CCLuaEngine.h</code>,即由<code class="language-plaintext highlighter-rouge">CCLuaEngine</code>类实现</p>
<p>追入<code class="language-plaintext highlighter-rouge">executeScriptFile</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>int CCLuaEngine::executeScriptFile(const char* filename)
{
// 读入文件
int nRet = luaL_dofile(m_state, filename);
// lua_gc(m_state, LUA_GCCOLLECT, 0);
if (nRet != 0)
{
CCLOG("[LUA ERROR] %s", lua_tostring(m_state, -1));
lua_pop(m_state, 1);
return nRet;
}
return 0;
}
</code></pre></div></div>
<p>追入<code class="language-plaintext highlighter-rouge">luaL_dofile</code>, 在文件<code class="language-plaintext highlighter-rouge">libs/lua/lua/lauxlib.h</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#define luaL_dofile(L, fn) \
(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">luaL_loadfile</code>实现在<code class="language-plaintext highlighter-rouge">libs/lua/lua/lauxlib.c</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) {
...
status = lua_load(L, getF, &lf, lua_tostring(L, -1));
...
}
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">lua_load</code>就是lua读入脚本文件的方法. <br />
其具体实现没太看明白, 简单的说就是通过<code class="language-plaintext highlighter-rouge">getF</code>方法作为reader(文件读入器)将文件内容读到内存, 然后进行一系列解析</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>static const char *getF (lua_State *L, void *ud, size_t *size) {
LoadF *lf = (LoadF *)ud;
(void)L;
if (lf->extraline) {
lf->extraline = 0;
*size = 1;
return "\n";
}
if (feof(lf->f)) return NULL;
memset(lf->buff, 0, sizeof(lf->buff));
//将文件读入LoadF结构体的buff内存, 并将实际读到的长度赋予size,
*size = fread(lf->buff, 1, sizeof(lf->buff), lf->f);
return (*size > 0) ? lf->buff : NULL;
}
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">LoadF</code>结构体:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>typedef struct LoadF {
int extraline;
FILE *f;
unsigned char buff[LUAL_BUFFERSIZE];
} LoadF;
</code></pre></div></div>
<p>所以, 如果文件是经过加密的文件, 在fread之后把buff的数据进行解密即可, 就像这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>static const char *getF (lua_State *L, void *ud, size_t *size) {
...
//将文件读入LoadF结构体的buff内存, 并将实际读到的长度赋予size,
*size = fread(lf->buff, 1, sizeof(lf->buff), lf->f);
//将buff内容解密
*size = aes256_decrypt(key, lf->buff,*size);
return (*size > 0) ? lf->buff : NULL;
}
</code></pre></div></div>
<p>需要注意的是</p>
<ol>
<li>文件的大小大于LUAL_BUFFERSIZE时, 会分多次读入</li>
<li>LUAL_BUFFERSIZE的大小必须是加密块的整倍数</li>
<li>解密之后必须将size赋值为strlen. 例如块大小为32字节, 加密3B的文件内容<code class="language-plaintext highlighter-rouge">123</code>后文件大小还是为32B, 解密后buff内容为 0x30,0x31,0x32,0x00….0x00, 会出错</li>
</ol>
Ios越狱开发系统配置 Iosopendev
2012-12-12T00:00:00-06:00
http://www.zhaoxiaodan.com/ios/ios越狱开发系统配置-iosopendev
<p>##mac端环境</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Manually download the zip and extract into the following into the respective folders:
/opt/iOSOpenDev/ https://github.com/kokoabim/iOSOpenDev
/opt/iOSOpenDev/templates/ https://github.com/kokoabim/iOSOpenDev-Xcode-Templates
/opt/iOSOpenDev/frameworks/ https://github.com/kokoabim/iOSOpenDev-Framework-Header-Files
Comment out the following lines in your /opt/iOSOpenDev/iod-setup file: (lines 530-532)
downloadGithubTarball “https://nodeload.github.com/kokoabim/iOSOpenDev/tarball/master” “$iOSOpenDevPath” “iOSOpenDev base”
downloadGithubTarball “https://nodeload.github.com/kokoabim/iOSOpenDev-Xcode-Templates/tarball/master” “$iOSOpenDevPath/templates” “Xcode templates”
downloadGithubTarball “https://nodeload.github.com/kokoabim/iOSOpenDev-Framework-Header-Files/tarball/master” “$iOSOpenDevPath/frameworks” “framework header files”
from terminal run:
sudo /opt/iOSOpenDev-Setup/iod-setup base
sudo /opt/iOSOpenDev-Setup/iod-setup sdk -sdk iphoneos
</code></pre></div></div>
<p>##手机端环境,cydia安装openssh:</p>
<ul>
<li>openSSH</li>
<li>toggle ssh</li>
<li>APT 0.6 Transitional</li>
</ul>
<p>##mac端安装ssh证书</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iosod sshkey -h 手机当前ip
</code></pre></div></div>
<p>##手机端,使用ssh登录手机使用<code class="language-plaintext highlighter-rouge">apt-get</code>安装其他依赖</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt-get install coreutils diskdev-cmds file-cmds \
system-cmds com.saurik.substrate.safemode \
mobilesubstrate preferenceloader
</code></pre></div></div>
<p>##获得手机syslog</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt-get install socat
socat – UNIX-CONNECT:/var/run/lockdown/syslog.sock
>watch
</code></pre></div></div>
<p>##注意</p>
<p>如果更新了xcode或者ios版本, 或者编译打包时报异常:”but there’s no such product type for the ‘iphoneos’ platform” 需要更新指定一下sdk :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo /opt/iOSOpenDevSetup/bin/iod-setup sdk -sdk iphoneos
</code></pre></div></div>
给cocos2d X2.02的lua加异步httpclient
2012-09-10T00:00:00-05:00
http://www.zhaoxiaodan.com/cocos2dx/给cocos2d-x2.02的lua加异步httpclient
<p>ocos2d-x 2.02 多了一个HttpClient的扩展包, 是异步请求的. 但是却没有映射到lua中.<br />
所以我稍微改了改, 写了个MyHttpClient, 方便lua的使用</p>
<h1 id="lua里使用">lua里使用:</h1>
<figure class="highlight"><pre><code class="language-lua" data-lang="lua"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="code"><pre> <span class="k">function</span> <span class="nf">hello</span><span class="p">(</span><span class="n">code</span><span class="p">,</span><span class="n">data</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">code</span><span class="o">..</span><span class="s2">","</span><span class="o">..</span><span class="n">data</span><span class="p">);</span>
<span class="k">end</span>
<span class="n">MyHttpClient</span><span class="p">:</span><span class="n">doGet</span><span class="p">(</span><span class="s2">"http://www.sina.com.cn/"</span><span class="p">,</span><span class="n">hello</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></figure>
<h1 id="myhttpclienth">MyHttpClient.h</h1>
<figure class="highlight"><pre><code class="language-c--" data-lang="c++"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
</pre></td><td class="code"><pre> <span class="cp">#ifndef __MyHttpClient_H_
</span> <span class="cp">#define __MyHttpClient_H_
</span>
<span class="cp">#include "cocos2d.h"
</span> <span class="cp">#include "HttpClient.h"
</span>
<span class="k">using</span> <span class="k">namespace</span> <span class="n">cocos2d</span><span class="p">;</span>
<span class="k">using</span> <span class="k">namespace</span> <span class="n">extension</span><span class="p">;</span>
<span class="k">class</span> <span class="nc">MyHttpClient</span> <span class="o">:</span> <span class="k">public</span> <span class="n">cocos2d</span><span class="o">::</span><span class="n">CCObject</span>
<span class="p">{</span>
<span class="nl">public:</span>
<span class="k">static</span> <span class="kt">void</span> <span class="n">doGet</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">url</span><span class="p">,</span><span class="kt">int</span> <span class="n">handler</span><span class="p">);</span>
<span class="k">virtual</span> <span class="kt">void</span> <span class="n">onHttpRequestCompleted</span><span class="p">(</span><span class="n">cocos2d</span><span class="o">::</span><span class="n">CCNode</span> <span class="o">*</span><span class="n">sender</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">data</span><span class="p">);</span>
<span class="k">virtual</span> <span class="kt">void</span> <span class="n">executeFunction</span><span class="p">(</span><span class="kt">int</span> <span class="n">responseCode</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">resp</span><span class="p">);</span>
<span class="nl">private:</span>
<span class="kt">int</span> <span class="n">m_nHandler</span><span class="p">;</span>
<span class="p">};</span>
<span class="cp">#endif //__MyHttpClient_H_
</span>
</pre></td></tr></tbody></table></code></pre></figure>
<h1 id="myhttpclientcpp">MyHttpClient.cpp</h1>
<figure class="highlight"><pre><code class="language-cpp" data-lang="cpp"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
</pre></td><td class="code"><pre> <span class="c1">//</span>
<span class="c1">// Created by liangwei on 12-9-10.</span>
<span class="c1">//</span>
<span class="c1">// To change the template use AppCode | Preferences | File Templates.</span>
<span class="c1">//</span>
<span class="cp">#include "MyHttpClient.h"
</span> <span class="cp">#include "CCLuaEngine.h"
</span>
<span class="kt">void</span> <span class="n">MyHttpClient</span><span class="o">::</span><span class="n">doGet</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">url</span><span class="p">,</span><span class="kt">int</span> <span class="n">handler</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">MyHttpClient</span> <span class="o">*</span><span class="n">myHttpClient</span><span class="o">=</span><span class="k">new</span> <span class="n">MyHttpClient</span><span class="p">();</span>
<span class="n">CCHttpRequest</span><span class="o">*</span> <span class="n">request</span> <span class="o">=</span> <span class="k">new</span> <span class="n">CCHttpRequest</span><span class="p">();</span>
<span class="n">request</span><span class="o">-></span><span class="n">setUrl</span><span class="p">(</span><span class="n">url</span><span class="p">);</span>
<span class="n">myHttpClient</span><span class="o">-></span><span class="n">m_nHandler</span><span class="o">=</span><span class="n">handler</span><span class="p">;</span>
<span class="n">request</span><span class="o">-></span><span class="n">setRequestType</span><span class="p">(</span><span class="n">CCHttpRequest</span><span class="o">::</span><span class="n">kHttpGet</span><span class="p">);</span>
<span class="n">request</span><span class="o">-></span><span class="n">setResponseCallback</span><span class="p">(</span><span class="n">myHttpClient</span><span class="p">,</span> <span class="n">callfuncND_selector</span><span class="p">(</span><span class="n">MyHttpClient</span><span class="o">::</span><span class="n">onHttpRequestCompleted</span><span class="p">));</span>
<span class="n">CCHttpClient</span><span class="o">::</span><span class="n">getInstance</span><span class="p">()</span><span class="o">-></span><span class="n">send</span><span class="p">(</span><span class="n">request</span><span class="p">);</span>
<span class="n">request</span><span class="o">-></span><span class="n">release</span><span class="p">();</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="n">MyHttpClient</span><span class="o">::</span><span class="n">executeFunction</span><span class="p">(</span><span class="kt">int</span> <span class="n">responseCode</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">data</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">CCScriptEngineProtocol</span> <span class="o">*</span><span class="n">engine</span> <span class="o">=</span> <span class="n">CCScriptEngineManager</span><span class="o">::</span><span class="n">sharedManager</span><span class="p">()</span><span class="o">-></span><span class="n">getScriptEngine</span><span class="p">();</span>
<span class="n">lua_State</span><span class="o">*</span> <span class="n">m_state</span><span class="o">=</span><span class="n">engine</span><span class="o">-></span><span class="n">getLuaState</span><span class="p">();</span>
<span class="n">lua_pushinteger</span><span class="p">(</span><span class="n">m_state</span><span class="p">,</span> <span class="n">responseCode</span><span class="p">);</span>
<span class="n">lua_pushstring</span><span class="p">(</span><span class="n">m_state</span><span class="p">,</span><span class="n">data</span><span class="p">);</span>
<span class="n">engine</span><span class="o">-></span><span class="n">executeFunctionByHandler</span><span class="p">(</span><span class="k">this</span><span class="o">-></span><span class="n">m_nHandler</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="n">MyHttpClient</span><span class="o">::</span><span class="n">onHttpRequestCompleted</span><span class="p">(</span><span class="n">CCNode</span> <span class="o">*</span><span class="n">sender</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">resp</span><span class="p">)</span> <span class="p">{</span>
<span class="n">CCLog</span><span class="p">(</span><span class="s">"onHttpRequestCompleted"</span><span class="p">);</span>
<span class="n">CCHttpResponse</span> <span class="o">*</span><span class="n">response</span> <span class="o">=</span> <span class="p">(</span><span class="n">CCHttpResponse</span><span class="o">*</span><span class="p">)</span><span class="n">resp</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">response</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="n">statusCode</span> <span class="o">=</span> <span class="n">response</span><span class="o">-></span><span class="n">getResponseCode</span><span class="p">();</span>
<span class="n">std</span><span class="o">::</span><span class="n">vector</span> <span class="o">*</span><span class="n">buffer</span> <span class="o">=</span> <span class="n">response</span><span class="o">-></span><span class="n">getResponseData</span><span class="p">();</span>
<span class="kt">char</span> <span class="n">data</span><span class="p">[</span><span class="n">buffer</span><span class="o">-></span><span class="n">size</span><span class="p">()</span><span class="o">+</span><span class="mi">1</span><span class="p">];</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">buffer</span><span class="o">-></span><span class="n">size</span><span class="p">();</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">=</span><span class="p">(</span><span class="o">*</span><span class="n">buffer</span><span class="p">)[</span><span class="n">i</span><span class="p">];</span>
<span class="p">}</span>
<span class="n">data</span><span class="p">[</span><span class="n">buffer</span><span class="o">-></span><span class="n">size</span><span class="p">()]</span><span class="o">=</span><span class="sc">'\0'</span><span class="p">;</span>
<span class="k">this</span><span class="o">-></span><span class="n">executeFunction</span><span class="p">(</span><span class="n">statusCode</span><span class="p">,</span> <span class="n">data</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<h1 id="liangwei_extensionh">liangwei_extension.h</h1>
<figure class="highlight"><pre><code class="language-cpp" data-lang="cpp"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="code"><pre> <span class="cp">#ifndef __LIANGWEI_EXTENSION_H_
</span> <span class="cp">#define __LIANGWEI_EXTENSION_H_
</span>
<span class="k">extern</span> <span class="s">"C"</span> <span class="p">{</span>
<span class="cp">#include "tolua++.h"
</span> <span class="cp">#include "tolua_fix.h"
</span> <span class="p">}</span>
<span class="cp">#include "cocos2d.h"
</span>
<span class="k">using</span> <span class="k">namespace</span> <span class="n">cocos2d</span><span class="p">;</span>
<span class="n">TOLUA_API</span> <span class="kt">int</span> <span class="nf">tolua_liangwei_extension_open</span><span class="p">(</span><span class="n">lua_State</span><span class="o">*</span> <span class="n">tolua_S</span><span class="p">);</span>
<span class="cp">#endif // __COCOS2DX_EXTENSION_NETWORK_H_
</span>
</pre></td></tr></tbody></table></code></pre></figure>
<h1 id="liangwei_extensionc">liangwei_extension.c</h1>
<figure class="highlight"><pre><code class="language-cpp" data-lang="cpp"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
</pre></td><td class="code"><pre> <span class="cp">#include "liangwei_extension.h"
</span> <span class="cp">#include "MyHttpClient.h"
</span>
<span class="k">static</span> <span class="kt">int</span> <span class="nf">tolua_liangwei_MyHttpClient_doGet00</span><span class="p">(</span><span class="n">lua_State</span><span class="o">*</span> <span class="n">tolua_S</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">tolua_Error</span> <span class="n">tolua_err</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span>
<span class="o">!</span><span class="n">tolua_isusertable</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="s">"MyHttpClient"</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="o">&</span><span class="n">amp</span><span class="p">;</span><span class="n">tolua_err</span><span class="p">)</span> <span class="o">||</span>
<span class="o">!</span><span class="n">tolua_isstring</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="o">&</span><span class="n">amp</span><span class="p">;</span><span class="n">tolua_err</span><span class="p">)</span> <span class="o">||</span>
<span class="o">!</span><span class="n">tolua_isfunction</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="o">&</span><span class="n">amp</span><span class="p">;</span><span class="n">tolua_err</span><span class="p">)</span> <span class="o">||</span>
<span class="o">!</span><span class="n">tolua_isnoobj</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="o">&</span><span class="n">amp</span><span class="p">;</span><span class="n">tolua_err</span><span class="p">)</span>
<span class="p">)</span>
<span class="k">goto</span> <span class="n">tolua_lerror</span><span class="p">;</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">url</span> <span class="o">=</span> <span class="p">((</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span><span class="p">)</span> <span class="n">tolua_tostring</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">0</span><span class="p">));</span>
<span class="kt">int</span> <span class="n">funcID</span> <span class="o">=</span> <span class="p">(</span><span class="n">tolua_ref_function</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">0</span><span class="p">));</span>
<span class="p">{</span>
<span class="n">MyHttpClient</span><span class="o">::</span><span class="n">doGet</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">funcID</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="nl">tolua_lerror:</span>
<span class="n">tolua_error</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="s">"#ferror in function 'node'."</span><span class="p">,</span><span class="o">&</span><span class="n">amp</span><span class="p">;</span><span class="n">tolua_err</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">TOLUA_API</span> <span class="kt">int</span> <span class="nf">tolua_liangwei_extension_open</span> <span class="p">(</span><span class="n">lua_State</span><span class="o">*</span> <span class="n">tolua_S</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">tolua_open</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">);</span>
<span class="n">tolua_usertype</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="s">"MyHttpClient"</span><span class="p">);</span>
<span class="n">tolua_module</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="nb">NULL</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
<span class="n">tolua_beginmodule</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="nb">NULL</span><span class="p">);</span>
<span class="c1">//注册函数和类</span>
<span class="n">tolua_cclass</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span> <span class="s">"MyHttpClient"</span><span class="p">,</span> <span class="s">"MyHttpClient"</span><span class="p">,</span> <span class="s">"CCObject"</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
<span class="n">tolua_beginmodule</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="s">"MyHttpClient"</span><span class="p">);</span>
<span class="n">tolua_function</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="s">"doGet"</span><span class="p">,</span><span class="n">tolua_liangwei_MyHttpClient_doGet00</span><span class="p">);</span>
<span class="n">tolua_endmodule</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">);</span>
<span class="n">tolua_endmodule</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="cp">#if defined(LUA_VERSION_NUM) &amp;&amp; LUA_VERSION_NUM &gt;= 501
</span> <span class="n">TOLUA_API</span> <span class="kt">int</span> <span class="nf">luaopen_liangwei_extension</span> <span class="p">(</span><span class="n">lua_State</span><span class="o">*</span> <span class="n">tolua_S</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">tolua_liangwei_extension_open</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">);</span>
<span class="p">};</span>
<span class="cp">#endif
</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>lua的绑定解析可以参考:tolua映射解析</p>
<p>cocos2d-x 的HttpClient扩展需要curl模块<br />
编译参考:curl for ios 的编译方法及脚本</p>
Cocos2d X2.0.2的httpclient扩展数据不全bug
2012-09-10T00:00:00-05:00
http://www.zhaoxiaodan.com/cocos2dx/cocos2d-x2.0.2的httpclient扩展数据不全bug
<p>刚绑完lua, 试用的时候就发现取到的数据不全, debug了之后发现是cocos2d-x 里httpclient扩展的一个小bug;</p>
<p>writeData是当curl收到数据的时候的数据copy函数, 也就是把curl收到的一段一段的数据包,我们把它”拼接”成一个完成的data;然后把整个data返回给调用者, 那自然是不能clear()的, 不然整个结果肯定就收到的就是最后一次的数据;</p>
<p>HttpClient.ccp 的writeData 修改为:</p>
<figure class="highlight"><pre><code class="language-cpp" data-lang="cpp"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="code"><pre> <span class="kt">size_t</span> <span class="nf">writeData</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">ptr</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">size</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">nmemb</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">stream</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="kt">char</span><span class="o">></span> <span class="o">*</span><span class="n">recvBuffer</span> <span class="o">=</span> <span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o"><</span><span class="kt">char</span><span class="o">>*</span><span class="p">)</span><span class="n">stream</span><span class="p">;</span>
<span class="kt">size_t</span> <span class="n">sizes</span> <span class="o">=</span> <span class="n">size</span> <span class="o">*</span> <span class="n">nmemb</span><span class="p">;</span>
<span class="c1">// recvBuffer->clear();</span>
<span class="c1">// recvBuffer->assign((char*)ptr, (char*)ptr + sizes);</span>
<span class="n">recvBuffer</span><span class="o">-></span><span class="n">insert</span><span class="p">(</span><span class="n">recvBuffer</span><span class="o">-></span><span class="n">end</span><span class="p">(),</span> <span class="p">(</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">ptr</span><span class="p">,</span> <span class="p">(</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">ptr</span> <span class="o">+</span> <span class="n">sizes</span><span class="p">);</span>
<span class="k">return</span> <span class="n">sizes</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
Tolua映射解析,以及如何在cocos2d X里把自己的c++class映射到lua中
2012-09-09T00:00:00-05:00
http://www.zhaoxiaodan.com/cocos2dx/tolua映射解析,以及如何在cocos2d-x里把自己的c++class映射到lua中
<p>不管引擎多强大,总会自己拓展一些类. 或者使用一些有源码包. 用c++实现然后再映射到lua里.</p>
<p>1.映射</p>
<blockquote>
<p>这个代码还是比较好懂,整个函数就是把类和所继承的类和对应的lua做一个映射.
然后做函数的映射;也就是把MyHttpClient的doGet函数映射到tolua_liangwei_MyHttpClient_doGet00函数里去,
由 tolua_liangwei_MyHttpClient_doGet00 去做 调用时参数的获取和转换,
然后再由tolua_liangwei_MyHttpClient_doGet00真正的调用MyHttpClient的doGet函数;</p>
</blockquote>
<figure class="highlight"><pre><code class="language-cpp" data-lang="cpp"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
</pre></td><td class="code"><pre> <span class="n">TOLUA_API</span> <span class="kt">int</span> <span class="nf">tolua_liangwei_extension_open</span> <span class="p">(</span><span class="n">lua_State</span><span class="o">*</span> <span class="n">tolua_S</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">tolua_open</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">);</span>
<span class="c1">//注册类型名</span>
<span class="n">tolua_usertype</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="s">"MyHttpClient"</span><span class="p">);</span>
<span class="n">tolua_module</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="nb">NULL</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
<span class="n">tolua_beginmodule</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="nb">NULL</span><span class="p">);</span>
<span class="c1">//注册函数和类</span>
<span class="n">tolua_cclass</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span> <span class="s">"MyHttpClient"</span><span class="p">,</span> <span class="s">"MyHttpClient"</span><span class="p">,</span> <span class="s">"CCObject"</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
<span class="n">tolua_beginmodule</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="s">"MyHttpClient"</span><span class="p">);</span>
<span class="n">tolua_function</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="s">"doGet"</span><span class="p">,</span><span class="n">tolua_liangwei_MyHttpClient_doGet00</span><span class="p">);</span>
<span class="n">tolua_endmodule</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">);</span>
<span class="n">tolua_endmodule</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>2.然后来看tolua_liangwei_MyHttpClient_doGet00</p>
<figure class="highlight"><pre><code class="language-cpp" data-lang="cpp"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
</pre></td><td class="code"><pre> <span class="k">static</span> <span class="kt">int</span> <span class="nf">tolua_liangwei_MyHttpClient_doGet00</span><span class="p">(</span><span class="n">lua_State</span><span class="o">*</span> <span class="n">tolua_S</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">tolua_Error</span> <span class="n">tolua_err</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span>
<span class="c1">//函数类型检测, 如果不满足就goto tolua_lerror报错</span>
<span class="o">!</span><span class="n">tolua_isusertable</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="s">"MyHttpClient"</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="o">&</span><span class="n">tolua_err</span><span class="p">)</span> <span class="o">||</span>
<span class="o">!</span><span class="n">tolua_isstring</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="o">&</span><span class="n">tolua_err</span><span class="p">)</span> <span class="o">||</span>
<span class="o">!</span><span class="n">tolua_isfunction</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="o">&</span><span class="n">tolua_err</span><span class="p">)</span> <span class="o">||</span>
<span class="o">!</span><span class="n">tolua_isnoobj</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="o">&</span><span class="n">tolua_err</span><span class="p">)</span>
<span class="p">)</span>
<span class="k">goto</span> <span class="n">tolua_lerror</span><span class="p">;</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="c1">//满足, 取出第一个参数. tolua_tostring, 顾名思义 ,lua类型转换成c++的string(char *);</span>
<span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">url</span> <span class="o">=</span> <span class="p">((</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span><span class="p">)</span> <span class="n">tolua_tostring</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">0</span><span class="p">));</span>
<span class="c1">//取出第二个参数, lua的function是一个int型的"描述符"</span>
<span class="kt">int</span> <span class="n">funcID</span> <span class="o">=</span> <span class="p">(</span><span class="n">tolua_ref_function</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">0</span><span class="p">));</span>
<span class="p">{</span>
<span class="c1">//真正调用方法;</span>
<span class="n">MyHttpClient</span><span class="o">::</span><span class="n">doGet</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">funcID</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="nl">tolua_lerror:</span>
<span class="n">tolua_error</span><span class="p">(</span><span class="n">tolua_S</span><span class="p">,</span><span class="s">"#ferror in function 'node'."</span><span class="p">,</span><span class="o">&</span><span class="n">tolua_err</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>3.那么tolua_liangwei_extension_open 是什么时候”被”调用?一般在register lua engine 完成之后调用; 也就是在AppDelegate::applicationDidFinishLaunching() 中:</p>
<figure class="highlight"><pre><code class="language-cpp" data-lang="cpp"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre> <span class="c1">// register lua engine</span>
<span class="n">CCScriptEngineProtocol</span><span class="o">*</span> <span class="n">pEngine</span> <span class="o">=</span> <span class="n">CCLuaEngine</span><span class="o">::</span><span class="n">engine</span><span class="p">();</span>
<span class="n">CCScriptEngineManager</span><span class="o">::</span><span class="n">sharedManager</span><span class="p">()</span><span class="o">-></span><span class="n">setScriptEngine</span><span class="p">(</span><span class="n">pEngine</span><span class="p">);</span>
<span class="c1">//注册自己的扩展</span>
<span class="n">lua_State</span><span class="o">*</span> <span class="n">L</span> <span class="o">=</span> <span class="n">pEngine</span><span class="o">-></span><span class="n">getLuaState</span><span class="p">();</span>
<span class="n">tolua_liangwei_extension_open</span><span class="p">(</span><span class="n">L</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>4.MyHttpClient 的原型:</p>
<figure class="highlight"><pre><code class="language-cpp" data-lang="cpp"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre> <span class="k">class</span> <span class="nc">MyHttpClient</span> <span class="o">:</span> <span class="k">public</span> <span class="n">cocos2d</span><span class="o">::</span><span class="n">CCObject</span>
<span class="p">{</span>
<span class="nl">public:</span>
<span class="k">static</span> <span class="kt">void</span> <span class="n">doGet</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">url</span><span class="p">,</span><span class="kt">int</span> <span class="n">handler</span><span class="p">);</span>
<span class="k">virtual</span> <span class="kt">void</span> <span class="n">onHttpRequestCompleted</span><span class="p">(</span><span class="n">cocos2d</span><span class="o">::</span><span class="n">CCNode</span> <span class="o">*</span><span class="n">sender</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">data</span><span class="p">);</span>
<span class="k">virtual</span> <span class="kt">void</span> <span class="n">executeFunction</span><span class="p">(</span><span class="kt">int</span> <span class="n">responseCode</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">resp</span><span class="p">);</span>
<span class="nl">private:</span>
<span class="kt">int</span> <span class="n">m_nHandler</span><span class="p">;</span>
<span class="p">};</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>onHttpRequestCompleted和executeFunction 都不需要暴露给lua, 所以就不需要映射;</p>
Curl For Ios的编译方法及脚本
2012-09-09T00:00:00-05:00
http://www.zhaoxiaodan.com/ios/curl-for-ios的编译方法及脚本
<p>转自: https://github.com/miyabichan/cURL-SSL-for-iOS</p>
<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
</pre></td><td class="code"><pre> <span class="c">#!/bin/sh</span>
<span class="c"># Automatic build script for libcurl</span>
<span class="c"># for iPhoneOS and iPhoneSimulator</span>
<span class="c">#</span>
<span class="c"># Created by Miyabi Kazamatsuri on 19.04.11.</span>
<span class="c"># Copyright 2011 Miyabi Kazamatsuri. All rights reserved.</span>
<span class="c">#</span>
<span class="c"># Licensed under the Apache License, Version 2.0 (the "License");</span>
<span class="c"># you may not use this file except in compliance with the License.</span>
<span class="c"># You may obtain a copy of the License at</span>
<span class="c">#</span>
<span class="c"># http://www.apache.org/licenses/LICENSE-2.0</span>
<span class="c">#</span>
<span class="c"># Unless required by applicable law or agreed to in writing, software</span>
<span class="c"># distributed under the License is distributed on an "AS IS" BASIS,</span>
<span class="c"># WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</span>
<span class="c"># See the License for the specific language governing permissions and</span>
<span class="c"># limitations under the License.</span>
<span class="c">#</span>
<span class="c">###########################################################################</span>
<span class="c"># Change values here #</span>
<span class="c"># #</span>
<span class="nv">VERSION</span><span class="o">=</span><span class="s2">"7.21.5"</span> <span class="c">#</span>
<span class="nv">SDKVERSION</span><span class="o">=</span><span class="s2">"5.1"</span> <span class="c">#</span>
<span class="nv">OPENSSL</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">PWD</span><span class="k">}</span><span class="s2">/../OpenSSL"</span> <span class="c">#</span>
<span class="c"># #</span>
<span class="c">###########################################################################</span>
<span class="c"># #</span>
<span class="c"># Don't change anything under this line! #</span>
<span class="c"># #</span>
<span class="c">###########################################################################</span>
<span class="nv">CURRENTPATH</span><span class="o">=</span><span class="sb">`</span><span class="nb">pwd</span><span class="sb">`</span>
<span class="nv">DEVELOPER</span><span class="o">=</span><span class="sb">`</span>xcode-select <span class="nt">--print-path</span><span class="sb">`</span>
<span class="nb">set</span> <span class="nt">-e</span>
<span class="k">if</span> <span class="o">[</span> <span class="o">!</span> <span class="nt">-e</span> curl-<span class="k">${</span><span class="nv">VERSION</span><span class="k">}</span>.tar.gz <span class="o">]</span><span class="p">;</span> <span class="k">then
</span><span class="nb">echo</span> <span class="s2">"Downloading curl-</span><span class="k">${</span><span class="nv">VERSION</span><span class="k">}</span><span class="s2">.tar.gz"</span>
curl <span class="nt">-O</span> http://curl.haxx.se/download/curl-<span class="k">${</span><span class="nv">VERSION</span><span class="k">}</span>.tar.gz
<span class="k">else
</span><span class="nb">echo</span> <span class="s2">"Using curl-</span><span class="k">${</span><span class="nv">VERSION</span><span class="k">}</span><span class="s2">.tar.gz"</span>
<span class="k">fi
if</span> <span class="o">[</span> <span class="nt">-d</span> <span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span>/src <span class="o">]</span><span class="p">;</span> <span class="k">then
</span><span class="nb">rm</span> <span class="nt">-rf</span> <span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span>/src
<span class="k">fi
if</span> <span class="o">[</span> <span class="nt">-d</span> <span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span>/bin <span class="o">]</span><span class="p">;</span> <span class="k">then
</span><span class="nb">rm</span> <span class="nt">-rf</span> <span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span>/bin
<span class="k">fi
</span><span class="nb">mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span><span class="s2">/src"</span>
<span class="nb">tar </span>zxf curl-<span class="k">${</span><span class="nv">VERSION</span><span class="k">}</span>.tar.gz <span class="nt">-C</span> <span class="s2">"</span><span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span><span class="s2">/src"</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span><span class="s2">/src/curl-</span><span class="k">${</span><span class="nv">VERSION</span><span class="k">}</span><span class="s2">"</span>
<span class="c">############</span>
<span class="c"># iPhone Simulator</span>
<span class="nv">ARCH</span><span class="o">=</span><span class="s2">"i386"</span>
<span class="nv">PLATFORM</span><span class="o">=</span><span class="s2">"iPhoneSimulator"</span>
<span class="nb">echo</span> <span class="s2">"Building libcurl for </span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="s2">"Please stand by..."</span>
<span class="nb">export </span><span class="nv">CC</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">DEVELOPER</span><span class="k">}</span><span class="s2">/Platforms/</span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}</span><span class="s2">.platform/Developer/usr/bin/gcc"</span>
<span class="nb">export </span><span class="nv">CFLAGS</span><span class="o">=</span><span class="s2">"-arch </span><span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span><span class="s2"> -isysroot </span><span class="k">${</span><span class="nv">DEVELOPER</span><span class="k">}</span><span class="s2">/Platforms/</span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}</span><span class="s2">.platform/Developer/SDKs/</span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="s2">.sdk -I</span><span class="k">${</span><span class="nv">OPENSSL</span><span class="k">}</span><span class="s2">/include -L</span><span class="k">${</span><span class="nv">OPENSSL</span><span class="k">}</span><span class="s2">"</span>
<span class="nb">mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span><span class="s2">/bin/</span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="s2">.sdk"</span>
<span class="nv">LOG</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span><span class="s2">/bin/</span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="s2">.sdk/build-libcurl-</span><span class="k">${</span><span class="nv">VERSION</span><span class="k">}</span><span class="s2">.log"</span>
<span class="nb">echo</span> <span class="s2">"Configure libcurl for </span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span><span class="s2">"</span>
./configure <span class="nt">-prefix</span><span class="o">=</span><span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span>/bin/<span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}${</span><span class="nv">SDKVERSION</span><span class="k">}</span>.sdk <span class="nt">-disable-shared</span> <span class="nt">-with-random</span><span class="o">=</span>/dev/urandom <span class="nt">--with-ssl</span> &gt<span class="p">;</span> <span class="s2">"</span><span class="k">${</span><span class="nv">LOG</span><span class="k">}</span><span class="s2">"</span> 2&gt<span class="p">;</span>&amp<span class="p">;</span>1
<span class="nb">echo</span> <span class="s2">"Make libcurl for </span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span><span class="s2">"</span>
make &gt<span class="p">;</span>&gt<span class="p">;</span> <span class="s2">"</span><span class="k">${</span><span class="nv">LOG</span><span class="k">}</span><span class="s2">"</span> 2&gt<span class="p">;</span>&amp<span class="p">;</span>1
make <span class="nb">install</span> &gt<span class="p">;</span>&gt<span class="p">;</span> <span class="s2">"</span><span class="k">${</span><span class="nv">LOG</span><span class="k">}</span><span class="s2">"</span> 2&gt<span class="p">;</span>&amp<span class="p">;</span>1
make clean &gt<span class="p">;</span>&gt<span class="p">;</span> <span class="s2">"</span><span class="k">${</span><span class="nv">LOG</span><span class="k">}</span><span class="s2">"</span> 2&gt<span class="p">;</span>&amp<span class="p">;</span>1
<span class="nb">echo</span> <span class="s2">"Building libcurl for </span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span><span class="s2">, finished"</span>
<span class="c">#############</span>
<span class="c">#############</span>
<span class="c"># iPhoneOS armv6</span>
<span class="nv">ARCH</span><span class="o">=</span><span class="s2">"armv6"</span>
<span class="nv">PLATFORM</span><span class="o">=</span><span class="s2">"iPhoneOS"</span>
<span class="nb">echo</span> <span class="s2">"Building libcurl for </span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="s2">"Please stand by..."</span>
<span class="nb">export </span><span class="nv">CC</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">DEVELOPER</span><span class="k">}</span><span class="s2">/Platforms/</span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}</span><span class="s2">.platform/Developer/usr/bin/gcc"</span>
<span class="nb">export </span><span class="nv">CFLAGS</span><span class="o">=</span><span class="s2">"-arch </span><span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span><span class="s2"> -isysroot </span><span class="k">${</span><span class="nv">DEVELOPER</span><span class="k">}</span><span class="s2">/Platforms/</span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}</span><span class="s2">.platform/Developer/SDKs/</span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="s2">.sdk -I</span><span class="k">${</span><span class="nv">OPENSSL</span><span class="k">}</span><span class="s2">/include -L</span><span class="k">${</span><span class="nv">OPENSSL</span><span class="k">}</span><span class="s2">"</span>
<span class="nb">mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span><span class="s2">/bin/</span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="s2">-</span><span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span><span class="s2">.sdk"</span>
<span class="nv">LOG</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span><span class="s2">/bin/</span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="s2">-</span><span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span><span class="s2">.sdk/build-libcurl-</span><span class="k">${</span><span class="nv">VERSION</span><span class="k">}</span><span class="s2">.log"</span>
<span class="nb">echo</span> <span class="s2">"Configure libcurl for </span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span><span class="s2">"</span>
./configure <span class="nt">-prefix</span><span class="o">=</span><span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span>/bin/<span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}${</span><span class="nv">SDKVERSION</span><span class="k">}</span>-<span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span>.sdk <span class="nt">--host</span><span class="o">=</span><span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span><span class="nt">-apple-darwin</span> <span class="nt">--disable-shared</span> <span class="nt">-with-random</span><span class="o">=</span>/dev/urandom <span class="nt">--with-ssl</span> &gt<span class="p">;</span> <span class="s2">"</span><span class="k">${</span><span class="nv">LOG</span><span class="k">}</span><span class="s2">"</span> 2&gt<span class="p">;</span>&amp<span class="p">;</span>1
<span class="nb">echo</span> <span class="s2">"Make libcurl for </span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span><span class="s2">"</span>
make &gt<span class="p">;</span>&gt<span class="p">;</span> <span class="s2">"</span><span class="k">${</span><span class="nv">LOG</span><span class="k">}</span><span class="s2">"</span> 2&gt<span class="p">;</span>&amp<span class="p">;</span>1
make <span class="nb">install</span> &gt<span class="p">;</span>&gt<span class="p">;</span> <span class="s2">"</span><span class="k">${</span><span class="nv">LOG</span><span class="k">}</span><span class="s2">"</span> 2&gt<span class="p">;</span>&amp<span class="p">;</span>1
make clean &gt<span class="p">;</span>&gt<span class="p">;</span> <span class="s2">"</span><span class="k">${</span><span class="nv">LOG</span><span class="k">}</span><span class="s2">"</span> 2&gt<span class="p">;</span>&amp<span class="p">;</span>1
<span class="nb">echo</span> <span class="s2">"Building libcurl for </span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span><span class="s2">, finished"</span>
<span class="c">#############</span>
<span class="c">#############</span>
<span class="c"># iPhoneOS armv7</span>
<span class="nv">ARCH</span><span class="o">=</span><span class="s2">"armv7"</span>
<span class="nv">PLATFORM</span><span class="o">=</span><span class="s2">"iPhoneOS"</span>
<span class="nb">echo</span> <span class="s2">"Building libcurl for </span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="s2">"Please stand by..."</span>
<span class="nb">export </span><span class="nv">CC</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">DEVELOPER</span><span class="k">}</span><span class="s2">/Platforms/</span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}</span><span class="s2">.platform/Developer/usr/bin/gcc"</span>
<span class="nb">export </span><span class="nv">CFLAGS</span><span class="o">=</span><span class="s2">"-arch </span><span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span><span class="s2"> -isysroot </span><span class="k">${</span><span class="nv">DEVELOPER</span><span class="k">}</span><span class="s2">/Platforms/</span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}</span><span class="s2">.platform/Developer/SDKs/</span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="s2">.sdk -I</span><span class="k">${</span><span class="nv">OPENSSL</span><span class="k">}</span><span class="s2">/include -L</span><span class="k">${</span><span class="nv">OPENSSL</span><span class="k">}</span><span class="s2">"</span>
<span class="nb">mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span><span class="s2">/bin/</span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="s2">-</span><span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span><span class="s2">.sdk"</span>
<span class="nv">LOG</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span><span class="s2">/bin/</span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="s2">-</span><span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span><span class="s2">.sdk/build-libcurl-</span><span class="k">${</span><span class="nv">VERSION</span><span class="k">}</span><span class="s2">.log"</span>
<span class="nb">echo</span> <span class="s2">"Configure libcurl for </span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span><span class="s2">"</span>
./configure <span class="nt">-prefix</span><span class="o">=</span><span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span>/bin/<span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}${</span><span class="nv">SDKVERSION</span><span class="k">}</span>-<span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span>.sdk <span class="nt">--host</span><span class="o">=</span><span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span><span class="nt">-apple-darwin</span> <span class="nt">--disable-shared</span> <span class="nt">-with-random</span><span class="o">=</span>/dev/urandom <span class="nt">--with-ssl</span> &gt<span class="p">;</span> <span class="s2">"</span><span class="k">${</span><span class="nv">LOG</span><span class="k">}</span><span class="s2">"</span> 2&gt<span class="p">;</span>&amp<span class="p">;</span>1
<span class="nb">echo</span> <span class="s2">"Make libcurl for </span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span><span class="s2">"</span>
make &gt<span class="p">;</span>&gt<span class="p">;</span> <span class="s2">"</span><span class="k">${</span><span class="nv">LOG</span><span class="k">}</span><span class="s2">"</span> 2&gt<span class="p">;</span>&amp<span class="p">;</span>1
make <span class="nb">install</span> &gt<span class="p">;</span>&gt<span class="p">;</span> <span class="s2">"</span><span class="k">${</span><span class="nv">LOG</span><span class="k">}</span><span class="s2">"</span> 2&gt<span class="p">;</span>&amp<span class="p">;</span>1
make clean &gt<span class="p">;</span>&gt<span class="p">;</span> <span class="s2">"</span><span class="k">${</span><span class="nv">LOG</span><span class="k">}</span><span class="s2">"</span> 2&gt<span class="p">;</span>&amp<span class="p">;</span>1
<span class="nb">echo</span> <span class="s2">"Building libcurl for </span><span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="s2"> </span><span class="k">${</span><span class="nv">ARCH</span><span class="k">}</span><span class="s2">, finished"</span>
<span class="c">#############</span>
<span class="c">#############</span>
<span class="c"># Universal Library</span>
<span class="nb">echo</span> <span class="s2">"Build universal library..."</span>
lipo <span class="nt">-create</span> <span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span>/bin/iPhoneSimulator<span class="k">${</span><span class="nv">SDKVERSION</span><span class="k">}</span>.sdk/lib/libcurl.a <span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span>/bin/iPhoneOS<span class="k">${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="nt">-armv6</span>.sdk/lib/libcurl.a <span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span>/bin/iPhoneOS<span class="k">${</span><span class="nv">SDKVERSION</span><span class="k">}</span><span class="nt">-armv7</span>.sdk/lib/libcurl.a <span class="nt">-output</span> <span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span>/libcurl.a
<span class="nb">mkdir</span> <span class="nt">-p</span> <span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span>/include
<span class="nb">cp</span> <span class="nt">-R</span> <span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span>/bin/iPhoneSimulator<span class="k">${</span><span class="nv">SDKVERSION</span><span class="k">}</span>.sdk/include/curl <span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span>/include/
<span class="nb">echo</span> <span class="s2">"Building all steps done."</span>
<span class="nb">echo</span> <span class="s2">"Cleaning up..."</span>
<span class="nb">rm</span> <span class="nt">-rf</span> <span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span>/src
<span class="nb">rm</span> <span class="nt">-rf</span> <span class="k">${</span><span class="nv">CURRENTPATH</span><span class="k">}</span>/bin
<span class="nb">echo</span> <span class="s2">"Done."</span>
</pre></td></tr></tbody></table></code></pre></figure>
Mysql的timestamp类型自动更新问题
2012-09-07T00:00:00-05:00
http://www.zhaoxiaodan.com/lnmp/MySQL的timestamp类型自动更新问题
<p>在CREATE TABLE语句中,第1个TIMESTAMP列可以用下面的任何一种方式声明:</p>
<ul>
<li>
<p>如果定义时DEFAULT CURRENT_TIMESTAMP和ON UPDATE CURRENT_TIMESTAMP子句都有,列值为默认使用当前的时间戳,并且自动更新。</p>
</li>
<li>
<p>如果不使用DEFAULT或ON UPDATE子句,那么它等同于DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP。</p>
</li>
<li>
<p>如果只有DEFAULT CURRENT_TIMESTAMP子句,而没有ON UPDATE子句,列值默认为当前时间戳但不自动更新。</p>
</li>
<li>
<p>如果没用DEFAULT子句,但有ON UPDATE CURRENT_TIMESTAMP子句,列默认为0并自动更新。</p>
</li>
<li>
<p>如果有一个常量值DEFAULT,该列会有一个默认值,而且不会自动初始化为当前时间戳。如果该列还有一个ON UPDATE CURRENT_TIMESTAMP子句,这个时间戳会自动更新,否则该列有一个默认的常量但不会自动更新。</p>
</li>
</ul>
Innodb Memcached Daemon Plugin–mysql官方5.6.6内嵌memcached支持
2012-08-05T00:00:00-05:00
http://www.zhaoxiaodan.com/lnmp/InnoDB-Memcached-Daemon-Plugin–Mysql官方5.6.6内嵌memcached支持
<p>官方文档:</p>
<p>http://dev.mysql.com/doc/refman/5.6/en/innodb-memcached.html</p>
<p>当前版本提供的功能:</p>
<ul>
<li>memcached as a daemon plugin of mysqld: both mysqld and memcached run in the same process space, with very low latency access to data.</li>
</ul>
<p>当作mysqld的一个守护进程: mysqld 和memcached 运行在同一个进程空间,对数据访问有非常低的延迟;</p>
<ul>
<li>Direct access to InnoDB tables, bypassing the SQL parser, the optimizer, and even the Handler API layer.</li>
</ul>
<p>绕过SQL语句的解析,优化,甚至整个SQL Handler API, 直接从InnoDB 表(文件)读取数据</p>
<ul>
<li>Standard memcached protocols, both the text-based protocol and the binary protocol. The memcached+ InnoDB combination passes all 55 compatibility tests from the memcapable command.</li>
</ul>
<p>(使用)标准的memcached协议, (支持)字符数据和二进制数据; memcached+InnoDB的组合(还有其他组合?) 通过了所有55项memcapable 命令的兼容性测试;</p>
<ul>
<li>Multi-column support: you can map multiple columns into the “value” part of the key/value store, with column values delimited by a user-specified separator character.</li>
</ul>
<p>多列支持:可以把多个列使用用户指定的分隔符链接起来 , 作为key/value存储系统中value的一部分,(存储起来)</p>
<ul>
<li>By default, you use the memcached protocol to read and write data directly to InnoDB, and let MySQL manage the in-memory caching through the InnoDB buffer pool. Advanced users can also configure the system as a traditional memcached server, with all data cached only in memory, or use a combination ofmemcached caching and InnoDB persistent storage. The default settings represent the combination of high reliability with the fewest surprises for database applications. For example, the default settings avoid uncommitted data on the database side, or stale data returned for memcached get requests.</li>
</ul>
<p>默认情况, 你可以使用memecached协议来直接读写数据到InnoDB表, 并让mysql 通过 InnoDB 缓冲池 将数据缓存到内存里. 高级用户还可以进行设置 ,选择是让数据是像传统memcached 一样只缓存到内存 , 还是使用一个memcached缓存+InnoDB持久化的组合. 这种组合的默认设置就给数据库应用带来了高可靠性. 例如 ,避免了数据只在持久化一端(只存在数据库中,而没有被缓存) ,又避免了当使用memcache的get命令获取到旧的数据; (感觉整体其实就是说 ,这个插件帮你管了 持久化端 的时候 memcached 端的更新, 不用你自己手动去管了.)</p>
<ul>
<li>You can control how often data is passed back and forth between InnoDB and memcached operations through thedaemon_memcached_r_batch_size and daemon_memcached_w_batch_size configuration options. Both of these options default to a value of 1 for maximum reliability.</li>
</ul>
<p>你可以通过修改daemon_memcached_r_batch_size 和 daemon_memcached_w_batch_size 这两个配置选项来控制InnoDB 端和memcached 端 交互数据的频率 .这两个配置的默认值都是1 ,以获得最高的可靠性;</p>
<ul>
<li>You can specify any memcached configuration options through the MySQL configuration variabledaemon_memcached_option. For example, you might change the port that memcached listens on, reduce the maximum number of simultaneous connections, change the maximum memory size for a key/value pair, or enable debugging messages for the error log.</li>
</ul>
<p>你可以通过mysql 配置变量 daemon_memcached_option特别指定 memcached的配置 . 例如 ,改变memcache的监听端口, 修改最大连接数, 修改最大内存大小 或者 打开 debug 信息;</p>
<ul>
<li>A configuration option innodb_api_trx_level lets you control the transaction isolation level on queries processed by the memcached interface. Although memcached has concept of transactions, you might use this property to control how soon memcached sees changes caused by SQL statements, if you issue DML statements on the same table that memcached interfaces with. By default, it is set to READ UNCOMMITTED.</li>
</ul>
<p>配置项 innodb_api_trx_level 让你控制当通过memcached接口访问数据时的隔离级别. (虽然memcached也有事务的概念) ,你可以用此控制当数据被 SQL 端修改后 ,memcached端多快能看到(同时被更新); 默认, 这个设置的默认是 READ UNCOMMITTED 级别;</p>
Android的boot.img头含义
2012-08-03T00:00:00-05:00
http://www.zhaoxiaodan.com/android/android的boot.img头含义
<ul>
<li>4 * 2, magic,固定为”ANDROID!”</li>
<li>4 * 1, kernel长度,小端unsigned类型</li>
<li>4 * 1, kernel地址,应为base + 0×00008000</li>
<li>4 * 1, ramdisk长度,小端unsigned</li>
<li>4 * 1, ramdisk地址,应为base + 0×01000000</li>
<li>4 * 1, second stage长度,小端unsigned,为0</li>
<li>4 * 1, second stage地址,应为base + 0x00f00000</li>
<li>4 * 1, tags地址,应为base + 0×00000100</li>
<li>4 * 1, page大小,小端unsigned, 为2048或者4096</li>
<li>4 * 2, 未使用,固定为0×00</li>
<li>4 * 4, 板子名字,一般为空</li>
<li>4 * 128, 内核命令参数,一大串</li>
<li>4 * 8, id,不知道啥玩意,0×00</li>
</ul>
Android镜像制作方法
2012-08-01T00:00:00-05:00
http://www.zhaoxiaodan.com/android/android镜像制作方法
<h1 id="updatezip包的制作">update.zip包的制作</h1>
<p>1.新建一个目标,在此目录下准备好需要的文件,如system目录文件、boot.img、recoveryimg等</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> mkdir testupdate
cp system/ testupdate/ -tf
注:如果文件是system.img镜像可以用unyaffs解压出来得到system
</code></pre></div></div>
<p>1.用make-update-script工具生成update-script脚本,如下</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> cp make-update-script testupdate/
cp android-info.txt testupdate/
cd testupdate
./make-update-script system android-info.txt > update-script
rm make-update-script android-info.txt
vi update-script //根据需要适当修改些脚本
说明:system是要更新的目录,android-info.txt是板的版本信息,update-script是输出文件名
</code></pre></div></div>
<p>1.建立一个目录名称为META-INF/com/google/android,把上面生成的脚本放进去</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> mkdir -p META-INF/com/google/android
mv update-script META-INF/com/google/android/
</code></pre></div></div>
<p>1.zip压缩</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> zip -r update.zip system META-INF
</code></pre></div></div>
<p>1.给压缩文件添加sign</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> mv update.zip ../signapk/
cd ../signapk/
java -jar signapk.jar testkey.x509.pem testkey.pk8 update.zip signed-update.zip
</code></pre></div></div>
<p>1.删除多余的文件,并把生成的包rename</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> rm update.zip
mv signed-update.zip ../update.zip
cd ../
</code></pre></div></div>
<p>1.大功告成,把更新包update.zip拷到sdcard根目录下去验证吧!</p>
<h2 id="注意">注意:</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1)如果文件里有连接,应该在获取update-script之后在原文件里删除链接文件,再打包,否则symlink将出错;
2)如果原文件里有空目录,所获的签名将失去此记录,所以如果空目录必须存在,更新之后的文件将与原文件不同(少了空目录)
</code></pre></div></div>
<h1 id="ramdiskimg-制作">ramdisk.img 制作</h1>
<ul>
<li>
<p>方法1:</p>
<ol>
<li>
<p>解压a:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> mv ramdisk.img ramdisk.img.gz
gunzip ramdisk,img.gz
mkdir ramdisk;cd ramdisk
cpio -i -F ../ramdisk.img
</code></pre></div> </div>
</li>
<li>
<p>压缩:</p>
<p>#产生要pack的目录list,也可以自己列
cpio -i -t -F ../ramdisk.img > list
#利用刚生成的list文件列表,cpio归档
cpio -o -H newc -O new.img < list
#压缩
gzip new.img</p>
</li>
</ol>
</li>
<li>
<h2 id="方法2">方法2:</h2>
<ol>
<li>
<p>解压:</p>
<table>
<tbody>
<tr>
<td>gunzip -c ../your-ramdisk-file</td>
<td>cpio -i</td>
</tr>
</tbody>
</table>
</li>
<li>
<p>压缩:</p>
<table>
<tbody>
<tr>
<td>find .</td>
<td>cpio -o -H newc</td>
<td>gzip > ../newramdisk.cpio.gz</td>
</tr>
</tbody>
</table>
</li>
</ol>
<p><em>注意:</em></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> 在android里的做法是
1)先得到ramdisk所需要的文件,比如root目录
2)用mkbootfs制作ramdisk.img,用法如下
mkbootfs root | gzip > ramdisk.img
</code></pre></div> </div>
</li>
</ul>
<h1 id="bootimg的制作">boot.img的制作</h1>
<p>1:android正常做法</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1):连接
mkbootimg –kernel your-kernel-file –ramdisk newramdisk.cpio.gz –cmdline “mem=128 console=ttymxc0,115200n8 init=/init rw” –output mynewimage.img
或
mkbootimg –kernel your-kernel-file –ramdisk newramdisk.cpio.gz –cmdline –output mynewimage.img
2):提取img中的kernel和ramdisk
./split_bootimg.pl mynewimage.img
</code></pre></div></div>
<p>2:uboot</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>直接把uImage重命名为boot.img即可
</code></pre></div></div>
<p>3:system.img的制作(只为 yaffs2格式)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1)压制:
./mkyaffs2image system/ system.img
2)解压:
./unyaffs system.img
</code></pre></div></div>
<p>4:system.img的制作(只为yaffs2格式)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1)压制:
./mkyaffs2image system/ system.img
2)解压:
./unyaffs system.img
</code></pre></div></div>
<p>5:recovery.img的制作
1:如果recovery的镜像是只有文件系统部分时候可以如第四所示范
2:如果recovery为ramdisk形式</p>
<h1 id="制-作ramdisk的过程">制 作ramdisk的过程。</h1>
<p>1.在/mnt下创建rdmnt 和 rdimg 目录</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> mkdir rdmnt
mkdir rdimg
</code></pre></div></div>
<p>2.创建一个ramdisk文件,大小32768 X 1k。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> dd if=/dev/zero of=rdimg/ramdisk bs=1k count=32768
</code></pre></div></div>
<p>3.使用ext2方式格式该文件</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> mke2fs -F -v -m0 rdimg/ramdisk
</code></pre></div></div>
<p>4.将该ramdisk文件和rdmnt挂载</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> mount -o loop rdimg/ramdisk rdmnt/
</code></pre></div></div>
<p>5.拷贝文件到挂载目录中。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> 文件系统目录在:/home/xrqun/workdir/filesys/
cp –av /home/xrqun/workdir/filesys/* rdmnt
</code></pre></div></div>
<p>6.卸载ramdisk</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> umount rdmnt
</code></pre></div></div>
<p>7压缩 ramdisk文件</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> gzip –c -9 rdimg/ramdisk.gz
</code></pre></div></div>
<p>8.拷贝该ramdisk.gz映像到tftpboot目录下</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> cp rdimg/ramdisk.gz /tftpboot/
</code></pre></div></div>
<ol>
<li>
<p>使用mkimage工具</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> mkimage -n “uboot.ramdisk.filesys” -A arm -O linux -T ramdisk -C gzip -d ramdisk.gz uboot.ramdisk.gz
</code></pre></div> </div>
</li>
</ol>
Nginx_tcp_proxy_module对mysql做负载均衡被block的解决办法
2012-07-20T00:00:00-05:00
http://www.zhaoxiaodan.com/lnmp/nginx_tcp_proxy_module对mysql做负载均衡被block的解决办法
<p>上个文章用nginx_tcp_proxy_module 来给mysql 做均衡负载 ,
在此模块对mysql 做健康检查时 发送空包 ,被max_connect_errors block 的问题 ,
今天简单弄了下 ,让它发送 一个 user=root ,password 为空 的 mysql login packet .
测试了3000多次检查 , 即使登陆不成功 ,也不会被block掉</p>
<p>至于带密码的login packet ,涉及到跟mysql 的握手步骤 ,还有待研究
不过感觉就健康检查没必要真的login 进去 ,
而且即使mysql有login 失败的block ,专门给nginx 所在IP 开一个 密码为空的用户也是可以接受的方案</p>
<p>下面是针对nginx_tcp_proxy_module-v0.26 的patch</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>diff -urN yaoweibin-nginx_tcp_proxy_module-b83e5a6/ngx_tcp_upstream_check.c yaoweibin-nginx_tcp_proxy_module-b83e5a6_new/ngx_tcp_upstream_check.c
--- yaoweibin-nginx_tcp_proxy_module-b83e5a6/ngx_tcp_upstream_check.c 2011-12-30 10:43:33.000000000 +0800
+++ yaoweibin-nginx_tcp_proxy_module-b83e5a6_new/ngx_tcp_upstream_check.c 2012-07-20 14:40:00.888077351 +0800
@@ -77,6 +77,18 @@
"\x00" /* Compression Type : 0x00 = NULL compression */
};
+const char mysql_login_pkt[] = {
+ "\x3c\x00\x00" /* Packet Length , : 封包长度 */
+ "\x01" /* Packet Number : 封包序列号 ,login packet 固定为 0x01 */
+ "\x05\xa6" /* Client Capabilities : 不懂啥意思 */
+ "\x0f\x00" /* Extended Client Capabilities : */
+ "\x00\x00\x00\x01" /* Max packet : =0x01000000 */
+ "\x21" /* Character set : 默认字符集 */
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" /* Unknown */
+ "\x72\x6f\x6f\x74\x00" /* User Name : 0x726f6f74 = 'root' ,字符串的 acsii 码 ,'\0'结尾, 长度变化时,上面的 Packet Length 要跟着变 */
+ "\x00" /* Password : 空密码 , */
+ "\x6d\x79\x73\x71\x6c\x5f\x6e\x61\x74\x69\x76\x65\x5f\x70\x61\x73\x73\x77\x6f\x72\x64\x00" /* 结尾固定字符串 "mysql_native_password" */
+};
#define HANDSHAKE 0x16
#define SERVER_HELLO 0x02
@@ -133,7 +145,7 @@
{
NGX_TCP_CHECK_MYSQL,
"mysql",
- ngx_null_string,
+ ngx_string(mysql_login_pkt),
0,
ngx_tcp_check_send_handler,
ngx_tcp_check_recv_handler,
@@ -1094,6 +1106,9 @@
ctx->send.start = ctx->send.pos = (u_char *)uscf->send.data;
ctx->send.end = ctx->send.last = ctx->send.start + uscf->send.len;
+ ngx_log_debug1(NGX_LOG_DEBUG_TCP, ngx_cycle->log, 0,
+ "mysql_init: send:%V", &uscf->send);
+
ctx->recv.start = ctx->recv.pos = NULL;
ctx->recv.end = ctx->recv.last = NULL;
</code></pre></div></div>
用nginx_tcp_proxy_module给mysql做负载均衡
2012-07-19T00:00:00-05:00
http://www.zhaoxiaodan.com/lnmp/用nginx_tcp_proxy_module给mysql做负载均衡
<p>LVS 太重了, 自己在php里rand() slave又没办法健康检查, 自动上下线挂掉的mysql
整个轻量的mysql负载均衡方案</p>
<p>在42.121.16.232 和 192.168.56.101 分别都有一个mysql</p>
<p>在192.168.56.101 上放 nginx ,也就是 和mysql 在同一台机器</p>
<p>nginx 占用了mysql 原来的3306端口 ,则本机的mysql 挪到端口 3307</p>
<ol>
<li>
<p>下载 module</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> wget https://github.com/yaoweibin/nginx_tcp_proxy_module/tarball/v0.26 -O yaoweibin-nginx_tcp_proxy_module-v0.26-0-g9677e00.tar.gz
tar -xvf yaoweibin-nginx_tcp_proxy_module-v0.26-0-g9677e00.tar.gz
</code></pre></div> </div>
</li>
<li>
<p>给tengine打补丁</p>
</li>
</ol>
<blockquote>
<p>将一下内容存为tcp.patch</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>diff -Nur tengine-1.3.0_bak/src/core/ngx_log.c tengine-1.3.0/src/core/ngx_log.c
--- tengine-1.3.0_bak/src/core/ngx_log.c 2012-07-19 14:52:55.929140710 +0800
+++ tengine-1.3.0/src/core/ngx_log.c 2012-07-19 14:53:32.900674183 +0800
@@ -67,7 +67,7 @@</code>
static const char *debug_levels[] = {
"debug_core", "debug_alloc", "debug_mutex", "debug_event",
- "debug_http", "debug_mail", "debug_mysql"
+ "debug_http", "debug_mail", "debug_mysql","debug_tcp"
};
diff -Nur tengine-1.3.0_bak/src/core/ngx_log.h tengine-1.3.0/src/core/ngx_log.h
--- tengine-1.3.0_bak/src/core/ngx_log.h 2012-07-19 14:52:55.929140710 +0800
+++ tengine-1.3.0/src/core/ngx_log.h 2012-07-19 14:55:46.099001142 +0800
@@ -30,6 +30,7 @@
#define NGX_LOG_DEBUG_HTTP 0x100
#define NGX_LOG_DEBUG_MAIL 0x200
#define NGX_LOG_DEBUG_MYSQL 0x400
+#define NGX_LOG_DEBUG_TCP 0x800
/*
* do not forget to update debug_levels[] in src/core/ngx_log.c
@@ -37,7 +38,7 @@
*/
#define NGX_LOG_DEBUG_FIRST NGX_LOG_DEBUG_CORE
-#define NGX_LOG_DEBUG_LAST NGX_LOG_DEBUG_MYSQL
+#define NGX_LOG_DEBUG_LAST NGX_LOG_DEBUG_TCP
#define NGX_LOG_DEBUG_CONNECTION 0x80000000
#define NGX_LOG_DEBUG_ALL 0x7ffffff0
diff -Nur tengine-1.3.0_bak/src/event/ngx_event_connect.h tengine-1.3.0/src/event/ngx_event_connect.h
--- tengine-1.3.0_bak/src/event/ngx_event_connect.h 2012-07-19 14:52:56.093138648 +0800
+++ tengine-1.3.0/src/event/ngx_event_connect.h 2012-07-19 14:57:56.645359897 +0800
@@ -33,6 +33,7 @@
void *data);
#endif
+#define NGX_INVALED_INDEX (-1)
struct ngx_peer_connection_s {
ngx_connection_t *connection;
@@ -43,6 +44,8 @@
ngx_uint_t tries;
+ ngx_uint_t check_index;
+
ngx_event_get_peer_pt get;
ngx_event_free_peer_pt free;
void *data;
</code></pre></div></div>
<blockquote>
<p>打补丁</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd tengine-1.3.0
patch -p1
</code></pre></div></div>
<p>3.编译 tengine</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ./configure --prefix=/server/tengine --with-file-aio --with-http_lua_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-pcre --add-module=../chaoslawful-drizzle-nginx-module-272cabf --add-module=../agentzh-rds-json-nginx-module-74c21b3 --add-module=../yaoweibin-nginx_tcp_proxy_module-b83e5a6
make &amp;&amp; sudo make install
</code></pre></div></div>
<p>4.配置</p>
<blockquote>
<p>顶节点:</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>tcp {
upstream mysql_cluster {
# simple round-robin
server 42.121.16.232:3306;
server 192.168.56.101:3307;
check interval=3000 rise=2 fall=5 timeout=10000 type=mysql;
}
server {
listen 3306;
proxy_pass mysql_cluster;
}
}
</code></pre></div></div>
<h2 id="问题">问题:</h2>
<p>因为mysql中默认的max_connect_errors是10,
发空包到mysql的端口, mysql认为连接出错,
超过10次,当在访问的时候就被锁住了, 并屏蔽主机的进一步连接请求。</p>
<p>另:
有人在github上给作者提过这个问题, 也没解决</p>
<p>猜测作者测试时用127.0.0.1 来监控 ,
但mysql不会把”本机”给屏蔽掉
many connection errors 的错误就不会重现</p>
<p>可以用telnet 192.168.XX.XX 3306 和telnet 127.0.0.1 3306 两种方式来测
127的怎么telnet都不会有问题 , 192的多试几次就会:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>liangwei@liangwei-ubuntu:~$ telnet 192.168.56.101 3307
Trying 192.168.56.101...
Connected to 192.168.56.101.
Escape character is '^]'.
Host '192.168.56.101' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'Connection closed by foreign host.
</code></pre></div></div>
<p>解决方案设想:</p>
<p>发送 mysql login packet:</p>
Php Fpm使用效率更高的unix_socket
2012-07-17T00:00:00-05:00
http://www.zhaoxiaodan.com/lnmp/php-fpm使用效率更高的unix_socket
<p>通过上个文章 , 本来觉得 在php-fpm 与mysql 通信时也需要开新的端口</p>
<p>但是检查发现, 其实php-fpm 并没有通过打开TCP端口和mysql的3306端口进行通信, 查看my.conf, 发现</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> socket = /var/run/mysqld/mysqld.sock
</code></pre></div></div>
<p>这是一个unix socket, 查资料说是unix socket 比tcp 效率更高,</p>
<blockquote>
<p>什么是UNIX SOCKET?</p>
<p>socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket。虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.1),但是UNIX Domain Socket用于IPC更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。这是因为,IPC机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。UNIX Domain Socket也提供面向流和面向数据包两种API接口,类似于TCP和UDP,但是面向消息的UNIX Domain Socket也是可靠的,消息既不会丢失也不会顺序错乱。</p>
</blockquote>
<p>那么何不让 nginx 访问 php-fpm时 也采用这种方式?</p>
<p>修改 php-fpm.conf(pool.d/www.conf)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> listen = 127.0.0.1:9000
</code></pre></div></div>
<p>改为:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> listen = /dev/shm/php.socket
</code></pre></div></div>
<p>修改nginx 的php-fpm 转发配置:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
</code></pre></div></div>
<p>中的</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> fastcgi_pass 127.0.0.1:9000;
</code></pre></div></div>
<p>改为:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> fastcgi_pass unix:/dev/shm/php.socket;
</code></pre></div></div>
Nginx高效mysql访问模块drizzle安装和使用
2012-07-17T00:00:00-05:00
http://www.zhaoxiaodan.com/lnmp/nginx高效mysql访问模块drizzle安装和使用
<p>1.编译安装 libdrizzle-1.0</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> http://agentzh.org/misc/nginx/drizzle7-2011.07.21.tar.gz
tar xzvf drizzle7-2011.07.21.tar.gz
cd drizzle7-2011.07.21/
./configure –without-server
make libdrizzle-1.0
make install-libdrizzle-1.0
</code></pre></div></div>
<p>2.下载drizzle-nginx-module</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> wget https://github.com/chaoslawful/drizzle-nginx-module/tarball/v0.1.2rc7 -O chaoslawful-drizzle-nginx-module-v0.1.2rc7-0-g272cabf.tar.bz2
</code></pre></div></div>
<p>3.下载rds-json-nginx-module</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> wget https://github.com/agentzh/rds-json-nginx-module/tarball/v0.12rc10 -O agentzh-rds-json-nginx-module-v0.12rc10-0-g74c21b3.tar.gz
</code></pre></div></div>
<p>4.编译安装tengine</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ./configure –prefix=/server/tengine –with-file-aio –with-http_lua_module –with-http_ssl_module –with-http_stub_status_module –with-http_sub_module –with-pcre –add-module=../chaoslawful-drizzle-nginx-module-272cabf –add-module=../agentzh-rds-json-nginx-module-74c21b3
make && sudo make install
</code></pre></div></div>
<p>5.配置</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>>http节点下:
upstream cluster {
drizzle_server 127.0.0.1:3306 dbname=mysql password=liangwei user=root protocol=mysql;
}
>server节点下:
location /mysql {
set $my_sql 'select * from user';
drizzle_query $my_sql;
drizzle_module_header off;
drizzle_pass cluster;
rds_json on;
}
</code></pre></div></div>
<p>6.简单的压力测试:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> liangwei@liangwei-ubuntu:/server/tengine/conf$ webbench -t 20 -c 10000 http://192.168.56.101/mysql
Webbench – Simple Web Benchmark 1.5
Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.
Benchmarking: GET http://192.168.56.101/mysql
10000 clients, running 20 sec.
Speed=82350 pages/min, 7930495 bytes/sec.
Requests: 26978 susceed, 472 failed.
liangwei@liangwei-ubuntu:/server/tengine/conf$ webbench -t 20 -c 10000 http://192.168.56.101/index.php
Webbench – Simple Web Benchmark 1.5
Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.
Benchmarking: GET http://192.168.56.101/index.php
10000 clients, running 20 sec.
Speed=48501 pages/min, 4362751 bytes/sec.
Requests: 16167 susceed, 0 failed.
</code></pre></div></div>
<p>7.错误解决</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>>1)./configure: error: SSL modules require the OpenSSL library.
apt-get install libssl-dev
>2)./configure: error: ngx_http_lua_module requires the Lua library.
apt-get install liblua5.1-0-dev
</code></pre></div></div>
打开linux Tcp端口快速回收
2012-07-07T00:00:00-05:00
http://www.zhaoxiaodan.com/lnmp/打开linux-tcp端口快速回收
<h1 id="事件">事件:</h1>
<p>之前看到一些文章说关于流量升高导致TIME_WAIT增加,MySQL连接大量失败的问题, 当时并不是很理解究竟怎么回事</p>
<p>今天在对阿里云服务器上的tengine 做 1000个并发的webbench 测试时,ssh 会被踢掉,</p>
<p>怀疑网络链接的问题 ,用netstat 查看 ,看到有很多很多 TIME_WAIT 状态的 tcp 链接:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>…
tcp 0 0 42.121.16.232:www 42.121.16.220:44091 TIME_WAIT
tcp 0 0 42.121.16.232:www 42.121.16.220:42309 TIME_WAIT
tcp 0 0 42.121.16.232:www 42.121.16.220:43896 TIME_WAIT
tcp 0 0 42.121.16.232:www 42.121.16.220:44150 TIME_WAIT
tcp 0 0 localhost:9000 localhost:34634 TIME_WAIT
tcp 0 0 42.121.16.232:www 42.121.16.220:44493 TIME_WAIT
tcp 0 0 localhost:9000 localhost:35354 TIME_WAIT
tcp 0 0 42.121.16.232:www 42.121.16.220:42770 TIME_WAIT
tcp 0 0 localhost:9000 localhost:34995 TIME_WAIT
tcp 0 0 localhost:9000 localhost:35038 TIME_WAIT
tcp 0 0 localhost:9000 localhost:34108 TIME_WAIT
tcp 0 0 42.121.16.232:www 42.121.16.220:41764 TIME_WAIT
tcp 0 0 42.121.16.232:www 42.121.16.220:42337 TIME_WAIT
tcp 0 0 42.121.16.232:www 42.121.16.220:44179 TIME_WAIT
…
</code></pre></div></div>
<p>nginx将php解析通过TCP转发给php-fpm,需要占用一个TCP,php-fpm中的mysql连接, 又需要占用一个TCP</p>
<p>(但是检查发现, 其实没有 php-fpm -> mysql 的链接, 因为mysql使用的不是TCP而是UNIX SOCKET, 具体看 <a href="/php/2012/07/17/php-fpm%20%E4%BD%BF%E7%94%A8%20%E6%95%88%E7%8E%87%E6%9B%B4%E9%AB%98%E7%9A%84unix%20socket">这里</a> )</p>
<p>再看:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~# netstat -n | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’
SYN_RECV 186
TIME_WAIT 30245
ESTABLISHED 211
FIN_WAIT1 333
</code></pre></div></div>
<p>TIME_WAIT 居然有3W多个 ,随着时间推移还会极速增加中</p>
<p>怪不得了, 端口也就65536个, 用完了自然就没了!!</p>
<p>而且,</p>
<p>解决办法:打开LINUX TCP 端口快速回收</p>
<p>修改 /etc/sysctl.conf, 添加两条数据</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#打开重用
net.ipv4.tcp_tw_reuse = 1
#打开快速回收
net.ipv4.tcp_tw_recycle = 1
</code></pre></div></div>
<p>保存后执行 sysctl -p 生效</p>
<h2 id="注">注:</h2>
<p>如果nginx,php-fpm,mysql都在同一台机器, 推荐php-fpm和mysql都使用UNIX SOKCET. 方法看 <a href="/php/2012/07/17/php-fpm%20%E4%BD%BF%E7%94%A8%20%E6%95%88%E7%8E%87%E6%9B%B4%E9%AB%98%E7%9A%84unix%20socket">这里</a></p>
<h1 id="time_wait产生原因">TIME_WAIT产生原因:</h1>
<p>1、nginx现有的负载均衡模块实现php fastcgi负载均衡,nginx使用了短连接方式,所以会造成大量处于TIME_WAIT状态的连接。</p>
<p>2、TCP/IP设计者本来是这么设计的, 主要有两个原因</p>
<p>(1) 防止上一次连接中的包,迷路后重新出现,影响新连接(经过2MSL,上一次连接中所有的重复包都会消失)</p>
<p>(2) 可靠的关闭TCP连接, 在主动关闭方发送的最后一个 ack(fin) ,有可能丢失,这时被动方会重新发fin, 如果这时主动方处于 CLOSED 状态 ,就会响应 rst 而不是 ack。所以主动方要处于 TIME_WAIT 状态,而不能是 CLOSED 。</p>
<h1 id="为什么短连接tcp在高并发大流量会扛不住">为什么短连接TCP在高并发大流量会扛不住?</h1>
<p>一般LNMP服务器架构, 都是nginx接受http请求, 然后将php文件的解析转发给php-fpm, 这个过程nginx需要使用新的TCP端口与php-fpm的9000端口去建立TCP链接, 那么, 一个php请求就需要一个新的端口</p>
<p>若2MSL时间为1分钟以上,同时,不同用户http两次请求间隔很短。那么反代的端口,5秒用1W个,10秒2W,15秒3W,20秒4W,25秒5W,30秒6W!!!而linux最大端口就是65535个, 前面已经用掉的端口还没被回收掉。那么将会有很多的用户请求被nginx接收之后,因没有端口资源而无法与php-fpm创建tcp连接… 所以服务器会’挂掉’</p>
可以访问sftp但是不能ssh登录的配置方法
2012-05-18T00:00:00-05:00
http://www.zhaoxiaodan.com/lnmp/可以访问sftp但是不能ssh登录的配置方法
<p>终于搞定了,sftp就有了ftp功能,但是一般可以用sftp的帐号都可以登录ssh</p>
<p>不用特意给web开发另外开ftp直接用sftp</p>
<p>又可以防止他们ssh登陆</p>
<p>修改</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#Subsystem sftp /usr/lib/openssh/sftp-server 为:
Subsystem sftp internal-sftp
</code></pre></div></div>
<p>然后加入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#如果是webdev组,就禁止登陆,并绑定目录
#/var/www 这个目录必须是root:root的
Match group webdev
ChrootDirectory /var/www
X11Forwarding no
AllowTcpForwarding no
ForceCommand internal-sftp
</code></pre></div></div>
Phpredis模块中sessionhandle功能的并发一致性问题
2012-05-08T00:00:00-05:00
http://www.zhaoxiaodan.com/lnmp/phpredis模块中sessionhandle功能的并发一致性问题
<p>session 在php中的生命周期,已文件存储为例</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>* session_start():打开session文件,将session的数据读出来,反序列化后存入$_SESSION变量
* 改变$_SESSION的key值时并不写入 session文件
* session_write_close()后,或者php文件退出后,才将$_SESSION序列化后存入文件
</code></pre></div></div>
<p>这样$_SESSION不管有多少个KEY,都是整体读出,整体写入的。那么这些key的值就会有一致性问题;</p>
<p>例如,原来key1=1111, key2=2222,程序流程:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>线程1:读取session -> 其他操作 --- > 修改key1 为 3333 -> 保存
线程2:读取session -> 修改key2为4444 -> 保存
</code></pre></div></div>
<p>那么此时的结果会是 key1=3333 ,key2=2222</p>
<p>在文件为session的存储方法时,php源码在fopen了session_file_handle之后,会对这个session_file_handle做一个flock(,LOCK_EX),也就是加一个写锁;将并发变成顺序</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>线程1:获取锁 -> 读取session -> 其他操作- > 修改key1 为 3333 -> 保存
线程2:等待锁 ————————————————————————-> 读取session -> key2为4444 -> 保存
</code></pre></div></div>
<p>而phpredis使用redis作为存储介质, 没有做这个锁, 那么会有这个并发一致性问题存在</p>
<p>使用下面的代码即可重现这个问题:</p>
<ul>
<li>先执行one.php</li>
<li>再执行two.php</li>
<li>不等two.php返回立刻three.php</li>
<li>等two.php返回后four.php输出最后结果</li>
</ul>
<p>测试代码:</p>
<ul>
<li>one.php:</li>
</ul>
<figure class="highlight"><pre><code class="language-php" data-lang="php"> <span class="cp"><?php</span>
<span class="k">require_once</span><span class="p">(</span><span class="s2">"redis.php"</span><span class="p">);</span>
<span class="nb">session_start</span><span class="p">();</span>
<span class="nv">$_SESSION</span><span class="p">[</span><span class="s1">'key1'</span><span class="p">]</span><span class="o">=</span><span class="mi">11111111</span><span class="p">;</span>
<span class="nv">$_SESSION</span><span class="p">[</span><span class="s1">'key2'</span><span class="p">]</span><span class="o">=</span><span class="mi">22222222</span><span class="p">;</span>
<span class="k">print</span> <span class="s2">"session_id:"</span><span class="o">.</span><span class="nb">session_id</span><span class="p">()</span><span class="o">.</span><span class="s2">"<br>"</span><span class="p">;</span>
<span class="k">print</span> <span class="nv">$_SESSION</span><span class="p">[</span><span class="s1">'key1'</span><span class="p">]</span><span class="o">.</span><span class="s2">"|"</span><span class="o">.</span><span class="nv">$_SESSION</span><span class="p">[</span><span class="s1">'key2'</span><span class="p">];</span>
<span class="cp">?></span>
</code></pre></figure>
<ul>
<li>two.php:</li>
</ul>
<figure class="highlight"><pre><code class="language-php" data-lang="php"> <span class="cp"><?php</span>
<span class="k">require_once</span><span class="p">(</span><span class="s2">"redis.php"</span><span class="p">);</span>
<span class="nb">session_start</span><span class="p">();</span>
<span class="nb">sleep</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span>
<span class="nv">$_SESSION</span><span class="p">[</span><span class="s1">'key2'</span><span class="p">]</span><span class="o">=</span><span class="mi">444444444</span><span class="p">;</span>
<span class="k">print</span> <span class="s2">"session_id:"</span><span class="o">.</span><span class="nb">session_id</span><span class="p">()</span><span class="o">.</span><span class="s2">"<br>"</span><span class="p">;</span>
<span class="cp">?></span>
</code></pre></figure>
<ul>
<li>three.php:</li>
</ul>
<figure class="highlight"><pre><code class="language-php" data-lang="php"> <span class="cp"><?php</span>
<span class="k">require_once</span><span class="p">(</span><span class="s2">"redis.php"</span><span class="p">);</span>
<span class="nb">session_start</span><span class="p">();</span>
<span class="nv">$_SESSION</span><span class="p">[</span><span class="s1">'key1'</span><span class="p">]</span><span class="o">=</span><span class="mi">333333333</span><span class="p">;</span>
<span class="k">print</span> <span class="s2">"session_id:"</span><span class="o">.</span><span class="nb">session_id</span><span class="p">()</span><span class="o">.</span><span class="s2">"<br>"</span><span class="p">;</span>
<span class="k">print</span> <span class="nv">$_SESSION</span><span class="p">[</span><span class="s1">'key1'</span><span class="p">]</span><span class="o">.</span><span class="s2">"|"</span><span class="o">.</span><span class="nv">$_SESSION</span><span class="p">[</span><span class="s1">'key2'</span><span class="p">];</span>
<span class="cp">?></span>
</code></pre></figure>
<ul>
<li>four.php:</li>
</ul>
<figure class="highlight"><pre><code class="language-php" data-lang="php"> <span class="cp"><?php</span>
<span class="k">require_once</span><span class="p">(</span><span class="s2">"redis.php"</span><span class="p">);</span>
<span class="nb">session_start</span><span class="p">();</span>
<span class="k">print</span> <span class="s2">"session_id:"</span><span class="o">.</span><span class="nb">session_id</span><span class="p">()</span><span class="o">.</span><span class="s2">"<br>"</span><span class="p">;</span>
<span class="k">print</span> <span class="nv">$_SESSION</span><span class="p">[</span><span class="s1">'key1'</span><span class="p">]</span><span class="o">.</span><span class="s2">"|"</span><span class="o">.</span><span class="nv">$_SESSION</span><span class="p">[</span><span class="s1">'key2'</span><span class="p">];</span>
<span class="cp">?></span>
</code></pre></figure>
<ul>
<li>redis.php:</li>
</ul>
<figure class="highlight"><pre><code class="language-php" data-lang="php"> <span class="cp"><?php</span>
<span class="nb">ini_set</span><span class="p">(</span><span class="s2">"session.save_handler"</span><span class="p">,</span><span class="s2">"redis"</span><span class="p">);</span>
<span class="nb">ini_set</span><span class="p">(</span><span class="s2">"session.save_path"</span><span class="p">,</span><span class="s2">"tcp://127.0.0.1:6379?timeout=1"</span><span class="p">);</span>
<span class="cp">?></span>
</code></pre></figure>