<?php
include_once $_SERVER['DOCUMENT_ROOT'] . '/include/shared-manual.inc';
$TOC = array();
$TOC_DEPRECATED = array();
$PARENTS = array();
include_once dirname(__FILE__) ."/toc/book.yar.inc";
$setup = array (
  'home' => 
  array (
    0 => 'index.php',
    1 => 'PHP Manual',
  ),
  'head' => 
  array (
    0 => 'UTF-8',
    1 => 'zh',
  ),
  'this' => 
  array (
    0 => 'yar.protocol.php',
    1 => 'Yar 协议',
    2 => 'Yar 协议',
  ),
  'up' => 
  array (
    0 => 'book.yar.php',
    1 => 'Yar',
  ),
  'prev' => 
  array (
    0 => 'book.yar.php',
    1 => 'Yar',
  ),
  'next' => 
  array (
    0 => 'yar.setup.php',
    1 => '安装/配置',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'zh',
    'path' => 'reference/yar/protocol.xml',
  ),
  'history' => 
  array (
  ),
);
$setup["toc"] = $TOC;
$setup["toc_deprecated"] = $TOC_DEPRECATED;
$setup["parents"] = $PARENTS;
manual_setup($setup);

contributors($setup);

?>
<div id="yar.protocol" class="chapter">
 <h1 class="title">Yar 协议</h1>

 <p class="simpara">
  Yar 不依赖 schema 或 IDL 文件：网络上传输的一切都是纯字节。
  任何能够读写字节的语言都可以与 Yar 服务通信，完全不需要安装任何框架——
  只需构造一个固定大小的二进制请求头和一个序列化后的请求体，
  把它们发送到服务 URI，再解析响应即可。
 </p>
 <p class="simpara">
  一条消息由一个固定大小为 82 字节的头部和紧随其后的消息体组成。
  头部的布局与下面的 C 结构体完全一致，紧凑排列、没有填充字节，
  并按声明顺序逐个字段写入网络：
 </p>
 <div class="example-contents">
<div class="ccode"><pre class="ccode">typedef struct _yar_header {
    uint32_t       id;            /* transaction id */
    uint16_t       version;       /* protocol version, currently always 0 */
    uint32_t       magic_num;     /* must be 0x80DFEC60 */
    uint32_t       reserved;
    unsigned char  provider[32];  /* request from whom (authentication) */
    unsigned char  token[32];     /* request token (authentication) */
    uint32_t       body_len;      /* length of the whole body, including
                                     the packager identifier */
} __attribute__ ((packed)) yar_header_t;</pre>
</div>
 </div>

 <p class="simpara">
  其中 <code class="literal">id</code>、<code class="literal">magic_num</code>、<code class="literal">reserved</code>
  和 <code class="literal">body_len</code> 字段以网络字节序（大端）存储；
  其余字段是原始字节。
 </p>
 <p class="simpara">
  消息体以一个 8 字节的打包器标识符开头——<code class="literal">PHP</code>、<code class="literal">JSON</code>
  或 <code class="literal">MSGPACK</code>，不足部分以零填充——用于告知接收方其余内容的编码方式，
  其后是序列化内容本身。
 </p>
 <ul class="itemizedlist">
  <li class="listitem">
   <p class="simpara">
    请求体解码后是一个数组，包含以下键：<code class="literal">i</code>（事务 id）、<code class="literal">m</code>
    （被调用的方法）和 <code class="literal">p</code>（参数列表）。
   </p>
  </li>
  <li class="listitem">
   <p class="simpara">
    响应体解码后是一个数组，包含以下键：<code class="literal">i</code>（事务 id）、<code class="literal">s</code>
    （状态，取值为 <code class="literal">YAR_ERR_*</code> 常量之一）、<code class="literal">r</code>（返回值）、
    <code class="literal">o</code>（服务方法产生的任何输出）以及 <code class="literal">e</code>
    （调用失败时的错误或异常）。
   </p>
  </li>
 </ul>
 <p class="simpara">
  通过 HTTP 传输时，消息作为 POST 请求的正文发送，响应作为回复的正文到达；
  通过 TCP 或 Unix socket 传输时，消息直接写入流中。
 </p>
 <div class="example" id="example-1">
  <p><strong>示例 #1 在不安装扩展的情况下调用 Yar 服务</strong></p>
  <div class="example-contents"><p>
   下面这个独立脚本仅使用标准 socket，就为 <code class="literal">php</code>
   打包器构造了一个有效的 Yar 请求，将其发送到服务 URI，
   并输出解码后的响应。用
   <a href="yar.examples.php" class="link">示例</a>中的
   <span class="classname"><strong class="classname">Operator</strong></span> 服务运行该脚本，输出为
   <code class="literal">int(3)</code>。
  </p></div>
  <div class="example-contents">
<div class="phpcode"><pre><code style="color: #000000"><span style="color: #0000BB">&lt;?php

$uri </span><span style="color: #007700">= </span><span style="color: #DD0000">"http://api.example.com/operator.php"</span><span style="color: #007700">;

</span><span style="color: #FF8000">/* 1. the body: packager identifier + serialized request */
</span><span style="color: #0000BB">$serialized </span><span style="color: #007700">= </span><span style="color: #0000BB">serialize</span><span style="color: #007700">(array(</span><span style="color: #DD0000">"i" </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">1</span><span style="color: #007700">, </span><span style="color: #DD0000">"m" </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">"add"</span><span style="color: #007700">, </span><span style="color: #DD0000">"p" </span><span style="color: #007700">=&gt; array(</span><span style="color: #0000BB">1</span><span style="color: #007700">, </span><span style="color: #0000BB">2</span><span style="color: #007700">)));
</span><span style="color: #0000BB">$body </span><span style="color: #007700">= </span><span style="color: #0000BB">str_pad</span><span style="color: #007700">(</span><span style="color: #DD0000">"PHP"</span><span style="color: #007700">, </span><span style="color: #0000BB">8</span><span style="color: #007700">, </span><span style="color: #DD0000">"\0"</span><span style="color: #007700">) . </span><span style="color: #0000BB">$serialized</span><span style="color: #007700">;

</span><span style="color: #FF8000">/* 2. the header: 82 bytes, multi-byte integers in network byte order */
</span><span style="color: #0000BB">$header </span><span style="color: #007700">= </span><span style="color: #0000BB">pack</span><span style="color: #007700">(</span><span style="color: #DD0000">"N"</span><span style="color: #007700">, </span><span style="color: #0000BB">1</span><span style="color: #007700">)                    </span><span style="color: #FF8000">/* id */
        </span><span style="color: #007700">. </span><span style="color: #0000BB">pack</span><span style="color: #007700">(</span><span style="color: #DD0000">"v"</span><span style="color: #007700">, </span><span style="color: #0000BB">0</span><span style="color: #007700">)                    </span><span style="color: #FF8000">/* version */
        </span><span style="color: #007700">. </span><span style="color: #0000BB">pack</span><span style="color: #007700">(</span><span style="color: #DD0000">"N"</span><span style="color: #007700">, </span><span style="color: #0000BB">0x80DFEC60</span><span style="color: #007700">)           </span><span style="color: #FF8000">/* magic number */
        </span><span style="color: #007700">. </span><span style="color: #0000BB">pack</span><span style="color: #007700">(</span><span style="color: #DD0000">"N"</span><span style="color: #007700">, </span><span style="color: #0000BB">0</span><span style="color: #007700">)                    </span><span style="color: #FF8000">/* reserved */
        </span><span style="color: #007700">. </span><span style="color: #0000BB">str_pad</span><span style="color: #007700">(</span><span style="color: #DD0000">""</span><span style="color: #007700">, </span><span style="color: #0000BB">32</span><span style="color: #007700">, </span><span style="color: #DD0000">"\0"</span><span style="color: #007700">)           </span><span style="color: #FF8000">/* provider */
        </span><span style="color: #007700">. </span><span style="color: #0000BB">str_pad</span><span style="color: #007700">(</span><span style="color: #DD0000">""</span><span style="color: #007700">, </span><span style="color: #0000BB">32</span><span style="color: #007700">, </span><span style="color: #DD0000">"\0"</span><span style="color: #007700">)           </span><span style="color: #FF8000">/* token */
        </span><span style="color: #007700">. </span><span style="color: #0000BB">pack</span><span style="color: #007700">(</span><span style="color: #DD0000">"N"</span><span style="color: #007700">, </span><span style="color: #0000BB">strlen</span><span style="color: #007700">(</span><span style="color: #0000BB">$body</span><span style="color: #007700">));       </span><span style="color: #FF8000">/* body length */

/* 3. send it as the body of a POST request */
</span><span style="color: #0000BB">$stream </span><span style="color: #007700">= </span><span style="color: #0000BB">stream_context_create</span><span style="color: #007700">(array(</span><span style="color: #DD0000">"http" </span><span style="color: #007700">=&gt; array(
    </span><span style="color: #DD0000">"method"  </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">"POST"</span><span style="color: #007700">,
    </span><span style="color: #DD0000">"header"  </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">"Content-Type: application/octet-stream\r\n"</span><span style="color: #007700">,
    </span><span style="color: #DD0000">"content" </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$header </span><span style="color: #007700">. </span><span style="color: #0000BB">$body</span><span style="color: #007700">,
)));
</span><span style="color: #0000BB">$reply </span><span style="color: #007700">= </span><span style="color: #0000BB">file_get_contents</span><span style="color: #007700">(</span><span style="color: #0000BB">$uri</span><span style="color: #007700">, </span><span style="color: #0000BB">false</span><span style="color: #007700">, </span><span style="color: #0000BB">$stream</span><span style="color: #007700">);

</span><span style="color: #FF8000">/* 4. parse the reply: 82-byte header, then the response body */
</span><span style="color: #0000BB">$response </span><span style="color: #007700">= </span><span style="color: #0000BB">unserialize</span><span style="color: #007700">(</span><span style="color: #0000BB">substr</span><span style="color: #007700">(</span><span style="color: #0000BB">$reply</span><span style="color: #007700">, </span><span style="color: #0000BB">82 </span><span style="color: #007700">+ </span><span style="color: #0000BB">8</span><span style="color: #007700">));
</span><span style="color: #0000BB">var_dump</span><span style="color: #007700">(</span><span style="color: #0000BB">$response</span><span style="color: #007700">[</span><span style="color: #DD0000">"r"</span><span style="color: #007700">]);
</span><span style="color: #0000BB">?&gt;</span></code></pre></div>
  </div>

 </div>
 <p class="simpara">
  一个更完整的纯 PHP 客户端实现位于
  <a href="https://github.com/laruence/yar" class="link external">&raquo;&nbsp;Yar 源码仓库</a>的
  <code class="literal">tools/</code> 目录中，它还会解码响应头，并支持并发调用。
 </p>
</div>
<?php manual_footer($setup); ?>