<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Yumeng Li's Blog]]></title>
  <link href="http://liyumeng.me/atom.xml" rel="self"/>
  <link href="http://liyumeng.me/"/>
  <updated>2015-04-14T23:25:31+08:00</updated>
  <id>http://liyumeng.me/</id>
  <author>
    <name><![CDATA[Yumeng Li]]></name>
    <email><![CDATA[lym199286@live.com]]></email>
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[WebSocket Server 的Python实现]]></title>
    <link href="http://liyumeng.me/blog/2015/04/13/websocket-server-de-python-shixian-dot-md/"/>
    <updated>2015-04-13T20:02:49+08:00</updated>
    <id>http://liyumeng.me/blog/2015/04/13/websocket-server-de-python-shixian-dot-md</id>
    <content type="html"><![CDATA[<p><strong>WebSocket</strong> 是一种HTML5新的协议，它实现了浏览器与服务器全双工通信。为了大家能对WebSocket有更深入的了解，我们还是先看看浏览器的正常的Http请求都干了什么。</p>

<p><img src="http://i2.tietuku.com/14cbfc2972b5206f.jpg" alt="" /></p>

<!--more-->


<p>如上图，我们平时用浏览器访问网页，一般都是由浏览器向服务器发送一个Http请求，服务器返回相应的数据，其过程可描述为:</p>

<ol>
<li>客户端（浏览器）与服务器建立TCP连接，进行TCP连接的三次握手</li>
<li>TCP连接建立之后，浏览器向服务端发送HTTP请求数据包</li>
<li>服务端收到浏览器的数据，根据请求内容，向浏览器发送数据</li>
<li>进行四次挥手关闭TCP连接</li>
</ol>


<p>按照上面的方式，我们在访问一次网页的时候就需要多次建立TCP连接，TCP的建立和关闭是比较耗资源的，所以为了更有效地利用TCP连接，在HTTP1.1中规定了默认保持长连接，也就是数据传输完成后，不进行四次挥手关闭TCP连接，而是保持TCP连接，等待同域名下继续使用这个通道传输数据。</p>

<p>但Http长连接的方式对于服务器来说仍然只是被动的接收数据，无法主动向浏览器发送数据，因此便有了WebSocket。比如原来想实现一个WebQQ的功能，就需要浏览器轮询式地每隔几秒向服务器发送查询请求，看看是否有新消息。有了WebSocket，服务端一旦发现有新消息通知了，就会立即主动向浏览器发送数据，即快捷又节省资源。</p>

<h3>WebSocket的握手</h3>

<p>进行WebSocket连接也需要发送一次Http请求，这次请求通常被称为<strong>握手</strong>，这个握手和TCP连接的三次握手不是一个概念，TCP是在传输层的握手，这次握手是在应用层的，是在传输层基础上的。说白了就是人为规定了一种Http请求头格式，一旦浏览器按这种格式发送，就视为要进行WebSocket连接。格式如下：</p>

<p><strong>Request Headers:</strong></p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>GET ws://localhost:8000/echo HTTP/1.1
</span><span class='line'>Host: localhost:8000
</span><span class='line'>Upgrade: websocket
</span><span class='line'>Connection: Upgrade
</span><span class='line'>Sec-WebSocket-Version: 13
</span><span class='line'>Sec-WebSocket-Key: 8zFkAy+naxm3Gg8pnMHKwA==</span></code></pre></td></tr></table></div></figure>


<p><strong>Response Headers:</strong></p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class=''><span class='line'> HTTP/1.1 101 Switching Protocols
</span><span class='line'> Upgrade: websocket
</span><span class='line'> Connection: Upgrade
</span><span class='line'> Sec-WebSocket-Accept: sDV9wEDoveIgEhi7kD3G2Vlr6gA=
</span><span class='line'> Sec-WebSocket-Protocol: chat </span></code></pre></td></tr></table></div></figure>


<p>看完了WebSocket握手的协议，我们先来写点代码吧，看看用Python如何来实现WebSocket的Server。既然是Server，我们只需要完成Response Headers的生成，Request Headers是浏览器自己生成的。</p>

<p>首先创建一个socket监听，用来接收浏览器发来的请求</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="n">s</span><span class="o">=</span><span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span><span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
</span><span class='line'><span class="n">s</span><span class="o">.</span><span class="n">bind</span><span class="p">(</span><span class="s">&#39;127.0.0.1&#39;</span><span class="p">,</span><span class="s">&#39;8000&#39;</span><span class="p">)</span>
</span><span class='line'><span class="n">s</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
</span><span class='line'><span class="k">print</span> <span class="s">&#39;Waiting for connection ......&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<p>这里socket监听的端口设置的是8000，也可设置成其他端口,<code>listen(5)</code>表示的是最多同时建立5个socket连接</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
</span><span class='line'>  <span class="n">sock</span><span class="p">,</span><span class="n">addr</span><span class="o">=</span><span class="n">s</span><span class="o">.</span><span class="n">accept</span><span class="p">()</span>
</span><span class='line'>  <span class="n">t</span><span class="o">=</span><span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">websocketlink</span><span class="p">,</span><span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span><span class="n">addr</span><span class="p">))</span>
</span><span class='line'>  <span class="n">t</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
</span></code></pre></td></tr></table></div></figure>


<p>上面的代码主要是不断接收浏览器请求，然后创建一个处理线程，去处理这个请求，主线程只负责监听并接收请求。
创建线程这两个参数<code>target</code>, <code>args</code> 一个是函数名，一个是传入的参数，可见我们之后需要创建一个名叫<code>websocketlink</code>的函数来处理请求。</p>

<p>这里需要注意的是浏览器发送过来的握手信息（也就是Http请求）必须携带头字段<code>Sec-WebSocket-Key</code>,而服务端收到这个请求之后，返回的握手信息，也一定要携带<code>Sec-WebSocket-Accept</code>。这两个值的关系有如下处理逻辑：将<code>Sec-WebSocket-Key</code>的值与<code>258EAFA5-E914-47DA-95CA-C5AB0DC85B11</code>（这个值是协议里规定的）相连接，组成的字符串进行SHA-1散列（160位），再进行base-64编码，即得到了Sec-WebSocket-Accept的值。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="k">def</span> <span class="nf">websocketlink</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span><span class="n">addr</span><span class="p">):</span>
</span><span class='line'>    <span class="k">print</span> <span class="s">&#39;Accept new web socket from </span><span class="si">%s</span><span class="s">:</span><span class="si">%s</span><span class="s">...&#39;</span> <span class="o">%</span> <span class="n">addr</span>
</span><span class='line'>
</span><span class='line'>    <span class="c">#从socket数据中转换成header的dictionary</span>
</span><span class='line'>    <span class="n">headers</span> <span class="o">=</span> <span class="n">get_headers</span><span class="p">(</span><span class="n">sock</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>    <span class="c">#这里根据浏览器发送来的Sec-WebSocket-Key计算出要返回的Sec-WebSocket-Accept的值</span>
</span><span class='line'>    <span class="n">key</span> <span class="o">=</span> <span class="n">headers</span><span class="p">[</span><span class="s">&#39;Sec-WebSocket-Key&#39;</span><span class="p">]</span>
</span><span class='line'>    <span class="n">sha1</span> <span class="o">=</span> <span class="n">hashlib</span><span class="o">.</span><span class="n">sha1</span><span class="p">()</span>
</span><span class='line'>    <span class="n">sha1</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">key</span><span class="o">+</span><span class="s">&#39;258EAFA5-E914-47DA-95CA-C5AB0DC85B11&#39;</span><span class="p">)</span>
</span><span class='line'>    <span class="n">accept_value</span> <span class="o">=</span> <span class="n">base64</span><span class="o">.</span><span class="n">b64encode</span><span class="p">(</span><span class="n">sha1</span><span class="o">.</span><span class="n">digest</span><span class="p">())</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">response</span> <span class="o">=</span> <span class="p">(</span><span class="s">&#39;HTTP/1.1 101 Switching Protocols</span><span class="se">\r\n</span><span class="s">&#39;</span>
</span><span class='line'>              <span class="s">&#39;Upgrade: websocket</span><span class="se">\r\n</span><span class="s">&#39;</span>
</span><span class='line'>              <span class="s">&#39;Connection: Upgrade</span><span class="se">\r\n</span><span class="s">&#39;</span>
</span><span class='line'>              <span class="s">&#39;Sec-WebSocket-Accept: </span><span class="si">%s</span><span class="se">\r\n</span><span class="s">&#39;</span>
</span><span class='line'>              <span class="s">&#39;</span><span class="se">\r\n</span><span class="s">&#39;</span> <span class="o">%</span> <span class="n">accept_value</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>    <span class="c">#使用socket发送数据</span>
</span><span class='line'>    <span class="n">sock</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">response</span><span class="p">)</span>
</span><span class='line'>    <span class="c">#握手完毕</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
</span><span class='line'>      <span class="c">#开始接受数据</span>
</span><span class='line'>      <span class="n">is_finished</span><span class="p">,</span><span class="n">opcode</span><span class="p">,</span><span class="n">data</span> <span class="o">=</span> <span class="n">receive_frame</span><span class="p">(</span><span class="n">sock</span><span class="p">)</span>
</span><span class='line'>      <span class="k">print</span> <span class="n">data</span>
</span><span class='line'>      <span class="c">#将收到的数据发回去</span>
</span><span class='line'>      <span class="k">print</span> <span class="n">send_frame</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>
</span><span class='line'>      <span class="k">pass</span>
</span></code></pre></td></tr></table></div></figure>


<p>从上面可以看出，我们要实现的功能，就是把浏览器发送的信息，再输出回去。get_headers函数的内容如下：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="c">#从socket数据中转换成header的dictionary</span>
</span><span class='line'><span class="k">def</span> <span class="nf">get_headers</span><span class="p">(</span><span class="n">sock</span><span class="p">):</span>
</span><span class='line'>  <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
</span><span class='line'>      <span class="n">data</span> <span class="o">=</span> <span class="n">sock</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span>
</span><span class='line'>      <span class="k">print</span> <span class="s">&#39;request:&#39;</span><span class="p">,</span><span class="n">data</span>
</span><span class='line'>      <span class="k">if</span> <span class="ow">not</span> <span class="n">data</span> <span class="ow">or</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">1024</span><span class="p">:</span>
</span><span class='line'>          <span class="k">break</span>
</span><span class='line'>  <span class="n">headers</span> <span class="o">=</span> <span class="p">{}</span>
</span><span class='line'>  <span class="k">for</span> <span class="n">header</span> <span class="ow">in</span> <span class="n">data</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">&#39;</span><span class="se">\r\n</span><span class="s">&#39;</span><span class="p">):</span>
</span><span class='line'>      <span class="n">items</span> <span class="o">=</span> <span class="n">header</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">&#39;:&#39;</span><span class="p">,</span><span class="mi">2</span><span class="p">)</span>
</span><span class='line'>      <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">items</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">:</span>
</span><span class='line'>          <span class="k">continue</span>
</span><span class='line'>      <span class="n">headers</span><span class="p">[</span><span class="n">items</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span> <span class="o">=</span> <span class="n">items</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
</span><span class='line'>  <span class="k">return</span> <span class="n">headers</span>
</span></code></pre></td></tr></table></div></figure>


<h3>数据传输阶段</h3>

<p>完成了WebSocket握手之后，我们便可以开始进行数据传输了，编写传输代码之前，我们需要了解WebSocket的基本帧协议，如下图：</p>

<p><img src="http://i2.tietuku.com/70b219809b2daab0.png" alt="" /></p>

<p>这是官方协议中给出的帧协议图，如果看着不方便，可以看看下面我自己画的这个：</p>

<p><img src="http://i2.tietuku.com/85363867a3755d87.jpg" alt="" /></p>

<p>按照这个协议便可写出传输数据的代码了，如下：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
</pre></td><td class='code'><pre><code class='py'><span class='line'><span class="c">#接收浏览器的数据</span>
</span><span class='line'><span class="k">def</span> <span class="nf">receive_frame</span><span class="p">(</span><span class="n">sock</span><span class="p">):</span>
</span><span class='line'>    <span class="n">bytes_1_2</span> <span class="o">=</span> <span class="n">sock</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
</span><span class='line'>    <span class="n">byte_1</span> <span class="o">=</span> <span class="nb">ord</span><span class="p">(</span><span class="n">bytes_1_2</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
</span><span class='line'>    <span class="n">byte_2</span> <span class="o">=</span> <span class="nb">ord</span><span class="p">(</span><span class="n">bytes_1_2</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
</span><span class='line'>    <span class="c">#读最高位,结束标识</span>
</span><span class='line'>    <span class="n">is_finished</span> <span class="o">=</span> <span class="n">byte_1</span> <span class="o">&gt;&gt;</span> <span class="mi">7</span> <span class="o">&amp;</span> <span class="mi">1</span>
</span><span class='line'>    <span class="c">#低4位是传输数据的类型</span>
</span><span class='line'>    <span class="n">opcode</span> <span class="o">=</span> <span class="n">byte_1</span> <span class="o">&amp;</span> <span class="mh">0xf</span>
</span><span class='line'>    <span class="k">if</span> <span class="n">opcode</span> <span class="o">==</span> <span class="mh">0x8</span><span class="p">:</span>
</span><span class='line'>    <span class="k">print</span> <span class="s">&#39;connection closed.&#39;</span>
</span><span class='line'>    <span class="k">return</span> <span class="n">is_finished</span><span class="p">,</span><span class="n">opcode</span><span class="p">,</span><span class="s">&#39;&#39;</span>
</span><span class='line'>    <span class="c">#第2个字节的最高位表示是否对数据进行了掩码运算</span>
</span><span class='line'>    <span class="n">is_mask</span> <span class="o">=</span> <span class="n">byte_2</span> <span class="o">&gt;&gt;</span> <span class="mi">7</span> <span class="o">&amp;</span> <span class="mi">1</span>
</span><span class='line'>    <span class="c">#低7位表示传输数据的长度</span>
</span><span class='line'>    <span class="n">length</span> <span class="o">=</span> <span class="n">byte_2</span> <span class="o">&amp;</span> <span class="mh">0x7f</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">length_data</span> <span class="o">=</span> <span class="s">&quot;&quot;</span>
</span><span class='line'>    <span class="c">#如果负载长度是126,之后的两个字节也将被解释为16位无符号整数的负载长度</span>
</span><span class='line'>    <span class="k">if</span> <span class="n">length</span> <span class="o">==</span> <span class="mh">0x7e</span><span class="p">:</span>
</span><span class='line'>    <span class="n">length_data</span> <span class="o">=</span> <span class="n">sock</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
</span><span class='line'>    <span class="n">length</span> <span class="o">=</span> <span class="n">struct</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="s">&quot;!H&quot;</span><span class="p">,</span> <span class="n">length_data</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
</span><span class='line'>    <span class="c">#如果负载长度是127,之后的8个字节也将被解释为一个64位无符号整数的负载长度</span>
</span><span class='line'>    <span class="k">elif</span> <span class="n">length</span> <span class="o">==</span> <span class="mh">0x7f</span><span class="p">:</span>
</span><span class='line'>    <span class="n">length_data</span> <span class="o">=</span> <span class="n">sock</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span>
</span><span class='line'>    <span class="n">length</span> <span class="o">=</span> <span class="n">struct</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="s">&quot;!Q&quot;</span><span class="p">,</span> <span class="n">length_data</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
</span><span class='line'>    <span class="n">mask_key</span> <span class="o">=</span> <span class="s">&quot;&quot;</span>
</span><span class='line'>    <span class="c">#如果进行了掩码运算,要读出32位的掩码数据</span>
</span><span class='line'>    <span class="k">if</span> <span class="n">is_mask</span><span class="p">:</span>
</span><span class='line'>    <span class="n">mask_key</span> <span class="o">=</span> <span class="n">sock</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
</span><span class='line'>    <span class="c">#接下来就是真正传输的数据了</span>
</span><span class='line'>    <span class="n">data</span> <span class="o">=</span> <span class="n">sock</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="n">length</span><span class="p">)</span>
</span><span class='line'>    <span class="k">if</span> <span class="n">is_mask</span><span class="p">:</span>
</span><span class='line'>    <span class="n">data</span> <span class="o">=</span> <span class="n">mask_or_unmask</span><span class="p">(</span><span class="n">mask_key</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>
</span><span class='line'>    <span class="k">return</span> <span class="n">is_finished</span><span class="p">,</span> <span class="n">opcode</span><span class="p">,</span> <span class="n">data</span>
</span></code></pre></td></tr></table></div></figure>


<p>掩码mask_key是客户端随机选择的32位值，每个帧必须选择一个新的掩码，掩码是不可预测的。它不影响数据的长度。进行掩码和解掩码的过程相同，所以可以使用相同的函数，掩码长度是4个字节，具体算法是：数据的每4个字节都与掩码取异或。代码如下：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="k">def</span> <span class="nf">mask_or_unmask</span><span class="p">(</span><span class="n">mask_key</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span>
</span><span class='line'>    <span class="c">#1个字节的int</span>
</span><span class='line'>    <span class="n">_m</span> <span class="o">=</span> <span class="n">array</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="s">&quot;B&quot;</span><span class="p">,</span> <span class="n">mask_key</span><span class="p">)</span>
</span><span class='line'>    <span class="n">_d</span> <span class="o">=</span> <span class="n">array</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="s">&quot;B&quot;</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>
</span><span class='line'>    <span class="c">#mask_key是4个字节,所以余4</span>
</span><span class='line'>    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">_d</span><span class="p">)):</span>
</span><span class='line'>        <span class="n">_d</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">^=</span> <span class="n">_m</span><span class="p">[</span><span class="n">i</span> <span class="o">%</span> <span class="mi">4</span><span class="p">]</span>
</span><span class='line'>    <span class="k">return</span> <span class="n">_d</span><span class="o">.</span><span class="n">tostring</span><span class="p">()</span>
</span></code></pre></td></tr></table></div></figure>


<p>服务端发送数据不需要进行掩码，所以<code>is_mask=False</code>,代码如下：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="k">def</span> <span class="nf">send_frame</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span><span class="n">data</span><span class="p">):</span>
</span><span class='line'>    <span class="n">is_finish</span><span class="o">=</span><span class="bp">True</span>
</span><span class='line'>    <span class="n">is_mask</span><span class="o">=</span><span class="bp">False</span>
</span><span class='line'>    <span class="n">opcode</span><span class="o">=</span><span class="mh">0x01</span>
</span><span class='line'>    <span class="n">byte_1</span><span class="o">=</span><span class="n">opcode</span>
</span><span class='line'>    <span class="k">if</span> <span class="n">is_finish</span><span class="p">:</span>
</span><span class='line'>        <span class="n">byte_1</span><span class="o">=</span><span class="n">byte_1</span><span class="o">|</span><span class="mh">0x80</span>
</span><span class='line'>    <span class="n">frame_data</span><span class="o">=</span><span class="n">struct</span><span class="o">.</span><span class="n">pack</span><span class="p">(</span><span class="s">&#39;B&#39;</span><span class="p">,</span><span class="n">byte_1</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">length</span><span class="o">=</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
</span><span class='line'>    <span class="n">byte_2</span><span class="o">=</span><span class="mi">0</span>
</span><span class='line'>    <span class="k">if</span> <span class="n">is_mask</span><span class="p">:</span>
</span><span class='line'>        <span class="n">byte_2</span><span class="o">=</span><span class="mh">0x80</span>
</span><span class='line'>    <span class="c">#如果数据太长,126和127只是个标识</span>
</span><span class='line'>    <span class="k">if</span> <span class="n">length</span><span class="o">&lt;</span><span class="mi">126</span><span class="p">:</span>
</span><span class='line'>        <span class="n">frame_data</span><span class="o">+=</span><span class="n">struct</span><span class="o">.</span><span class="n">pack</span><span class="p">(</span><span class="s">&#39;B&#39;</span><span class="p">,</span><span class="n">byte_2</span><span class="o">|</span><span class="n">length</span><span class="p">)</span>
</span><span class='line'>    <span class="k">elif</span> <span class="n">length</span><span class="o">&lt;=</span><span class="mh">0xFFFF</span><span class="p">:</span>
</span><span class='line'>        <span class="n">frame_data</span><span class="o">+=</span><span class="n">struct</span><span class="o">.</span><span class="n">pack</span><span class="p">(</span><span class="s">&#39;!BH&#39;</span><span class="p">,</span><span class="n">byte_2</span><span class="o">|</span><span class="mi">126</span><span class="p">,</span><span class="n">length</span><span class="p">)</span>
</span><span class='line'>    <span class="k">else</span><span class="p">:</span>
</span><span class='line'>        <span class="n">frame_data</span><span class="o">+=</span><span class="n">struct</span><span class="o">.</span><span class="n">pack</span><span class="p">(</span><span class="s">&#39;!BQ&#39;</span><span class="p">,</span><span class="n">byte_2</span><span class="o">|</span><span class="mi">127</span><span class="p">,</span><span class="n">length</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>    <span class="c">#data=data.encode(&#39;utf-8&#39;)</span>
</span><span class='line'>    <span class="k">if</span> <span class="n">is_mask</span><span class="p">:</span>
</span><span class='line'>        <span class="n">mask</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">urandom</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
</span><span class='line'>        <span class="n">data</span><span class="o">=</span><span class="n">mask</span><span class="o">+</span><span class="n">mask_or_unmask</span><span class="p">(</span><span class="n">mask</span><span class="p">,</span><span class="n">data</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">frame_data</span><span class="o">+=</span><span class="n">data</span>
</span><span class='line'>    <span class="n">sock</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">frame_data</span><span class="p">)</span>
</span><span class='line'>    <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="n">frame_data</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>到此，websocket的服务端就完成了，我们还需要一个浏览器的页面，向服务端发送请求，大家可以使用点击进入这个网页对自己的websocket server 程序进行测试，也可以下载下面的两个源码文件，进行测试。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[[Leetcode] Trapping Rain Water - 递减栈]]></title>
    <link href="http://liyumeng.me/blog/2015/03/27/leetcode-trapping-rain-water/"/>
    <updated>2015-03-27T09:12:24+08:00</updated>
    <id>http://liyumeng.me/blog/2015/03/27/leetcode-trapping-rain-water</id>
    <content type="html"><![CDATA[<p>题目：Trapping Rain Water</p>

<p>Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.
For example,
Given <code>[0,1,0,2,1,0,1,3,2,1,2,1]</code>, return <code>6</code>.</p>

<p><img src="http://i2.tietuku.com/79e8d2e8540134af.png" alt="" /></p>

<p>The above elevation map is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped. Thanks Marcos for contributing this image!</p>

<!--more-->


<hr />

<h3>解题思路：</h3>

<p>时刻维护一个递减的栈。</p>

<ol>
<li>当前元素比栈顶元素小时，直接进栈；</li>
<li><p>当前元素大于等于栈顶元素时，弹出栈顶元素，由弹出后新的栈顶元素与当前元素构成一个凹槽，但凹槽并不一定是见底的。比如4、2、3,其中的4和3形成一个凹槽,但因为中间有个2，所以只能容纳1个单位的水。这也就是min(A[k],A[i])-t的原因，t就是这个2，A[k]=4,A[i]=3。</p></li>
<li><p>将每次出栈产生的结果加到一起就行了。</p></li>
</ol>


<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
</pre></td><td class='code'><pre><code class='c++'><span class='line'><span class="k">class</span> <span class="nc">Solution</span> <span class="p">{</span>
</span><span class='line'><span class="k">public</span><span class="o">:</span>
</span><span class='line'>    <span class="kt">int</span> <span class="n">trap</span><span class="p">(</span><span class="kt">int</span> <span class="n">A</span><span class="p">[],</span> <span class="kt">int</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="kt">int</span> <span class="n">top</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span>
</span><span class='line'>        <span class="k">if</span><span class="p">(</span><span class="n">n</span><span class="o">&lt;</span><span class="mi">3</span><span class="p">)</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span><span class='line'>        <span class="n">s</span><span class="p">[</span><span class="n">top</span><span class="o">++</span><span class="p">]</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span>
</span><span class='line'>        <span class="kt">int</span> <span class="n">res</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span><span class="n">t</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span><span class="n">pre</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span>
</span><span class='line'>        <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">1</span><span class="p">;</span><span class="n">i</span><span class="o">&lt;</span><span class="n">n</span><span class="p">;</span><span class="n">i</span><span class="o">++</span><span class="p">)</span>
</span><span class='line'>        <span class="p">{</span>
</span><span class='line'>            <span class="k">while</span><span class="p">(</span><span class="n">top</span><span class="o">&gt;</span><span class="mi">0</span><span class="o">&amp;&amp;</span><span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">&gt;=</span><span class="n">A</span><span class="p">[</span><span class="n">s</span><span class="p">[</span><span class="n">top</span><span class="o">-</span><span class="mi">1</span><span class="p">]])</span>
</span><span class='line'>            <span class="p">{</span>
</span><span class='line'>                <span class="n">t</span><span class="o">=</span><span class="n">A</span><span class="p">[</span><span class="n">s</span><span class="p">[</span><span class="o">--</span><span class="n">top</span><span class="p">]];</span><span class="c1">//栈顶元素，能进到这层循环里，说明A[i]比栈顶元素要大</span>
</span><span class='line'>                <span class="k">if</span><span class="p">(</span><span class="n">top</span><span class="o">&gt;</span><span class="mi">0</span><span class="p">)</span><span class="c1">//弹出刚刚的栈顶元素后,那这次栈顶的元素一定比刚刚弹出的那个大</span>
</span><span class='line'>                <span class="p">{</span>
</span><span class='line'>                    <span class="n">pre</span><span class="o">=</span><span class="n">s</span><span class="p">[</span><span class="n">top</span><span class="o">-</span><span class="mi">1</span><span class="p">];</span><span class="c1">//新的栈顶元素</span>
</span><span class='line'>                    <span class="n">res</span><span class="o">+=</span><span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="n">pre</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="n">min</span><span class="p">(</span><span class="n">A</span><span class="p">[</span><span class="n">k</span><span class="p">],</span><span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">])</span><span class="o">-</span><span class="n">t</span><span class="p">);</span><span class="c1">//这样新栈顶元素与A[i]这间就形成了一个凹槽</span>
</span><span class='line'>                <span class="p">}</span>
</span><span class='line'>            <span class="p">}</span>
</span><span class='line'>            <span class="n">s</span><span class="p">[</span><span class="n">top</span><span class="o">++</span><span class="p">]</span><span class="o">=</span><span class="n">i</span><span class="p">;</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>        <span class="k">return</span> <span class="n">res</span><span class="p">;</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="k">private</span><span class="o">:</span>
</span><span class='line'>    <span class="kt">int</span> <span class="n">s</span><span class="p">[</span><span class="mi">100000</span><span class="p">];</span>
</span><span class='line'><span class="p">};</span>
</span></code></pre></td></tr></table></div></figure>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[在这儿的第一篇博文]]></title>
    <link href="http://liyumeng.me/blog/2015/03/27/zai-zhe-er-de-di-yi-pian-bo-wen/"/>
    <updated>2015-03-27T01:22:56+08:00</updated>
    <id>http://liyumeng.me/blog/2015/03/27/zai-zhe-er-de-di-yi-pian-bo-wen</id>
    <content type="html"><![CDATA[<p>最近比较闲，便抽出一部分时间在github上搭建了这个博客。
应该是很早之前就有这个想法了，因为各种原因一拖再拖。今天终于可以为此庆贺一下了。
好久不写这类文字了，竟不知道写什么了，今天在此Mark一下吧，以后争取经常更新。</p>
]]></content>
  </entry>
  
</feed>
