﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-loop_in_codes-随笔分类-network</title><link>http://www.cppblog.com/kevinlynx/category/7029.html</link><description>低调做技术__欢迎移步我的独立博客 &lt;a href="http://codemacro.com"&gt;codemaro.com&lt;/a&gt; 微博 &lt;a href="http://weibo.com/kevinlynx"&gt;kevinlynx&lt;/a&gt;</description><language>zh-cn</language><lastBuildDate>Sun, 19 Oct 2014 08:54:19 GMT</lastBuildDate><pubDate>Sun, 19 Oct 2014 08:54:19 GMT</pubDate><ttl>60</ttl><item><title>图解zookeeper FastLeader选举算法</title><link>http://www.cppblog.com/kevinlynx/archive/2014/10/19/208622.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Sun, 19 Oct 2014 07:58:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2014/10/19/208622.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/208622.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2014/10/19/208622.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/208622.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/208622.html</trackback:ping><description><![CDATA[<div class="entry-content">
<p>zookeeper配置为集群模式时，在启动或异常情况时会选举出一个实例作为Leader。其默认选举算法为<code>FastLeaderElection</code>。</p>

<p>不知道zookeeper的可以考虑这样一个问题：某个服务可以配置为多个实例共同构成一个集群对外提供服务。其每一个实例本地都存有冗余数据，每一个实例都可以直接对外提供读写服务。在这个集群中为了保证数据的一致性，需要有一个Leader来协调一些事务。那么问题来了：如何确定哪一个实例是Leader呢？</p>

<p>问题的难点在于：</p>

<ul>
<li>没有一个仲裁者来选定Leader</li>
<li>每一个实例本地可能已经存在数据，不确定哪个实例上的数据是最新的</li>
</ul>


<p>分布式选举算法正是用来解决这个问题的。</p>

<p>本文基于zookeeper 3.4.6 的源码进行分析。FastLeaderElection算法的源码全部位于<code>FastLeaderElection.java</code>文件中，其对外接口为<code>FastLeaderElection.lookForLeader</code>，该接口是一个同步接口，直到选举结束才会返回。同样由于网上已有类似文章，所以我就从图示的角度来阐述。阅读一些其他文章有利于获得初步印象：</p>

<ul>
<li>
<a href="http://iwinit.iteye.com/blog/1773531">深入浅出Zookeeper之五 Leader选举</a>，代码导读</li>
<li>
<a href="http://blog.csdn.net/xhh198781/article/details/6619203">zookeeper3.3.3源码分析(二)FastLeader选举算法</a>，文字描述较细</li>
</ul>


<h2>主要流程</h2>

<p>阅读代码和以上推荐文章可以把整个流程梳理清楚。实现上，包括了一个消息处理主循环，也是选举的主要逻辑，以及一个消息发送队列处理线程和消息解码线程。主要流程可概括为下图：</p>

<!-- more -->


<p><img src="http://codemacro.com/assets/res/zk/fle-flow.png" alt="fle-flow.png" /></p>

<p>推荐对照着推荐的文章及代码理解，不赘述。</p>

<p>我们从感性上来理解这个算法。</p>

<p>每一个节点，相当于一个选民，他们都有自己的推荐人，最开始他们都推荐自己。谁更适合成为Leader有一个简单的规则，例如sid够大（配置）、持有的数据够新(zxid够大)。每个选民都告诉其他选民自己目前的推荐人是谁，类似于出去搞宣传拉拢其他选民。每一个选民发现有比自己更适合的人时就转而推荐这个更适合的人。最后，大部分人意见一致时，就可以结束选举。</p>

<p>就这么简单。总体上有一种不断演化逼近结果的感觉。</p>

<p>当然，会有些特殊情况的处理。例如总共3个选民，1和2已经确定3是Leader，但3还不知情，此时就走入<code>LEADING/FOLLOWING</code>的分支，选民3只是接收结果。</p>

<p>代码中不是所有逻辑都在这个大流程中完成的。在接收消息线程中，还可能单独地回应某个节点(<code>WorkerReceiver.run</code>)：</p>

<p><img src="http://codemacro.com/assets/res/zk/recv.png" alt="recv.png" /></p>

<p>从这里可以看出，当某个节点已经确定选举结果不再处于<code>LOOKING</code>状态时，其收到<code>LOOKING</code>消息时都会直接回应选举的最终结果。结合上面那个比方，相当于某次选举结束了，这个时候来了选民4又发起一次新的选举，那么其他选民就直接告诉它当前的Leader情况。相当于，在这个集群主从已经就绪的情况下，又开启了一个实例，这个实例就会直接使用当前的选举结果。</p>

<h2>状态转换</h2>

<p>每个节点上有一些关键的数据结构：</p>

<ul>
<li>当前推荐人，初始推荐自己，每次收到其他更好的推荐人时就更新</li>
<li>其他人的投票集合，用于确定何时选举结束</li>
</ul>


<p>每次推荐人更新时就会进行广播，正是这个不断地广播驱动整个算法趋向于结果。假设有3个节点A/B/C，其都还没有数据，按照sid关系为C&gt;B&gt;A，那么按照规则，C更可能成为Leader，其各个节点的状态转换为：</p>

<p><img src="http://codemacro.com/assets/res/zk/state.png" alt="state.png" /></p>

<p>图中，v(A)表示当前推荐人为A；r[]表示收到的投票集合。</p>

<p>可以看看当其他节点已经确定投票结果时，即不再是<code>LOOKING</code>时的状态：</p>

<p><img src="http://codemacro.com/assets/res/zk/state-ret.png" alt="state-ret.png" /></p>

<p>代码中有一个特殊的投票集合<code>outofelection</code>，我理解为选举已结束的那些投票，这些投票仅用于表征选举结果。</p>

<p>当一个新启动的节点加入集群时，它对集群内其他节点发出投票请求，而其他节点已不处于<code>LOOKING</code>状态，此时其他节点回应选举结果，该节点收集这些结果到<code>outofelection</code>中，最终在收到合法LEADER消息且这些选票也构成选举结束条件时，该节点就结束自己的选举行为。<em>注意到代码中会<code>logicalclock = n.electionEpoch;</code>更新选举轮数</em></p>

<p><em>完</em></p>

<p class="post-footer">
            原文地址：
            <a href="http://codemacro.com/2014/10/19/zk-fastleaderelection/">http://codemacro.com/2014/10/19/zk-fastleaderelection/</a><br />
            written by <a href="http://codemacro.com">Kevin Lynx</a>
            &nbsp;posted at <a href="http://codemacro.com">http://codemacro.com</a>
            </p>

</div><img src ="http://www.cppblog.com/kevinlynx/aggbug/208622.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2014-10-19 15:58 <a href="http://www.cppblog.com/kevinlynx/archive/2014/10/19/208622.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>图解分布式一致性协议Paxos</title><link>http://www.cppblog.com/kevinlynx/archive/2014/10/15/208580.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Wed, 15 Oct 2014 14:45:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2014/10/15/208580.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/208580.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2014/10/15/208580.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/208580.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/208580.html</trackback:ping><description><![CDATA[<div class="entry-content">
<p>Paxos协议/算法是分布式系统中比较重要的协议，它有多重要呢？</p>

<p><a href="http://coolshell.cn/articles/10910.html">&lt;分布式系统的事务处理&gt;</a>：</p>

<blockquote><p>Google Chubby的作者Mike Burrows说过这个世界上只有一种一致性算法，那就是Paxos，其它的算法都是残次品。</p></blockquote>

<p><a href="http://book.douban.com/subject/25723658/">&lt;大规模分布式存储系统&gt;</a>：</p>

<blockquote><p>理解了这两个分布式协议之后(Paxos/2PC)，学习其他分布式协议会变得相当容易。</p></blockquote>

<p>学习Paxos算法有两部分：a) 算法的原理/证明；b) 算法的理解/运作。</p>

<p>理解这个算法的运作过程其实基本就可以用于工程实践。而且理解这个过程相对来说也容易得多。</p>

<p>网上我觉得讲Paxos讲的好的属于这篇：<a href="http://coderxy.com/archives/121">paxos图解</a>及<a href="http://coderxy.com/archives/136">Paxos算法详解</a>，我这里就结合<a href="http://zh.wikipedia.org/zh-cn/Paxos%E7%AE%97%E6%B3%95#.E5.AE.9E.E4.BE.8B">wiki上的实例</a>进一步阐述。一些paxos基础通过这里提到的两篇文章，以及wiki上的内容基本可以理解。</p>

<!-- more -->


<h2>算法内容</h2>

<p>Paxos在原作者的《Paxos Made Simple》中内容是比较精简的：</p>

<blockquote>
<p>Phase  1</p>

<p>  (a) A proposer selects a proposal number n  and sends a prepare request with number  n to a majority of acceptors.</p>

<p>  (b)  If  an  acceptor  receives  a prepare  request  with  number  n  greater than  that  of  any  prepare  request  to  which  it  has  already  responded, then it responds to the request with a promise not to accept any more proposals numbered less than  n  and with the highest-numbered pro-posal (if any) that it has accepted.</p>

<p>  Phase  2</p>

<p>  (a)  If  the  proposer  receives  a  response  to  its  prepare requests (numbered  n)  from  a  majority  of  acceptors,  then  it  sends  an  accept request to each of those acceptors for a proposal numbered  n  with a value v , where v is the value of the highest-numbered proposal among the responses, or is any value if the responses reported no proposals.</p>

<p>  (b) If an acceptor receives an accept request for a proposal numbered n, it accepts the proposal unless it has already responded to a prepare request having a number greater than  n.</p>
</blockquote>

<p>借用<a href="http://coderxy.com/archives/121">paxos图解</a>文中的流程图可概括为：</p>

<p><img src="http://codemacro.com/assets/res/paxos/paxos-flow.png" alt="" /></p>

<h2>实例及详解</h2>

<p>Paxos中有三类角色<code>Proposer</code>、<code>Acceptor</code>及<code>Learner</code>，主要交互过程在<code>Proposer</code>和<code>Acceptor</code>之间。</p>

<p><code>Proposer</code>与<code>Acceptor</code>之间的交互主要有4类消息通信，如下图：</p>

<p><img src="http://codemacro.com/assets/res/paxos/paxos-messages.png" alt="" /></p>

<p>这4类消息对应于paxos算法的两个阶段4个过程：</p>

<ul>
<li>phase 1

<ul>
<li>a) proposer向网络内超过半数的acceptor发送prepare消息</li>
<li>b) acceptor正常情况下回复promise消息</li>
</ul>
</li>
<li>phase 2

<ul>
<li>a) 在有足够多acceptor回复promise消息时，proposer发送accept消息</li>
<li>b) 正常情况下acceptor回复accepted消息</li>
</ul>
</li>
</ul>


<p>因为在整个过程中可能有其他proposer针对同一件事情发出以上请求，所以在每个过程中都会有些特殊情况处理，这也是为了达成一致性所做的事情。如果在整个过程中没有其他proposer来竞争，那么这个操作的结果就是确定无异议的。但是如果有其他proposer的话，情况就不一样了。</p>

<p>以<a href="http://zh.wikipedia.org/zh-cn/Paxos%E7%AE%97%E6%B3%95#.E5.AE.9E.E4.BE.8B">paxos中文wiki上的例子</a>为例。简单来说该例子以若干个议员提议税收，确定最终通过的法案税收比例。</p>

<p>以下图中基本只画出proposer与一个acceptor的交互。时间标志T2总是在T1后面。propose number简称N。</p>

<p>情况之一如下图：</p>

<p><img src="http://codemacro.com/assets/res/paxos/paxos-e1.png" alt="" /></p>

<p>A3在T1发出accepted给A1，然后在T2收到A5的prepare，在T3的时候A1才通知A5最终结果(税率10%)。这里会有两种情况：</p>

<ul>
<li>A5发来的N5小于A1发出去的N1，那么A3直接拒绝(reject)A5</li>
<li>A5发来的N5大于A1发出去的N1，那么A3回复promise，但带上A1的(N1, 10%)</li>
</ul>


<p>这里可以与paxos流程图对应起来，更好理解。<strong>acceptor会记录(MaxN, AcceptN, AcceptV)</strong>。</p>

<p>A5在收到promise后，后续的流程可以顺利进行。但是发出accept时，因为收到了(AcceptN, AcceptV)，所以会取最大的AcceptN对应的AcceptV，例子中也就是A1的10%作为AcceptV。如果在收到promise时没有发现有其他已记录的AcceptV，则其值可以由自己决定。</p>

<p>针对以上A1和A5冲突的情况，最终A1和A5都会广播接受的值为10%。</p>

<p>其实4个过程中对于acceptor而言，在回复promise和accepted时由于都可能因为其他proposer的介入而导致特殊处理。所以基本上看在这两个时间点收到其他proposer的请求时就可以了解整个算法了。例如在回复promise时则可能因为proposer发来的N不够大而reject：</p>

<p><img src="http://codemacro.com/assets/res/paxos/paxos-e2.png" alt="" /></p>

<p>如果在发accepted消息时，对其他更大N的proposer发出过promise，那么也会reject该proposer发出的accept，如图：</p>

<p><img src="http://codemacro.com/assets/res/paxos/paxos-e3.png" alt="" /></p>

<p>这个对应于Phase 2 b)：</p>

<blockquote><p>it accepts the proposal unless it has already responded to a prepare request having a number greater than  n.</p></blockquote>

<h2>总结</h2>

<p>Leslie Lamport没有用数学描述Paxos，但是他用英文阐述得很清晰。将Paxos的两个Phase的内容理解清楚，整个算法过程还是不复杂的。</p>

<p>至于Paxos中一直提到的一个全局唯一且递增的proposer number，其如何实现，引用如下：</p>

<blockquote><p>如何产生唯一的编号呢？在《Paxos made simple》中提到的是让所有的Proposer都从不相交的数据集合中进行选择，例如系统有5个Proposer，则可为每一个Proposer分配一个标识j(0~4)，则每一个proposer每次提出决议的编号可以为5*i + j(i可以用来表示提出议案的次数)</p></blockquote>

<h2>参考文档</h2>

<ul>
<li>paxos图解, <a href="http://coderxy.com/archives/121">http://coderxy.com/archives/121</a>
</li>
<li>Paxos算法详解, <a href="http://coderxy.com/archives/136">http://coderxy.com/archives/136</a>
</li>
<li>Paxos算法 wiki, <a href="http://zh.wikipedia.org/zh-cn/Paxos%E7%AE%97%E6%B3%95#.E5.AE.9E.E4.BE.8B">http://zh.wikipedia.org/zh-cn/Paxos%E7%AE%97%E6%B3%95#.E5.AE.9E.E4.BE.8B</a>
</li>
</ul>


<p class="post-footer">
            原文地址：
            <a href="http://codemacro.com/2014/10/15/explain-poxos/">http://codemacro.com/2014/10/15/explain-poxos/</a><br />
            written by <a href="http://codemacro.com">Kevin Lynx</a>
            &nbsp;posted at <a href="http://codemacro.com">http://codemacro.com</a>
            </p>

</div><img src ="http://www.cppblog.com/kevinlynx/aggbug/208580.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2014-10-15 22:45 <a href="http://www.cppblog.com/kevinlynx/archive/2014/10/15/208580.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>淘宝分布式配置管理服务Diamond</title><link>http://www.cppblog.com/kevinlynx/archive/2014/10/12/208549.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Sun, 12 Oct 2014 04:57:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2014/10/12/208549.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/208549.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2014/10/12/208549.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/208549.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/208549.html</trackback:ping><description><![CDATA[<div class="entry-content">
<p>在一个分布式环境中，同类型的服务往往会部署很多实例。这些实例使用了一些配置，为了更好地维护这些配置就产生了配置管理服务。通过这个服务可以轻松地管理这些应用服务的配置问题。应用场景可概括为：</p>

<p><img src="http://codemacro.com/assets/res/diamond/disconf.PNG" alt="" /></p>

<p>zookeeper的一种应用就是分布式配置管理(<a href="http://wenku.baidu.com/view/ee86ca90daef5ef7ba0d3c7d.html">基于ZooKeeper的配置信息存储方案的设计与实现</a>)。百度也有类似的实现：<a href="https://github.com/knightliao/disconf">disconf</a>。</p>

<p><a href="http://code.taobao.org/p/diamond/src/">Diamond</a>则是淘宝开源的一种分布式配置管理服务的实现。Diamond本质上是一个Java写的Web应用，其对外提供接口都是基于HTTP协议的，在阅读代码时可以从实现各个接口的controller入手。</p>

<h2>分布式配置管理</h2>

<p>分布式配置管理的本质基本上就是一种<strong><a href="http://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern">推送-订阅</a></strong>模式的运用。配置的应用方是订阅者，配置管理服务则是推送方。概括为下图：</p>

<p><img src="http://codemacro.com/assets/res/diamond/pubsub.PNG" alt="" /></p>

<p>其中，客户端包括管理人员publish数据到配置管理服务，可以理解为添加/更新数据；配置管理服务notify数据到订阅者，可以理解为推送。</p>

<!-- more -->


<p>配置管理服务往往会封装一个客户端库，应用方则是基于该库与配置管理服务进行交互。在实际实现时，客户端库可能是主动拉取(pull)数据，但对于应用方而言，一般是一种事件通知方式。</p>

<p>Diamond中的数据是简单的key-value结构。应用方订阅数据则是基于key来订阅，未订阅的数据当然不会被推送。数据从类型上又划分为聚合和非聚合。因为数据推送者可能很多，在整个分布式环境中，可能有多个推送者在推送相同key的数据，这些数据如果是聚合的，那么所有这些推送者推送的数据会被合并在一起；反之如果是非聚合的，则会出现覆盖。</p>

<p>数据的来源可能是人工通过管理端录入，也可能是其他服务通过配置管理服务的推送接口自动录入。</p>

<h2>架构及实现</h2>

<p>Diamond服务是一个集群，是一个去除了单点的协作集群。如图：</p>

<p><img src="http://codemacro.com/assets/res/diamond/arch.PNG" alt="" /></p>

<p>图中可分为以下部分讲解：</p>

<h3>服务之间同步</h3>

<p>Diamond服务集群每一个实例都可以对外完整地提供服务，那么意味着每个实例上都有整个集群维护的数据。Diamond有两种方式保证这一点：</p>

<ul>
<li>任何一个实例都有其他实例的地址；任何一个实例上的数据变更时，都会将改变的数据同步到mysql上，然后通知其他所有实例从mysql上进行一次数据拉取(<code>DumpService::dump</code>)，这个过程只拉取改变了的数据</li>
<li>任何一个实例启动后都会以较长的时间间隔（几小时），从mysql进行一次全量的数据拉取(<code>DumpAllProcessor</code>)</li>
</ul>


<p>实现上为了一致性，通知其他实例实际上也包含自己。以服务器收到添加聚合数据为例，处理过程大致为：</p>

<pre><code>DatumController::addDatum // /datum.do?method=addDatum
    PersistService::addAggrConfigInfo 
    MergeDatumService::addMergeTask // 添加一个MergeDataTask，异步处理

MergeTaskProcessor::process
    PersistService::insertOrUpdate
        EventDispatcher.fireEvent(new ConfigDataChangeEvent // 派发一个ConfigDataChangeEvent事件

NotifyService::onEvent // 接收事件并处理
    TaskManager::addTask(..., new NotifyTask // 由此，当数据发生变动，则最终创建了一个NoticyTask

// NotifyTask同样异步处理
NotifyTaskProcessor::process
    foreach server in serverList // 包含自己
        notifyToDump // 调用 /notify.do?method=notifyConfigInfo 从mysql更新变动的数据
</code></pre>

<p>虽然Diamond去除了单点问题，不过问题都下降到了mysql上。但由于其作为配置管理的定位，其数据量就mysql的应用而言算小的了，所以可以一定程度上保证整个服务的可用性。</p>

<h3>数据一致性</h3>

<p>由于Diamond服务器没有master，任何一个实例都可以读写数据，那么针对同一个key的数据则可能面临冲突。这里应该是通过mysql来保证数据的一致性。每一次客户端请求写数据时，Diamond都将写请求投递给mysql，然后通知集群内所有Diamond实例（包括自己）从mysql拉取数据。当然，拉取数据则可能不是每一次写入都能拉出来，也就是最终一致性。</p>

<p>Diamond中没有把数据放入内存，但会放到本地文件。对于客户端的读操作而言，则是直接返回本地文件里的数据。</p>

<h3>服务实例列表</h3>

<p>Diamond服务实例列表是一份静态数据，直接将每个实例的地址存放在一个web server上。无论是Diamond服务还是客户端都从该web server上取出实例列表。</p>

<p>对于客户端而言，当其取出了该列表后，则是随机选择一个节点(<code>ServerListManager.java</code>)，以后的请求都会发往该节点。</p>

<h3>数据同步</h3>

<p>客户端库中以固定时间间隔从服务器拉取数据(<code>ClientWorker::ClientWorker</code>，<code>ClientWorker::checkServerConfigInfo</code>)。只有应用方关心的数据才可能被拉取。另外，为了数据推送的及时，Diamond还使用了一种long polling的技术，其实也是为了突破HTTP协议的局限性。<em>如果整个服务是基于TCP的自定义协议，客户端与服务器保持长连接则没有这些问题</em>。</p>

<h3>数据的变更</h3>

<p>Diamond中很多操作都会检查数据是否发生了变化。标识数据变化则是基于数据对应的MD5值来实现的。</p>

<h2>容灾</h2>

<p>在整个Diamond系统中，几个角色为了提高容灾性，都有自己的缓存，概括为下图：</p>

<p><img src="http://codemacro.com/assets/res/diamond/failover.PNG" alt="" /></p>

<p>每一个角色出问题时，都可以尽量保证客户端对应用层提供服务。</p>

<h2>参考文档</h2>

<ul>
<li><a href="http://code.taobao.org/p/diamond/src">diamond project</a></li>
<li><a href="http://jm-blog.aliapp.com/?p=1588">diamond专题</a></li>
<li><a href="http://jm-blog.aliapp.com/?p=3450">中间件技术及双十一实践&#183;软负载篇</a></li>
</ul>


<p class="post-footer">
            原文地址：
            <a href="http://codemacro.com/2014/10/12/diamond/">http://codemacro.com/2014/10/12/diamond/</a><br />
            written by <a href="http://codemacro.com">Kevin Lynx</a>
            &nbsp;posted at <a href="http://codemacro.com">http://codemacro.com</a>
            </p>

</div><img src ="http://www.cppblog.com/kevinlynx/aggbug/208549.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2014-10-12 12:57 <a href="http://www.cppblog.com/kevinlynx/archive/2014/10/12/208549.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>zookeeper节点数与watch的性能测试</title><link>http://www.cppblog.com/kevinlynx/archive/2014/09/21/208372.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Sun, 21 Sep 2014 12:56:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2014/09/21/208372.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/208372.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2014/09/21/208372.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/208372.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/208372.html</trackback:ping><description><![CDATA[<div class="entry-content">
<p>zookeeper中节点数量理论上仅受限于内存，但一个节点下的子节点数量<a href="http://zookeeper-user.578899.n2.nabble.com/ZooKeeper-Limitation-td6675643.html">受限于request/response 1M数据</a> (<a href="http://web.archiveorange.com/archive/v/AQXskdBodZB7kWpjpjHw">size of data / number of znodes</a>)</p>

<p>zookeeper的watch机制用于数据变更时zookeeper的主动通知。watch可以被附加到每一个节点上，那么如果一个应用有10W个节点，那zookeeper中就可能有10W个watch（甚至更多）。每一次在zookeeper完成改写节点的操作时就会检测是否有对应的watch，有的话则会通知到watch。<a href="http://shift-alt-ctrl.iteye.com/blog/1847320">Zookeeper-Watcher机制与异步调用原理</a></p>

<p>本文将关注以下内容：</p>

<ul>
<li>zookeeper的性能是否会受节点数量的影响</li>
<li>zookeeper的性能是否会受watch数量的影响</li>
</ul>


<h2>测试方法</h2>

<p>在3台机器上分别部署一个zookeeper，版本为<code>3.4.3</code>，机器配置：</p>

<pre><code>Intel(R) Xeon(R) CPU E5-2430 0 @ 2.20GHz

16G

java version "1.6.0_32"
Java(TM) SE Runtime Environment (build 1.6.0_32-b05)
OpenJDK (Taobao) 64-Bit Server VM (build 20.0-b12-internal, mixed mode)
</code></pre>

<p>大部分实验JVM堆大小使用默认，也就是<code>1/4 RAM</code>：</p>

<pre><code>java -XX:+PrintFlagsFinal -version | grep HeapSize
</code></pre>

<p>测试客户端使用<a href="https://github.com/phunt/zk-smoketest">zk-smoketest</a>，针对watch的测试则是我自己写的。基于zk-smoketest我写了些脚本可以自动跑数据并提取结果，相关脚本可以在这里找到：<a href="https://github.com/kevinlynx/zk-benchmark">https://github.com/kevinlynx/zk-benchmark</a></p>

<!-- more -->


<h2>测试结果</h2>

<h3>节点数对读写性能的影响</h3>

<p>测试最大10W个节点，度量1秒内操作数(ops)：</p>

<p><img src="http://codemacro.com/assets/res/zk_benchmark/node_count.png" alt="" /></p>

<p>可见节点数的增加并不会对zookeeper读写性能造成影响。</p>

<h3>节点数据大小对读写性能的影响</h3>

<p>这个网上其实已经有公认的结论。本身单个节点数据越大，对网络方面的吞吐就会造成影响，所以其数据越大读写性能越低也在预料之中。</p>

<p><img src="http://codemacro.com/assets/res/zk_benchmark/node_size.png" alt="" /></p>

<p>写数据会在zookeeper集群内进行同步，所以其速度整体会比读数据更慢。该实验需要把超时时间进行一定上调，同时我也把JVM最大堆大小调整到8G。</p>

<p>测试结果很明显，节点数据大小会严重影响zookeeper效率。</p>

<h2>watch对读写性能的影响</h2>

<p>zk-smoketest自带的latency测试有个参数<code>--watch_multiple</code>用来指定watch的数量，但其实仅是指定客户端的数量，在server端通过<code>echo whcp | nc 127.0.0.1 4181</code>会发现实际每个节点还是只有一个watch。</p>

<p>在我写的测试中，则是通过创建多个客户端来模拟单个节点上的多个watch。这也更符合实际应用。同时对节点的写也是在另一个独立的客户端中，这样可以避免zookeeper client的实现对测试带来的干扰。</p>

<p>每一次完整的测试，首先是对每个节点添加节点数据的watch，然后在另一个客户端中对这些节点进行数据改写，收集这些改写操作的耗时，以确定添加的watch对这些写操作带来了多大的影响。</p>

<p><img src="http://codemacro.com/assets/res/zk_benchmark/watch.png" alt="" /></p>

<p>图中，<code>0 watch</code>表示没有对节点添加watch；<code>1 watch</code>表示有一个客户端对每个节点进行了watch；<code>3 watch</code>表示有其他3个客户端对每个节点进行了watch；依次类推。</p>

<p>可见，watch对写操作还是有较大影响的，毕竟需要进行网络传输。同样，这里也显示出整个zookeeper的watch数量同节点数量一样对整体性能没有影响。</p>

<h2>总体结论</h2>

<ul>
<li>对单个节点的操作并不会因为zookeeper中节点的总数而受到影响</li>
<li>数据大小对zookeeper的性能有较大影响，性能和内存都会</li>
<li>单个节点上独立session的watch数对性能有一定影响</li>
</ul>


<p class="post-footer">
            原文地址：
            <a href="http://codemacro.com/2014/09/21/zk-watch-benchmark/">http://codemacro.com/2014/09/21/zk-watch-benchmark/</a><br />
            written by <a href="http://codemacro.com">Kevin Lynx</a>
            &nbsp;posted at <a href="http://codemacro.com">http://codemacro.com</a>
            </p>

</div><img src ="http://www.cppblog.com/kevinlynx/aggbug/208372.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2014-09-21 20:56 <a href="http://www.cppblog.com/kevinlynx/archive/2014/09/21/208372.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>分布式环境中的负载均衡策略</title><link>http://www.cppblog.com/kevinlynx/archive/2014/08/26/208130.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Mon, 25 Aug 2014 16:11:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2014/08/26/208130.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/208130.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2014/08/26/208130.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/208130.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/208130.html</trackback:ping><description><![CDATA[<div class="entry-content">
<p>在分布式系统中相同的服务常常会部署很多台，每一台被称为一个服务节点（实例）。通过一些负载均衡策略将服务请求均匀地分布到各个节点，以实现整个系统支撑海量请求的需求。本文描述一些简单的负载均衡策略。</p>

<h2>Round-robin</h2>

<p>简单地轮询。记录一个选择位置，每次请求来时调整该位置到下一个节点：</p>

<pre><code>curId = ++curId % nodeCnt
</code></pre>

<h2>随机选择</h2>

<p>随机地在所有节点中选择：</p>

<pre><code>id = random(nodeCnt);
</code></pre>

<h2>本机优先</h2>

<p>访问后台服务的访问者可能本身是一个整合服务，或者是一个proxy，如果后台服务节点恰好有节点部署在本机的，则可以优先使用。在未找到本机节点时则可以继续走Round-robin策略：</p>

<pre><code>if (node-&gt;ip() == local_ip) {
    return node;
} else {
    return roundRobin();
}
</code></pre>

<!-- more -->


<p>一旦遍历到本机节点，则后面的请求会一直落到本机节点。所以这里可以加上一些权重机制，仅是保证本机节点会被优先选择，但不会被一直选择。例如：</p>

<pre><code>// initial
cur_weight = 100;
...
// select node
cur_weight -= 5;
if (cur_weight &lt;= 0)
    cur_weight = 100;
if (cur_weight &gt; 50 &amp;&amp; node-&gt;ip() == local_ip) {
    return node;
} else {
    return roundRobin();
}
</code></pre>

<h2>本机房优先</h2>

<p>服务节点可能会被部署到多个机房，有时候确实是需要考虑跨机房服务。同<code>本机优先</code>策略类似，本机房优先则是优先考虑位于相同机房内的服务节点。该请求是从哪个机房中的前端服务发送过来的，则需要前端在请求参数中携带上机房ID。</p>

<p>在服务节点对应的数据结构中，也最好按照机房来组织。</p>

<p>本机房优先策略实际上会作为节点选择的第一道工序，它可以把非本机房的节点先过滤掉，然后再传入后面的各种节点选择策略。这里还可以考虑节点数参数，如果本机房的节点过少，则可以不使用该策略，避免流量严重不均。</p>

<h2>Weighted Round-Robin</h2>

<p>加权轮询。相对于普通轮询而言，该策略中每一个节点都有自己的权重，优先选择权重更大的节点。权重可以根据机器性能预先配置。摘抄一下网上的算法：</p>

<pre><code>假设有一组服务器S = {S0, S1, &#8230;, Sn-1}，W(Si)表示服务器Si的权值，一个
指示变量i表示上一次选择的服务器，指示变量cw表示当前调度的权值，max(S)
表示集合S中所有服务器的最大权值，gcd(S)表示集合S中所有服务器权值的最大
公约数。变量i初始化为-1，cw初始化为零。

while (true) {
  i = (i + 1) mod n;
  if (i == 0) {
     cw = cw - gcd(S); 
     if (cw &lt;= 0) {
       cw = max(S);
       if (cw == 0)
         return NULL;
     }
  } 
  if (W(Si) &gt;= cw) 
    return Si;
}
</code></pre>

<p>遍历完所有节点后权重衰减，衰减到0后重新开始。这样可以让权重更大的节点被选择得更多。</p>

<h2>Consistent Hash</h2>

<p>一致性哈希。一致性哈希用于在分布式环境中，分布在各个节点上的请求，不会因为新增节点（扩容）或减少节点（节点宕机）而变化。如果每个服务节点上都有自己的缓存，其保存了该节点响应请求时的回应。正常情况下，这些缓存都可以很好地被运用，也即cache命中率较高。</p>

<p>如果某个节点不可用了，我们的选择策略又是基于所有节点的公平选择，那么原来一直分配在节点A上请求就很可能被分配到节点B上，从而导致节点A上的缓存较难被命中。这个时候就可以运用一致性哈希来解决。</p>

<p>其基本思想是，在节点选择区间内，在找节点时以顺时针方向找到不小于该请求对应的哈希值的节点。在这个区间里增加很多虚拟节点，每一个虚拟节点相当于一个物理节点的引用，这样相当于把物理节点变成了一个哈希值区间。这个哈希值区间不会因为增加节点和减少节点而变化，那么对某个请求而言，它就会始终落到这个区间里，也就会始终被分配到原来的节点。</p>

<p>至于这个不可用的节点，其上的请求也会被均匀地分配到其他节点中。</p>

<p>摘抄网上的一段代码：</p>

<pre><code>// 添加一个物理节点时，会随之增加很多虚拟节点
template &lt;class Node, class Data, class Hash&gt;
size_t HashRing&lt;Node, Data, Hash&gt;::AddNode(const Node&amp; node)
{
    size_t hash;
    std::string nodestr = Stringify(node);
    for (unsigned int r = 0; r &lt; replicas_; r++) {
        hash = hash_((nodestr + Stringify(r)).c_str());
        ring_[hash] = node;  // 物理节点和虚拟节点都保存在一个std::map中
    }
    return hash;
}

// 选择data对应的节点，data可以是请求
template &lt;class Node, class Data, class Hash&gt;
const Node&amp; HashRing&lt;Node, Data, Hash&gt;::GetNode(const Data&amp; data) const
{
    if (ring_.empty()) {
        throw EmptyRingException();
    }
    size_t hash = hash_(Stringify(data).c_str()); // 对请求进行哈希
    typename NodeMap::const_iterator it;
    // Look for the first node &gt;= hash
    it = ring_.lower_bound(hash); // 找到第一个不小于请求哈希的节点
    if (it == ring_.end()) {
        // Wrapped around; get the first node
        it = ring_.begin();
    }
    return it-&gt;second;
}
</code></pre>

<p>参考<a href="http://blog.csdn.net/sparkliang/article/details/5279393">一致性 hash 算法(consistent hashing)</a>，<a href="http://www.martinbroadhurst.com/Consistent-Hash-Ring.html">Consistent Hash Ring</a></p>
</div><img src ="http://www.cppblog.com/kevinlynx/aggbug/208130.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2014-08-26 00:11 <a href="http://www.cppblog.com/kevinlynx/archive/2014/08/26/208130.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>select真的有限制吗</title><link>http://www.cppblog.com/kevinlynx/archive/2014/06/01/207172.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Sun, 01 Jun 2014 15:45:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2014/06/01/207172.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/207172.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2014/06/01/207172.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/207172.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/207172.html</trackback:ping><description><![CDATA[<div class="entry-content">
<p>在刚开始学习网络编程时，似乎莫名其妙地就会被某人/某资料告诉<code>select</code>函数是有fd(file descriptor)数量限制的。在最近的一次记忆里还有个人笑说<code>select</code>只支持64个fd。我甚至还写过一篇不负责任甚至错误的博客(<a href="http://www.cppblog.com/kevinlynx/archive/2008/05/20/50500.html">突破select的FD_SETSIZE限制</a>)。有人说，直接重新定义<code>FD_SETSIZE</code>就可以突破这个<code>select</code>的限制，也有人说除了重定义这个宏之外还的重新编译内核。</p>

<p>事实具体是怎样的？实际上，造成这些混乱的原因恰好是不同平台对<code>select</code>的实现不一样。</p>

<h2>Windows的实现</h2>

<p><a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms740141(v=vs.85">MSDN</a>.aspx)上对<code>select</code>的说明：</p>

<pre><code>int select(
  _In_     int nfds,
  _Inout_  fd_set *readfds,
  _Inout_  fd_set *writefds,
  _Inout_  fd_set *exceptfds,
  _In_     const struct timeval *timeout
);

nfds [in] Ignored. The nfds parameter is included only for compatibility with Berkeley sockets.
</code></pre>

<p>第一个参数MSDN只说没有使用，其存在仅仅是为了保持与Berkeley Socket的兼容。</p>

<blockquote><p>The variable FD_SETSIZE determines the maximum number of descriptors in a set. (The default value of FD_SETSIZE is 64, which can be modified by defining FD_SETSIZE to another value before including Winsock2.h.) Internally, socket handles in an fd_set structure are not represented as bit flags as in Berkeley Unix.</p></blockquote>

<p>Windows上<code>select</code>的实现不同于Berkeley Unix，<strong>后者使用位标志来表示socket</strong>。</p>

<!-- more -->


<p>在MSDN的评论中有人提到：</p>

<blockquote><p>Unlike the Linux versions of these macros which use a single calculation to set/check the fd, the Winsock versions use a loop which goes through the entire set of fds each time you call FD_SET or FD_ISSET (check out winsock2.h and you&#8217;ll see). So you might want to consider an alternative if you have thousands of sockets!</p></blockquote>

<p>不同于Linux下处理<code>fd_set</code>的那些宏(FD_CLR/FD_SET之类)，Windows上这些宏的实现都使用了一个循环，看看这些宏的大致实现(Winsock2.h)：</p>

<pre><code>#define FD_SET(fd, set) do { \
    u_int __i; \
    for (__i = 0; __i &lt; ((fd_set FAR *)(set))-&gt;fd_count; __i++) { \
        if (((fd_set FAR *)(set))-&gt;fd_array[__i] == (fd)) { \
            break; \
        } \
    } \
    if (__i == ((fd_set FAR *)(set))-&gt;fd_count) { \
        if (((fd_set FAR *)(set))-&gt;fd_count &lt; FD_SETSIZE) { \
            ((fd_set FAR *)(set))-&gt;fd_array[__i] = (fd); \
            ((fd_set FAR *)(set))-&gt;fd_count++; \
        } \
    } \
} while(0)
</code></pre>

<p>看下Winsock2.h中关于<code>fd_set</code>的定义：</p>

<pre><code>typedef struct fd_set {
    u_int fd_count;
    SOCKET fd_array[FD_SETSIZE];
} fd_set;
</code></pre>

<p>再看一篇更重要的MSDN <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms739169(v=vs.85">Maximum Number of Sockets Supported</a>.aspx)：</p>

<blockquote><p>The Microsoft Winsock provider limits the maximum number of sockets supported only by available memory on the local computer.
The maximum number of sockets that a Windows Sockets application can use is not affected by the manifest constant FD_SETSIZE.
If an application is designed to be capable of working with more than 64 sockets using the select and WSAPoll functions, the implementor should define the manifest FD_SETSIZE in every source file before including the Winsock2.h header file.</p></blockquote>

<p>Windows上<code>select</code>支持的socket数量并不受宏<code>FD_SETSIZE</code>的影响，而仅仅受内存的影响。如果应用程序想使用超过<code>FD_SETSIZE</code>的socket，仅需要重新定义<code>FD_SETSIZE</code>即可。</p>

<p>实际上稍微想想就可以明白，既然<code>fd_set</code>里面已经有一个socket的数量计数，那么<code>select</code>的实现完全可以使用这个计数，而不是<code>FD_SETSIZE</code>这个宏。那么结论是，<strong><code>select</code>至少在Windows上并没有socket支持数量的限制。</strong>当然效率问题这里不谈。</p>

<p>这看起来推翻了我们一直以来没有深究的一个事实。</p>

<h2>Linux的实现</h2>

<p>在上面提到的MSDN中，其实已经提到了Windows与Berkeley Unix实现的不同。在<code>select</code>的API文档中也看到了第一个参数并没有说明其作用。看下Linux的<a href="http://linux.die.net/man/2/select">man</a>：</p>

<blockquote><p>nfds is the highest-numbered file descriptor in any of the three sets, plus 1.</p></blockquote>

<p>第一个参数简单来说就是最大描述符+1。</p>

<blockquote><p>An fd_set is a fixed size buffer. Executing FD_CLR() or FD_SET() with a value of fd that is negative or is equal to or larger than FD_SETSIZE will result in undefined behavior.</p></blockquote>

<p>明确说了，如果调用<code>FD_SET</code>之类的宏fd超过了<code>FD_SETSIZE</code>将导致<code>undefined behavior</code>。也有人专门做了测试：<a href="http://www.moythreads.com/wordpress/2009/12/22/select-system-call-limitation/">select system call limitation in Linux</a>。也有现实遇到的问题：<a href="http://serverfault.com/questions/497086/socket-file-descriptor-1063-is-larger-than-fd-setsize-1024-you-probably-nee">socket file descriptor (1063) is larger than FD_SETSIZE (1024), you probably need to rebuild Apache with a larger FD_SETSIZE</a></p>

<p>看起来在Linux上使用<code>select</code>确实有<code>FD_SETSIZE</code>的限制。有必要看下相关的实现 <a href="http://fxr.watson.org/fxr/source/sys/fd_set.h?v=NETBSD">fd_set.h</a>：</p>

<pre><code>typedef __uint32_t      __fd_mask;

/* 32 = 2 ^ 5 */
#define __NFDBITS       (32)
#define __NFDSHIFT      (5)
#define __NFDMASK       (__NFDBITS - 1)

/*
 * Select uses bit fields of file descriptors.  These macros manipulate
 * such bit fields.  Note: FD_SETSIZE may be defined by the user.
 */

#ifndef FD_SETSIZE
#define FD_SETSIZE      256
#endif

#define __NFD_SIZE      (((FD_SETSIZE) + (__NFDBITS - 1)) / __NFDBITS)

typedef struct fd_set {
    __fd_mask       fds_bits[__NFD_SIZE];
} fd_set;
</code></pre>

<p>在这份实现中不同于Windows实现，它使用了位来表示fd。看下<code>FD_SET</code>系列宏的大致实现：</p>

<pre><code>#define FD_SET(n, p)    \
   ((p)-&gt;fds_bits[(unsigned)(n) &gt;&gt; __NFDSHIFT] |= (1 &lt;&lt; ((n) &amp; __NFDMASK)))
</code></pre>

<p>添加一个fd到<code>fd_set</code>中也不是Windows的遍历，而是直接位运算。这里也有人对另一份类似实现做了剖析：<a href="http://my.oschina.net/u/870054/blog/212063">linux的I/O多路转接select的fd_set数据结构和相应FD_宏的实现分析</a>。在APUE中也提到<code>fd_set</code>：</p>

<blockquote><p>这种数据类型(fd_set)为每一可能的描述符保持了一位。</p></blockquote>

<p>既然<code>fd_set</code>中不包含其保存了多少个fd的计数，那么<code>select</code>的实现里要知道自己要处理多少个fd，那只能使用FD_SETSIZE宏去做判定，但Linux的实现选用了更好的方式，即通过第一个参数让应用层告诉<code>select</code>需要处理的最大fd（这里不是数量）。那么其实现大概为：</p>

<pre><code>for (int i = 0; i &lt; nfds; ++i) {
    if (FD_ISSET...
       ...
}
</code></pre>

<p>如此看来，<strong>Linux的<code>select</code>实现则是受限于<code>FD_SETSIZE</code>的大小</strong>。这里也看到，<code>fd_set</code>使用位数组来保存fd，那么fd本身作为一个int数，其值就不能超过<code>FD_SETSIZE</code>。<strong>这不仅仅是数量的限制，还是其取值的限制</strong>。实际上，Linux上fd的取值是保证了小于<code>FD_SETSIZE</code>的（但不是不变的）<a href="http://stackoverflow.com/questions/12583927/is-the-value-of-a-linux-file-descriptor-always-smaller-than-the-open-file-limits">Is the value of a Linux file descriptor always smaller than the open file limits?</a>：</p>

<blockquote><p>Each process is further limited via the setrlimit(2) RLIMIT_NOFILE per-process limit on the number of open files. 1024 is a common RLIMIT_NOFILE limit. (It&#8217;s very easy to change this limit via /etc/security/limits.conf.)</p></blockquote>

<p>fd的取值会小于<code>RLIMIT_NOFILE</code>，有很多方法可以改变这个值。这个值默认情况下和<code>FD_SETSIZE</code>应该是一样的。这个信息告诉我们，<strong>Linux下fd的取值应该是从0开始递增的</strong>（理论上，实际上还有stdin/stdout/stderr之类的fd）。这才能保证<code>select</code>的那些宏可以工作。</p>

<h2>应用层使用</h2>

<p>标准的<code>select</code>用法应该大致如下：</p>

<pre><code>while (true) {
    ...
    select(...)
    for-each socket {
        if (FD_ISSET(fd, set))
            ...
    }

    ...
}
</code></pre>

<p>即遍历目前管理的fd，通过<code>FD_ISSET</code>去判定当前fd是否有IO事件。因为Windows的实现<code>FD_ISSET</code>都是一个循环，所以有了另一种不跨平台的用法：</p>

<pre><code>while (true) {
    ...
    select(. &amp;read_sockets, &amp;write_sockets..)
    for-each read_socket {
        use fd.fd_array[i)
    }
    ...
}
</code></pre>

<h2>总结</h2>

<ul>
<li>Windows上<code>select</code>没有fd数量的限制，但因为使用了循环来检查，所以效率相对较低</li>
<li>Linux上<code>select</code>有<code>FD_SETSIZE</code>的限制，但其相对效率较高</li>
</ul>
<p class="post-footer">
            原文地址：
            <a href="http://codemacro.com/2014/06/01/select-limit/">http://codemacro.com/2014/06/01/select-limit/</a><br />
            written by <a href="http://codemacro.com">Kevin Lynx</a>
            &nbsp;posted at <a href="http://codemacro.com">http://codemacro.com</a>
            </p>

</div><img src ="http://www.cppblog.com/kevinlynx/aggbug/207172.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2014-06-01 23:45 <a href="http://www.cppblog.com/kevinlynx/archive/2014/06/01/207172.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Muduo源码阅读</title><link>http://www.cppblog.com/kevinlynx/archive/2014/05/04/206817.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Sun, 04 May 2014 10:22:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2014/05/04/206817.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/206817.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2014/05/04/206817.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/206817.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/206817.html</trackback:ping><description><![CDATA[<div class="entry-content">
<p>最近简单读了下<a href="http://blog.csdn.net/solstice/article/details/5848547">muduo</a>的源码，本文对其主要实现/结构简单总结下。</p>

<p>muduo的主要源码位于net文件夹下，base文件夹是一些基础代码，不影响理解网络部分的实现。muduo主要类包括：</p>

<ul>
<li>EventLoop</li>
<li>Channel</li>
<li>Poller</li>
<li>TcpConnection</li>
<li>TcpClient</li>
<li>TcpServer</li>
<li>Connector</li>
<li>Acceptor</li>
<li>EventLoopThread</li>
<li>EventLoopThreadPool</li>
</ul>
<p>其中，Poller（及其实现类）包装了Poll/EPoll，封装了OS针对设备(fd)的操作；Channel是设备fd的包装，在muduo中主要包装socket；TcpConnection抽象一个TCP连接，无论是客户端还是服务器只要建立了网络连接就会使用TcpConnection；TcpClient/TcpServer分别抽象TCP客户端和服务器；Connector/Acceptor分别包装TCP客户端和服务器的建立连接/接受连接；EventLoop是一个主控类，是一个事件发生器，它驱动Poller产生/发现事件，然后将事件派发到Channel处理；EventLoopThread是一个带有EventLoop的线程；EventLoopThreadPool自然是一个EventLoopThread的资源池，维护一堆EventLoopThread。</p>

<p>阅读库源码时可以从库的接口层着手，看看关键功能是如何实现的。对于muduo而言，可以从TcpServer/TcpClient/EventLoop/TcpConnection这几个类着手。接下来看看主要功能的实现：</p>

<!-- more -->


<h2>建立连接</h2>

<div class="highlight">
<pre><code class="c">    <span class="n">TcpClient</span><span class="o">::</span><span class="n">connect</span> 
        <span class="o">-&gt;</span> <span class="n">Connector</span><span class="o">::</span><span class="n">start</span> 
            <span class="o">-&gt;</span> <span class="n">EventLoop</span><span class="o">::</span><span class="n">runInLoop</span><span class="p">(</span><span class="n">Connector</span><span class="o">::</span><span class="n">startInLoop</span><span class="p">...</span>
            <span class="o">-&gt;</span> <span class="n">Connector</span><span class="o">::</span><span class="n">connect</span>             
</code></pre>
</div>


<p>EventLoop::runInLoop接口用于在this所在的线程运行某个函数，这个后面看下EventLoop的实现就可以了解。 网络连接的最终建立是在Connector::connect中实现，建立连接之后会创建一个Channel来代表这个socket，并且绑定事件监听接口。最后最重要的是，调用<code>Channel::enableWriting</code>。<code>Channel</code>有一系列的enableXX接口，这些接口用于标识自己关心某IO事件。后面会看到他们的实现。</p>

<p>Connector监听的主要事件无非就是连接已建立，用它监听读数据/写数据事件也不符合设计。TcpConnection才是做这种事的。</p>

<h2>客户端收发数据</h2>

<p>当Connector发现连接真正建立好后，会回调到<code>TcpClient::newConnection</code>，在TcpClient构造函数中：</p>

<div class="highlight">
<pre><code class="c">    <span class="n">connector_</span><span class="o">-&gt;</span><span class="n">setNewConnectionCallback</span><span class="p">(</span>
      <span class="n">boost</span><span class="o">::</span><span class="n">bind</span><span class="p">(</span><span class="o">&amp;</span><span class="n">TcpClient</span><span class="o">::</span><span class="n">newConnection</span><span class="p">,</span> <span class="n">this</span><span class="p">,</span> <span class="n">_1</span><span class="p">));</span>
</code></pre>
</div>


<p><code>TcpClient::newConnection</code>中创建一个TcpConnection来代表这个连接：</p>

<div class="highlight">
<pre><code class="c">    <span class="n">TcpConnectionPtr</span> <span class="nf">conn</span><span class="p">(</span><span class="n">new</span> <span class="n">TcpConnection</span><span class="p">(</span><span class="n">loop_</span><span class="p">,</span>
                                            <span class="n">connName</span><span class="p">,</span>
                                            <span class="n">sockfd</span><span class="p">,</span>
                                            <span class="n">localAddr</span><span class="p">,</span>
                                            <span class="n">peerAddr</span><span class="p">));</span>

    <span class="n">conn</span><span class="o">-&gt;</span><span class="n">setConnectionCallback</span><span class="p">(</span><span class="n">connectionCallback_</span><span class="p">);</span>
    <span class="n">conn</span><span class="o">-&gt;</span><span class="n">setMessageCallback</span><span class="p">(</span><span class="n">messageCallback_</span><span class="p">);</span>
    <span class="n">conn</span><span class="o">-&gt;</span><span class="n">setWriteCompleteCallback</span><span class="p">(</span><span class="n">writeCompleteCallback_</span><span class="p">);</span>
    <span class="p">...</span>
    <span class="n">conn</span><span class="o">-&gt;</span><span class="n">connectEstablished</span><span class="p">();</span>
</code></pre>
</div>


<p>并同时设置事件回调，以上设置的回调都是应用层（即库的使用者）的接口。每一个TcpConnection都有一个Channel，毕竟每一个网络连接都对应了一个socket fd。在TcpConnection构造函数中创建了一个Channel，并设置事件回调函数。</p>

<p><code>TcpConnection::connectEstablished</code>函数最主要的是通知Channel自己开始关心IO读取事件：</p>

<div class="highlight">
<pre><code class="c">    <span class="kt">void</span> <span class="n">TcpConnection</span><span class="o">::</span><span class="n">connectEstablished</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="p">...</span>
        <span class="n">channel_</span><span class="o">-&gt;</span><span class="n">enableReading</span><span class="p">();</span>
</code></pre>
</div>


<p>这是自此我们看到的第二个<code>Channel::enableXXX</code>接口，这些接口是如何实现关心IO事件的呢？这个后面讲到。</p>

<p>muduo的数据发送都是通过<code>TcpConnection::send</code>完成，这个就是一般网络库中在不使用OS的异步IO情况下的实现：缓存应用层传递过来的数据，在IO设备可写的情况下尽量写入数据。这个主要实现在<code>TcpConnection::sendInLoop</code>中。</p>

<div class="highlight">
<pre><code class="c">    <span class="n">TcpConnection</span><span class="o">::</span><span class="n">sendInLoop</span><span class="p">(....)</span> <span class="p">{</span>
        <span class="p">...</span>
        <span class="c1">// if no thing in output queue, try writing directly</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">channel_</span><span class="o">-&gt;</span><span class="n">isWriting</span><span class="p">()</span> <span class="o">&amp;&amp;</span> <span class="n">outputBuffer_</span><span class="p">.</span><span class="n">readableBytes</span><span class="p">()</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>  <span class="c1">// 设备可写且没有缓存时立即写入</span>
        <span class="p">{</span> 
            <span class="n">nwrote</span> <span class="o">=</span> <span class="n">sockets</span><span class="o">::</span><span class="n">write</span><span class="p">(</span><span class="n">channel_</span><span class="o">-&gt;</span><span class="n">fd</span><span class="p">(),</span> <span class="n">data</span><span class="p">,</span> <span class="n">len</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="p">...</span>
        <span class="c1">// 否则加入数据到缓存，等待IO可写时再写</span>
        <span class="n">outputBuffer_</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">static_cast</span><span class="o">&lt;</span><span class="k">const</span> <span class="kt">char</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">data</span><span class="p">)</span><span class="o">+</span><span class="n">nwrote</span><span class="p">,</span> <span class="n">remaining</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">channel_</span><span class="o">-&gt;</span><span class="n">isWriting</span><span class="p">())</span>
        <span class="p">{</span>
            <span class="c1">// 注册关心IO写事件，Poller就会对写做检测</span>
            <span class="n">channel_</span><span class="o">-&gt;</span><span class="n">enableWriting</span><span class="p">();</span>
        <span class="p">}</span>
        <span class="p">...</span>     
    <span class="p">}</span>
</code></pre>
</div>


<p>当IO可写时，Channel就会回调<code>TcpConnection::handleWrite</code>（构造函数中注册）</p>

<div class="highlight">
<pre><code class="c">    <span class="kt">void</span> <span class="n">TcpConnection</span><span class="o">::</span><span class="n">handleWrite</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="p">...</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">channel_</span><span class="o">-&gt;</span><span class="n">isWriting</span><span class="p">())</span>
        <span class="p">{</span>
            <span class="kt">ssize_t</span> <span class="n">n</span> <span class="o">=</span> <span class="n">sockets</span><span class="o">::</span><span class="n">write</span><span class="p">(</span><span class="n">channel_</span><span class="o">-&gt;</span><span class="n">fd</span><span class="p">(),</span>
                               <span class="n">outputBuffer_</span><span class="p">.</span><span class="n">peek</span><span class="p">(),</span>
                               <span class="n">outputBuffer_</span><span class="p">.</span><span class="n">readableBytes</span><span class="p">());</span>
</code></pre>
</div>


<p>服务器端的数据收发同客户端机制一致，不同的是连接(TcpConnection)的建立方式不同。</p>

<h2>服务器接收连接</h2>

<p>服务器接收连接的实现在一个网络库中比较重要。muduo中通过Acceptor类来接收连接。在TcpClient中，其Connector通过一个关心Channel可写的事件来通过连接已建立；在Acceptor中则是通过一个Channel可读的事件来表示有新的连接到来：</p>

<div class="highlight">
<pre><code class="c">    <span class="n">Acceptor</span><span class="o">::</span><span class="n">Acceptor</span><span class="p">(....)</span> <span class="p">{</span>
        <span class="p">...</span>
        <span class="n">acceptChannel_</span><span class="p">.</span><span class="n">setReadCallback</span><span class="p">(</span>
            <span class="n">boost</span><span class="o">::</span><span class="n">bind</span><span class="p">(</span><span class="o">&amp;</span><span class="n">Acceptor</span><span class="o">::</span><span class="n">handleRead</span><span class="p">,</span> <span class="n">this</span><span class="p">));</span>
        <span class="p">...</span> 
    <span class="p">}</span>

    <span class="kt">void</span> <span class="n">Acceptor</span><span class="o">::</span><span class="n">handleRead</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="p">...</span>
        <span class="kt">int</span> <span class="n">connfd</span> <span class="o">=</span> <span class="n">acceptSocket_</span><span class="p">.</span><span class="n">accept</span><span class="p">(</span><span class="o">&amp;</span><span class="n">peerAddr</span><span class="p">);</span> <span class="c1">// 接收连接获得一个新的socket</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">connfd</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="p">...</span>
            <span class="n">newConnectionCallback_</span><span class="p">(</span><span class="n">connfd</span><span class="p">,</span> <span class="n">peerAddr</span><span class="p">);</span> <span class="c1">// 回调到TcpServer::newConnection</span>
</code></pre>
</div>


<p><code>TcpServer::newConnection</code>中建立一个TcpConnection，并将其附加到一个EventLoopThread中，简单来说就是给其配置一个线程：</p>

<div class="highlight">
<pre><code class="c">    <span class="kt">void</span> <span class="n">TcpServer</span><span class="o">::</span><span class="n">newConnection</span><span class="p">(</span><span class="kt">int</span> <span class="n">sockfd</span><span class="p">,</span> <span class="k">const</span> <span class="n">InetAddress</span><span class="o">&amp;</span> <span class="n">peerAddr</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="p">...</span>
        <span class="n">EventLoop</span><span class="o">*</span> <span class="n">ioLoop</span> <span class="o">=</span> <span class="n">threadPool_</span><span class="o">-&gt;</span><span class="n">getNextLoop</span><span class="p">();</span>
        <span class="n">TcpConnectionPtr</span> <span class="nf">conn</span><span class="p">(</span><span class="n">new</span> <span class="n">TcpConnection</span><span class="p">(</span><span class="n">ioLoop</span><span class="p">,</span>
                                                <span class="n">connName</span><span class="p">,</span>
                                                <span class="n">sockfd</span><span class="p">,</span>
                                                <span class="n">localAddr</span><span class="p">,</span>
                                                <span class="n">peerAddr</span><span class="p">));</span>
        <span class="n">connections_</span><span class="p">[</span><span class="n">connName</span><span class="p">]</span> <span class="o">=</span> <span class="n">conn</span><span class="p">;</span>
        <span class="p">...</span>
        <span class="n">ioLoop</span><span class="o">-&gt;</span><span class="n">runInLoop</span><span class="p">(</span><span class="n">boost</span><span class="o">::</span><span class="n">bind</span><span class="p">(</span><span class="o">&amp;</span><span class="n">TcpConnection</span><span class="o">::</span><span class="n">connectEstablished</span><span class="p">,</span> <span class="n">conn</span><span class="p">));</span>
</code></pre>
</div>


<h2>IO的驱动</h2>

<p>之前提到，一旦要关心某IO事件了，就调用<code>Channel::enableXXX</code>，这个如何实现的呢？</p>

<div class="highlight">
<pre><code class="c">    <span class="n">class</span> <span class="n">Channel</span> <span class="p">{</span>
        <span class="p">...</span>
        <span class="kt">void</span> <span class="n">enableReading</span><span class="p">()</span> <span class="p">{</span> <span class="n">events_</span> <span class="o">|=</span> <span class="n">kReadEvent</span><span class="p">;</span> <span class="n">update</span><span class="p">();</span> <span class="p">}</span>
        <span class="kt">void</span> <span class="n">enableWriting</span><span class="p">()</span> <span class="p">{</span> <span class="n">events_</span> <span class="o">|=</span> <span class="n">kWriteEvent</span><span class="p">;</span> <span class="n">update</span><span class="p">();</span> <span class="p">}</span>
       
    <span class="kt">void</span> <span class="n">Channel</span><span class="o">::</span><span class="n">update</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="n">loop_</span><span class="o">-&gt;</span><span class="n">updateChannel</span><span class="p">(</span><span class="n">this</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kt">void</span> <span class="n">EventLoop</span><span class="o">::</span><span class="n">updateChannel</span><span class="p">(</span><span class="n">Channel</span><span class="o">*</span> <span class="n">channel</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="p">...</span>
        <span class="n">poller_</span><span class="o">-&gt;</span><span class="n">updateChannel</span><span class="p">(</span><span class="n">channel</span><span class="p">);</span>
    <span class="p">}</span>
</code></pre>
</div>


<p>最终调用到<code>Poller::upateChannel</code>。muduo中有两个Poller的实现，分别是Poll和EPoll，可以选择简单的Poll来看：</p>

<div class="highlight">
<pre><code class="c">    <span class="kt">void</span> <span class="n">PollPoller</span><span class="o">::</span><span class="n">updateChannel</span><span class="p">(</span><span class="n">Channel</span><span class="o">*</span> <span class="n">channel</span><span class="p">)</span>
    <span class="p">{</span>
      <span class="p">...</span>
      <span class="k">if</span> <span class="p">(</span><span class="n">channel</span><span class="o">-&gt;</span><span class="n">index</span><span class="p">()</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span>
      <span class="p">{</span>
        <span class="c1">// a new one, add to pollfds_</span>
        <span class="n">assert</span><span class="p">(</span><span class="n">channels_</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">channel</span><span class="o">-&gt;</span><span class="n">fd</span><span class="p">())</span> <span class="o">==</span> <span class="n">channels_</span><span class="p">.</span><span class="n">end</span><span class="p">());</span>
        <span class="k">struct</span> <span class="n">pollfd</span> <span class="n">pfd</span><span class="p">;</span>
        <span class="n">pfd</span><span class="p">.</span><span class="n">fd</span> <span class="o">=</span> <span class="n">channel</span><span class="o">-&gt;</span><span class="n">fd</span><span class="p">();</span>
        <span class="n">pfd</span><span class="p">.</span><span class="n">events</span> <span class="o">=</span> <span class="n">static_cast</span><span class="o">&lt;</span><span class="kt">short</span><span class="o">&gt;</span><span class="p">(</span><span class="n">channel</span><span class="o">-&gt;</span><span class="n">events</span><span class="p">());</span> <span class="c1">// 也就是Channel::enableXXX操作的那个events_</span>
        <span class="n">pfd</span><span class="p">.</span><span class="n">revents</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="n">pollfds_</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">pfd</span><span class="p">);</span> <span class="c1">// 加入一个新的pollfd</span>
        <span class="kt">int</span> <span class="n">idx</span> <span class="o">=</span> <span class="n">static_cast</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="n">pollfds_</span><span class="p">.</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="n">channel</span><span class="o">-&gt;</span><span class="n">set_index</span><span class="p">(</span><span class="n">idx</span><span class="p">);</span>
        <span class="n">channels_</span><span class="p">[</span><span class="n">pfd</span><span class="p">.</span><span class="n">fd</span><span class="p">]</span> <span class="o">=</span> <span class="n">channel</span><span class="p">;</span>
</code></pre>
</div>


<p>可见Poller就是把Channel关心的IO事件转换为OS提供的IO模型数据结构上。通过查看关键的<code>pollfds_</code>的使用，可以发现其主要是在Poller::poll接口里。这个接口会在EventLoop的主循环中不断调用：</p>

<div class="highlight">
<pre><code class="c">    <span class="kt">void</span> <span class="n">EventLoop</span><span class="o">::</span><span class="n">loop</span><span class="p">()</span>
    <span class="p">{</span>
      <span class="p">...</span>
      <span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">quit_</span><span class="p">)</span>
      <span class="p">{</span>
        <span class="n">activeChannels_</span><span class="p">.</span><span class="n">clear</span><span class="p">();</span>
        <span class="n">pollReturnTime_</span> <span class="o">=</span> <span class="n">poller_</span><span class="o">-&gt;</span><span class="n">poll</span><span class="p">(</span><span class="n">kPollTimeMs</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">activeChannels_</span><span class="p">);</span>
        <span class="p">...</span>
        <span class="k">for</span> <span class="p">(</span><span class="n">ChannelList</span><span class="o">::</span><span class="n">iterator</span> <span class="n">it</span> <span class="o">=</span> <span class="n">activeChannels_</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span>
            <span class="n">it</span> <span class="o">!=</span> <span class="n">activeChannels_</span><span class="p">.</span><span class="n">end</span><span class="p">();</span> <span class="o">++</span><span class="n">it</span><span class="p">)</span>
        <span class="p">{</span>
          <span class="n">currentActiveChannel_</span> <span class="o">=</span> <span class="o">*</span><span class="n">it</span><span class="p">;</span>
          <span class="n">currentActiveChannel_</span><span class="o">-&gt;</span><span class="n">handleEvent</span><span class="p">(</span><span class="n">pollReturnTime_</span><span class="p">);</span> <span class="c1">// 获得IO事件，通知各注册回调</span>
        <span class="p">}</span>
</code></pre>
</div>


<p>整个流程可总结为：各Channel内部会把自己关心的事件告诉给Poller，Poller由EventLoop驱动检测IO，然后返回哪些Channel发生了事件，EventLoop再驱动这些Channel调用各注册回调。</p>

<p>从这个过程中可以看出，EventLoop就是一个事件产生器。</p>

<h2>线程模型</h2>

<p>在muduo的服务器中，muduo的线程模型是怎样的呢？它如何通过线程来支撑高并发呢？其实很简单，它为每一个线程配置了一个EventLoop，这个线程同时被附加了若干个网络连接，这个EventLoop服务于这些网络连接，为这些连接收集并派发IO事件。</p>

<p>回到<code>TcpServer::newConnection</code>中：</p>

<div class="highlight">
<pre><code class="c">    <span class="kt">void</span> <span class="n">TcpServer</span><span class="o">::</span><span class="n">newConnection</span><span class="p">(</span><span class="kt">int</span> <span class="n">sockfd</span><span class="p">,</span> <span class="k">const</span> <span class="n">InetAddress</span><span class="o">&amp;</span> <span class="n">peerAddr</span><span class="p">)</span>
    <span class="p">{</span>
      <span class="p">...</span>
      <span class="n">EventLoop</span><span class="o">*</span> <span class="n">ioLoop</span> <span class="o">=</span> <span class="n">threadPool_</span><span class="o">-&gt;</span><span class="n">getNextLoop</span><span class="p">();</span>
      <span class="p">...</span>
      <span class="n">TcpConnectionPtr</span> <span class="n">conn</span><span class="p">(</span><span class="n">new</span> <span class="n">TcpConnection</span><span class="p">(</span><span class="n">ioLoop</span><span class="p">,</span> <span class="c1">// 使用这个选择到的线程中的EventLoop</span>
                                              <span class="n">connName</span><span class="p">,</span>
                                              <span class="n">sockfd</span><span class="p">,</span>
                                              <span class="n">localAddr</span><span class="p">,</span>
                                              <span class="n">peerAddr</span><span class="p">));</span>
      <span class="p">...</span>
      <span class="n">ioLoop</span><span class="o">-&gt;</span><span class="n">runInLoop</span><span class="p">(</span><span class="n">boost</span><span class="o">::</span><span class="n">bind</span><span class="p">(</span><span class="o">&amp;</span><span class="n">TcpConnection</span><span class="o">::</span><span class="n">connectEstablished</span><span class="p">,</span> <span class="n">conn</span><span class="p">));</span>
</code></pre>
</div>


<p>注意<code>TcpConnection::connectEstablished</code>是如何通过Channel注册关心的IO事件到<code>ioLoop</code>的。</p>

<p>极端来说，muduo的每一个连接线程可以只为一个网络连接服务，这就有点类似于thread per connection模型了。</p>

<h2>网络模型</h2>

<p>传说中的Reactor模式，以及one loop per thread，基于EventLoop的作用，以及线程池与TcpConnection的关系，可以醍醐灌顶般理解以下这张muduo的网络模型图了：</p>

<p><img src="http://codemacro.com/assets/res/muduo-model.png" width="634" height="480" alt="" /><br /></p>

<h2>总结</h2>

<p>本文主要对muduo的主要结构及主要机制的实现做了描述，其他如Buffer的实现、定时器的实现大家都可以自行研究。muduo的源码很清晰，通过源码及配合<a href="http://blog.csdn.net/solstice">陈硕博客</a>上的内容可以学到一些网络编程方面的经验。</p>

<p class="post-footer">
            原文地址：
            <a href="http://codemacro.com/2014/05/04/muduo-source/">http://codemacro.com/2014/05/04/muduo-source/</a><br />
            written by <a href="http://codemacro.com">Kevin Lynx</a>
            &nbsp;posted at <a href="http://codemacro.com">http://codemacro.com</a>
            </p>

</div><img src ="http://www.cppblog.com/kevinlynx/aggbug/206817.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2014-05-04 18:22 <a href="http://www.cppblog.com/kevinlynx/archive/2014/05/04/206817.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Dhtcrawler2换用sphinx搜索</title><link>http://www.cppblog.com/kevinlynx/archive/2013/08/08/202417.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Thu, 08 Aug 2013 15:04:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2013/08/08/202417.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/202417.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2013/08/08/202417.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/202417.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/202417.html</trackback:ping><description><![CDATA[<div class="entry-content">
<p>dhtcrawler2最开始使用mongodb自带的全文搜索引擎搜索资源。搜索一些短关键字时很容易导致erlang进程call timeout，也就是查询时间太长。对于像<code>avi</code>这种关键字，搜索时间长达十几秒。搜索的资源数量200万左右。这其中大部分资源只是对root文件名进行了索引，即对于多文件资源而言没有索引单个文件名。索引方式有部分资源是按照字符串子串的形式，没有拆词，非常占用存储空间；有部分是使用了rmmseg（我编译了rmmseg-cpp作为erlang nif库调用 <a href="https://github.com/kevinlynx/erl-rmmseg">erl-rmmseg</a>）进行了拆词，占用空间小了很多，但由于词库问题很多片里的词汇没拆出来。</p>

<p>很早以前我以为搜索耗时的原因是因为数据库太忙，想部署个mongodb集群出来。后来发现数据库没有任何读写的状态下，查询依然慢。终于只好放弃mongodb自带的文本搜索。于是我改用sphinx。简单起见，我直接下载了<a href="http://www.coreseek.cn/">coreseek4.1</a>（sphinx的一个支持中文拆词的包装）。</p>

<p>现在，已经导入了200多万的资源进sphinx，并且索引了所有文件名，索引文件达800M。对于<code>avi</code>关键字的搜索大概消耗0.2秒的时间。<a href="http://bt.cm/e/http_handler:search?q=avi">搜索试试</a>。</p>

<p>以下记录下sphinx在dhtcrawler的应用</p>

<h3>sphinx简介</h3>

<p>sphinx包含两个主要的程序：indexer和searchd。indexer用于建立文本内容的索引，然后searchd基于这些索引提供文本搜索功能，而要使用该功能，可以遵循searchd的网络协议连接searchd这个服务来使用。</p>

<p>indexer可以通过多种方式来获取这些文本内容，文本内容的来源称为数据源。sphinx内置mysql这种数据源，意思是可以直接从mysql数据库中取得数据。sphinx还支持xmlpipe2这种数据源，其数据以xml格式提供给indexer。要导入mongodb数据库里的内容，可以选择使用xmlpipe2这种方式。</p>

<!-- more -->


<h3>sphinx document</h3>

<p>xmlpipe2数据源需要按照以下格式提交：</p>

<pre><code>&lt;sphinx:docset&gt;
    &lt;sphinx:schema&gt;
        &lt;sphinx:field name="subject"/&gt;
        &lt;sphinx:field name="files"/&gt;
        &lt;sphinx:attr name="hash1" type="int" bits="32"/&gt;
        &lt;sphinx:attr name="hash2" type="int" bits="32"/&gt;
    &lt;/sphinx:schema&gt;
    &lt;sphinx:document id="1"&gt;
        &lt;subject&gt;this is the subject&lt;/subject&gt;
        &lt;files&gt;file content&lt;/files&gt;
        &lt;hash1&gt;111&lt;/hash1&gt;
    &lt;/sphinx:document&gt;
&lt;/sphinx:docset&gt;
</code></pre>

<p>该文件包含两大部分：<code>schema</code>和<code>documents</code>，其中<code>schema</code>又包含两部分：<code>field</code>和<code>attr</code>，其中由<code>field</code>标识的字段就会被indexer读取并全部作为输入文本建立索引，而<code>attr</code>则标识查询结果需要附带的信息；<code>documents</code>则是由一个个<code>sphinx:document</code>组成，即indexer真正要处理的数据。注意其中被<code>schema</code>引用的属性名。</p>

<p>document一个很重要的属性就是它的id。这个id对应于sphinx需要唯一，查询结果也会包含此id。一般情况下，此id可以直接是数据库主键，可用于查询到详细信息。searchd搜索关键字，其实可以看作为搜索这些document，搜索出来的结果也是这些document，搜索结果中主要包含schema中指定的attr。</p>

<h3>增量索引</h3>

<p>数据源的数据一般是变化的，新增的数据要加入到sphinx索引文件中，才能使得searchd搜索到新录入的数据。要不断地加入新数据，可以使用增量索引机制。增量索引机制中，需要一个主索引和一个次索引(delta index)。每次新增的数据都建立为次索引，然后一段时间后再合并进主索引。这个过程主要还是使用indexer和searchd程序。实际上，searchd是一个需要一直运行的服务，而indexer则是一个建立完索引就退出的工具程序。所以，这里的增量索引机制，其中涉及到的&#8220;每隔一定时间就合并&#8221;这种工作，需要自己写程序来协调（或通过其他工具）</p>

<h3>sphinx与mongodb</h3>

<p>上面提到，一般sphinx document的id都是使用的数据库主键，以方便查询。但mongodb中默认情况不使用数字作为主键。dhtcrawler的资源数据库使用的是资源info-hash作为主键，这无法作为sphinx document的id。一种解决办法是，将该hash按位拆分，拆分成若干个sphinx document attr支持位数的整数。例如，info-hash是一个160位的id，如果使用32位的attr（高版本的sphinx支持64位的整数），那么可以把该info-hash按位拆分成5个attr。而sphinx document id则可以使用任意数字，只要保证不冲突就行。当获得查询结果时，取得对应的attr，组合为info-hash即可。</p>

<p>mongodb默认的Object id也可以按这种方式拆分。</p>

<h3>dhtcrawler2与sphinx</h3>

<p>dhtcrawler2中我自己写了一个导入程序。该程序从mongodb中读出数据，数据到一定量时，就输出为xmlpipe2格式的xml文件，然后建立为次索引，最后合并进主索引。过程很简单，包含两次启动外部进程的工作，这个可以通过erlang中os:cmd完成。</p>

<p>值得注意的是，在从mongodb中读数据时，使用skip基本是不靠谱的，skip 100万个数据需要好几分钟，为了不增加额外的索引字段，我只好在<code>created_at</code>字段上加索引，然后按时间段来读取资源，这一切都是为了支持程序关闭重启后，可以继续上次工作，而不是重头再来。200万的数据，已经处理了好几天了。</p>

<p>后头数据建立好了，需要在前台展示出来。erlang中似乎只有一个sphinx客户端库：<a href="https://github.com/kevsmith/giza">giza</a>。这个库有点老，写成的时候貌似还在使用sphinx0.9版本。其中查询代码包含了版本判定，已经无法在我使用的sphinx2.x版本中使用。无奈之下我只好修改了这个库的源码，幸运的是查询功能居然是正常的，意味着sphinx若干个版本了也没改动通信协议？后来，我为了取得查询的统计信息，例如消耗时间以及总结果，我再一次修改了giza的源码。新的版本可以在我的github上找到：<a href="https://github.com/kevinlynx/giza">my giza</a>，看起来我没侵犯版本协议吧？</p>

<p>目前dhtcrawler的搜索，先是基于sphinx搜索出hash列表，然后再去mongodb中搜索hash对应的资源。事实上，可以为sphinx的document直接附加这些资源的描述信息，就可以避免去数据库查询。但我想，这样会增加sphinx索引文件的大小，担心会影响搜索速度。实际测试时，发现数据库查询有时候还真的很消耗时间，尽管我做了分页，以使得单页仅对数据库进行少量查询。</p>

<h3>xml unicode</h3>

<p>在导入xml到sphinx的索引过程中，本身我输出的内容都是unicode的，但有很多资源会导致indexer解析xml出错。出错后indexer直接停止对当前xml的处理。后来查阅资料发现是因为这些无法被indexer处理的xml内容包含unicode里的控制字符，例如 &#228; (U+00E4)。我的解决办法是直接过滤掉这些控制字符。unicode的控制字符参看<a href="http://www.utf8-chartable.de/">UTF-8 encoding table and Unicode characters</a>。在erlang中干这个事居然不复杂：</p>

<div class="highlight">
<pre><code class="erlang"><span class="nf">strip_invalid_unicode</span><span class="p">(</span><span class="o">&lt;&lt;&gt;&gt;</span><span class="p">)</span> <span class="o">-&gt;</span>
    <span class="o">&lt;&lt;&gt;&gt;</span><span class="p">;</span>
<span class="nf">strip_invalid_unicode</span><span class="p">(</span><span class="o">&lt;&lt;</span><span class="nv">C</span><span class="o">/</span><span class="n">utf8</span><span class="p">,</span> <span class="nv">R</span><span class="o">/</span><span class="n">binary</span><span class="o">&gt;&gt;</span><span class="p">)</span> <span class="o">-&gt;</span>
    <span class="k">case</span> <span class="n">is_valid_unicode</span><span class="p">(</span><span class="nv">C</span><span class="p">)</span> <span class="k">of</span>
        <span class="n">true</span> <span class="o">-&gt;</span>
            <span class="nv">RR</span> <span class="o">=</span> <span class="n">strip_invalid_unicode</span><span class="p">(</span><span class="nv">R</span><span class="p">),</span>
            <span class="o">&lt;&lt;</span><span class="nv">C</span><span class="o">/</span><span class="n">utf8</span><span class="p">,</span> <span class="nv">RR</span><span class="o">/</span><span class="n">binary</span><span class="o">&gt;&gt;</span><span class="p">;</span>
        <span class="n">false</span> <span class="o">-&gt;</span>
            <span class="n">strip_invalid_unicode</span><span class="p">(</span><span class="nv">R</span><span class="p">)</span>
    <span class="k">end</span><span class="p">;</span>
<span class="nf">strip_invalid_unicode</span><span class="p">(</span><span class="o">&lt;&lt;</span><span class="p">_,</span> <span class="nv">R</span><span class="o">/</span><span class="n">binary</span><span class="o">&gt;&gt;</span><span class="p">)</span> <span class="o">-&gt;</span>
    <span class="n">strip_invalid_unicode</span><span class="p">(</span><span class="nv">R</span><span class="p">).</span>
    
<span class="nf">is_valid_unicode</span><span class="p">(</span><span class="nv">C</span><span class="p">)</span> <span class="k">when</span> <span class="nv">C</span> <span class="o">&lt;</span> <span class="mi">16#20</span> <span class="o">-&gt;</span>
    <span class="n">false</span><span class="p">;</span>
<span class="nf">is_valid_unicode</span><span class="p">(</span><span class="nv">C</span><span class="p">)</span> <span class="k">when</span> <span class="nv">C</span> <span class="o">&gt;=</span> <span class="mi">16#7f</span><span class="p">,</span> <span class="nv">C</span> <span class="o">=&lt;</span> <span class="mi">16#ff</span> <span class="o">-&gt;</span>
    <span class="n">false</span><span class="p">;</span>
<span class="nf">is_valid_unicode</span><span class="p">(_)</span> <span class="o">-&gt;</span>
    <span class="n">true</span><span class="p">.</span>
</code></pre>
</div>




<p class="post-footer">
            原文地址：
            <a href="http://codemacro.com/2013/08/08/sphinx-dhtcrawler/">http://codemacro.com/2013/08/08/sphinx-dhtcrawler/</a><br />
            written by <a href="http://codemacro.com">Kevin Lynx</a>
            &nbsp;posted at <a href="http://codemacro.com">http://codemacro.com</a>
            </p>

</div><img src ="http://www.cppblog.com/kevinlynx/aggbug/202417.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2013-08-08 23:04 <a href="http://www.cppblog.com/kevinlynx/archive/2013/08/08/202417.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>磁力搜索第二版-dhtcrawler2</title><link>http://www.cppblog.com/kevinlynx/archive/2013/07/20/201994.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Sat, 20 Jul 2013 08:37:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2013/07/20/201994.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/201994.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2013/07/20/201994.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/201994.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/201994.html</trackback:ping><description><![CDATA[<div class="entry-content">
<p>接<a href="http://codemacro.com/2013/06/21/magnet-search-impl/">上篇</a>。</p>

<h2>下载使用</h2>

<p>目前为止dhtcrawler2相对dhtcrawler而言，数据库部分调整很大，DHT部分基本沿用之前。但单纯作为一个爬资源的程序而言，DHT部分可以进行大幅削减，这个以后再说。这个版本更快、更稳定。为了方便，我将编译好的erlang二进制文件作为git的主分支，我还添加了一些Windows下的批处理脚本，总之基本上下载源码以后即可运行。</p>

<p>项目地址：<a href="https://github.com/kevinlynx/dhtcrawler2">https://github.com/kevinlynx/dhtcrawler2</a></p>

<h3>使用方法</h3>

<ul>
<li>下载erlang，我测试的是R16B版本，确保erl等程序被加入<code>Path</code>环境变量</li>
<li>
<p>下载mongodb，解压即用：</p>

<pre><code>  mongod --dbpath xxx --setParameter textSearchEnabled=true
</code></pre>
</li>
<li>
<p>下载dhtcrawler2</p>

<pre><code>  git clone https://github.com/kevinlynx/dhtcrawler2.git
</code></pre>
</li>
<li><p>运行<code>win_start_crawler.bat</code></p></li>
<li>运行<code>win_start_hash.bat</code>
</li>
<li>运行<code>win_start_http.bat</code>
</li>
<li>打开<code>localhost:8000</code>查看<code>stats</code>
</li>
</ul>
<p>爬虫每次运行都会保存DHT节点状态，早期运行的时候收集速度会不够。dhtcrawler2将程序分为3部分：</p>

<ul>
<li>crawler，即DHT爬虫部分，仅负责收集hash</li>
<li>hash，准确来讲叫<code>hash reader</code>，处理爬虫收集的hash，处理过程主要涉及到下载种子文件</li>
<li>http，使用hash处理出来的数据库，以作为Web端接口</li>
</ul>
<p>我没有服务器，但程序有被部署在别人的服务器上：<a href="http://bt.cm">bt.cm</a>，<a href="http://222.175.114.126:8000/">http://222.175.114.126:8000/</a>。</p>

<!-- more -->


<h3>其他工具</h3>

<p>为了提高资源索引速度，我陆续写了一些工具，包括：</p>

<ul>
<li>import_tors，用于导入本地种子文件到数据库</li>
<li>tor_cache，用于下载种子到本地，仅仅提供下载的功能，hash_reader在需要种子文件时，可以先从本地取</li>
<li>cache_indexer，目前hash_reader取种子都是从torrage.com之类的种子缓存站点取，这些站点提供了种子列表，cache_indexer将这些列表导入数据库，hash_reader在请求种子文件前可以通过该数据库检查torrage.com上有无此种子，从而减少多余的http请求</li>
</ul>
<p>这些工具的代码都被放在dhtcrawler2中，可以查看对应的启动脚本来查看具体如何启动。</p>

<h3>OS/Database</h3>

<p>根据实际的测试效果来看，当收集的资源量过百万时（目前bt.cm录入近160万资源），4G内存的Windows平台，mongodb很容易就会挂掉。挂掉的原因全是1455，页面文件太小。有人建议不要在Windows下使用mongodb，Linux下我自己没做过测试。</p>

<p>mongodb可以部署为集群形式(replica-set)，当初我想把http部分的查询放在一个只读的mongodb实例上，但因为建立集群时，要同步已有的10G数据库，而每次同步都以mongodb挂掉结束，遂放弃。在目前bt.cm的配置中，数据库torrent的锁比例（db lock）很容易上50%，这也让http在搜索时，经常出现搜索超时的情况。</p>

<h2>技术信息</h2>

<p>dhtcrawler最早的版本有很多问题，修复过的最大的一个问题是关于erlang定时器的，在DHT实现中，需要对每个节点每个peer做超时处理，在erlang中的做法直接是针对每个节点注册了一个定时器。这不是问题，问题在于定时器资源就像没有GC的内存资源一样，是会由于程序员的代码问题而出现资源泄漏。所以，dhtcrawler第一个版本在节点数配置在100以上的情况下，用不了多久就会内存耗尽，最终导致erlang虚拟机core dump。</p>

<p>除了这个问题以外，dhtcrawler的资源收录速度也不是很快。这当然跟数据库和获取种子的速度有直接关系。尤其是获取种子，使用的是一些提供info-hash到种子映射的网站，通过HTTP请求来下载种子文件。我以为通过BT协议直接下载种子会快些，并且实时性也要高很多，因为这个种子可能未被这些缓存网站收录，但却可以直接向对方请求得到。为此，我还特地翻阅了相关<a href="http://www.bittorrent.org/beps/bep_0009.html">协议</a>，并且用erlang实现了（以后的文章我会讲到具体实现这个协议）。</p>

<p>后来我怀疑get_peers的数量会不会比announce_peer多，但是理论上一般的客户端在get_peers之后都是announce_peer，但是如果get_peers查询的peers恰好不在线呢？这意味着很多资源虽然已经存在，只不过你恰好暂时请求不到。实际测试时，发现get_peers基本是announce_peer数量的10倍。</p>

<p>将hash的获取方式做了调整后，dhtcrawler在几分钟以内以几乎每秒上百个新增种子的速度工作。然后，程序挂掉。</p>

<p>从dhtcrawler到今天为止的dhtcrawler2，中间间隔了刚好1个月。我的所有业余时间全部扑在这个项目上，面临的问题一直都是程序的内存泄漏、资源收录的速度不够快，到后来又变为数据库压力过大。每一天我都以为我将会完成一个稳定版本，然后终于可以去干点别的事情，但总是干不完，目前完没完都还在观察。我始终明白在做优化前需要进行详尽的数据收集和分析，从而真正地优化到正确的点上，但也总是凭直觉和少量数据分析就开始尝试。</p>

<p>这里谈谈遇到的一些问题。</p>

<h3>erlang call timeout</h3>

<p>最开始遇到erlang中<code>gen_server:call</code>出现<code>timeout</code>错误时，我还一直以为是进程死锁了。相关代码读来读去，实在觉得不可能发生死锁。后来发现，当erlang虚拟机压力上去后，例如内存太大，但没大到耗尽系统所有内存（耗进所有内存基本就core dump了），进程间的调用就会出现timeout。</p>

<p>当然，内存占用过大可能只是表象。其进程过多，进程消息队列太长，也许才是导致出现timeout的根本原因。消息队列过长，也可能是由于发生了<em>消息泄漏</em>的缘故。消息泄漏我指的是这样一种情况，进程自己给自己发消息（当然是cast或info），这个消息被处理时又会发送相同的消息，正常情况下，gen_server处理了一个该消息，就会从消息队列里移除它，然后再发送相同的消息，这不会出问题。但是当程序逻辑出问题，每次处理该消息时，都会发生多余一个的同类消息，那消息队列自然就会一直增长。</p>

<p>保持进程逻辑简单，以避免这种逻辑错误。</p>

<h3>erlang gb_trees</h3>

<p>我在不少的地方使用了gb_trees，dht_crawler里就可能出现<code>gb_trees:get(xxx, nil)</code>这种错误。乍一看，我以为我真的传入了一个<code>nil</code>值进去。然后我苦看代码，以为在某个地方我会把这个gb_trees对象改成了nil。但事情不是这样的，gb_tress使用一个tuple作为tree的节点，当某个节点没有子节点时，就会以nil表示。</p>

<p><code>gb_trees:get(xxx, nil)</code>类似的错误，实际指的是<code>xxx</code>没有在这个gb_trees中找到。</p>

<h3>erlang httpc</h3>

<p>dht_crawler通过http协议从torrage.com之类的缓存网站下载种子。最开始我为了尽量少依赖第三方库，使用的是erlang自带的httpc。后来发现程序有内存泄漏，google发现erlang自带的httpc早为人诟病，当然也有大神说在某个版本之后这个httpc已经很不错。为了省事，我直接换了ibrowse，替换之后正常很多。但是由于没有具体分析测试过，加之时间有点远了，我也记不太清细节。因为早期的http请求部分，没有做数量限制，也可能是由于我的使用导致的问题。</p>

<p>某个版本后，我才将http部分严格地与hash处理部分区分开来。相较数据库操作而言，http请求部分慢了若干数量级。在hash_reader中将这两块分开，严格限制了提交给httpc的请求数，以获得稳定性。</p>

<p>对于一个复杂的网络系统而言，分清哪些是耗时的哪些是不大耗时的，才可能获得性能的提升。对于hash_reader而言，处理一个hash的速度，虽然很大程度取决于数据库，但相较http请求，已经快很多。它在处理这些hash时，会将数据库已收录的资源和待下载的资源分离开，以尽快的速度处理已存在的，而将待下载的处理速度交给httpc的响应速度。</p>

<h3>erlang httpc ssl</h3>

<p>ibrowse处理https请求时，默认和erlang自带的httpc使用相同的SSL实现。这经常导致出现<code>tls_connection</code>进程挂掉的错误，具体原因不明。</p>

<h3>erlang调试</h3>

<p>首先合理的日志是任何系统调试的必备。</p>

<p>我面临的大部分问题都是内存泄漏相关，所以依赖的erlang工具也是和内存相关的：</p>

<ul>
<li>
<p>使用<code>etop</code>，可以检查内存占用多的进程、消息队列大的进程、CPU消耗多的进程等等：</p>

<pre><code>  spawn(fun() -&gt; etop:start([{output, text}, {interval, 10}, {lines, 20}, {sort, msg_q }]) end).
</code></pre>
</li>
<li><p>使用<code>erlang:system_info(allocated_areas).</code>检查内存使用情况，其中会输出系统<code>timer</code>数量</p></li>
<li>使用<code>erlang:process_info</code>查看某个具体的进程，这个甚至会输出消息队列里的消息</li>
</ul>
<h3>hash_writer/crawler</h3>

<p>crawler本身仅收集hash，然后写入数据库，所以可以称crawler为hash_writer。这些hash里存在大量的重复。hash_reader从数据库里取出这些hash然后做处理。处理过程会首先判定该hash对应的资源是否被收录，没有收录就先通过http获取种子。</p>

<p>在某个版本之后，crawler会简单地预先处理这些hash。它缓存一定数量的hash，接收到新hash时，就合并到hash缓存里，以保证缓存里没有重复的hash。这个重复率经过实际数据分析，大概是50%左右，即收到的100个请求里，有50个是重复的。这样的优化，不仅会降低hash数据库的压力，hash_reader处理的hash数量少了，也会对torrent数据库有很大提升。</p>

<p>当然进一步的方案可以将crawler和hash_reader之间交互的这些hash直接放在内存中处理，省去中间数据库。但是由于mongodb大量使用虚拟内存的缘故（内存映射文件），经常导致服务器内存不够（4G），内存也就成了珍稀资源。当然这个方案还有个弊端是难以权衡hash缓存的管理。crawler收到hash是一个不稳定的过程，在某些时间点这些hash可能爆多，而hash_reader处理hash的速度也会不太稳定，受限于收到的hash类别（是新增资源还是已存在资源）、种子请求速度、是否有效等。</p>

<p>当然，也可以限制缓存大小，以及对hash_reader/crawler处理速度建立关系来解决这些问题。但另一方面，这里的优化是否对目前的系统有提升，是否是目前系统面临的最大问题，却是需要考究的事情。</p>

<h3>cache indexer</h3>

<p>dht_crawler是从torrage.com等网站获取种子文件，这些网站看起来都是使用了相同的接口，其都有一个sync目录，里面存放了每天每个月索引的种子hash，例如 http://torrage.com/sync/。这个网站上是否有某个hash对应的种子，就可以从这些索引中检查。</p>

<p>hash_reader在处理新资源时，请求种子的过程中发现大部分在这些服务器上都没有找到，也就是发起的很多http请求都是404回应，这不但降低了系统的处理能力、带宽，也降低了索引速度。所以我写了一个工具，先手工将sync目录下的所有文件下载到本地，然后通过这个工具 (cache indexer) 将这些索引文件里的hash全部导入数据库。在以后的运行过程中，该工具仅下载当天的索引文件，以更新数据库。 hash_reader 根据配置，会首先检查某个hash是否存在该数据库中，存在的hash才可能在torrage.com上下载得到。</p>

<h3>种子缓存</h3>

<p>hash_reader可以通过配置，将下载得到的种子保存在本地文件系统或数据库中。这可以建立自己的种子缓存，但保存在数据库中会对数据库造成压力，尤其在当前测试服务器硬件环境下；而保存为本地文件，又特别占用硬盘空间。</p>

<h3>基于BT协议的种子下载</h3>

<p>通过http从种子缓存里取种子文件，可能会没有直接从P2P网络里取更实时。目前还没来得及查看这些种子缓存网站的实现原理。但是通过BT协议获取种子会有点麻烦，因为dht_crawler是根据<code>get_peer</code>请求索引资源的，所以如果要通过BT协议取种子，那么这里还得去DHT网络里查询该种子，这个查询过程可能会较长，相比之下会没有http下载快。而如果通过<code>announce_peer</code>来索引新资源的话，其索引速度会大大降低，因为<code>announce_peer</code>请求比<code>get_peer</code>请求少很多，几乎10倍。</p>

<p>所以，这里的方案可能会结合两者，新开一个服务，建立自己的种子缓存。</p>

<h3>中文分词</h3>

<p>mongodb的全文索引是不支持中文的。我在之前提到，为了支持搜索中文，我将字符串拆成了若干子串。这样的后果就是字符串索引会稍稍偏大，而且目前这一块的代码还特别简单，会将很多非文字字符也算在内。后来我加了个中文分词库，使用的是rmmseg-cpp。我将其C++部分抽离出来编译成erlang nif，这可以在我的github上找到。</p>

<p>但是这个库拆分中文句子依赖于词库，而这个词库不太新，dhtcrawler爬到的大部分资源类型你们也懂，那些词汇拆出来的比率不太高，这会导致搜索出来的结果没你想的那么直白。当然更新词库应该是可以解决这个问题的，目前还没有时间顾这一块。</p>

<h2>总结</h2>

<p>一个老外对我说过，&#8221;i have 2 children to feed, so i will not do this only for fun&#8221;。</p>

<p>你的大部分编程知识来源于网络，所以稍稍回馈一下不会让你丢了饭碗。</p>

<p>我很穷，如果你能让我收获金钱和编程成就，还不会嫌我穿得太邋遢，that&#8217;s really kind of you。</p>

<p class="post-footer">
            原文地址：
            <a href="http://codemacro.com/2013/07/02/dhtcrawler2/">http://codemacro.com/2013/07/02/dhtcrawler2/</a><br />
            written by <a href="http://codemacro.com">Kevin Lynx</a>
            &nbsp;posted at <a href="http://codemacro.com">http://codemacro.com</a>
            </p>

</div><img src ="http://www.cppblog.com/kevinlynx/aggbug/201994.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2013-07-20 16:37 <a href="http://www.cppblog.com/kevinlynx/archive/2013/07/20/201994.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用erlang实现P2P磁力搜索-实现</title><link>http://www.cppblog.com/kevinlynx/archive/2013/06/20/201179.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Thu, 20 Jun 2013 12:40:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2013/06/20/201179.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/201179.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2013/06/20/201179.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/201179.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/201179.html</trackback:ping><description><![CDATA[<div class="entry-content">
<p>接<a href="http://codemacro.com/2013/06/20/magnet-search/">上篇</a>，本篇谈谈一些实现细节。</p>

<p>这个爬虫程序主要的问题在于如何获取P2P网络中分享的资源，获取到资源后索引到数据库中，搜索就是自然而然的事情。</p>

<h2>DHT</h2>

<p>DHT网络本质上是一个用于查询的网络，其用于查询一个资源有哪些计算机正在下载。每个资源都有一个20字节长度的ID用于标示，称为infohash。当一个程序作为DHT节点加入这个网络时，就会有其他节点来向你查询，当你做出回应后，对方就会记录下你。对方还会询问其他节点，当对方开始下载这个infohash对应的资源时，他就会告诉所有曾经询问过的节点，包括你。这个时候就可以确定，这个infohash对应的资源在这个网络中是有效的。</p>

<p>关于这个网络的工作原理，参看：<a href="http://codemacro.com/2013/05/19/crawl-dht/">P2P中DHT网络爬虫</a>以及<a href="http://xiaoxia.org/2013/05/11/magnet-search-engine/">写了个磁力搜索的网页</a>。</p>

<p>获取到infohash后能做什么？关键点在于，我们现在使用的磁力链接(magnet url)，是和infohash对应起来的。也就是拿到infohash，就等于拿到一个磁力链接。但是这个爬虫还需要建立资源的信息，这些信息来源于种子文件。种子文件其实也是对应到一个资源，种子文件包含资源名、描述、文件列表、文件大小等信息。获取到infohash时，其实也获取到了对应的计算机地址，我们可以在这些计算机上下载到对应的种子文件。</p>

<!-- more -->


<p>但是我为了简单，在获取到infohash后，从一些提供映射磁力链到种子文件服务的网站上直接下载了对应的种子。dhtcrawler里使用了以下网站：</p>

<pre><code>http://torrage.com
https://zoink.it
http://bt.box.n0808.com
</code></pre>

<p>使用这些网站时，需提供磁力哈希（infohash可直接转换），构建特定的URL，发出HTTP请求即可。</p>

<div class="highlight">
<pre><code class="erlang">   <span class="nv">U1</span> <span class="o">=</span> <span class="s">"http://torrage.com/torrent/"</span> <span class="o">++</span> <span class="nv">MagHash</span> <span class="o">++</span> <span class="s">".torrent"</span><span class="p">,</span>
    <span class="nv">U2</span> <span class="o">=</span> <span class="s">"https://zoink.it/torrent/"</span> <span class="o">++</span> <span class="nv">MagHash</span> <span class="o">++</span> <span class="s">".torrent"</span><span class="p">,</span>
    <span class="nv">U3</span> <span class="o">=</span> <span class="n">format_btbox_url</span><span class="p">(</span><span class="nv">MagHash</span><span class="p">),</span>

<span class="nf">format_btbox_url</span><span class="p">(</span><span class="nv">MagHash</span><span class="p">)</span> <span class="o">-&gt;</span>
    <span class="nv">H</span> <span class="o">=</span> <span class="nn">lists</span><span class="p">:</span><span class="nf">sublist</span><span class="p">(</span><span class="nv">MagHash</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span>
    <span class="nv">T</span> <span class="o">=</span> <span class="nn">lists</span><span class="p">:</span><span class="nf">nthtail</span><span class="p">(</span><span class="mi">38</span><span class="p">,</span> <span class="nv">MagHash</span><span class="p">),</span>
    <span class="s">"http://bt.box.n0808.com/"</span> <span class="o">++</span> <span class="nv">H</span> <span class="o">++</span> <span class="s">"/"</span> <span class="o">++</span> <span class="nv">T</span> <span class="o">++</span> <span class="s">"/"</span> <span class="o">++</span> <span class="nv">MagHash</span> <span class="o">++</span> <span class="s">".torrent"</span><span class="p">.</span>
</code></pre>
</div>


<p>但是，以一个节点的身份加入DHT网络，是无法获取大量查询的。在DHT网络中，每个节点都有一个ID。每个节点在查询信息时，仅询问离信息较近的节点。这里的信息除了infohash外还包含节点，即节点询问一个节点，这个节点在哪里。DHT的典型实现中（Kademlia），使用两个ID的xor操作来确定距离。既然距离的计算是基于ID的，为了尽可能获取整个DHT网络交换的信息，爬虫程序就可以建立尽可能多的DHT节点，让这些节点的ID均匀地分布在ID取值区间内，以这样的方式加入网络。</p>

<p>在dhtcrawler中，我使用以下方式产生了N个大致均匀分布的ID：</p>

<div class="highlight">
<pre><code class="erlang"><span class="nf">create_discrete_ids</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="o">-&gt;</span>
    <span class="p">[</span><span class="nn">dht_id</span><span class="p">:</span><span class="nf">random</span><span class="p">()];</span>
<span class="nf">create_discrete_ids</span><span class="p">(</span><span class="nv">Count</span><span class="p">)</span> <span class="o">-&gt;</span>
    <span class="nv">Max</span> <span class="o">=</span> <span class="nn">dht_id</span><span class="p">:</span><span class="nf">max</span><span class="p">(),</span>
    <span class="nv">Piece</span> <span class="o">=</span> <span class="nv">Max</span> <span class="ow">div</span> <span class="nv">Count</span><span class="p">,</span>
    <span class="p">[</span><span class="nn">random</span><span class="p">:</span><span class="nf">uniform</span><span class="p">(</span><span class="nv">Piece</span><span class="p">)</span> <span class="o">+</span> <span class="nv">Index</span> <span class="o">*</span> <span class="nv">Piece</span> <span class="p">||</span> <span class="nv">Index</span> <span class="o">&lt;-</span> <span class="nn">lists</span><span class="p">:</span><span class="nf">seq</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nv">Count</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)].</span>
</code></pre>
</div>


<p>除了尽可能多地往DHT网络里部署节点之外，对单个节点而言，也有些注意事项。例如应尽可能快地将自己告诉尽可能多的节点，这可以在启动时进行大量的随机infohash的查询。随着查询过程的深入，该节点会与更多的节点打交道。因为DHT网络里的节点实际上是不稳定的，它今天在线，明天后天可能不在线，所以计算你的ID固定，哪些节点与你较近，本身就是个相对概念。节点在程序退出时，也最好将自己的路由信息（与自己交互的节点列表）保存起来，这样下次启动时就可以更快地加入网络。</p>

<p>在dhtcrawler的实现中，每个节点每个一定时间，都会向网络中随机查询一个infohash，这个infohash是随机产生的。其查询目的不在于infohash，而在于告诉更多的节点，以及在其他节点上保持自己的活跃。</p>

<div class="highlight">
<pre><code class="erlang"><span class="nf">handle_event</span><span class="p">(</span><span class="n">startup</span><span class="p">,</span> <span class="p">{</span><span class="nv">MyID</span><span class="p">})</span> <span class="o">-&gt;</span>
    <span class="nn">timer</span><span class="p">:</span><span class="nf">apply_interval</span><span class="p">(</span><span class="o">?</span><span class="nv">QUERY_INTERVAL</span><span class="p">,</span> <span class="o">?</span><span class="nv">MODULE</span><span class="p">,</span> <span class="n">start_tell_more_nodes</span><span class="p">,</span> <span class="p">[</span><span class="nv">MyID</span><span class="p">]).</span>

<span class="nf">start_tell_more_nodes</span><span class="p">(</span><span class="nv">MyID</span><span class="p">)</span> <span class="o">-&gt;</span>
    <span class="nb">spawn</span><span class="p">(</span><span class="o">?</span><span class="nv">MODULE</span><span class="p">,</span> <span class="n">tell_more_nodes</span><span class="p">,</span> <span class="p">[</span><span class="nv">MyID</span><span class="p">]).</span>

<span class="nf">tell_more_nodes</span><span class="p">(</span><span class="nv">MyID</span><span class="p">)</span> <span class="o">-&gt;</span>
    <span class="p">[</span><span class="nn">search</span><span class="p">:</span><span class="nf">get_peers</span><span class="p">(</span><span class="nv">MyID</span><span class="p">,</span> <span class="nn">dht_id</span><span class="p">:</span><span class="nf">random</span><span class="p">())</span> <span class="p">||</span> <span class="p">_</span> <span class="o">&lt;-</span> <span class="nn">lists</span><span class="p">:</span><span class="nf">seq</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">)].</span>
</code></pre>
</div>


<p>DHT节点的完整实现是比较繁琐的，涉及到查询以及繁杂的各种对象的超时（节点、桶、infohash），而超时的处理并不是粗暴地做删除操作。因为本身是基于UDP协议，你得对这些超时对象做进一步的查询才能正确地进一步做其他事情。而搜索也是个繁杂的事情，递归地查询节点，感觉上，你不一定离目标越来越近，由于被查询节点的不确定性（无法确定对方是否在玩弄你，或者本身对方就是个傻逼），你很可能接下来要查询的节点反而离目标变远了。</p>

<p>在我第一次的DHT实现中，我使用了类似transmission里DHT实现的方法，不断无脑递归，当搜索有太久时间没得到响应后终止搜索。第二次实现时，我就使用了etorrent里的实现。这个搜索更聪明，它记录搜索过的节点，并且检查是否离目标越来越远。当远离目标时，就认为搜索是不太有效的，不太有效的搜索尝试几次就可以放弃。</p>

<p>实际上，爬虫的实现并不需要完整地实现DHT节点的正常功能。<strong>爬虫作为一个DHT节点的唯一动机仅是获取网络里其他节点的查询</strong>。而要完成这个功能，你只需要装得像个正常人就行。这里不需要保存infohash对应的peer列表，面临每一次查询，你随便回复几个节点地址就可以。但是这里有个责任问题，如果整个DHT网络有2000个节点，而你这个爬虫就有1000个节点，那么你的随意回复，就可能导致对方根本找不到正确的信息，这样你依然得不到有效的资源。（可以利用这一点破坏DHT网络）</p>

<p>DHT的实现没有使用第三方库。</p>

<h2>种子</h2>

<p>种子文件的格式同DHT网络消息格式一样，使用一种称为bencode的文本格式来编码。种子文件分为两类：单个文件和多个文件。</p>

<p>文件的信息无非就是文件名、大小。文件名可能包含utf8编码的名字，为了后面处理的方便，dhtcrawler都会优先使用utf8编码。</p>

<div class="highlight">
<pre><code class="erlang">   <span class="p">{</span><span class="n">ok</span><span class="p">,</span> <span class="p">{</span><span class="n">dict</span><span class="p">,</span> <span class="nv">Info</span><span class="p">}}</span> <span class="o">=</span> <span class="nn">dict</span><span class="p">:</span><span class="nf">find</span><span class="p">(</span><span class="o">&lt;&lt;</span><span class="s">"info"</span><span class="o">&gt;&gt;</span><span class="p">,</span> <span class="nv">TD</span><span class="p">),</span>
    <span class="k">case</span> <span class="n">type</span><span class="p">(</span><span class="nv">Info</span><span class="p">)</span> <span class="k">of</span>
        <span class="n">single</span> <span class="o">-&gt;</span> <span class="p">{</span><span class="n">single</span><span class="p">,</span> <span class="n">parse_single</span><span class="p">(</span><span class="nv">Info</span><span class="p">)};</span>
        <span class="n">multi</span> <span class="o">-&gt;</span> <span class="p">{</span><span class="n">multi</span><span class="p">,</span> <span class="n">parse_multi</span><span class="p">(</span><span class="nv">Info</span><span class="p">)}</span>
    <span class="k">end</span><span class="p">.</span>
<span class="nf">parse_single</span><span class="p">(</span><span class="nv">Info</span><span class="p">)</span> <span class="o">-&gt;</span>
    <span class="nv">Name</span> <span class="o">=</span> <span class="n">read_string</span><span class="p">(</span><span class="s">"name"</span><span class="p">,</span> <span class="nv">Info</span><span class="p">),</span>
    <span class="p">{</span><span class="n">ok</span><span class="p">,</span> <span class="nv">Length</span><span class="p">}</span> <span class="o">=</span> <span class="nn">dict</span><span class="p">:</span><span class="nf">find</span><span class="p">(</span><span class="o">&lt;&lt;</span><span class="s">"length"</span><span class="o">&gt;&gt;</span><span class="p">,</span> <span class="nv">Info</span><span class="p">),</span>
    <span class="p">{</span><span class="nv">Name</span><span class="p">,</span> <span class="nv">Length</span><span class="p">}.</span>

<span class="nf">parse_multi</span><span class="p">(</span><span class="nv">Info</span><span class="p">)</span> <span class="o">-&gt;</span>
    <span class="nv">Root</span> <span class="o">=</span> <span class="n">read_string</span><span class="p">(</span><span class="s">"name"</span><span class="p">,</span> <span class="nv">Info</span><span class="p">),</span>
    <span class="p">{</span><span class="n">ok</span><span class="p">,</span> <span class="p">{</span><span class="n">list</span><span class="p">,</span> <span class="nv">Files</span><span class="p">}}</span> <span class="o">=</span> <span class="nn">dict</span><span class="p">:</span><span class="nf">find</span><span class="p">(</span><span class="o">&lt;&lt;</span><span class="s">"files"</span><span class="o">&gt;&gt;</span><span class="p">,</span> <span class="nv">Info</span><span class="p">),</span>
    <span class="nv">FileInfo</span> <span class="o">=</span> <span class="p">[</span><span class="n">parse_file_item</span><span class="p">(</span><span class="nv">Item</span><span class="p">)</span> <span class="p">||</span> <span class="p">{</span><span class="n">dict</span><span class="p">,</span> <span class="nv">Item</span><span class="p">}</span> <span class="o">&lt;-</span> <span class="nv">Files</span><span class="p">],</span>
    <span class="p">{</span><span class="nv">Root</span><span class="p">,</span> <span class="nv">FileInfo</span><span class="p">}.</span>
</code></pre>
</div>


<h2>数据库</h2>

<p>我最开始在选用数据库时，为了不使用第三方库，打算使用erlang自带的mnesia。但是因为涉及到字符串匹配搜索，mnesia的查询语句在我看来太不友好，在经过一些资料查阅后就直接放弃了。</p>

<p>然后我打算使用couchdb，因为它是erlang写的，而我正在用erlang写程序。第一次接触非关系型数据库，发现NoSQL数据库使用起来比SQL类的简单多了。但是在erlang里要使用couchdb实在太折腾了。我使用的客户端库是couchbeam。</p>

<p>因为couchdb暴露的API都是基于HTTP协议的，其数据格式使用了json，所以couchbeam实际上就是对各种HTTP请求、回应和json的包装。但是它竟然使用了ibrowse这个第三方HTTP客户端库，而不是erlang自带的。ibrowse又使用了jiffy这个解析json的库。这个库更惨烈的是它的解析工作都是交给C语言写的动态库来完成，我还得编译那个C库。</p>

<p>couchdb看起来不支持字符串查询，我得自己创建一个view，这个view里我通过翻阅了一些资料写了一个将每个doc的name拆分成若干次查询结果的map。这个map在处理每一次查询时，我都得动态更新之。couchdb是不支持局部更新的，这还不算大问题。然后很高兴，终于支持字符串查询了。这里的字符串查询都是基于字符串的子串查询。但是问题在于，太慢了。每一次在WEB端的查询，都直接导致erlang进程的call超时。</p>

<p>要让couchdb支持字符串查询，要快速，当然是有解决方案的。但是这个时候我已经没有心思继续折腾，任何一个库、程序如果接口设计得如此不方便，那就可以考虑换一个其他的。</p>

<p>我选择了mongodb。同样的基于文档的数据库。2.4版本还支持全文搜索。什么是全文搜索呢，这是一种基于单词的全文搜索方式。<code>hello world</code>我可以搜索<code>hello</code>，基于单词。mongodb会自动拆词。更关键更让人爽的是，要开启这个功能非常简单：设置启动参数、建立索引。没了。mongodb的erlang客户端库mongodb-erlang也只依赖一个bson-erlang库。然后我又埋头苦干，几个小时候我的这个爬虫程序就可以在浏览器端搜索关键字了。</p>

<p>后来我发现，mongodb的全文搜索是不支持中文的。因为它还不知道中文该怎么拆词。恰好我有个同事做过中文拆词的研究，看起来涉及到很复杂的算法。直到这个时候，我他妈才醒悟，我为什么需要基于单词的搜索。我们大部分的搜索其实都是基于子字符串的搜索。</p>

<p>于是，我将种子文件的名字拆分成了若干个子字符串，将这些子字符串以数组的形式作为种子文档的一个键值存储，而我依然还可以使用全文索引，因为全文索引会将整个字符串作为单词比较。实际上，基于一般的查询方式也是可以的。当然，索引还是得建立。</p>

<p>使用mongodb时唯一让我很不爽的是mongodb-erlang这个客户端库的文档太欠缺。这还不算大问题，因为看看源码参数还是可以大概猜到用法。真正悲剧的是mongodb的有些查询功能它是不支持的。例如通过cursor来排序来限制数量。在cursor模块并没有对应的mongodb接口。最终我只好通过以下方式查询，我不明白batchsize，但它可以工作：</p>

<div class="highlight">
<pre><code class="erlang"><span class="nf">search_announce_top</span><span class="p">(</span><span class="nv">Conn</span><span class="p">,</span> <span class="nv">Count</span><span class="p">)</span> <span class="o">-&gt;</span>
    <span class="nv">Sel</span> <span class="o">=</span> <span class="p">{</span><span class="n">'$query'</span><span class="p">,</span> <span class="p">{},</span> <span class="n">'$orderby'</span><span class="p">,</span> <span class="p">{</span><span class="n">announce</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">}},</span>
    <span class="nv">List</span> <span class="o">=</span> <span class="n">mongo_do</span><span class="p">(</span><span class="nv">Conn</span><span class="p">,</span> <span class="k">fun</span><span class="p">()</span> <span class="o">-&gt;</span>
        <span class="nv">Cursor</span> <span class="o">=</span> <span class="nn">mongo</span><span class="p">:</span><span class="nf">find</span><span class="p">(</span><span class="o">?</span><span class="nv">COLLNAME</span><span class="p">,</span> <span class="nv">Sel</span><span class="p">,</span> <span class="p">[],</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">Count</span><span class="p">),</span> 
        <span class="nn">mongo_cursor</span><span class="p">:</span><span class="nf">rest</span><span class="p">(</span><span class="nv">Cursor</span><span class="p">)</span>
    <span class="k">end</span><span class="p">),</span>
    <span class="p">[</span><span class="n">decode_torrent_item</span><span class="p">(</span><span class="nv">Item</span><span class="p">)</span> <span class="p">||</span> <span class="nv">Item</span> <span class="o">&lt;-</span> <span class="nv">List</span><span class="p">].</span>
</code></pre>
</div>


<p>另一个悲剧的是，mongodb-erlang还不支持文档的局部更新，它的update接口直接要求传入整个文档。几经折腾，我可以通过runCommand来完成：</p>

<div class="highlight">
<pre><code class="erlang"><span class="nf">inc_announce</span><span class="p">(</span><span class="nv">Conn</span><span class="p">,</span> <span class="nv">Hash</span><span class="p">)</span> <span class="k">when</span> <span class="nb">is_list</span><span class="p">(</span><span class="nv">Hash</span><span class="p">)</span> <span class="o">-&gt;</span>
    <span class="nv">Cmd</span> <span class="o">=</span> <span class="p">{</span><span class="n">findAndModify</span><span class="p">,</span> <span class="o">?</span><span class="nv">COLLNAME</span><span class="p">,</span> <span class="k">query</span><span class="p">,</span> <span class="p">{</span><span class="n">'_id'</span><span class="p">,</span> <span class="nb">list_to_binary</span><span class="p">(</span><span class="nv">Hash</span><span class="p">)},</span> 
        <span class="n">update</span><span class="p">,</span> <span class="p">{</span><span class="n">'$inc'</span><span class="p">,</span> <span class="p">{</span><span class="n">announce</span><span class="p">,</span> <span class="mi">1</span><span class="p">}},</span>
        <span class="n">new</span><span class="p">,</span> <span class="n">true</span><span class="p">},</span>
    <span class="nv">Ret</span> <span class="o">=</span> <span class="n">mongo_do</span><span class="p">(</span><span class="nv">Conn</span><span class="p">,</span> <span class="k">fun</span><span class="p">()</span> <span class="o">-&gt;</span>
        <span class="nn">mongo</span><span class="p">:</span><span class="nf">command</span><span class="p">(</span><span class="nv">Cmd</span><span class="p">)</span>
    <span class="k">end</span><span class="p">).</span>
</code></pre>
</div>


<h2>Unicode</h2>

<p>不知道在哪里我看到过erlang说自己其实是不需要支持unicode的，因为这门语言本身是通过list来模拟字符串。对于unicode而言，对应的list保存的本身就是整数值。但是为了方便处理，erlang还是提供了一些unicode操作的接口。</p>

<p>因为我需要将种子的名字按字拆分，对于<code>a中文</code>这样的字符串而言，我需要拆分成以下结果：</p>

<pre><code>a
a中
a中文
中
中文
文
</code></pre>

<p>那么，在erlang中当我获取到一个字符串list时，我就需要知道哪几个整数合起来实际上对应着一个汉字。erlang里unicode模块里有几个函数可以将unicode字符串list对应的整数合起来，例如：<code>[111, 222, 333]</code>可能表示的是一个汉字，将其转换以下可得到<code>[111222333]</code>这样的形式。</p>

<div class="highlight">
<pre><code class="erlang"><span class="nf">split</span><span class="p">(</span><span class="nv">Str</span><span class="p">)</span> <span class="k">when</span> <span class="nb">is_list</span><span class="p">(</span><span class="nv">Str</span><span class="p">)</span> <span class="o">-&gt;</span>
    <span class="nv">B</span> <span class="o">=</span> <span class="nb">list_to_binary</span><span class="p">(</span><span class="nv">Str</span><span class="p">),</span> <span class="c">% 必须转换为binary</span>
    <span class="k">case</span> <span class="nn">unicode</span><span class="p">:</span><span class="nf">characters_to_list</span><span class="p">(</span><span class="nv">B</span><span class="p">)</span> <span class="k">of</span>
        <span class="p">{</span><span class="n">error</span><span class="p">,</span> <span class="nv">L</span><span class="p">,</span> <span class="nv">D</span><span class="p">}</span> <span class="o">-&gt;</span>
            <span class="p">{</span><span class="n">error</span><span class="p">,</span> <span class="nv">L</span><span class="p">,</span> <span class="nv">D</span><span class="p">};</span>
        <span class="p">{</span><span class="n">incomplete</span><span class="p">,</span> <span class="nv">L</span><span class="p">,</span> <span class="nv">D</span><span class="p">}</span> <span class="o">-&gt;</span>
            <span class="p">{</span><span class="n">incomplete</span><span class="p">,</span> <span class="nv">L</span><span class="p">,</span> <span class="nv">D</span><span class="p">};</span>
        <span class="nv">UL</span> <span class="o">-&gt;</span>
        <span class="p">{</span><span class="n">ok</span><span class="p">,</span> <span class="n">subsplit</span><span class="p">(</span><span class="nv">UL</span><span class="p">)}</span>
    <span class="k">end</span><span class="p">.</span>

<span class="nf">subsplit</span><span class="p">([])</span> <span class="o">-&gt;</span>
    <span class="p">[];</span>

<span class="nf">subsplit</span><span class="p">(</span><span class="nv">L</span><span class="p">)</span> <span class="o">-&gt;</span>
    <span class="p">[_|</span><span class="nv">R</span><span class="p">]</span> <span class="o">=</span> <span class="nv">L</span><span class="p">,</span>
    <span class="p">{</span><span class="nv">PreL</span><span class="p">,</span> <span class="p">_}</span> <span class="o">=</span> <span class="nn">lists</span><span class="p">:</span><span class="nf">splitwith</span><span class="p">(</span><span class="k">fun</span><span class="p">(</span><span class="nv">Ch</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="ow">not</span> <span class="n">is_spliter</span><span class="p">(</span><span class="nv">Ch</span><span class="p">)</span> <span class="k">end</span><span class="p">,</span> <span class="nv">L</span><span class="p">),</span>
    <span class="p">[</span><span class="nn">unicode</span><span class="p">:</span><span class="nf">characters_to_binary</span><span class="p">(</span><span class="nn">lists</span><span class="p">:</span><span class="nf">sublist</span><span class="p">(</span><span class="nv">PreL</span><span class="p">,</span> <span class="nv">Len</span><span class="p">))</span> 
        <span class="p">||</span> <span class="nv">Len</span> <span class="o">&lt;-</span> <span class="nn">lists</span><span class="p">:</span><span class="nf">seq</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nb">length</span><span class="p">(</span><span class="nv">PreL</span><span class="p">))]</span> <span class="o">++</span> <span class="n">subsplit</span><span class="p">(</span><span class="nv">R</span><span class="p">).</span>
</code></pre>
</div>


<p>除了这里的拆字之外，URL的编码、数据库的存储都还好，没遇到问题。</p>

<p><strong>注意</strong>，以上针对数据库本身的吐槽，完全基于我不熟悉该数据库的情况下，不建议作为你工具选择的参考。</p>

<h2>erlang的稳定性</h2>

<p>都说可以用erlang来编写高容错的服务器程序。看看它的supervisor，监视子进程，自动重启子进程。天生的容错功能，就算你宕个几次，单个进程自动重启，整个程序看起来还稳健地在运行，多牛逼啊。再看看erlang的进程，轻量级的语言特性，就像OOP语言里的一个对象一样轻量。如果说使用OOP语言写程序得think in object，那用erlang你就得think in process，多牛逼多骇人啊。</p>

<p>实际上，以我的经验来看，你还得以传统的思维去看待erlang的进程。一些多线程程序里的问题，在erlang的进程环境中依然存在，例如死锁。</p>

<p>在erlang中，对于一些异步操作，你可以通过进程间的交互将这个操作包装成同步接口，例如ping的实现，可以等到对方回应之后再返回。被阻塞的进程反正很轻量，其包含的逻辑很单一。这不但是一种良好的包装，甚至可以说是一种erlang-style。但这很容易带来死锁。在最开始的时候我没有注意这个问题，当爬虫节点数上升的时候，网络数据复杂的时候，似乎就出现了死锁型宕机（进程互相等待太久，直接timeout）。</p>

<p>另一个容易在多进程环境下出现的问题就是消息依赖的上下文改变问题。当投递一个消息到某个进程，到这个消息被处理之前，这段时间这个消息关联的逻辑运算所依赖的上下文环境改变了，例如某个ets元素不见了，在处理这个消息时，你还得以多线程编程的思维来编写代码。</p>

<p>至于supervisor，这玩意你得端正态度。它不是用来包容你的傻逼错误的。当你写下傻逼代码导致进程频繁崩溃的时候，supervisor屁用没有。supervisor的唯一作用，仅仅是在一个确实本身可靠的系统，确实人品问题万分之一崩溃了，重启它。毕竟，一个重启频率的推荐值，是一个小时4次。</p>

<p class="post-footer">
            原文地址：
            <a href="http://codemacro.com/2013/06/21/magnet-search-impl/">http://codemacro.com/2013/06/21/magnet-search-impl/</a><br />
            written by <a href="http://codemacro.com">Kevin Lynx</a>
            &nbsp;posted at <a href="http://codemacro.com">http://codemacro.com</a>
            </p>

</div><img src ="http://www.cppblog.com/kevinlynx/aggbug/201179.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2013-06-20 20:40 <a href="http://www.cppblog.com/kevinlynx/archive/2013/06/20/201179.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用erlang实现P2P磁力搜索(开源)</title><link>http://www.cppblog.com/kevinlynx/archive/2013/06/20/201175.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Thu, 20 Jun 2013 06:44:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2013/06/20/201175.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/201175.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2013/06/20/201175.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/201175.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/201175.html</trackback:ping><description><![CDATA[<div class="entry-content">
<p>接上回对<a href="http://codemacro.com/2013/05/19/crawl-dht/">DHT网络的研究</a>，我用erlang克隆了一个<a href="http://bt.shousibaocai.com/">磁力搜索引擎</a>。我这个实现包含了完整的功能，DHT网络的加入、infohash的接收、种子的获取、资源信息的索引、搜索。</p>

<p>如下图：</p>

<p><img src="https://raw.github.com/kevinlynx/dhtcrawler/master/screenshot.png" alt="screenshot" /></p>

<!-- more -->


<p>在我的笔记本上，我开启了100个DHT节点，大致均匀地分布在DHT网络里，资源索引速度大概在1小时一万个左右（包含重复资源）。</p>

<p>这个程序包含三大部分：</p>

<ul>
<li>DHT实现，kdht，<a href="https://github.com/kevinlynx/kdht">https://github.com/kevinlynx/kdht</a>
</li>
<li>基于该DHT实现的搜索引擎，dhtcrawler，<a href="https://github.com/kevinlynx/dhtcrawler">https://github.com/kevinlynx/dhtcrawler</a>，该项目包含爬虫部分和一个简单的WEB端</li>
</ul>
<p>这两个项目总共包含大概2500行的erlang代码。其中，DHT实现部分将DHT网络的加入包装成一个库，爬虫部分在搜索种子时，暂时没有使用P2P里的种子下载方式，而是使用现成的磁力链转种子的网站服务，这样我只需要使用erlang自带的HTTP客户端就可以获取种子信息。爬虫在获取到种子信息后，将数据存储到mongodb里。WEB端我为了尽量少用第三方库，我只好使用erlang自带的HTTP服务器，因此网页内容的创建没有模板系统可用，只好通过字符串构建，编写起来不太方便。</p>

<h2>使用</h2>

<p>整个程序依赖了两个库：bson-erlang和mongodb-erlang，但下载依赖库的事都可以通过rebar解决，项目文件里我已经包含了rebar的执行程序。我仅在Windows7上测试过，但理论上在所有erlang支持的系统上都可以。</p>

<ul>
<li>下载安装<a href="http://www.mongodb.org/downloads">mongodb</a>
</li>
<li>
<p>进入mongodb bin目录启动mongodb，数据库目录保存在db下，需手动建立该目录</p>

<pre><code>  mongod --dbpath db --setParameter textSearchEnabled=true
</code></pre>
</li>
<li><p>下载<a href="http://www.erlang.org/download.html">erlang</a>，我使用的是R16B版本</p></li>
<li>
<p>下载dhtcrawler，不需要单独下载kdht，待会下载依赖项的时候会自动下载</p>

<pre><code>  git clone git@github.com:kevinlynx/dhtcrawler.git
</code></pre>
</li>
<li>
<p>cmd进入dhtcrawler目录，下载依赖项前需保证环境变量里有git，例如<code>D:\Program Files (x86)\Git\cmd</code>，需注意不要将bash的目录加入进来，使用以下命令下载依赖项</p>

<pre><code>  rebar get-deps
</code></pre>
</li>
<li>
<p>编译</p>

<pre><code>  rebar compile
</code></pre>
</li>
<li>
<p>在dhtcrawler目录下，启动erlang</p>

<pre><code>  erl -pa ebin
</code></pre>
</li>
<li>
<p>在erlang shell里运行爬虫，<strong>erlang语句以点号(.)作为结束</strong></p>

<pre><code>  crawler_app:start().
</code></pre>
</li>
<li>
<p>erlang shell里运行HTTP服务器</p>

<pre><code>  crawler_http:start().
</code></pre>
</li>
<li><p>浏览器里输入<code>localhost:8000/index.html</code>，这个时候还没有索引到资源，建议监视网络流量以观察爬虫程序是否正确工作</p></li>
</ul>
<p>爬虫程序启动时会读取<code>priv/dhtcrawler.config</code>配置文件，该文件里配置了DHT节点的UDP监听端口、节点数量、数据库地址等，可自行配置。</p>

<p>接下来我会谈谈各部分的实现方法。</p>

<p class="post-footer">
            原文地址：
            <a href="http://codemacro.com/2013/06/20/magnet-search/">http://codemacro.com/2013/06/20/magnet-search/</a><br />
            written by <a href="http://codemacro.com">Kevin Lynx</a>
            &nbsp;posted at <a href="http://codemacro.com">http://codemacro.com</a>
            </p>

</div><img src ="http://www.cppblog.com/kevinlynx/aggbug/201175.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2013-06-20 14:44 <a href="http://www.cppblog.com/kevinlynx/archive/2013/06/20/201175.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>P2P中DHT网络爬虫</title><link>http://www.cppblog.com/kevinlynx/archive/2013/05/19/200410.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Sun, 19 May 2013 13:51:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2013/05/19/200410.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/200410.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2013/05/19/200410.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/200410.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/200410.html</trackback:ping><description><![CDATA[<div class="entry-content">
<p>DHT网络爬虫基于DHT网络构建了一个P2P资源搜索引擎。这个搜索引擎不但可以用于构建DHT网络中活跃的资源索引（活跃的资源意味着该网络中肯定有人至少持有该资源的部分数据），还可以分析出该网络中的热门分享资源。<a href="http://xiaoxia.org/">小虾</a>不久前发布了一个这样的搜索引擎：<a href="http://bt.shousibaocai.com/">磁力搜索</a>。他也写博客对此稍作了介绍：<a href="http://xiaoxia.org/2013/05/11/magnet-search-engine/">写了个磁力搜索的网页 － 收录最近热门分享的资源</a>。网络上其实也有其他人做了类似的应用：<a href="http://pacsec.jp/psj11/PacSec2011_Massive-Monitoring_en.pdf">DHT monitoring</a>，<a href="https://www.defcon.org/images/defcon-18/dc-18-presentations/Wolchok/DEFCON-18-Wolchok-Crawling-Bittorrent-DHTS.pdf">Crawling Bittorrent DHT</a></p>
<p>但是他的这篇文章仅介绍了DHT网络的大致工作原理，并且这个爬虫的具体工作原理也没有提到。对此我查阅了些文章及代码，虽然从原理上梳理出了整个实现方案，但很多细节还是不甚清楚。所以本文仅作概要介绍。</p>
<h2>DHT/Magnet/Torrent</h2>
<p>在P2P网络中，要通过种子文件下载一个资源，需要知道整个P2P网络中有哪些计算机正在下载/上传该资源。这里将这些提供某个资源下载的计算机定义为<code>peer</code>。传统的P2P网络中，存在一些<code>tracker</code>服务器，这些服务器的作用主要用于跟踪某个资源有哪些关联的peer。下载这个资源当然得首先取得这些peer。</p>
<p>DHT的出现用于解决当tracker服务器不可用时，P2P客户端依然可以取得某个资源的peer。DHT解决这个问题，是因为它将原来tracker上的资源peer信息分散到了整个网络中。这里将实现了DHT协议的计算机定义为节点(node)。通常一个P2P客户端程序既是peer也是节点。DHT网络有多种实现算法，例如Kademlia。</p>
<p>当某个P2P客户端通过种子文件下载资源时，如果没有tracker服务器，它就会向DHT网络查询这个资源对应的peer列表。资源的标识在DHT网络中称为<code>infohash</code>，是一个20字节长的字符串，一般通过sha1算法获得，也就是一个类似UUID的东西。</p>
<p>实际上，种子文件本身就对应着一个infohash，这个infohash是通过种子文件的文件描述信息动态计算得到。一个种子文件包含了对应资源的描述信息，例如文件名、文件大小等。Magnet，这里指的是磁力链接，它是一个类似URL的字符串地址。P2P软件通过磁力链接，会下载到一个种子文件，然后根据该种子文件继续真实资源的下载。</p>
<p>磁力链接中包含的最重要的信息就是infohash。这个infohash一般为40字节或32字节，它其实只是资源infohash（20字节）的一种编码形式。</p>
<!-- more -->
<h2>Kademlia</h2>
<p>Kademlia是DHT网络的一种实现。网络上关于这个算法的文章，主要是围绕整个DHT网络的实现原理进行论述。个人觉得这些文章很蛋疼，基本上读了之后对于要如何去实现一个DHT客户端还是没有概念。这里主要可参考<a href="http://blog.csdn.net/mergerly/article/details/7989281">P2P中DHT网络介绍</a>，以及BitTorrent网站上的<a href="http://www.bittorrent.org/beps/bep_0005.html">DHT协议描述</a></p>
<p>Kad的主要目的是用于查询某个资源对应的peer列表，而这个peer列表实际上是分散在整个网络中。网络中节点数量很大，如果要获得peer列表，最简单的做法无非就是依次询问网络中的每个节点。这当然不可行。所以在Kad算法中，设立了一个路由表。每一个节点都有一份路由表。这个是按照节点之间的距离关系构建出来的。节点之间的距离当然也有特定的算法定义，在Kad中通过对两个节点的ID进行异或操作得到。节点的ID和infohash通过相同算法构建，都是20字节长度。节点和infohash之间也有距离关系，实际上表示的是节点和资源的距离关系。</p>
<p>有了这个路由表之后，再通过一个基于距离关系的查找算法，就可以实现不用挨个遍历就找到特定的节点。而查找资源peer这个操作，正是基于节点查找这个过程。</p>
<p>路由表的实现，按我的理解，有点类似一般的hash表结构。在这个表中有160个桶，称为K桶，这个桶的数量在实现上可以动态增长。每个桶保存有限个元素，例如K取值为8，指的就是这个桶最多保存8个元素。每个元素就是一个节点，节点包含节点ID、地址信息以及peer信息。这些桶可以通过距离值索引得到，即距离值会经过一个hash算法，使其值落到桶的索引范围内。</p>
<p>要加入一个DHT网络，需要首先知道这个网络中的任意一个节点。如何获得这个节点？在一些开源的P2P软件中，会提供一些节点地址，例如<a href="http://www.transmissionbt.com/">transmission</a>中提供的dht.transmissionbt.com:6881。</p>
<h3>协议</h3>
<p>Kad定义了节点之间的交互协议。这些协议支撑了整个DHT网络里信息分布式存储的实现。这些协议都是使用UDP来传送。其协议格式使用一种称为<a href="http://en.wikipedia.org/wiki/Bencode">bencode</a>的编码方式来编码协议数据。bencode是一种文本格式的编码，它还用于种子文件内的信息编码。</p>
<p>Kad协议具体格式可参考BitTorrent的定义：<a href="(http://www.bittorrent.org/beps/bep_0005.html">DHT Protocol</a>。这些协议包括4种请求：ping，find_node，get_peer，announce_peer。在有些文档中这些请求的名字会有不同，例如announce_peer又被称为store，get_peer被称为find_value。这4种请求中，都会有对应的回应消息。其中最重要的消息是<code>get_peer</code>，其目的在于在网络中查找某个资源对应的peer列表。</p>
<p>值得一提的是，所有这些请求，包括各种回应，都可以用于处理该消息的节点构建路由表。因为路由表本质就是存储网络中的节点信息。</p>
<h4>ping</h4>
<p>用于确定某个节点是否在线。这个请求主要用于辅助路由表的更新。</p>
<h4>find_node</h4>
<p>用于查找某个节点，以获得其地址信息。当某个节点接收到该请求后，如果目标节点不在自己的路由表里，那么就返回离目标节点较近的K个节点。这个消息可用于节点启动时构建路由表。通过find_node方式构建路由表，其实现方式为向DHT网络查询自己。那么，接收该查询的节点就会一直返回其他节点了列表，查询者递归查询，直到无法查询为止。那么，什么时候无法继续查询呢？这一点我也不太清楚。每一次查询得到的都是离目标节点更接近的节点集，那么理论上经过若干次递归查询后，就无法找到离目标节点更近的节点了，因为最近的节点是自己，但自己还未完全加入网络。这意味着最后所有节点都会返回空的节点集合，这样就算查询结束？</p>
<p>实际上，通过find_node来构建路由表，以及顺带加入DHT网络，这种方式什么时候停止在我看来并不重要。路由表的构建并不需要在启动时构建完成，在以后与其他节点的交互过程中，路由表本身就会慢慢地得到构建。在初始阶段尽可能地通过find_node去与其他节点交互，最大的好处无非就是尽早地让网络中的其他节点认识自己。</p>
<h4>get_peer</h4>
<p>通过资源的infohash获得资源对应的peer列表。当查询者获得资源的peer列表后，它就可以通过这些peer进行资源下载了。收到该请求的节点会在自己的路由表中查找该infohash，如果有收录，就返回对应的peer列表。如果没有，则返回离该infohash较近的若干个节点。查询者若收到的是节点列表，那么就会递归查找。这个过程同find_node一样。</p>
<p>值得注意的是，get_peer的回应消息里会携带一个token，该token会用于稍后的announce_peer请求。</p>
<h4>announce_peer</h4>
<p>该请求主要目的在于通知，通知其他节点自己开始下载某个资源。这个消息用于构建网络中资源的peer列表。当一个已经加入DHT网络的P2P客户端通过种子文件开始下载资源时，首先在网络中查询该资源的peer列表，这个过程通过get_peer完成。当某个节点从get_peer返回peer时，查询者开始下载，然后通过announce_peer告诉返回这个peer的节点。</p>
<p>announce_peer中会携带get_peer回应消息里的token。关于这一点，我有一个疑问是，在<a href="http://blog.csdn.net/mergerly/article/details/7989281">P2P中DHT网络介绍</a>文档中提到：</p>
<blockquote>
<p>(announce_peer)同时会把自己的peer信息发送给先前的告诉者和自己K桶中的k个最近的节点存储该peer-list信息</p>
</blockquote>
<p>不管这里提到的K的最近的节点是离自己最近，还是离资源infohash最近的节点，因为处理announce_peer消息时，有一个token的验证过程。但是这K个节点中，并没有在之前创建对应的token。我通过transmission中的DHT实现做了个数据收集，可以证明的是，announce_peer消息是不仅仅会发给get_peer的回应者的。</p>
<h2>DHT爬虫</h2>
<p>DHT爬虫是一个遵循Kad协议的假节点程序。具体可以参考小虾发布的那个网站应用：<a href="http://bt.shousibaocai.com/">磁力搜索</a>。</p>
<p>这个爬虫的实现方式，主要包含以下内容：</p>
<ul>
     <li>通过其他节点的announce_peer发来的infohash确认网络中有某个资源可被下载</li>
     <li>通过从网络中获取这个资源的种子文件，来获得该资源的描述</li>
</ul>
<p>通过累计收集得到的资源信息，就可以提供一个资源搜索引擎，或者构建资源统计信息。以下进一步描述实现细节。整个爬虫的实现依赖了一个很重要的信息，那就是资源的infohash实际上就是一个磁力链接（当然需要包装一下数据）。这意味着一旦我们获得了一个infohash，我们就等于获得了一个种子。</p>
<h3>获得资源通知</h3>
<p>当爬虫程序加入DHT网络后，它总会收到其他节点发来的announce_peer消息。announce_peer消息与get_peer消息里都带了资源的infohash，但是get_peer里的infohash对应的资源在该网络中不一定存在，即该资源没有任何可用peer。而announce_peer则表示已经确认了该网络中有节点正在下载该资源，也即该资源的数据确实存在该网络中。</p>
<p>所以，爬虫程序需要尽最大努力地获取其他节点发来的announce_peer消息。如果announce_peer消息会发送给离消息发送节点较近的节点，那么，一方面，爬虫程序应该将自己告诉网络中尽可能多的节点。这可以通过一次完整的find_node操作实现。另一方面，爬虫程序内部实现可以部署多个DHT节点，总之目的在于尽可能地让爬虫程序称为其他节点的较近者。</p>
<p>当收集到infohash之后，爬虫程序还需要通过该infohash获得对应资源的描述信息。</p>
<h3>获取资源信息</h3>
<p>获得资源描述信息，其实就是通过infohash获得对应的种子文件。这需要实现P2P协议里的文件分享协议。种子文件的获取其实就是一个文件下载过程，下载到种子文件之后，就可以获取到资源描述。这个过程一种简单的方法，就是从infohash构建出一个磁力链接，然后交给一个支持磁力下载的程序下载种子。</p>
<p>从infohash构建出磁力链接非常简单，只需要将infohash编码成磁力链接的xt字段即可，构建实现可以从transmission源码里找到：</p>
<div class="highlight">
<pre><code class="c"><span class="cm">/* 这个算法其实和printf("%02x", sha1[i])一样 */</span>
<span class="kt">void</span> <span class="nf">tr_sha1_to_hex</span> <span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="n">out</span><span class="p">,</span> <span class="k">const</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">sha1</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">int</span> <span class="n">i</span><span class="p">;</span>
<span class="k">static</span> <span class="k">const</span> <span class="kt">char</span> <span class="n">hex</span><span class="p">[]</span> <span class="o">=</span> <span class="s">"0123456789abcdef"</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="n">i</span><span class="o">&lt;</span><span class="mi">20</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
<span class="k">const</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">val</span> <span class="o">=</span> <span class="o">*</span><span class="n">sha1</span><span class="o">++</span><span class="p">;</span>
<span class="o">*</span><span class="n">out</span><span class="o">++</span> <span class="o">=</span> <span class="n">hex</span><span class="p">[</span><span class="n">val</span> <span class="o">&gt;&gt;</span> <span class="mi">4</span><span class="p">];</span>
<span class="o">*</span><span class="n">out</span><span class="o">++</span> <span class="o">=</span> <span class="n">hex</span><span class="p">[</span><span class="n">val</span> <span class="o">&amp;</span> <span class="mh">0xf</span><span class="p">];</span>
<span class="p">}</span>
<span class="o">*</span><span class="n">out</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">appendMagnet</span><span class="p">(</span><span class="kt">FILE</span> <span class="o">*</span><span class="n">fp</span><span class="p">,</span> <span class="k">const</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">info_hash</span><span class="p">)</span> <span class="p">{</span>
<span class="kt">char</span> <span class="n">out</span><span class="p">[</span><span class="mi">48</span><span class="p">];</span>
<span class="n">tr_sha1_to_hex</span><span class="p">(</span><span class="n">out</span><span class="p">,</span> <span class="n">info_hash</span><span class="p">);</span>
<span class="n">fprintf</span><span class="p">(</span><span class="n">fp</span><span class="p">,</span> <span class="s">"magnet:?xt=urn:btih:%s"</span><span class="p">,</span> <span class="n">out</span><span class="p">);</span>
<span class="p">}</span>
</code></pre>
</div>
<p>现在你就可以做一个实验，在transmission的DHT实现中，在announce_peer消息的处理代码中，将收到的infohash通过上面的<code>appendMagnet</code>转换为磁力链接输出到日志文件里。然后，可以通过支持磁力链接的程序（例如QQ旋风）直接下载。有趣的是，当QQ旋风开始下载该磁力链接对应的种子文件时，你自己的测试程序能收到QQ旋风程序发出的announce_peer消息。当然，你得想办法让这个测试程序尽可能地让其他节点知道你，这可以通过很多方式实现。</p>
<h2>总结</h2>
<p>最终的DHT爬虫除了需要实现DHT协议之外，还需要实现P2P文件下载协议，甚至包括一个种子文件解析模块。看起来包含不小的工作量。而如果要包装为一个资源搜索引擎，还会涉及到资源存储以及搜索，更别说前端呈现了。这个时候，如果你使用的语言不包含这些工具库，那实在是太悲剧了。没错，我就没找到一个erlang DHT库（倒是有erlang实现的BT客户端，懒得去剥了）。</p>
<h2>UPDATE</h2><p>通过详细阅读transmission里的DHT实现，一些之前的疑惑随之解开。</p><h3>announce_peer会发给哪些节点</h3><p>在一次对infohash的查询过程中，所有对本节点发出的get_peer作出回应的节点（不论这个回应节点回应的是nodes还是peers），当本节点取得peer信息时，就会对所有这些节点发出announce_peer。get_peer的回应消息里，不论是peer还是node，都会携带一个token，这样在将来收到对方的announce_peer时，就可以验证该token。</p><h3>节点和bucket状态</h3><p>在本地的路由表中，保存的node是有状态之分的。状态分为三种：good/dubious/bad。good节点基本可以断定该节点是一个在线的并且可以正常回应消息的节点；而bad节点则是可确定的无效节点，通常会尽快从路由表中移除；而dubious则是介于good和bad节点之间，表示可能有问题的节点，需要进一步发送例如ping消息来确认其状态。路由表中应该尽可能保证保存的是good节点，对查询消息的回应里也需携带好的节点。</p><p>bucket也是有状态的，当一个bucket中的所有节点在一定时间之内都没有任何活动的时候，该bucket则应该考虑进行状态的确认，确认方式可以随机选择该bucket中的节点进行find_node操作（这也是find_node除了用于启动之外的唯一作用，但具体实现不见得使用这种方式）。没有消息来往的bucket则应该考虑移除。DHT中几乎所有操作都会涉及到bucket的索引，如果索引到一个所有节点都有问题的bucket，那么该操作可能就无法完成。</p><h3>search在何时停止</h3><p>首先，某次发起的search，无论是对node还是对peer，都可能导致进一步产生若干个search。这些search都是基于transaction id来标识的。由一次search导致产生的所有子search都拥有相同的transaction id，以使得在该search成功或失败时可以通过该transaction id来删除对应的所有search。transaction id也就是DHT中每个消息消息头&#8221;t&#8221;的值。</p><p>但是search何时停止？transmission中是通过超时机制来停止。在search过程中，如果长时间没有收到跟该search关联的节点发来的回应消息，那么就撤销该search，表示搜索失败。</p><h2>参考资料</h2>
<ul>
     <li><a href="http://www.bittorrent.org/beps/bep_0005.html">DHT Protocol</a></li>
     <li><a href="http://blog.csdn.net/mergerly/article/details/7989281">P2P中DHT网络介绍</a></li>
     <li><a href="http://blog.csdn.net/mergerly/article/details/8013694">Torrent文件结构解析</a></li>
     <li><a href="http://bitdht.sourceforge.net/">BitDHT源码</a></li>
     <li><a href="http://www.transmissionbt.com/">Transmission DHT源码</a></li>
     <li><a href="http://en.wikipedia.org/wiki/Bencode">bencode</a></li>
     <li><a href="http://en.wikipedia.org/wiki/Magnet">magnet</a></li>
     <li><a href="https://www.defcon.org/images/defcon-18/dc-18-presentations/Wolchok/DEFCON-18-Wolchok-Crawling-Bittorrent-DHTS.pdf">Crawling Bittorrent DHT</a></li>
</ul>
<p class="post-footer">
原文地址：
<a href="http://codemacro.com/2013/05/19/crawl-dht/">http://codemacro.com/2013/05/19/crawl-dht/</a><br />
written by <a href="http://codemacro.com">Kevin Lynx</a>
&nbsp;posted at <a href="http://codemacro.com">http://codemacro.com</a>
</p>
</div>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/200410.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2013-05-19 21:51 <a href="http://www.cppblog.com/kevinlynx/archive/2013/05/19/200410.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>为http server加入CGI--网页外观的应用程序</title><link>http://www.cppblog.com/kevinlynx/archive/2008/08/05/58089.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Tue, 05 Aug 2008 14:01:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/08/05/58089.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/58089.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/08/05/58089.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/58089.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/58089.html</trackback:ping><description><![CDATA[<p><font size=2>Implement CGI in your httpd </font>
<p><font size=2>author : Kevin Lynx </font>
<p><font size=2><strong>Purpose </strong></font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 为我们的http server加入CGI的功能，并不是要让其成为真正响应CGI query功能的web server。正如klhttpd的开发初衷一样，<br>更多的时候我们需要的是一个嵌入式(embeded)的web server。 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 而加入CGI功能（也许它算不上真正的CGI），则是为了让我们与client拥有更多的交互能力。我们也许需要为我们的应用程序<br>加入一个具有网页外观的界面，也许这才是我的目的。 </font>
<p><font size=2><strong>Brief </strong></font>
<p><font size=2>&nbsp;&nbsp;&nbsp; CGI，common gateway interface，在我看来它就是一个公共约定。客户端浏览器提交表单(form)操作结果给服务器，服务器<br>解析这些操作，完成这些操作，然后创建返回信息（例如一个静态网页），返回给浏览器。<br>&nbsp;&nbsp;&nbsp; 因为浏览器遵循了CGI发送请求的标准，那么我们所要做的就是解析收到的请求，处理我们自己的逻辑，返回信息给客户端即可。<br>Detail...<br>&nbsp;&nbsp;&nbsp; 如果你要获取CGI的更多信息，你可以在网上查阅相关信息。如果你要获取web server对CGI标准的处理方式，我无法提供给你<br>这些信息。不过我愿意提供我的臆测：<br>&nbsp;&nbsp;&nbsp; 处理CGI请求通常都是由脚本去完成（当然也可以用任何可执行程序完成）。就我所获取的资料来看，只要一门语言具备基本的<br>输入输出功能，它就可以被用作CGI脚本。浏览器以key-value的形式提交query string，一个典型的以GET方式提交query string的<br>URL类似于：</font><font size=2>http://host/cgi-bin/some-script.cgi?name=kevin+lynx&amp;age=22。不必拘泥于我上句话中出现的一些让你产生问号的</font><br><font size=2>名词。我可以告诉你，这里举出的URL（也许更准确的说法是URI）中问号后面的字符串即为一个query string。<br>&nbsp;&nbsp;&nbsp; 我希望你看过我的上一篇草文&lt;实现自己的http server&gt;，你应该知道一个典型的GET请求时怎样的格式，应该知道什么是initial<br>line，应该知道HTTP请求有很多method，例如GET、POST、HEAD等。<br>&nbsp;&nbsp;&nbsp; 一个CGI请求通常使用GET或POST作为其request method。对于一个GET类型的CGI请求，一个典型的request类似于：<br>&nbsp;&nbsp;&nbsp; GET /cgi-bin/some-script.cgi?name=kevin+lynx&amp;age=22 HTTP/1.1<br>&nbsp;&nbsp;&nbsp; other headers...<br>&nbsp;&nbsp;&nbsp; ...<br>&nbsp;&nbsp;&nbsp; 我上面举例的那个URL则可能出现在你的浏览器的地址栏中，它对我们不重要。<br>&nbsp;&nbsp;&nbsp; 而对于POST类型的CGI请求呢？query string只是被放到了optional body里，如：<br>&nbsp;&nbsp;&nbsp; POST /cgi-bin/some-script.cgi HTTP/1.1<br>&nbsp;&nbsp;&nbsp; other heads...<br>&nbsp;&nbsp;&nbsp; ...<br>&nbsp;&nbsp;&nbsp; name=kevin+lynx&amp;age=22<br>&nbsp;&nbsp;&nbsp; 不管我说到什么，我希望你不要陷入概念的泥潭，我希望你能明确我在brief里提到的what can we do and how to do。 </font>
<p><font size=2><strong>How about the web browser ?</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 我总是想引领你走在实践的路上(on the practice way)。作为一个程序员，你有理由自负，但是你没任何理由小觑任何一个<br>编程问题。打开你的编译器，现在就敲下main。<br>&nbsp;&nbsp;&nbsp; so，我们需要知道如何利用我们的浏览器，让我们更为顺手地实践我们的想法。<br>&nbsp;&nbsp;&nbsp; 你也许会写：&lt;html&gt;&lt;head&gt;&lt;title&gt;HTML test&lt;/title&gt;&lt;/head&gt;&lt;body&gt;it's a html&lt;/body&gt;&lt;/html&gt;这样的HTML标记语言。但是<br>这没有利用上CGI。check out the url in the reference section and learn how to write FORMs.<br>&nbsp;&nbsp;&nbsp; 这个时候你应该明白：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp; <span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">html</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">head</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">title</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">CGI&nbsp;test</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">title</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">head</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">body</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">FORM&nbsp;</span><span style="COLOR: #ff0000">ACTION</span><span style="COLOR: #0000ff">="./cgi-bin/test.cgi"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">INPUT&nbsp;</span><span style="COLOR: #ff0000">TYPE</span><span style="COLOR: #0000ff">="text"</span><span style="COLOR: #ff0000">&nbsp;NAME</span><span style="COLOR: #0000ff">="name"</span><span style="COLOR: #ff0000">&nbsp;SIZE</span><span style="COLOR: #0000ff">=20&nbsp;</span><span style="COLOR: #ff0000">VALUE</span><span style="COLOR: #0000ff">="Your&nbsp;name"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">FORM</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">body</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">html</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; 加入&lt;FORM&gt;来添加表单，加入&lt;INPUT TYPE="text"加一个edit box控件。那么当你在里面输入名字按下回车键时，浏览器将整理<br>出一个CGI query string发给服务器。<br>&nbsp;&nbsp;&nbsp; 我希望你能在浏览器里看到以问号打头的key-value形式的字符串。 </font>
<p><font size=2><strong>So here we go...</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 在你自己已有的http server的代码基础上，让浏览器来请求一个包含FORM的网页，例如以上我举例的html。当浏览器将请求发<br>给服务器的时候，看看服务器这边收到的request。即使你猜到了什么，我也建议你看看，这样你将看到一个真正的CGI query是怎么<br>样的。<br>&nbsp;&nbsp;&nbsp; so you know，CGI请求本质上也就是一连串的key-value字符串。真正的http server会将这些字符串传给CGI脚本。脚本只需要<br>输出处理结果信息到标准输出，服务器就会收集这些输出然后发给客户端。<br>&nbsp;&nbsp;&nbsp; 你也许在揣摩，我们的httpd如何地exec一个外部程序或脚本，如何等待脚本执行完（需不需要异步？），然后如何收集标准输<br>出上的信息，然后是否需要同步地将这些信息送往客户端。<br>&nbsp;&nbsp;&nbsp; 这很没必要。如果我们的http server真的只是被embeded到其他程序的。我们只需要将query的逻辑处理交给上层模块即可。<br><strong></strong></font></p>
<p><font size=2><strong>What's the next..</strong> </font></p>
<p><font size=2>&nbsp;&nbsp;&nbsp; 现在你知道什么是CGI query string，你也许稍微思考下就知道接下来你该如何去做(if you smart enough)。记住我们的目标，<br>只是解析CGI query string，然后处理相关逻辑，返回处理结果信息给客户端。<br>&nbsp;&nbsp;&nbsp; 所以接下来，呃，接下来，everything is up to you：分析那些恼人的字符串，将其从&amp;的间隔中整理出来，得到一个key-value<br>的表，将这个表给你的上层模块，上层模块根据query scritp决定执行什么逻辑，根据这个表获取需要的参数，处理，将结果格式<br>化为HTML之类的东西，then response。<br>&nbsp;&nbsp;&nbsp; 就这么简单。 </font>
<p><font size=2><strong>Example...</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 同样，我提供了源码，从而证明我不是纸上谈兵夸夸其谈。我希望我公开源码的习惯能受到你的赞同。 </font>
<p><font size=2>&nbsp;&nbsp; <a href="http://www.cppblog.com/Files/kevinlynx/klhttpd-cgi.rar" target=_blank>下载带CGI的http server.</a></font>
<p><font size=2><strong>Reference:</strong> </font>
<p><a href="http://hoohoo.ncsa.uiuc.edu/cgi/"><font size=2>http://hoohoo.ncsa.uiuc.edu/cgi/</font></a><font size=2> (获取服务器需要做的)<br></font><a href="http://www.ietf.org/rfc/rfc3875"><font size=2>http://www.ietf.org/rfc/rfc3875</font></a><font size=2> (rfc cgi)<br></font><a href="http://www.cmis.brighton.ac.uk/~mas/mas/courses/html/html2.html"><font size=2>http://www.cmis.brighton.ac.uk/~mas/mas/courses/html/html2.html</font></a><font size=2> (学会写带FORM的HTML) </font></p>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/58089.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-08-05 22:01 <a href="http://www.cppblog.com/kevinlynx/archive/2008/08/05/58089.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实现自己的http server</title><link>http://www.cppblog.com/kevinlynx/archive/2008/07/30/57521.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Wed, 30 Jul 2008 08:14:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/07/30/57521.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/57521.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/07/30/57521.html#Feedback</comments><slash:comments>21</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/57521.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/57521.html</trackback:ping><description><![CDATA[<p><font size=2>Write your own http server </font>
<p><font size=2>author : Kevin Lynx<br></font></p>
<p><font size=2><strong>Why write your own?</strong> </font></p>
<p><font size=2>&nbsp;&nbsp;&nbsp; 看这个问题的人证明你知道什么是http server，世界上有很多各种规模的http server，为什么要自己实现一个？其实没什么<br>理由。我自己问自己，感觉就是在自己娱乐自己，或者说只是练习下网络编程，或者是因为某日我看到某个库宣称自己附带一个小<br>型的http server时，我不知道是什么东西，于是就想自己去实现一个。<br><strong></strong></font></p>
<p><font size=2><strong>What's httpd ?</strong> </font></p>
<p><font size=2>&nbsp;&nbsp;&nbsp; httpd就是http daemon，这个是类unix系统上的名称，也就是http server。httpd遵循HTTP协议，响应HTTP客户端的request，<br>然后返回response。<br>&nbsp;&nbsp;&nbsp; 那么，什么是HTTP协议？最简单的例子，就是你的浏览器与网页服务器之间使用的应用层协议。虽然官方文档说HTTP协议可以<br>建立在任何可靠传输的协议之上，但是就我们所见到的，HTTP还是建立在TCP之上的。<br>&nbsp;&nbsp;&nbsp; httpd最简单的response是返回静态的HTML页面。在这里我们的目标也只是一个响应静态网页的httpd而已(也许你愿意加入CGI<br>特性)。 </font>
<p><font size=2><strong>More details about HTTP protocol</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 在这里有必要讲解HTTP协议的更多细节，因为我们的httpd就是要去解析这个协议。<br>&nbsp;&nbsp;&nbsp; 关于HTTP协议的详细文档，可以参看<a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html">rfc2616</a>。但事实上对于实现一个简单的响应静态网页的httpd来说，完全没必要读这么一<br>分冗长的文档。在这里我推荐&lt;<a href="http://jmarshall.com/easy/http/" target=_blank>HTTP Made Really Easy</a>&gt;，以下内容基本取自于本文档。 </font>
<p><font size=2><strong>- HTTP协议结构</strong><br>&nbsp; HTTP协议无论是请求报文(request message)还是回应报文(response message)都分为四部分：<br>&nbsp; * 报文头 (initial line )<br>&nbsp; * 0个或多个header line<br>&nbsp; * 空行(作为header lines的结束)<br>&nbsp; * 可选body<br>&nbsp; HTTP协议是基于行的协议，每一行以\r\n作为分隔符。报文头通常表明报文的类型(例如请求类型)，报文头只占一行；header line<br>&nbsp; 附带一些特殊信息，每一个header line占一行，其格式为name:value，即以分号作为分隔；空行也就是一个\r\n；可选body通常<br>&nbsp; 包含数据，例如服务器返回的某个静态HTML文件的内容。举个例子，以下是一个很常见的请求报文，你可以截获浏览器发送的数据<br>&nbsp; 包而获得： </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 1&nbsp; GET /index.html HTTP/1.1<br>&nbsp;&nbsp;&nbsp; 2&nbsp; Accept-Language: zh-cn<br>&nbsp;&nbsp;&nbsp; 3&nbsp; Accept-Encoding: gzip, deflate<br>&nbsp;&nbsp;&nbsp; 4&nbsp; User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; MAXTHON 2.0)<br>&nbsp;&nbsp;&nbsp; 5&nbsp; Host: localhost<br>&nbsp;&nbsp;&nbsp; 6&nbsp; Connection: Keep-Alive<br>&nbsp;&nbsp;&nbsp; 7<br>&nbsp; 我为每一行都添加了行号，第1行就是initial line，2-6行是header lines，7行是一个header line的结束符，没有显示出来。<br>&nbsp; 以下是一个回应报文：<br>&nbsp;&nbsp;&nbsp; 1&nbsp; HTTP/1.1 200 OK<br>&nbsp;&nbsp;&nbsp; 2&nbsp; Server: klhttpd/0.1.0<br>&nbsp;&nbsp;&nbsp; 3&nbsp; Content-Type: text/html<br>&nbsp;&nbsp;&nbsp; 4&nbsp; Content-Length: 67<br>&nbsp;&nbsp;&nbsp; 5<br>&nbsp;&nbsp;&nbsp; 6&nbsp; &lt;head&gt;&lt;head&gt;&lt;title&gt;index.html&lt;/title&gt;&lt;/head&gt;&lt;body&gt;index.html&lt;/body&gt;<br>&nbsp; 第6行就是可选的body，这里是index.html这个文件的内容。 </font>
<p><font size=2><strong>- HTTP request method</strong><br>&nbsp; 因为我们做的事服务器端，所以我们重点对请求报文做说明。首先看initial line，该行包含几个字段，每个字段用空格分开，例<br>&nbsp; 如以上的GET /index.html HTTP/1.1就可以分为三部分：GET、/index.html、HTTP/1.1。其中第一个字段GET就是所谓的request<br>&nbsp; method。它表明请求类型，HTTP有很多method，例如：GET、POST、HEAD等。 </font>
<p><font size=2>&nbsp; 就我们的目标而言，我们只需要实现对GET和HEAD做响应即可。 </font>
<p><font size=2>&nbsp; GET是最普遍的method，表示请求一个资源。什么是资源？诸如HTML网页、图片、声音文件等都是资源。顺便提一句，HTTP协议<br>&nbsp; 中为每一个资源设置一个唯一的标识符，就是所谓的URI(更宽泛的URL)。<br>&nbsp; HEAD与GET一样，不过它不请求资源内容，而是请求资源信息，例如文件长度等信息。 </font>
<p><font size=2><strong>- More detail</strong> <br>&nbsp; 继续说说initial line后面的内容：<br>&nbsp; 对应于GET和HEAD两个method，紧接着的字段就是资源名，其实从这里可以看出，也就是文件名(相对于你服务器的资源目录)，例<br>&nbsp; 如这里的/index.html；最后一个字段表明HTTP协议版本号。目前我们只需要支持HTTP1.1和1.0，没有多大的技术差别。 </font>
<p><font size=2>&nbsp; 然后是header line。我们并不需要关注每一个header line。我只罗列有用的header line :<br>&nbsp; - Host : 对于HTTP1.1而言，请求报文中必须包含此header，如果没有包含，服务器需要返回bad request错误信息。<br>&nbsp; - Date : 用于回应报文，用于客户端缓存数据用。<br>&nbsp; - Content-Type : 用于回应报文，表示回应资源的文件类型，以MIME形式给出。什么是MIME？它们都有自己的格式，例如：<br>&nbsp;&nbsp;&nbsp; text/html, image/jpg, image/gif等。<br>&nbsp; - Content-Length : 用于回应报文，表示回应资源的文件长度。 </font>
<p><font size=2>body域很简单，你只需要将一个文件全部读入内存，然后附加到回应报文段后发送即可，即使是二进制数据。 </font>
<p><font size=2><strong>- 回应报文</strong><br>&nbsp; 之前提到的一个回应报文例子很典型，我们以其为例讲解。首先是initial line，第一个字段表明HTTP协议版本，可以直接以请求<br>&nbsp; 报文为准(即请求报文版本是多少这里就是多少)；第二个字段是一个status code，也就是回应状态，相当于请求结果，请求结果<br>&nbsp; 被HTTP官方事先定义，例如200表示成功、404表示资源不存在等；最后一个字段为status code的可读字符串，你随便给吧。 </font>
<p><font size=2>&nbsp; 回应报文中最好跟上Content-Type、Content-Length等header。 </font>
<p><font size=2><strong>具体实现</strong><br>&nbsp;&nbsp;&nbsp; 正式写代码之前我希望你能明白HTTP协议的这种请求/回应模式，即客户端发出一个请求，然后服务器端回应该请求。然后继续<br>这个过程(HTTP1.1是长连接模式，而HTTP1.0是短连接，当服务器端返回第一个请求时，连接就断开了)。<br>&nbsp;&nbsp;&nbsp; 这里，我们无论客户端，例如浏览器，发出什么样的请求，请求什么资源，我们都回应相同的数据： </font>
<p><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img id=Codehighlighter1_0_17_Open_Image onclick="this.style.display='none'; Codehighlighter1_0_17_Open_Text.style.display='none'; Codehighlighter1_0_17_Closed_Image.style.display='inline'; Codehighlighter1_0_17_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_0_17_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_0_17_Closed_Text.style.display='none'; Codehighlighter1_0_17_Open_Image.style.display='inline'; Codehighlighter1_0_17_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><span id=Codehighlighter1_0_17_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_0_17_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;阻塞地接受一个客户端连接&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SOCKET&nbsp;con&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;accept(&nbsp;s,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;);&nbsp;<br><img id=Codehighlighter1_68_85_Open_Image onclick="this.style.display='none'; Codehighlighter1_68_85_Open_Text.style.display='none'; Codehighlighter1_68_85_Closed_Image.style.display='inline'; Codehighlighter1_68_85_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_68_85_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_68_85_Closed_Text.style.display='none'; Codehighlighter1_68_85_Open_Image.style.display='inline'; Codehighlighter1_68_85_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_68_85_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_68_85_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;recv&nbsp;request&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img id=Codehighlighter1_116_120_Open_Image onclick="this.style.display='none'; Codehighlighter1_116_120_Open_Text.style.display='none'; Codehighlighter1_116_120_Closed_Image.style.display='inline'; Codehighlighter1_116_120_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_116_120_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_116_120_Closed_Text.style.display='none'; Codehighlighter1_116_120_Open_Image.style.display='inline'; Codehighlighter1_116_120_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;request[</span><span style="COLOR: #000000">1024</span><span style="COLOR: #000000">]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span id=Codehighlighter1_116_120_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_116_120_Open_Text><span style="COLOR: #000000">{&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;}</span></span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ret&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;recv(&nbsp;con,&nbsp;request,&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(&nbsp;request&nbsp;),&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(&nbsp;request&nbsp;);<br><img id=Codehighlighter1_216_259_Open_Image onclick="this.style.display='none'; Codehighlighter1_216_259_Open_Text.style.display='none'; Codehighlighter1_216_259_Closed_Image.style.display='inline'; Codehighlighter1_216_259_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_216_259_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_216_259_Closed_Text.style.display='none'; Codehighlighter1_216_259_Open_Image.style.display='inline'; Codehighlighter1_216_259_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_216_259_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_216_259_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;whatever&nbsp;we&nbsp;recv,&nbsp;we&nbsp;send&nbsp;200&nbsp;response&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img id=Codehighlighter1_269_622_Open_Image onclick="this.style.display='none'; Codehighlighter1_269_622_Open_Text.style.display='none'; Codehighlighter1_269_622_Closed_Image.style.display='inline'; Codehighlighter1_269_622_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_269_622_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_269_622_Closed_Text.style.display='none'; Codehighlighter1_269_622_Open_Image.style.display='inline'; Codehighlighter1_269_622_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_269_622_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_269_622_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;content[]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&lt;head&gt;&lt;head&gt;&lt;title&gt;index.html&lt;/title&gt;&lt;/head&gt;&lt;body&gt;index.html&lt;/body&gt;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;response[</span><span style="COLOR: #000000">512</span><span style="COLOR: #000000">];<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sprintf(&nbsp;response,&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">HTTP/1.1&nbsp;200&nbsp;OK\r\nContent-Type:&nbsp;text/html\r\nContent-Length:&nbsp;%d\r\n\r\n%s</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,&nbsp;strlen(&nbsp;content&nbsp;),&nbsp;content&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ret&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;send(&nbsp;con,&nbsp;response,&nbsp;strlen(&nbsp;response&nbsp;),&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;closesocket(&nbsp;con&nbsp;);&nbsp;</span></div>
<p></font>&nbsp;
<p><font size=2>&nbsp;&nbsp;&nbsp; 程序以最简单的阻塞模式运行，我们可以将重点放在协议的分析上。运行程序，在浏览器里输入http://localhost:8080/index.html<br>，然后就可以看到浏览器正常显示content中描述的HTML文件。假设程序在8080端口监听。 </font>
<p><font size=2>&nbsp;&nbsp; 现在你基本上明白了整个工作过程，我们可以把代码写得更全面一点，例如根据GET的URI来载入对应的文件然后回应给客户端。<br>其实这个很简单，只需要从initial line里解析出(很一般的字符串解析)URI字段，然后载入对应的文件即可。例如以下函数：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;http_response(&nbsp;SOCKET&nbsp;con,&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">request&nbsp;)<br><img id=Codehighlighter1_54_1189_Open_Image onclick="this.style.display='none'; Codehighlighter1_54_1189_Open_Text.style.display='none'; Codehighlighter1_54_1189_Closed_Image.style.display='inline'; Codehighlighter1_54_1189_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_54_1189_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_54_1189_Closed_Text.style.display='none'; Codehighlighter1_54_1189_Open_Image.style.display='inline'; Codehighlighter1_54_1189_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_54_1189_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_54_1189_Open_Text><span style="COLOR: #000000">{<br><img id=Codehighlighter1_60_79_Open_Image onclick="this.style.display='none'; Codehighlighter1_60_79_Open_Text.style.display='none'; Codehighlighter1_60_79_Closed_Image.style.display='inline'; Codehighlighter1_60_79_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_60_79_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_60_79_Closed_Text.style.display='none'; Codehighlighter1_60_79_Open_Image.style.display='inline'; Codehighlighter1_60_79_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_60_79_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_60_79_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;get&nbsp;the&nbsp;method&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">token&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;strtok(&nbsp;request,&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">uri&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;strtok(&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;file[</span><span style="COLOR: #000000">64</span><span style="COLOR: #000000">];<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;sprintf(&nbsp;file,&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">.%s</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,&nbsp;uri&nbsp;);&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img id=Codehighlighter1_215_1187_Open_Image onclick="this.style.display='none'; Codehighlighter1_215_1187_Open_Text.style.display='none'; Codehighlighter1_215_1187_Closed_Image.style.display='inline'; Codehighlighter1_215_1187_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_215_1187_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_215_1187_Closed_Text.style.display='none'; Codehighlighter1_215_1187_Open_Image.style.display='inline'; Codehighlighter1_215_1187_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_215_1187_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_215_1187_Open_Text><span style="COLOR: #000000">{<br><img id=Codehighlighter1_225_251_Open_Image onclick="this.style.display='none'; Codehighlighter1_225_251_Open_Text.style.display='none'; Codehighlighter1_225_251_Closed_Image.style.display='inline'; Codehighlighter1_225_251_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_225_251_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_225_251_Closed_Text.style.display='none'; Codehighlighter1_225_251_Open_Image.style.display='inline'; Codehighlighter1_225_251_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_225_251_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_225_251_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;load&nbsp;the&nbsp;file&nbsp;content&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FILE&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">fp&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;fopen(&nbsp;file,&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">rb</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;fp&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;)<br><img id=Codehighlighter1_323_502_Open_Image onclick="this.style.display='none'; Codehighlighter1_323_502_Open_Text.style.display='none'; Codehighlighter1_323_502_Closed_Image.style.display='inline'; Codehighlighter1_323_502_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_323_502_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_323_502_Closed_Text.style.display='none'; Codehighlighter1_323_502_Open_Image.style.display='inline'; Codehighlighter1_323_502_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_323_502_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_323_502_Open_Text><span style="COLOR: #000000">{<br><img id=Codehighlighter1_337_366_Open_Image onclick="this.style.display='none'; Codehighlighter1_337_366_Open_Text.style.display='none'; Codehighlighter1_337_366_Closed_Image.style.display='inline'; Codehighlighter1_337_366_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_337_366_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_337_366_Closed_Text.style.display='none'; Codehighlighter1_337_366_Open_Image.style.display='inline'; Codehighlighter1_337_366_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_337_366_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_337_366_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;response&nbsp;404&nbsp;status&nbsp;code&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;response[]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">HTTP/1.1&nbsp;404&nbsp;NOT&nbsp;FOUND\r\n\r\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;send(&nbsp;con,&nbsp;response,&nbsp;strlen(&nbsp;response&nbsp;),&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000"><br><img id=Codehighlighter1_525_1181_Open_Image onclick="this.style.display='none'; Codehighlighter1_525_1181_Open_Text.style.display='none'; Codehighlighter1_525_1181_Closed_Image.style.display='inline'; Codehighlighter1_525_1181_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_525_1181_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_525_1181_Closed_Text.style.display='none'; Codehighlighter1_525_1181_Open_Image.style.display='inline'; Codehighlighter1_525_1181_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_525_1181_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_525_1181_Open_Text><span style="COLOR: #000000">{<br><img id=Codehighlighter1_539_565_Open_Image onclick="this.style.display='none'; Codehighlighter1_539_565_Open_Text.style.display='none'; Codehighlighter1_539_565_Closed_Image.style.display='inline'; Codehighlighter1_539_565_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_539_565_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_539_565_Closed_Text.style.display='none'; Codehighlighter1_539_565_Open_Image.style.display='inline'; Codehighlighter1_539_565_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_539_565_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_539_565_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;response&nbsp;the&nbsp;resource&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img id=Codehighlighter1_579_604_Open_Image onclick="this.style.display='none'; Codehighlighter1_579_604_Open_Text.style.display='none'; Codehighlighter1_579_604_Closed_Image.style.display='inline'; Codehighlighter1_579_604_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_579_604_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_579_604_Closed_Text.style.display='none'; Codehighlighter1_579_604_Open_Image.style.display='inline'; Codehighlighter1_579_604_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_579_604_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_579_604_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;first,&nbsp;load&nbsp;the&nbsp;file&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;file_size&nbsp;;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">content;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;response[</span><span style="COLOR: #000000">1024</span><span style="COLOR: #000000">];<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fseek(&nbsp;fp,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,&nbsp;SEEK_END&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;file_size&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;ftell(&nbsp;fp&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fseek(&nbsp;fp,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,&nbsp;SEEK_SET&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;content&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)malloc(&nbsp;file_size&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fread(&nbsp;content,&nbsp;file_size,&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">,&nbsp;fp&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;content[file_size]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sprintf(&nbsp;response,&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">HTTP/1.1&nbsp;200&nbsp;OK\r\nContent-Type:&nbsp;text/html\r\nContent-Length:&nbsp;%d\r\n\r\n%s</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,&nbsp;file_size,&nbsp;content&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;send(&nbsp;con,&nbsp;response,&nbsp;strlen(&nbsp;response&nbsp;),&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;free(&nbsp;content&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br></font>
<p><font size=2><strong></p>
<p><br>其他</strong><br>&nbsp;&nbsp;&nbsp; 要将这个简易的httpd做完善，我们还需要注意很多细节。包括：对不支持的method返回501错误；对于HTTP1.1要求有Host这个<br>header；为了支持客户端cache，需要添加Date header；支持HEAD请求等。 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 相关下载中我提供了一个完整的httpd library，纯C的代码，在其上加上一层资源载入即可实现一个简单的httpd。在这里我将<br>对代码做简要的说明：<br>&nbsp;&nbsp;&nbsp; evbuffer.h/buffer.c ： 取自libevent的buffer，用于缓存数据；<br>&nbsp;&nbsp;&nbsp; klhttp-internal.h/klhttp-internal.c ：主要用于处理/解析HTTP请求，以及创建回应报文；<br>&nbsp;&nbsp;&nbsp; klhttp-netbase.h/klhttp-netbase.c ：对socket api的一个简要封装，使用select模型；<br>&nbsp;&nbsp;&nbsp; klhttp.h/klhttp.c ：库的最上层，应用层主要与该层交互，这一层主要集合internal和netbase。<br>&nbsp;&nbsp;&nbsp; test_klhttp.c ：一个测试例子。 </font>
<p><font size=2><strong>相关下载：</strong><br>&nbsp;&nbsp;&nbsp; <a href="http://www.cppblog.com/Files/kevinlynx/klhttpd.rar" target=_blank>klhttpd</a><br>&nbsp;&nbsp;&nbsp; <a href="http://www.cppblog.com/Files/kevinlynx/sample_httpd.rar" target=_blank>文中相关代码</a> </font>
<p><font size=2><strong>参考资料：</strong> </font>
<p><a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html">http://www.w3.org/Protocols/rfc2616/rfc2616.html</a><br><a href="http://jmarshall.com/easy/http/">http://jmarshall.com/easy/http/</a><br><a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html">http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html</a> </p>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/57521.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-07-30 16:14 <a href="http://www.cppblog.com/kevinlynx/archive/2008/07/30/57521.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>自己实现memcached客户端库</title><link>http://www.cppblog.com/kevinlynx/archive/2008/07/21/56768.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Mon, 21 Jul 2008 07:54:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/07/21/56768.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/56768.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/07/21/56768.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/56768.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/56768.html</trackback:ping><description><![CDATA[<p><font size=2>Kevin Lynx </font>
<p><font size=2>7.21.2008 </font>
<p><font size=2><strong>What's memcached ?</strong> </font>
<p><font size=2><a href="http://www.danga.com/memcached/" target=_blank>memcached</a>是一个以key-value的形式缓存数据的缓存系统。通过将数据缓存到内存中，从而提高数据的获取速度。<br>memcached以key-value的形式来保存数据，你可以为你每一段数据关联一个key，然后以后可以通过这个key获取<br>这段数据。 </font>
<p><font size=2>memcached是一个库还是什么？memcached其实是一个单独的网络服务器程序。它的网络底层基于<a href="http://www.monkey.org/~provos/libevent/" target=_blank>libevent</a>，你可以<br>将其运行在网络中的一台服务器上，通过网络，在遵循memcached的协议的基础上与memcached服务器进行通信。 </font>
<p><font size=2><strong>What do we want to wrap ?</strong> </font>
<p><font size=2>我们需要做什么？我们只需要遵循memcached的协议(<a href="http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt" target=_blank>参见该文档</a>)，封装网络层的通信，让上层可以通过调用诸如<br>add/get之类的接口即可实现往memcached服务器缓存数据，以及取数据。上层程序员根本不知道这些数据在网络<br>上存在过。 </font>
<p><font size=2>这个东西，也就是memcached官方所谓的client apis。你可以使用<a href="http://www.danga.com/memcached/apis.bml" target=_blank>现成的客户端库</a>，但是你也可以将这种重造轮子<br>的工作当作一次网络编程的练习。it's up to you.:D </font>
<p><font size=2><strong>Where to start ?</strong> </font>
<p><font size=2>很遗憾，对于windows用户而言，memcached官方没有给出一个可以执行或者可以直接F7即可得到可执行文件的下载<br>(如果你是vc用户)。幸运的是，已经有人做了这个转换工作。 </font>
<p><font size=2>你可以从http://jehiah.cz/projects/memcached-win32/这里下载到memcached的windows版本，包括可执行程序和<br>源代码。 </font>
<p><font size=2>我们直接可以运行memcached.exe来安装/开启memcached服务器，具体步骤在以上页面有所提及：</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">安装：memcached.exe&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">d&nbsp;install，这会在windows服务里添加一个memcached服务<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>运行：memcached.exe&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">d&nbsp;start，你也可以通过windows的服务管理运行。<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p>&nbsp;&nbsp;&nbsp; <br>然后，你可以在任务管理器里看到一个'memcached'的进程，很占内存，因为这是memcached。 </font>
<p><font size=2><strong>So, here we go ...</strong> </font>
<p><font size=2>通过以上步骤运行的memcached，默认在11211端口监听(是个TCP连接，可以通过netstat查看)。接下来，我们就可<br>以connect到该端口上，然后send/recv数据了。发送/接收数据只要遵循memcached的协议格式，一切都很简单。 </font>
<p><font size=2>使用最简单的阻塞socket连接memcached服务器： </font>
<p><font size=2></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SOCKET&nbsp;s&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;socket(&nbsp;AF_INET,&nbsp;SOCK_STREAM,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;sockaddr_in&nbsp;addr;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memset(&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">addr,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(&nbsp;addr&nbsp;)&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addr.sin_family&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;AF_INET;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addr.sin_port&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;htons(&nbsp;</span><span style="COLOR: #000000">11211</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addr.sin_addr.s_addr&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;inet_addr(&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">127.0.0.1</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;);&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ret&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;connect(&nbsp;s,&nbsp;(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;sockaddr</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">addr,&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(&nbsp;addr&nbsp;)&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;ret&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;)<br><img id=Codehighlighter1_359_408_Open_Image onclick="this.style.display='none'; Codehighlighter1_359_408_Open_Text.style.display='none'; Codehighlighter1_359_408_Closed_Image.style.display='inline'; Codehighlighter1_359_408_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_359_408_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_359_408_Closed_Text.style.display='none'; Codehighlighter1_359_408_Open_Image.style.display='inline'; Codehighlighter1_359_408_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_359_408_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_359_408_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">connect&nbsp;ok\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br></font><font size=2><strong>About the protocol</strong> </font>
<p><font size=2>简单地提一下memcached的协议。 </font>
<p><font size=2>可以说，memcached的协议是基于行的协议，因为无论是客户端请求还是服务器端应答，都是以"\r\n"作为结束符。<br>memcached的协议将数据(send/recv操作的数据)分为两种类型：命令和用户数据。 </font>
<p><font size=2>命令用于服务器和客户端进行交互；而用户数据，很显然，就是用户想要缓存的数据。 </font>
<p><font size=2>关于用户数据，你只需要将其编码成字节流(说白了，只要send函数允许即可)，并附带数据结束标志"\r\n"发送即可。 </font>
<p><font size=2>关于命令，memcached分为如下几种命令：存储数据、删除数据、取出数据、其他一些获取信息的命令。其实你换个角度<br>想想，memcached主要就是将数据存储到内存里，所以命令也多不了哪去，基本就停留在add/get/del上。 </font>
<p><font size=2>关于key，memcached用key来标识数据，每一个key都是一个不超过255个字符的字符串。 </font>
<p><font size=2>到这里，你可以发现memcached对于数据的存储方式(暴露给上层)多少有点像std::map，如果你愿意，你可以将客户端<br>API封装成map形式。= =# </font>
<p><font size=2><strong>具体实现</strong> </font>
<p><font size=2>接下来可以看看具体的实现了。 </font>
<p><font size=2>首先看看存储数据命令，存储数据命令有：add/set/replace/append/prepend/cas。存储命令的格式为：<br>&lt;command name&gt; &lt;key&gt; &lt;flags&gt; &lt;exptime&gt; &lt;bytes&gt; [noreply]\r\n<br>具体字段的含义参看<a href="http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt" target=_blank>protocol.txt</a>文件，这里我对set举例，如下代码，阻塞发送即可： </font>
<p><font size=2></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;cmd[</span><span style="COLOR: #000000">256</span><span style="COLOR: #000000">]&nbsp;;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;data[]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">test&nbsp;data</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sprintf(&nbsp;cmd,&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">set&nbsp;TestKey&nbsp;0&nbsp;0&nbsp;%d\r\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,&nbsp;strlen(&nbsp;data&nbsp;)&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ret&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;send(&nbsp;s,&nbsp;cmd,&nbsp;strlen(&nbsp;cmd&nbsp;),&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;);&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>注意：noreply选项对于有些memcached版本并不被支持，例如我们使用的1.2.2版本。注意官方的changelog即可。 </font>
<p><font size=2>当你发送了存储命令后，memcached会等待客户端发送数据块。所以我们继续发送数据块： </font>
<p><font size=2>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ret&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;send(&nbsp;s,&nbsp;data,&nbsp;strlen(&nbsp;data&nbsp;),&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ret&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;send(&nbsp;s,&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">\r\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;数据结束符</span></div>
<p>&nbsp;</font>
<p><font size=2>然后，正常的话，memcached服务器会返回应答信息给客户端。 </font>
<p><font size=2>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;reply[</span><span style="COLOR: #000000">256</span><span style="COLOR: #000000">];<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ret&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;recv(&nbsp;s,&nbsp;reply,&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(&nbsp;reply&nbsp;)&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reply[ret]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">server&nbsp;reply&nbsp;:&nbsp;%s\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,&nbsp;reply&nbsp;);&nbsp;</span></div>
<p></font>&nbsp;
<p><font size=2>如果存储成功，服务器会返回STORED字符串。memcached所有应答信息都是以字符串的形式给出的。所以可以直接printf出来。 </font>
<p><font size=2>关于其他的操作，我就不在这里列举例子了。我提供了我封装的memcached客户端库的<a href="http://www.cppblog.com/Files/kevinlynx/kl_memcached.rar" target=_blank>完整代码下载</a>，使用的是阻塞socket，<br>对应着memcached的协议看，很容易看懂的。 </font>
<p><font size=2><strong>It's a story about a programmer...</strong> </font>
<p><font size=2>最近发觉自己有点极端，要么写纯C的代码，要么写满是template的泛型代码。 </font>
<p><font size=2></font>&nbsp;</p>
<a href="http://www.cppblog.com/Files/kevinlynx/kl_memcached.rar" target=_blank><font size=2>相关代码下载</font></a>
<p><font size=2></font></p>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/56768.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-07-21 15:54 <a href="http://www.cppblog.com/kevinlynx/archive/2008/07/21/56768.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>libevent 源码分析：min_heap带来的超时机制</title><link>http://www.cppblog.com/kevinlynx/archive/2008/07/18/56511.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Fri, 18 Jul 2008 07:56:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/07/18/56511.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/56511.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/07/18/56511.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/56511.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/56511.html</trackback:ping><description><![CDATA[<p><font size=2>author : Kevin Lynx</font>
<p><font size=2><strong>什么是min heap ?</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 首先看什么是heap，heap是这样一种数据结构：1.它首先是一棵完成二叉树；2.父亲节点始终大于(或其他逻辑关系<br>)其孩子节点。根据父亲节点与孩子节点的这种逻辑关系，我们将heap分类，如果父亲节点小于孩子节点，那么这个heap<br>就是min heap。<br>&nbsp;&nbsp;&nbsp; 就我目前所看到的实现代码来看，heap基本上都是用数组(或者其他的连续存储空间)作为其存储结构的。这可以保证<br>数组第一个元素就是heap的根节点。这是一个非常重要的特性，它可以保证我们在取heap的最小元素时，其算法复杂度为<br>O(1)。<br>&nbsp;&nbsp;&nbsp; 原谅我的教科书式说教，文章后我会提供我所查阅的比较有用的关于heap有用的资料。 </font>
<p><font size=2><strong>What Can It Do?</strong><br>&nbsp;&nbsp;&nbsp; libevent中的min_heap其实不算做真正的MIN heap。它的节点值其实是一个时间值。libevent总是保证时间最晚的节<br>点为根节点。<br>&nbsp;&nbsp;&nbsp; libevent用这个数据结构来实现IO事件的超时控制。当某个事件(libevent中的struct event)被添加时(event_add)，<br>libevent将此事件按照其超时时间(由用户设置)保存在min_heap里。然后libevent会定期地去检查这个min_heap，从而实现<br>了超时机制。 </font>
<p><font size=2><strong>实现</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; min_heap相关源码主要集中在min_heap.h以及超时相关的event.c中。<br>&nbsp;&nbsp;&nbsp; 首先看下min_heap的结构体定义：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">typedef&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;min_heap<br><img id=Codehighlighter1_24_67_Open_Image onclick="this.style.display='none'; Codehighlighter1_24_67_Open_Text.style.display='none'; Codehighlighter1_24_67_Closed_Image.style.display='inline'; Codehighlighter1_24_67_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_24_67_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_24_67_Closed_Text.style.display='none'; Codehighlighter1_24_67_Open_Image.style.display='inline'; Codehighlighter1_24_67_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_24_67_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_24_67_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">**</span><span style="COLOR: #000000">&nbsp;p;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;n,&nbsp;a;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">&nbsp;min_heap_t;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>&nbsp;&nbsp;&nbsp; p指向了一个动态分配的数组(随便你怎么说，反正它是一个由realloc分配的连续内存空间)，数组元素为event*，这也是<br>heap中的节点类型。这里libevent使用连续空间去保存heap，也就是保存一棵树。因为heap是完成树，所以可以保证其元素在<br>数组中是连续的。n表示目前保存了多少个元素，a表示p指向的内存的尺寸。<br>&nbsp;&nbsp;&nbsp; struct event这个结构体定义了很多东西，但是我们只关注两个成员：min_heap_idx：表示该event保存在min_heap数组中<br>的索引，初始为-1；ev_timeout：该event的超时时间，将被用于heap操作中的节点值比较。 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 接下来看几个与堆操作不大相关的函数：<br>&nbsp;&nbsp;&nbsp; min_heap_elem_greater：比较两个event的超时值大小。<br>&nbsp;&nbsp;&nbsp; min_heap_size：返回heap元素值数量。<br>&nbsp;&nbsp;&nbsp; min_heap_reserve：调整内存空间大小，也就是调整p指向的内存区域大小。凡是涉及到内存大小调整的，都有一个策略问<br>题，这里采用的是初始大小为8，每次增大空间时以2倍的速度增加。<br>&nbsp;&nbsp;&nbsp; 看几个核心的：真正涉及到heap数据结构操作的函数：往堆里插入元素、从堆里取出元素：<br>&nbsp;&nbsp;&nbsp; 相关函数为：min_heap_push、min_heap_pop、min_heap_erase、min_heap_shift_up_、min_heap_shift_down_。 </font>
<p><font size=2><strong>heap的核心操作：</strong> </font>
<p><font size=2><strong>- 往堆里插入元素：</strong><br>&nbsp;&nbsp;&nbsp; 往堆里插入元素相对而言比较简单，如图所示，每一次插入时都从树的最右最下(也就是叶子节点)开始。然后比较即将插入<br>的节点值与该节点的父亲节点的值，如果小于父亲节点的话(不用在意这里的比较规则，上下文一致即可)，那么交换两个节点，<br>将新的父亲节点与其新的父亲节点继续比较。重复这个过程，直到比较到根节点。</font></p>
<p><font size=2>&nbsp; <a href="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/libeventmin_heap_E003/heap_add_2.jpg"><img style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height=174 alt=heap_add src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/libeventmin_heap_E003/heap_add_thumb.jpg" width=210 border=0></a> <br>&nbsp;&nbsp;&nbsp; libevent实现这个过程的函数主要是min_heap_shift_up_。每一次min_heap_push时，首先检查存储空间是否足够，然后直接<br>调用min_heap_shift_up_插入。主要代码如下：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;min_heap_shift_up_(min_heap_t</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;s,&nbsp;unsigned&nbsp;hole_index,&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;e)<br><img id=Codehighlighter1_77_440_Open_Image onclick="this.style.display='none'; Codehighlighter1_77_440_Open_Text.style.display='none'; Codehighlighter1_77_440_Closed_Image.style.display='inline'; Codehighlighter1_77_440_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_77_440_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_77_440_Closed_Text.style.display='none'; Codehighlighter1_77_440_Open_Image.style.display='inline'; Codehighlighter1_77_440_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_77_440_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_77_440_Open_Text><span style="COLOR: #000000">{<br><img id=Codehighlighter1_80_90_Open_Image onclick="this.style.display='none'; Codehighlighter1_80_90_Open_Text.style.display='none'; Codehighlighter1_80_90_Closed_Image.style.display='inline'; Codehighlighter1_80_90_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_80_90_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_80_90_Closed_Text.style.display='none'; Codehighlighter1_80_90_Open_Image.style.display='inline'; Codehighlighter1_80_90_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_80_90_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_80_90_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;获取父节点&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;parent&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(hole_index&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">;<br><img id=Codehighlighter1_137_156_Open_Image onclick="this.style.display='none'; Codehighlighter1_137_156_Open_Text.style.display='none'; Codehighlighter1_137_156_Closed_Image.style.display='inline'; Codehighlighter1_137_156_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_137_156_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_137_156_Closed_Text.style.display='none'; Codehighlighter1_137_156_Open_Image.style.display='inline'; Codehighlighter1_137_156_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_137_156_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_137_156_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;只要父节点还大于子节点就循环&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">(hole_index&nbsp;</span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000">&nbsp;min_heap_elem_greater(s</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">p[parent],&nbsp;e))<br><img id=Codehighlighter1_226_383_Open_Image onclick="this.style.display='none'; Codehighlighter1_226_383_Open_Text.style.display='none'; Codehighlighter1_226_383_Closed_Image.style.display='inline'; Codehighlighter1_226_383_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_226_383_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_226_383_Closed_Text.style.display='none'; Codehighlighter1_226_383_Open_Image.style.display='inline'; Codehighlighter1_226_383_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_226_383_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_226_383_Open_Text><span style="COLOR: #000000">{<br><img id=Codehighlighter1_230_239_Open_Image onclick="this.style.display='none'; Codehighlighter1_230_239_Open_Text.style.display='none'; Codehighlighter1_230_239_Closed_Image.style.display='inline'; Codehighlighter1_230_239_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_230_239_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_230_239_Closed_Text.style.display='none'; Codehighlighter1_230_239_Open_Image.style.display='inline'; Codehighlighter1_230_239_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_230_239_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_230_239_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;交换位置&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(s</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">p[hole_index]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;s</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">p[parent])</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">min_heap_idx&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;hole_index;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hole_index&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;parent;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parent&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(hole_index&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;(s</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">p[hole_index]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;e)</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">min_heap_idx&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;hole_index;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span></div>
<p></span><br></p>
<p></font><font size=2><strong><br>- 从堆里取元素：</strong><br>&nbsp;&nbsp;&nbsp; 大部分时候，从堆里取元素只局限于取根节点，因为这个节点是最有用的。对于数组存储结构而言，数组第一个元素即为根<br>节点。取完元素后，我们还需要重新调整整棵树以使其依然为一个heap。<br>&nbsp;&nbsp;&nbsp; 这需要保证两点：1.依然是完成树；2.父亲节点依然小于孩子节点。<br>&nbsp;&nbsp;&nbsp; 在具体实现heap的取元素操作时，具体到代码层次，方法都是有那么点微小差别的。libvent里的操作过程大致如图所示（实际上libevent中父节点的时间值小于子节点的时间值，时间值的比较通过evutil_timercmp实现）：</font></p>
<p><font size=2><a href="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/libeventmin_heap_E003/heap_remove.jpg"><img style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height=384 alt=heap_remove src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/libeventmin_heap_E003/heap_remove_thumb.jpg" width=294 border=0></a> <br>&nbsp;&nbsp;&nbsp; 主要过程分为两步：<br>&nbsp;&nbsp;&nbsp; 1.比较左右孩子节点，选择最大的节点，移到父亲节点上；按照这种方式处理被选择的孩子节点，直到没有孩子节点为止。例如，<br>&nbsp;&nbsp;&nbsp; 当移除了100这个节点后，我们在100的孩子节点19和36两个节点里选择较大节点，即36，将36放置到100处；然后选择原来的36的<br>左右孩子25和1，选25放置于原来的36处；<br>&nbsp;&nbsp;&nbsp; 2.按照以上方式处理后，树会出现一个空缺，例如原先的25节点，因为被移动到原先的36处，25处就空缺了。因此，为了保证完 <br>成性，就将最右最下的叶子节点(也就是连续存储结构中最后一个元素)，例如这里的7，移动到空缺处，然后按照插入元素的方式处理<br>新插入的节点7。<br>&nbsp;&nbsp;&nbsp; 完成整个过程。 </font></p>
<p><font size=2>&nbsp;&nbsp;&nbsp; libevent完成这个过程的函数主要是min_heap_shift_down_： <br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img id=Codehighlighter1_0_36_Open_Image onclick="this.style.display='none'; Codehighlighter1_0_36_Open_Text.style.display='none'; Codehighlighter1_0_36_Closed_Image.style.display='inline'; Codehighlighter1_0_36_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_0_36_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_0_36_Closed_Text.style.display='none'; Codehighlighter1_0_36_Open_Image.style.display='inline'; Codehighlighter1_0_36_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><span id=Codehighlighter1_0_36_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_0_36_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;hole_index&nbsp;为取出的元素的位置，e为最右最下的元素值&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;min_heap_shift_down_(min_heap_t</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;s,&nbsp;unsigned&nbsp;hole_index,&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;e)<br><img id=Codehighlighter1_117_764_Open_Image onclick="this.style.display='none'; Codehighlighter1_117_764_Open_Text.style.display='none'; Codehighlighter1_117_764_Closed_Image.style.display='inline'; Codehighlighter1_117_764_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_117_764_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_117_764_Closed_Text.style.display='none'; Codehighlighter1_117_764_Open_Image.style.display='inline'; Codehighlighter1_117_764_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_117_764_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_117_764_Open_Text><span style="COLOR: #000000">{<br><img id=Codehighlighter1_123_148_Open_Image onclick="this.style.display='none'; Codehighlighter1_123_148_Open_Text.style.display='none'; Codehighlighter1_123_148_Closed_Image.style.display='inline'; Codehighlighter1_123_148_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_123_148_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_123_148_Closed_Text.style.display='none'; Codehighlighter1_123_148_Open_Image.style.display='inline'; Codehighlighter1_123_148_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_123_148_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_123_148_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;取得hole_index的右孩子节点索引&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;min_child&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;(hole_index&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">(min_child&nbsp;</span><span style="COLOR: #000000">&lt;=</span><span style="COLOR: #000000">&nbsp;s</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">n)<br><img id=Codehighlighter1_230_687_Open_Image onclick="this.style.display='none'; Codehighlighter1_230_687_Open_Text.style.display='none'; Codehighlighter1_230_687_Closed_Image.style.display='inline'; Codehighlighter1_230_687_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_230_687_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_230_687_Closed_Text.style.display='none'; Codehighlighter1_230_687_Open_Image.style.display='inline'; Codehighlighter1_230_687_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_230_687_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_230_687_Open_Text><span style="COLOR: #000000">{<br><img id=Codehighlighter1_240_277_Open_Image onclick="this.style.display='none'; Codehighlighter1_240_277_Open_Text.style.display='none'; Codehighlighter1_240_277_Closed_Image.style.display='inline'; Codehighlighter1_240_277_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_240_277_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_240_277_Closed_Text.style.display='none'; Codehighlighter1_240_277_Open_Image.style.display='inline'; Codehighlighter1_240_277_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_240_277_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_240_277_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;有点恶心的一个表达式，目的就是取两个孩子节点中较大的那个孩子索引&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;min_child&nbsp;</span><span style="COLOR: #000000">-=</span><span style="COLOR: #000000">&nbsp;min_child&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;s</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">n&nbsp;</span><span style="COLOR: #000000">||</span><span style="COLOR: #000000">&nbsp;min_heap_elem_greater(s</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">p[min_child],&nbsp;s</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">p[min_child&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">]);<br><img id=Codehighlighter1_390_419_Open_Image onclick="this.style.display='none'; Codehighlighter1_390_419_Open_Text.style.display='none'; Codehighlighter1_390_419_Closed_Image.style.display='inline'; Codehighlighter1_390_419_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_390_419_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_390_419_Closed_Text.style.display='none'; Codehighlighter1_390_419_Open_Image.style.display='inline'; Codehighlighter1_390_419_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_390_419_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_390_419_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;找到了位置，这里似乎是个优化技巧，不知道具体原理&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">(min_heap_elem_greater(e,&nbsp;s</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">p[min_child])))<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">break</span><span style="COLOR: #000000">;<br><img id=Codehighlighter1_505_513_Open_Image onclick="this.style.display='none'; Codehighlighter1_505_513_Open_Text.style.display='none'; Codehighlighter1_505_513_Closed_Image.style.display='inline'; Codehighlighter1_505_513_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_505_513_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_505_513_Closed_Text.style.display='none'; Codehighlighter1_505_513_Open_Image.style.display='inline'; Codehighlighter1_505_513_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_505_513_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_505_513_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;换位置&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(s</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">p[hole_index]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;s</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">p[min_child])</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">min_heap_idx&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;hole_index;<br><img id=Codehighlighter1_596_607_Open_Image onclick="this.style.display='none'; Codehighlighter1_596_607_Open_Text.style.display='none'; Codehighlighter1_596_607_Closed_Image.style.display='inline'; Codehighlighter1_596_607_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_596_607_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_596_607_Closed_Text.style.display='none'; Codehighlighter1_596_607_Open_Image.style.display='inline'; Codehighlighter1_596_607_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_596_607_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_596_607_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;重复这个过程&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hole_index&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;min_child;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;min_child&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;(hole_index&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img id=Codehighlighter1_693_719_Open_Image onclick="this.style.display='none'; Codehighlighter1_693_719_Open_Text.style.display='none'; Codehighlighter1_693_719_Closed_Image.style.display='inline'; Codehighlighter1_693_719_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_693_719_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_693_719_Closed_Text.style.display='none'; Codehighlighter1_693_719_Open_Image.style.display='inline'; Codehighlighter1_693_719_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_693_719_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_693_719_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;执行第二步过程，将最右最下的节点插到空缺处&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;min_heap_shift_up_(s,&nbsp;hole_index,&nbsp;&nbsp;e);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br></font>
<p><font size=2><strong>STL中的heap</strong> </font>
<p><font size=2>值得一提的是，STL中提供了heap的相关操作算法，借助于模板的泛化特性，其适用范围非常广泛。相关函数为：<br>make_heap, pop_heap, sort_heap, is_heap, sort 。其实现原理同以上算法差不多，相关代码在algorithm里。SGI的<br>STL在stl_heap.h里。 </font>
<p><font size=2><strong>参考资料：</strong></font>
<p><a href="http://www.cprogramming.com/tutorial/computersciencetheory/heap.html" target=_blank>What is a heap?</a>
<p><a href="http://en.wikipedia.org/wiki/Heap_(data_structure)" target=_blank>Heap_(data_structure)</a>
<p><a href="http://www.vias.org/cppcourse/chap22_07.html" target=_blank>Heap Remove</a> </p>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/56511.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-07-18 15:56 <a href="http://www.cppblog.com/kevinlynx/archive/2008/07/18/56511.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>libevent 源码分析：evbuffer缓冲(附带libevent vs2005完整包下载)</title><link>http://www.cppblog.com/kevinlynx/archive/2008/07/16/56291.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Wed, 16 Jul 2008 06:28:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/07/16/56291.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/56291.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/07/16/56291.html#Feedback</comments><slash:comments>14</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/56291.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/56291.html</trackback:ping><description><![CDATA[<p><font size=2>Author : Kevin Lynx</font>
<p><font size=2><strong>前言</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 可以说对于任何网络库(模块)而言，一个缓冲模块都是必不可少的。缓冲模块主要用于缓冲从网络接收到的数据，以及<br>用户提交的数据(用于发送)。很多时候，我们还需要将网络模块层(非TCP层)的这些缓冲数据拷贝到用户层，而这些内存拷贝<br>都会消耗时间。<br>&nbsp;&nbsp;&nbsp; 在这里，我简要分析下libevent的相关代码(event.h和buffer.c)。 </font>
<p><font size=2><strong>结构</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 关于libevent的缓冲模块，主要就是围绕evbuffer结构体展开。先看下evbuffer的定义：<br></font>
<p><font size=2></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img id=Codehighlighter1_0_10_Open_Image onclick="this.style.display='none'; Codehighlighter1_0_10_Open_Text.style.display='none'; Codehighlighter1_0_10_Closed_Image.style.display='inline'; Codehighlighter1_0_10_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_0_10_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_0_10_Closed_Text.style.display='none'; Codehighlighter1_0_10_Open_Image.style.display='inline'; Codehighlighter1_0_10_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><span id=Codehighlighter1_0_10_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_0_10_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">event.h</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img id=Codehighlighter1_28_213_Open_Image onclick="this.style.display='none'; Codehighlighter1_28_213_Open_Text.style.display='none'; Codehighlighter1_28_213_Closed_Image.style.display='inline'; Codehighlighter1_28_213_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_28_213_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_28_213_Closed_Text.style.display='none'; Codehighlighter1_28_213_Open_Image.style.display='inline'; Codehighlighter1_28_213_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;evbuffer&nbsp;</span><span id=Codehighlighter1_28_213_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_28_213_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;u_char&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">buffer;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;u_char&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">orig_buffer;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;size_t&nbsp;misalign;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;size_t&nbsp;totallen;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;size_t&nbsp;off;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">cb)(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;evbuffer&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">,&nbsp;size_t,&nbsp;size_t,&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">cbarg;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>&nbsp;&nbsp;&nbsp; libevent的缓冲是一个连续的内存区域，其处理数据的方式(写数据和读数据)更像一个队列操作方式：从后写入，从前<br>读出。evbuffer分别设置相关指针(一个指标)用于指示读出位置和写入位置。其大致结构如图：</font></p>
<p><a href="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/libeventevbufferlibevent2005_CB59/evbuffer_str.jpg"><font size=2><img style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height=140 alt=evbuffer_str src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/libeventevbufferlibevent2005_CB59/evbuffer_str_thumb.jpg" width=290 border=0></font></a><font size=2> <br>&nbsp;&nbsp;&nbsp; orig_buffer指向由realloc分配的连续内存区域，buffer指向有效数据的内存区域，totallen表示orig_buffer指向的内存<br>区域的大小，misalign表示buffer相对于orig_buffer的偏移，off表示有效数据的长度。 </font></p>
<p><font size=2><strong>实际运作</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 这里我将结合具体的代码分析libevent是如何操作上面那个队列式的evbuffer的，先看一些辅助函数： </font>
<p><font size=2>evbuffer_drain:<br>&nbsp;&nbsp;&nbsp; 该函数主要操作一些指标，当每次从evbuffer里读取数据时，libevent便会将buffer指针后移，同时增大misalign，减小off，<br>而该函数正是做这件事的。说白了，该函数就是用于调整缓冲队列的前向指标。 </font>
<p><font size=2>evbuffer_expand:<br>&nbsp;&nbsp;&nbsp; 该函数用于扩充evbuffer的容量。每次向evbuffer写数据时，都是将数据写到buffer+off后，buffer到buffer+off之间已被<br>使用，保存的是有效数据，而orig_buffer和buffer之间则是因为读取数据移动指标而形成的无效区域。<br>&nbsp;&nbsp;&nbsp; evbuffer_expand的扩充策略在于，首先判断如果让出orig_buffer和buffer之间的空闲区域是否可以容纳添加的数据，如果<br>可以，则移动buffer和buffer+off之间的数据到orig_buffer和orig_buffer+off之间(有可能发生内存重叠，所以这里移动调用的<br>是memmove)，然后把新的数据拷贝到orig_buffer+off之后；如果不可以容纳，那么重新分配更大的空间(realloc)，同样会移动<br>数据。<br>&nbsp;&nbsp;&nbsp; 扩充内存的策略为：确保新的内存区域最小尺寸为256，且以乘以2的方式逐步扩大(256、512、1024、...)。 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 了解了以上两个函数，看其他函数就比较简单了。可以看看具体的读数据和写数据： </font>
<p><font size=2>evbuffer_add:<br>&nbsp;&nbsp;&nbsp; 该函数用于添加一段用户数据到evbuffer中。很简单，就是先判断是否有足够的空闲内存，如果没有则调用evbuffer_expand<br>扩充之，然后直接memcpy，更新off指标。 </font>
<p><font size=2>evbuffer_remove:<br>&nbsp;&nbsp;&nbsp; 该函数用于将evbuffer中的数据复制给用户空间(读数据)。简单地将数据memcpy，然后调用evbuffer_drain移动相关指标。 </font>
<p><font size=2><strong>其他</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 回过头看看libevent的evbuffer其实是非常简单的(跟我那个kl_net里的buffer一样)，不知道其他人有没有更优的缓冲管理<br>方案。evbuffer还提供了两个函数：evbuffer_write和evbuffer_read，用于直接在套接字(其他文件描述符)上写/读数据。 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 另外，关于libevent，因为官方提供的VC工程文件有问题，很多人在windows下编译不过。金庆曾提供过一种方法。其实主要<br>就是修改event-config.h文件，修改编译相关配置。这里我也提供一个解决步骤，顺便提供完整包下载： </font>
<p><font size=2>1. vs2005打开libevent.dsw，转换四个工程(event_test, libevent, signal_test, time_test)<br>2. 删除libevent项目中所有的文件，重新添加文件，文件列表如下：<br>&nbsp;&nbsp; buffer.c<br>&nbsp;&nbsp; evbuffer.c<br>&nbsp;&nbsp; evdns.c<br>&nbsp;&nbsp; evdns.h<br>&nbsp;&nbsp; event.c<br>&nbsp;&nbsp; event.h<br>&nbsp;&nbsp; event_tagging.c<br>&nbsp;&nbsp; event-config.h<br>&nbsp;&nbsp; event-internal.h<br>&nbsp;&nbsp; evhttp.h<br>&nbsp;&nbsp; evrpc.h<br>&nbsp;&nbsp; evrpc.c<br>&nbsp;&nbsp; evrpc-internal.h<br>&nbsp;&nbsp; evsignal.h<br>&nbsp;&nbsp; evutil.c<br>&nbsp;&nbsp; evutil.h<br>&nbsp;&nbsp; http.c<br>&nbsp;&nbsp; http-internal.h<br>&nbsp;&nbsp; log.c<br>&nbsp;&nbsp; log.h<br>&nbsp;&nbsp; min_heap.h<br>&nbsp;&nbsp; strlcpy.c<br>&nbsp;&nbsp; strlcpy-internal.h<br>&nbsp;&nbsp; tree.h<br>&nbsp;&nbsp; win32.c<br>&nbsp;&nbsp; config.h<br>&nbsp;&nbsp; signal.c<br>3. 替换event-config.h，使用libevent-iocp中的<br>4. 项目设置里添加HAVE_CONFIG_H预处理宏<br>5. 修改win32.c中win32_init函数，加入WSAStartup函数，类似于：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WSADATA wd;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int err;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct win32op *winop;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; size_t size;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if( ( err = WSAStartup( MAKEWORD( 2, 2 ), &amp;wd ) ) != 0 )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; event_err( 1, "winsock startup failed : %d", err );<br>6. 修改win32.c中win32_dealloc函数，在函数末尾加上WSACleanup的调用：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WSACleanup();<br>6. 至此libevent编译成功；<br>7. 几个例子程序，只需要加入HAVE_CONFIG_H预处理宏，以及连接ws2_32.lib即可；<br>&nbsp;&nbsp; (time_test需要修改time-test.c文件，即在包含event.h前包含windows.h)</font>
<p><a href="http://www.cppblog.com/Files/kevinlynx/libevent-1.4.5-stable-vs2005.zip" target=_blank><font size=2>libevent-1.4.5-stable-vs2005.zip下载</font></a>
<p><font size=2></font>&nbsp;
<p><font size=2></font></p>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/56291.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-07-16 14:28 <a href="http://www.cppblog.com/kevinlynx/archive/2008/07/16/56291.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>IOCP与线程</title><link>http://www.cppblog.com/kevinlynx/archive/2008/06/23/54390.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Mon, 23 Jun 2008 09:32:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/06/23/54390.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/54390.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/06/23/54390.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/54390.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/54390.html</trackback:ping><description><![CDATA[<p><font size="2">author : Kevin Lynx</font> <p><strong><font size="1"></font></strong>&nbsp; <p><strong>什么是完成包？</strong>  <p><font size="2">完成包，即IO Completion Packet，是指异步IO操作完毕后OS提交给应用层的通知包。IOCP维护了一个IO操作结果队列，里面<br>保存着各种完成包。应用层调用GQCS(也就是GetQueueCompletionStatus)函数获取这些完成包。 </font> <p><font size="2"><strong>最大并发线程数</strong> </font> <p><font size="2">在一个典型的IOCP程序里，会有一些线程调用GQCS去获取IO操作结果。最大并发线程数指定在同一时刻处理完成包的线程数目。<br>该参数在调用CreateIoCompletionPort时由NumberOfConcurrentThreads指定。 </font> <p><font size="2"><strong>工作者线程</strong> </font> <p><font size="2">工作者线程一般指的就是调用GQCS函数的线程。要注意的是，工作者线程数和最大并发线程数并不是同一回事(见下文)。工作者<br>线程由应用层显示创建(_beginthreadex 之类)。工作者线程通常是一个循环，会不断地GQCS到完成包，然后处理完成包。 </font> <p><font size="2"><strong>调度过程</strong> </font> <p><font size="2">工作者线程以是否阻塞分为两种状态：运行状态和等待状态。当线程做一些阻塞操作时(线程同步，甚至GQCS空的完成队列)，线程<br>处于等待状态；否则，线程处于运行状态。 </font> <p><font size="2">另一方面，OS会始终保持某一时刻处于运行状态的线程数小于最大并发线程数。每一个调用GQCS函数的线程OS实际上都会进行记录，<br>当完成队列里有完成包时，OS会首先检查当前处于运行状态的工作线程数是否小于最大并发线程数，如果小于，OS会按照LIFO的顺<br>序让某个工作者线程从GQCS返回(此工作者线程转换为运行状态)。如何决定这个LIFO？这是简单地通过调用GQCS函数的顺序决定的。 </font> <p><font size="2">从这里可以看出，这里涉及到线程唤醒和睡眠的操作。如果两个线程被放置于同一个CPU上，就会有线程切换的开销。因此，为了消<br>除这个开销，最大并发线程数被建议为设置成CPU数量。 </font> <p><font size="2">从以上调度过程还可以看出，如果某个处于运行状态的工作者线程在处理完成包时阻塞了(例如线程同步、其他IO操作)，那么就有<br>CPU资源处于空闲状态。因此，我们也看到很多文档里建议，工作者线程数为(CPU数*2+2)。 </font> <p><font size="2">在一个等待线程转换到运行状态时，有可能会出现短暂的时间运行线程数超过最大并发线程数，这个时候OS会迅速地让这个新转换<br>的线程阻塞，从而减少这个数量。(关于这个观点，MSDN上只说：by not allowing any new active threads，却没说明not allowing<br>what) </font> <p><font size="2"><strong>调度原理</strong> </font> <p><font size="2">这个知道了其实没什么意义，都是内核做的事，大致上都是操作线程control block，直接摘录&lt;Inside IO Completion Ports&gt;: </font> <p><font size="2">The list of threads hangs off the queue object. A thread's control block data structure has a pointer in it that <br>references the queue object of a queue that it is associated with; if the pointer is NULL then the thread is not <br>associated with a queue. </font> <p><font size="2">So how does NT keep track of threads that become inactive because they block on something other than the completion<br>port" The answer lies in the queue pointer in a thread's control block. The scheduler routines that are executed <br>in response to a thread blocking (KeWaitForSingleObject, KeDelayExecutionThread, etc.) check the thread's queue <br>pointer and if its not NULL they will call KiActivateWaiterQueue, a queue-related function. KiActivateWaiterQueue<br> decrements the count of active threads associated with the queue, and if the result is less than the maximum and <br>there is at least one completion packet in the queue then the thread at the front of the queue's thread list is <br>woken and given the oldest packet. Conversely, whenever a thread that is associated with a queue wakes up after <br>blocking the scheduler executes the function KiUnwaitThread, which increments the queue's active count. </font> <p><font size="2"><strong>参考资料</strong> </font> <p><font size="2">&lt;Inside I/O Completion Ports&gt;:<br></font><a href="http://technet.microsoft.com/en-us/sysinternals/bb963891.aspx"><font size="2">http://technet.microsoft.com/en-us/sysinternals/bb963891.aspx</font></a><br><font size="2">&lt;I/O Completion Ports&gt;:<br></font><a href="http://msdn.microsoft.com/en-us/library/aa365198(VS.85).aspx"><font size="2">http://msdn.microsoft.com/en-us/library/aa365198(VS.85).aspx</font></a><br><font size="2">&lt;INFO: Design Issues When Using IOCP in a Winsock Server&gt;:<br></font><a href="http://support.microsoft.com/kb/192800/en-us/"><font size="2">http://support.microsoft.com/kb/192800/en-us/</font></a></p><img src ="http://www.cppblog.com/kevinlynx/aggbug/54390.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-06-23 17:32 <a href="http://www.cppblog.com/kevinlynx/archive/2008/06/23/54390.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Proactor和Reactor模式_继续并发系统设计的扫盲</title><link>http://www.cppblog.com/kevinlynx/archive/2008/06/06/52356.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Fri, 06 Jun 2008 05:25:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/06/06/52356.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/52356.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/06/06/52356.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/52356.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/52356.html</trackback:ping><description><![CDATA[<p><font size=2>6.6.2008 </font>
<p><font size=2>Kevin Lynx </font>
<p><font size=2>Proactor和<a href="http://en.wikipedia.org/wiki/Reactor_pattern" target=_blank>Reactor</a>都是并发编程中的设计模式。在我看来，他们都是用于派发/分离IO操作事件的。这里所谓的<br>IO事件也就是诸如read/write的IO操作。"派发/分离"就是将单独的IO事件通知到上层模块。两个模式不同的地方<br>在于，Proactor用于异步IO，而Reactor用于同步IO。 </font>
<p><font size=2>摘抄一些关键的东西： </font>
<p><font size=2>"<br>Two patterns that involve event demultiplexors are called Reactor and Proactor [1]. The Reactor patterns <br>involve synchronous I/O, whereas the Proactor pattern involves asynchronous I/O.<br>" </font>
<p><font size=2>关于两个模式的大致模型，从以下文字基本可以明白： </font>
<p><font size=2>"<br>An example will help you understand the difference between Reactor and Proactor. We will focus on the read <br>operation here, as the write implementation is similar. Here's a read in Reactor: </font>
<p><font size=2>* An event handler declares interest in I/O events that indicate readiness for read on a particular socket ;<br>* The event demultiplexor waits for events ;<br>* An event comes in and wakes-up the demultiplexor, and the demultiplexor calls the appropriate handler; <br>* The event handler performs the actual read operation, handles the data read, declares renewed interest in <br>&nbsp; I/O events, and returns control to the dispatcher . </font>
<p><font size=2>By comparison, here is a read operation in Proactor (true async): </font>
<p><font size=2>* A handler initiates an asynchronous read operation (note: the OS must support asynchronous I/O). In this <br>&nbsp; case, the handler does not care about I/O readiness events, but is instead registers interest in receiving <br>&nbsp; completion events;<br>* The event demultiplexor waits until the operation is completed ;<br>* While the event demultiplexor waits, the OS executes the read operation in a parallel kernel thread, puts <br>&nbsp; data into a user-defined buffer, and notifies the event demultiplexor that the read is complete ;<br>* The event demultiplexor calls the appropriate handler; <br>* The event handler handles the data from user defined buffer, starts a new asynchronous operation, and returns<br>&nbsp; control to the event demultiplexor. </font>
<p><font size=2>" </font>
<p><font size=2>可以看出，两个模式的相同点，都是对某个IO事件的事件通知(即告诉某个模块，这个IO操作可以进行或已经完成)。在结构<br>上，两者也有相同点：demultiplexor负责提交IO操作(异步)、查询设备是否可操作(同步)，然后当条件满足时，就回调handler。<br>不同点在于，异步情况下(Proactor)，当回调handler时，表示IO操作已经完成；同步情况下(Reactor)，回调handler时，表示<br>IO设备可以进行某个操作(can read or can write)，handler这个时候开始提交操作。 </font>
<p><font size=2>用select模型写个简单的reactor，大致为： </font>
<p><font size=2></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img id=Codehighlighter1_0_3_Open_Image onclick="this.style.display='none'; Codehighlighter1_0_3_Open_Text.style.display='none'; Codehighlighter1_0_3_Closed_Image.style.display='inline'; Codehighlighter1_0_3_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_0_3_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_0_3_Closed_Text.style.display='none'; Codehighlighter1_0_3_Open_Image.style.display='inline'; Codehighlighter1_0_3_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><span id=Codehighlighter1_0_3_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_0_3_Open_Text><span style="COLOR: #808080">///</span></span><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;handler<br><img id=Codehighlighter1_18_124_Open_Image onclick="this.style.display='none'; Codehighlighter1_18_124_Open_Text.style.display='none'; Codehighlighter1_18_124_Closed_Image.style.display='inline'; Codehighlighter1_18_124_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_18_124_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_18_124_Closed_Text.style.display='none'; Codehighlighter1_18_124_Open_Image.style.display='inline'; Codehighlighter1_18_124_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_18_124_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_18_124_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;onRead()&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;onWrite()&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;onAccept()&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;dispatch<br><img id=Codehighlighter1_144_804_Open_Image onclick="this.style.display='none'; Codehighlighter1_144_804_Open_Text.style.display='none'; Codehighlighter1_144_804_Closed_Image.style.display='inline'; Codehighlighter1_144_804_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_144_804_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_144_804_Closed_Text.style.display='none'; Codehighlighter1_144_804_Open_Image.style.display='inline'; Codehighlighter1_144_804_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_144_804_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_144_804_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;poll()<br><img id=Codehighlighter1_174_696_Open_Image onclick="this.style.display='none'; Codehighlighter1_174_696_Open_Text.style.display='none'; Codehighlighter1_174_696_Closed_Image.style.display='inline'; Codehighlighter1_174_696_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_174_696_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_174_696_Closed_Text.style.display='none'; Codehighlighter1_174_696_Open_Image.style.display='inline'; Codehighlighter1_174_696_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_174_696_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_174_696_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;add&nbsp;fd&nbsp;in&nbsp;the&nbsp;set.<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;poll&nbsp;every&nbsp;fd</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;c&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;select(&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">read_fd,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">write_fd,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;c&nbsp;</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;)<br><img id=Codehighlighter1_329_690_Open_Image onclick="this.style.display='none'; Codehighlighter1_329_690_Open_Text.style.display='none'; Codehighlighter1_329_690_Closed_Image.style.display='inline'; Codehighlighter1_329_690_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_329_690_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_329_690_Closed_Text.style.display='none'; Codehighlighter1_329_690_Open_Image.style.display='inline'; Codehighlighter1_329_690_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_329_690_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_329_690_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">&nbsp;each&nbsp;fd&nbsp;</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">&nbsp;the&nbsp;read_fd_set<br><img id=Codehighlighter1_386_533_Open_Image onclick="this.style.display='none'; Codehighlighter1_386_533_Open_Text.style.display='none'; Codehighlighter1_386_533_Closed_Image.style.display='inline'; Codehighlighter1_386_533_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_386_533_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_386_533_Closed_Text.style.display='none'; Codehighlighter1_386_533_Open_Image.style.display='inline'; Codehighlighter1_386_533_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_386_533_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_386_533_Open_Text><span style="COLOR: #000000">{&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;fd&nbsp;can&nbsp;read<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_handler</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">onRead();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;fd&nbsp;can&nbsp;accept<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_handler</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">onAccept();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">&nbsp;each&nbsp;fd&nbsp;</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">&nbsp;the&nbsp;write_fd_set<br><img id=Codehighlighter1_593_680_Open_Image onclick="this.style.display='none'; Codehighlighter1_593_680_Open_Text.style.display='none'; Codehighlighter1_593_680_Closed_Image.style.display='inline'; Codehighlighter1_593_680_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_593_680_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_593_680_Closed_Text.style.display='none'; Codehighlighter1_593_680_Open_Image.style.display='inline'; Codehighlighter1_593_680_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_593_680_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_593_680_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;fd&nbsp;can&nbsp;write<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_handler</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">onWrite();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;setHandler(&nbsp;handler&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">_h&nbsp;)<br><img id=Codehighlighter1_739_768_Open_Image onclick="this.style.display='none'; Codehighlighter1_739_768_Open_Text.style.display='none'; Codehighlighter1_739_768_Closed_Image.style.display='inline'; Codehighlighter1_739_768_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_739_768_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_739_768_Closed_Text.style.display='none'; Codehighlighter1_739_768_Open_Image.style.display='inline'; Codehighlighter1_739_768_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_739_768_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_739_768_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_handler&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;_h;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;handler&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">_handler;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img id=Codehighlighter1_809_824_Open_Image onclick="this.style.display='none'; Codehighlighter1_809_824_Open_Text.style.display='none'; Codehighlighter1_809_824_Closed_Image.style.display='inline'; Codehighlighter1_809_824_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_809_824_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_809_824_Closed_Text.style.display='none'; Codehighlighter1_809_824_Open_Image.style.display='inline'; Codehighlighter1_809_824_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_809_824_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_809_824_Open_Text><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;application</span><span style="COLOR: #808080"></span></span><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;MyHandler&nbsp;:&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;handler<br><img id=Codehighlighter1_858_965_Open_Image onclick="this.style.display='none'; Codehighlighter1_858_965_Open_Text.style.display='none'; Codehighlighter1_858_965_Closed_Image.style.display='inline'; Codehighlighter1_858_965_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_858_965_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_858_965_Closed_Text.style.display='none'; Codehighlighter1_858_965_Open_Image.style.display='inline'; Codehighlighter1_858_965_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_858_965_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_858_965_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;onRead()<br><img id=Codehighlighter1_890_896_Open_Image onclick="this.style.display='none'; Codehighlighter1_890_896_Open_Text.style.display='none'; Codehighlighter1_890_896_Closed_Image.style.display='inline'; Codehighlighter1_890_896_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_890_896_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_890_896_Closed_Text.style.display='none'; Codehighlighter1_890_896_Open_Image.style.display='inline'; Codehighlighter1_890_896_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_890_896_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_890_896_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;onWrite()<br><img id=Codehighlighter1_923_929_Open_Image onclick="this.style.display='none'; Codehighlighter1_923_929_Open_Text.style.display='none'; Codehighlighter1_923_929_Closed_Image.style.display='inline'; Codehighlighter1_923_929_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_923_929_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_923_929_Closed_Text.style.display='none'; Codehighlighter1_923_929_Open_Image.style.display='inline'; Codehighlighter1_923_929_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_923_929_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_923_929_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;onAccept()<br><img id=Codehighlighter1_957_963_Open_Image onclick="this.style.display='none'; Codehighlighter1_957_963_Open_Text.style.display='none'; Codehighlighter1_957_963_Closed_Image.style.display='inline'; Codehighlighter1_957_963_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_957_963_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_957_963_Closed_Text.style.display='none'; Codehighlighter1_957_963_Open_Image.style.display='inline'; Codehighlighter1_957_963_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_957_963_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_957_963_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>在网上找了份Proactor模式比较正式的<a href="http://www.cppblog.com/Files/kevinlynx/proactor2.rar" target=_blank>文档</a>，其给出了一个总体的UML类图，比较全面： </font>
<p><a href="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/ProactorReactor__BCB1/proactor_uml.jpg"><img style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height=317 alt=proactor_uml src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/ProactorReactor__BCB1/proactor_uml_thumb.jpg" width=692 border=0></a>
<p><font size=2>根据这份图我随便写了个例子代码： </font>
<p><font size=2></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;AsyIOProcessor<br><img id=Codehighlighter1_21_227_Open_Image onclick="this.style.display='none'; Codehighlighter1_21_227_Open_Text.style.display='none'; Codehighlighter1_21_227_Closed_Image.style.display='inline'; Codehighlighter1_21_227_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_21_227_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_21_227_Closed_Text.style.display='none'; Codehighlighter1_21_227_Open_Image.style.display='inline'; Codehighlighter1_21_227_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_21_227_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_21_227_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;do_read()<br><img id=Codehighlighter1_54_189_Open_Image onclick="this.style.display='none'; Codehighlighter1_54_189_Open_Text.style.display='none'; Codehighlighter1_54_189_Closed_Image.style.display='inline'; Codehighlighter1_54_189_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_54_189_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_54_189_Closed_Text.style.display='none'; Codehighlighter1_54_189_Open_Image.style.display='inline'; Codehighlighter1_54_189_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_54_189_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_54_189_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/dot.gif">send&nbsp;read&nbsp;operation&nbsp;to&nbsp;OS<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;read&nbsp;io&nbsp;finished.and&nbsp;dispatch&nbsp;notification</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_proactor</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">dispatch_read();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Proactor&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">_proactor;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;Proactor<br><img id=Codehighlighter1_247_369_Open_Image onclick="this.style.display='none'; Codehighlighter1_247_369_Open_Text.style.display='none'; Codehighlighter1_247_369_Closed_Image.style.display='inline'; Codehighlighter1_247_369_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_247_369_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_247_369_Closed_Text.style.display='none'; Codehighlighter1_247_369_Open_Image.style.display='inline'; Codehighlighter1_247_369_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_247_369_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_247_369_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;dispatch_read()<br><img id=Codehighlighter1_286_323_Open_Image onclick="this.style.display='none'; Codehighlighter1_286_323_Open_Text.style.display='none'; Codehighlighter1_286_323_Closed_Image.style.display='inline'; Codehighlighter1_286_323_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_286_323_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_286_323_Closed_Text.style.display='none'; Codehighlighter1_286_323_Open_Image.style.display='inline'; Codehighlighter1_286_323_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_286_323_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_286_323_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_handlerMgr</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">onRead();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;HandlerManager&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">_handlerMgr;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;HandlerManager<br><img id=Codehighlighter1_395_634_Open_Image onclick="this.style.display='none'; Codehighlighter1_395_634_Open_Text.style.display='none'; Codehighlighter1_395_634_Closed_Image.style.display='inline'; Codehighlighter1_395_634_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_395_634_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_395_634_Closed_Text.style.display='none'; Codehighlighter1_395_634_Open_Image.style.display='inline'; Codehighlighter1_395_634_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_395_634_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_395_634_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;std::list</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">Handler</span><span style="COLOR: #000000">*&gt;</span><span style="COLOR: #000000">&nbsp;HandlerList;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;onRead()<br><img id=Codehighlighter1_482_593_Open_Image onclick="this.style.display='none'; Codehighlighter1_482_593_Open_Text.style.display='none'; Codehighlighter1_482_593_Closed_Image.style.display='inline'; Codehighlighter1_482_593_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_482_593_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_482_593_Closed_Text.style.display='none'; Codehighlighter1_482_593_Open_Image.style.display='inline'; Codehighlighter1_482_593_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_482_593_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_482_593_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;notify&nbsp;all&nbsp;the&nbsp;handlers.</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::for_each(&nbsp;_handlers.begin(),&nbsp;_handlers.end(),&nbsp;onRead&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;HandlerList&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">_handlers;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;Handler<br><img id=Codehighlighter1_653_694_Open_Image onclick="this.style.display='none'; Codehighlighter1_653_694_Open_Text.style.display='none'; Codehighlighter1_653_694_Closed_Image.style.display='inline'; Codehighlighter1_653_694_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_653_694_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_653_694_Closed_Text.style.display='none'; Codehighlighter1_653_694_Open_Image.style.display='inline'; Codehighlighter1_653_694_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_653_694_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_653_694_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;onRead()&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;application&nbsp;level&nbsp;handler.</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;MyHandler&nbsp;:&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;Handler<br><img id=Codehighlighter1_762_815_Open_Image onclick="this.style.display='none'; Codehighlighter1_762_815_Open_Text.style.display='none'; Codehighlighter1_762_815_Closed_Image.style.display='inline'; Codehighlighter1_762_815_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_762_815_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_762_815_Closed_Text.style.display='none'; Codehighlighter1_762_815_Open_Image.style.display='inline'; Codehighlighter1_762_815_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_762_815_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_762_815_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;onRead()&nbsp;<br><img id=Codehighlighter1_795_813_Open_Image onclick="this.style.display='none'; Codehighlighter1_795_813_Open_Text.style.display='none'; Codehighlighter1_795_813_Closed_Image.style.display='inline'; Codehighlighter1_795_813_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_795_813_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_795_813_Closed_Text.style.display='none'; Codehighlighter1_795_813_Open_Image.style.display='inline'; Codehighlighter1_795_813_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_795_813_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_795_813_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>Reactor通过某种变形，可以将其改装为Proactor，在某些不支持异步IO的系统上，也可以隐藏底层的实现，利于编写跨平台<br>代码。我们只需要在dispatch(也就是demultiplexor)中封装同步IO操作的代码，在上层，用户提交自己的缓冲区到这一层，<br>这一层检查到设备可操作时，不像原来立即回调handler，而是开始IO操作，然后将操作结果放到用户缓冲区(读)，然后再<br>回调handler。这样，对于上层handler而言，就像是proactor一样。详细技法参见<a href="http://www.artima.com/articles/io_design_patterns2.html" target=_blank>这篇文章</a>。 </font>
<p><font size=2>其实就设计模式而言，我个人觉得某个模式其实是没有完全固定的结构的。不能说某个模式里就肯定会有某个类，类之间的<br>关系就肯定是这样。在实际写程序过程中也很少去特别地实现某个模式，只能说模式会给你更多更好的架构方案。 </font>
<p><font size=2>最近在看spserver的代码，看到别人提各种并发系统中的模式，有点眼红，于是才来扫扫盲。知道什么是<a href="http://www.cppblog.com/kevinlynx/archive/2008/06/04/52127.html" target=_blank>leader follower模式</a>，<br>reactor, proactor，multiplexing，对于心中的那个网络库也越来越清晰。 </font>
<p><font size=2>最近还干了些离谱的事，写了传说中的字节流编码，用模板的方式实现，不但保持了扩展性，还少写很多代码；处于效率考虑，<br>写了个static array容器(其实就是template &lt;typename _Tp, std::size_t size&gt; class static_array { _Tp _con[size])，<br>加了iterator，遵循STL标准，可以结合进STL的各个generic algorithm用，自我感觉不错。基础模块搭建完毕，解析了公司<br>服务器网络模块的消息，我是不是真的打算用自己的网络模块重写我的验证服务器？在另一个给公司写的工具里，因为实在厌恶<br>越来越多的重复代码，索性写了几个宏，还真的做到了代码的自动生成:D。 </font>
<p><font size=2>对优雅代码的追求真的成了种癖好.&nbsp; = =| </font></p>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/52356.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-06-06 13:25 <a href="http://www.cppblog.com/kevinlynx/archive/2008/06/06/52356.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>半同步半异步模式以及Leader_Follwer模式</title><link>http://www.cppblog.com/kevinlynx/archive/2008/06/04/52127.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Wed, 04 Jun 2008 02:40:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/06/04/52127.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/52127.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/06/04/52127.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/52127.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/52127.html</trackback:ping><description><![CDATA[<p><font size="2">这里提到的两个设计模式都是用于高并发系统(例如一个高性能的网络服务器)的。这里我只是简单地提一下： </font> <p><font size="2"><strong>1.</strong><a href="http://www.cppblog.com/Files/kevinlynx/hh_lf.rar" target="_blank"><strong>半同步/半异步（half-sync/half-async</strong></a><strong>）：</strong> </font> <p><font size="2">在网<a href="http://www.javaeye.com/topic/60414" target="_blank">上一份资料</a>中引用了一本貌似很<a href="http://www.china-pub.com/14109" target="_blank">经典的书</a>里的比喻：<br>” <br>许多餐厅使用 半同步/半异步 模式的变体。例如，餐厅常常雇佣一个领班负责迎接顾客，并在餐厅繁忙时留意给顾客安排桌位，<br>为等待就餐的顾客按序排队是必要的。领班由所有顾客“共享”，不能被任何特定顾客占用太多时间。当顾客在一张桌子入坐后，<br>有一个侍应生专门为这张桌子服务。 <br>“ </font> <p><font size="2">按照另一份似乎比较权威的文档的描述，要实现半同步/半异步模式，需要实现三层：异步层、同步层、队列层。因为很多操作<br>采用异步方式会比较有效率(例如高效率的网络模型似乎都采用异步IO)，但是异步操作的复杂度比较高，不利于编程。而同步<br>操作相对之下编程要简单点。为了结合两者的优点，就提出了这个模式。而为了让异步层和同步层互相通信(模块间的通信)，系<br>统需要加入一个通信队列。异步层将操作结果放入队列，同步层从队列里获取操作结果。 </font> <p><font size="2">回过头来看看我之前写的那个select网络模型代码，个人认为基本上算是一个半同步半异步模式的简单例子：Buffer相当于通信<br>队列，网络底层将数据写入Buffer，上层再同步地从该队列里获取出数据。这样看来似乎也没什么难度。 = = </font> <p>关于例子代码，直接引用iunknown给的：  <p>//这就是一个典型的循环队列的定义，iget 是队列头，iput 是队列尾&lt;/STRONG&gt;&nbsp;&nbsp; <br>int clifd[MAXNCLI], iget, iput;&nbsp;&nbsp;&nbsp; <br>int main( int argc, char * argv[] )&nbsp;&nbsp; <br>{&nbsp;&nbsp; <br>&nbsp; ......&nbsp;&nbsp; <br>&nbsp; int listenfd = Tcp_listen( NULL, argv[ 1 ], &amp;addrlen );&nbsp;&nbsp; <br>&nbsp; ......&nbsp;&nbsp; <br>&nbsp; iget = iput = 0;&nbsp;&nbsp; <br>&nbsp; for( int i = 0; i &lt; nthreads; i++ ) {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; pthread_create( &amp;tptr[i].thread_tid, NULL, &amp;thread_main, (void*)i );&nbsp;&nbsp; <br>&nbsp; for( ; ; ) {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; connfd = accept( listenfd, cliaddr,, &amp;clilen );&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; clifd[ iput ] = connfd;&nbsp;&nbsp;&nbsp;&nbsp; // 接受到的连接句柄放入队列&lt;/STRONG&gt;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; if( ++iput == MAXNCLI ) iput = 0;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp; }&nbsp;&nbsp; <br>}&nbsp;&nbsp; <br>void * thread_main( void * arg )&nbsp;&nbsp; <br>{&nbsp;&nbsp; <br>&nbsp; for( ; ; ) {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; while( iget == iput ) pthread_cond_wait( ...... );&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; connfd = clifd[ iget ];&nbsp;&nbsp;&nbsp;&nbsp; // 从队列中获得连接句柄&lt;/STRONG&gt;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; if( ++iget == MAXNCLI ) iget = 0;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; ......&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; web_child( connfd );&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; close( connfd );&nbsp;&nbsp; <br>&nbsp; }&nbsp;&nbsp; <br>}&nbsp; <p><font size="2"><strong>2</strong><a href="http://www.cppblog.com/Files/kevinlynx/hh_lf.rar" target="_blank"><strong>.领导者/追随者（Leader/Followers）：</strong></a> </font> <p><font size="2">同样，给出别人引用的比喻：<br>”<br>在日常生活中，领导者/追随者模式用于管理许多飞机场出租车候车台。在该用例中，出租车扮演“线程”角色，排在第一辆的出<br>租车成为领导者，剩下的出租车成为追随者。同样，到达出租车候车台的乘客构成了必须被多路分解给出租车的事件，一般以先进<br>先出排序。一般来说，如果任何出租车可以为任何顾客服务，该场景就主要相当于非绑定句柄/线程关联。然而，如果仅仅是某些<br>出租车可以为某些乘客服务，该场景就相当于绑定句柄/线程关联。 <br>“ </font> <p><font size="2">其实这个更简单，我记得&lt;unix网络编程&gt;中似乎提到过这个。总之有一种网络模型(connection-per-thread?)里，一个线程用于<br>accept连接。当接收到一个新的连接时，这个线程就转为connection thread，而这个线程后面的线程则上升为accept线程。这里，<br>accept线程就相当于领导者线程，而其他线程则属于追随者线程。 </font> <p>iunknown 的例子代码：  <p>int listenfd;&nbsp;&nbsp; <br>int main( int argc, char * argv[] )&nbsp;&nbsp; <br>{&nbsp;&nbsp; <br>&nbsp; ......&nbsp;&nbsp; <br>&nbsp; listenfd = Tcp_listen( NULL, argv[ 1 ], &amp;addrlen );&nbsp;&nbsp; <br>&nbsp; ......&nbsp;&nbsp; <br>&nbsp; for( int i = 0; i &lt; nthreads; i++ ){&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; pthread_create( &amp;tptr[i].thread_tid, NULL, &amp;thread_main, (void*)i );&nbsp;&nbsp; <br>&nbsp; }&nbsp;&nbsp; <br>&nbsp; ......&nbsp;&nbsp; <br>}&nbsp;&nbsp; <br>void * thread_main( void * arg )&nbsp;&nbsp; <br>{&nbsp;&nbsp; <br>&nbsp; for( ; ; ){&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; ......&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; // 多个线程同时阻塞在这个 accept 调用上，依靠操作系统的队列&lt;/STRONG&gt;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; connfd = accept( listenfd, cliaddr, &amp;clilen );&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; ......&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; web_child( connfd );&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; close( connfd );&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; ......&nbsp;&nbsp; <br>&nbsp; }&nbsp;&nbsp; <br>}&nbsp; <p><font size="2"></font></p><img src ="http://www.cppblog.com/kevinlynx/aggbug/52127.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-06-04 10:40 <a href="http://www.cppblog.com/kevinlynx/archive/2008/06/04/52127.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>写了个简单的聊天服务器</title><link>http://www.cppblog.com/kevinlynx/archive/2008/05/28/51412.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Wed, 28 May 2008 09:13:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/05/28/51412.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/51412.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/05/28/51412.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/51412.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/51412.html</trackback:ping><description><![CDATA[<p><font size=2>没什么技术含量，将select模型做简单的封装，同时提供服务器端和客户端所用的接口。功能实现上对数据的发送和接收<br>都做了缓存，搞得跟异步IO一样 = =#。 </font>
<p><font size=2>这个例子聊天服务器可以使用telnet登录，服务器直接将telnet发来的字符串转发给所有客户端。我稍微写了一个小的网络<br>模块，可以用于以后写网络程序的例子代码，也算是练习下网络库的设计。 </font>
<p><font size=2>系统总体类图如下： </font>
<p><a href="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/d90839f5ba11_F1F6/classdiagram.jpg"><font size=2><img style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height=233 alt=classdiagram src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/d90839f5ba11_F1F6/classdiagram_thumb.jpg" width=454 border=0></font></a><font size=2> </font>
<p><font size=2>Address用于包装sockaddr_in结构体，目的就是让系统用起来更方便。 </font>
<p><font size=2>Buffer用于封装原始内存，主要目的是拿来做发送、接收数据缓冲。 </font>
<p><font size=2>Fdset差不多和FD_SET一样，只是这里自己写一个FD_SET，可以让连接数不受FD_SETSIZE的限制。 </font>
<p><font size=2>Socket封装了基本的SOCKET操作，包括创建、销毁套接字。 </font>
<p><font size=2>Session比较有意思，按我的意思，就是代表一个网络连接。对于服务器端，可能会有很多连接，每一个连接可以用一个<br>Session对象表示。而对于客户端，只有一个连接，那么就是一个Session对象。对于Session对象来说，可以进行数据的<br>发送和接收，因此这里Session有recv、send之类的接口。为了缓冲数据，所以Session对于读写分别有一个Buffer对象。 </font>
<p><font size=2>Server代表一个服务器，直接提供创建服务器的接口。同时使用一个unsigned long作为每一个连接的ID号。 </font>
<p><font size=2>Client代表一个客户端，可以直接用于连接服务器。 </font>
<p><font size=2>下载文件提供网络模块代码，以及三个例子程序。</font><a href="http://www.cppblog.com/Files/kevinlynx/kl_net0.1.2.rar" target=_blank><font size=2>点击下载</font></a></p>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/51412.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-05-28 17:13 <a href="http://www.cppblog.com/kevinlynx/archive/2008/05/28/51412.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>tcp要点学习-数据发送一</title><link>http://www.cppblog.com/kevinlynx/archive/2008/05/22/50770.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Thu, 22 May 2008 07:42:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/05/22/50770.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/50770.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/05/22/50770.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/50770.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/50770.html</trackback:ping><description><![CDATA[<p><font size=2>Author : Kevin Lynx</font></p>
<p><font size=2><strong>1. 什么是delayed ack algorithm</strong><br>&nbsp;&nbsp; delayed ack algorithm也就是&lt;TCP/IP详解&gt;中所谓的"经受时延的确认"(翻译得真饶舌 = =||)。在<a href="http://www.faqs.org/rfcs/rfc1122.html" target=_blank>RFC1122</a>中提到delayed ack<br>&nbsp;&nbsp; 的概念：<br>&nbsp;&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;</span><span style="COLOR: #000000">"<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;host&nbsp;that&nbsp;</span><span style="COLOR: #0000ff">is</span><span style="COLOR: #000000">&nbsp;receiving&nbsp;a&nbsp;stream&nbsp;of&nbsp;TCP&nbsp;data&nbsp;segments&nbsp;can<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;increase&nbsp;efficiency&nbsp;</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">&nbsp;both&nbsp;the&nbsp;Internet&nbsp;and&nbsp;the&nbsp;hosts&nbsp;by<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sending&nbsp;fewer&nbsp;than&nbsp;one&nbsp;ACK&nbsp;(acknowledgment)&nbsp;segment&nbsp;per&nbsp;data<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;segment&nbsp;received;&nbsp;</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">is</span><span style="COLOR: #000000">&nbsp;known&nbsp;</span><span style="COLOR: #0000ff">as</span><span style="COLOR: #000000">&nbsp;a&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">delayed&nbsp;ACK</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;[TCP:</span><span style="COLOR: #000000">5</span><span style="COLOR: #000000">].<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">"</span></div>
<p><br>&nbsp;&nbsp; 我在之前提到过，TCP在收到每一个数据包时，都会发送一个ACK报文给对方，用以告诉对方"我接收到你刚才发送的数据了"。并<br>&nbsp;&nbsp; 且会在报文的确认号字段中标志希望接收到的数据包。 </font></p>
<p><font size=2>&nbsp;&nbsp; 但是，如你所想，如果为每一个接收到的报文都发送一个ACK报文，那将会增加网络的负担。于是，为了解决这个问题，delayed <br>&nbsp;&nbsp; ack被提出。也就是说，实现了delayed ack的TCP，并不见得会对每一个接收到的数据包发送ACK确认报文。 </font>
<p><font size=2>&nbsp;&nbsp; 实际情况是，TCP延迟发送这个ACK。延迟多久？&lt;TCP/IP详解&gt;中说的是200ms，在RFC1122中说的则是500ms。delayed ack有时候<br>&nbsp;&nbsp; 还会附加到数据报文段一起发送，如果在延迟时间内有报文段要发送的话，如果没有，那么当延迟时间到时，就单独发送ACK。 </font>
<p><font size=2>&nbsp;&nbsp; 在<a href="http://tangentsoft.net/wskfaq/intermediate.html#delayed-ack" target=_blank>另一份文档</a>中，作者讲到delayed ack的好处：<br>&nbsp;&nbsp; a) to avoid the silly window syndrome; <br>&nbsp;&nbsp; b) to allow ACKs to piggyback on a reply frame if one is ready to go when the stack decides to do the ACK;<br>&nbsp;&nbsp; c) to allow the stack to send one ACK for several frames, if those frames arrive within the delay period. </font>
<p><font size=2>&nbsp;&nbsp; a) 所谓的糊涂窗口综合症(别人都这样翻译的，似乎有点搞笑:D)<br>&nbsp;&nbsp; b) 将ACK与将要发送的数据报文一起发送<br>&nbsp;&nbsp; c) 一个ack确认多个报文段，如果这几个报文段在延迟时间内到达 </font>
<p><font size=2><strong>2. 什么是Nagle algoritm ?</strong><br>&nbsp;&nbsp; 简而言之，nagle算法主要目的是减少网络流量，当你发送的数据包太小时，TCP并不立即发送该数据包，而是缓存起来直到数据包<br>&nbsp;&nbsp; 到达一定大小后才发送。(improving the efficiency of TCP/IP networks by reducing the number of packets that need to <br>&nbsp;&nbsp; be sent over the network.) </font>
<p><font size=2>&nbsp;&nbsp; 关于这个算法，我觉得<a href="http://en.wikipedia.org/wiki/Nagle's_algorithm" target=_blank>wikipedia</a>上讲的比较好。具体点说，当上层提交数据给TCP时，TCP觉得你的数据太小了(套用一般的例子，<br>&nbsp;&nbsp; 如果你要发送1一个字节的数据，当附加上TCP和IP头后，数据包通常就会增加到41字节，那么这显然是低效的)，就缓存你的数据，<br>&nbsp;&nbsp; 当数据缓存到一定长度后，如果之前发送的数据得到了ACK确认且接收方有足够空间容纳数据，就发送这些数据，否则继续等待。 </font>
<p><font size=2>&nbsp;&nbsp; wikipedia上给了一段nagle的伪代码：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;there&nbsp;</span><span style="COLOR: #0000ff">is</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;data&nbsp;to&nbsp;send<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;the&nbsp;window&nbsp;size&nbsp;</span><span style="COLOR: #000000">&gt;=</span><span style="COLOR: #000000">&nbsp;MSS&nbsp;and&nbsp;available&nbsp;data&nbsp;</span><span style="COLOR: #0000ff">is</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&gt;=</span><span style="COLOR: #000000">&nbsp;MSS<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;send&nbsp;complete&nbsp;MSS&nbsp;segment&nbsp;now<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;there&nbsp;</span><span style="COLOR: #0000ff">is</span><span style="COLOR: #000000">&nbsp;unconfirmed&nbsp;data&nbsp;still&nbsp;</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">&nbsp;the&nbsp;pipe<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;enqueue&nbsp;data&nbsp;</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">&nbsp;the&nbsp;buffer&nbsp;until&nbsp;an&nbsp;acknowledge&nbsp;</span><span style="COLOR: #0000ff">is</span><span style="COLOR: #000000">&nbsp;received<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;send&nbsp;data&nbsp;immediately<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;end&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p>&nbsp;&nbsp;&nbsp;<br></font><font size=2>&nbsp;&nbsp; TCP socket提供了关闭nagle算法的接口，你可以通过TCP_NODELAY选项决定是否开启该算法。不过MSDN上建议不要关闭此算法。如果<br>&nbsp;&nbsp; 你发送的数据不至于很小的话(&lt;40byte)，我也不建议你关闭。 </font>
<p><font size=2></font></p>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/50770.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-05-22 15:42 <a href="http://www.cppblog.com/kevinlynx/archive/2008/05/22/50770.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>剖析Etwork网络库</title><link>http://www.cppblog.com/kevinlynx/archive/2008/05/21/50687.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Wed, 21 May 2008 13:06:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/05/21/50687.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/50687.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/05/21/50687.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/50687.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/50687.html</trackback:ping><description><![CDATA[<p><font size=2>Author : Kevin Lynx</font>
<p><font size=2>从开始接触网络编程这个东西开始，我就不间断地阅读一些网络库(模块)的源代码，主要目的是为了获取别<br>人在这方面的经验，编程这东西，还是要多实践啊。 </font>
<p><font size=2>基本上，</font><a href="http://www.mindcontrol.org/~hplus/etwork/" target=_blank><font size=2>Etwork</font></a><font size=2>是一个很小巧的网络库。Etwork基于select模型，采用我之前说的技巧，理论上可以处理很<br>多连接(先不说效率)。 </font>
<p><font size=2>先看看下这个库的结构： </font>
<p><a href="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/Etwork_128C5/classgraph.jpg"><font size=2><img style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height=291 alt=classgraph src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/Etwork_128C5/classgraph_thumb.jpg" width=383 border=0></font></a><font size=2> </font>
<p><font size=2>如同很多网络库一样，总会有一个类似于ISocketManager的类，用于管理所有网络连接(当用户服务器时)。<br>而ISocket则用于代表一个网络连接。在其他库中，ISocketManager对应的可能就是Server，而ISocket对应<br>的则是Session。 </font>
<p><font size=2>在接口设计上，尽管Etwork写了很多接口类(看看那些IClass)，但是事实上它抽象得并不彻底。只是暴露给<br>客户端的代码很简洁，而库本身依然臃肿。不知道为什么，现在我比较喜欢纯C这种简洁的东西，对于OO以及<br>template，渐渐地有点心累。 </font>
<p><font size=2>在功能实现上，我以TCP服务器为例，CreateEtwork根据传来的参数建立服务器，在SocketManager::open中<br>是很常规的socket, bind, listen。当建立了服务器之后，需要在程序主循环里不断地轮询状态，这里主要<br>调用poll函数完成。 </font>
<p><font size=2>poll函数主体就是调用select。当select成功返回活动的套接字数量后，Etwork依次轮询读、写、错误fdset，<br>将保存的所有网络连接(就是那些ISocket对象)对应的套接字与fdset中当前的套接字做比较。大致逻辑为： </font>
<p><font size=2></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">fd_count&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;select(&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,&nbsp;readset,&nbsp;writeset,&nbsp;exceptset,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">timeout&nbsp;);&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(&nbsp;each&nbsp;fd&nbsp;</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">&nbsp;readset&nbsp;)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;fd&nbsp;</span><span style="COLOR: #0000ff">is</span><span style="COLOR: #000000">&nbsp;listening&nbsp;fd&nbsp;)&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;accept&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;connection<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(&nbsp;each&nbsp;socket&nbsp;</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">&nbsp;all&nbsp;connections&nbsp;)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;fd&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;socket&nbsp;)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;can&nbsp;read&nbsp;data&nbsp;on&nbsp;</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">&nbsp;socket&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(&nbsp;each&nbsp;fd&nbsp;</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">&nbsp;writeset&nbsp;)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><img src="http://www.cppblog.com/Images/dot.gif">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(&nbsp;each&nbsp;fd&nbsp;</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">&nbsp;exceptset&nbsp;)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><img src="http://www.cppblog.com/Images/dot.gif">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>没什么特别让人注意的地方(别觉得别人垃圾，耐心读别人的代码不是什么坏事)。每一次，当Etwork检测到<br>新的连接时，会创建新的ISocket对象，并关联对应的套接字，然后保存此对象到一个列表中。当poll结束<br>后，客户端程序通常会调用accept函数(Etwork中提供的接口)，该函数主要是将poll中保存的新的ISocket<br>对象全部拷贝出去。 </font>
<p><font size=2>在接收、发送网络数据上，Etwork如同几乎所有的网络库(模块)一样，采用了缓冲机制。这里所说的缓冲机<br>制是，网络模块接收到网络数据时，将数据保存起来，客户端程序想获取数据时，实际上就是从这个缓冲中<br>直接取，而不是从网络上获取；同理，发送数据时，客户端程序将数据提供给网络模块，网络模块将数据保<br>存起来，网络模块会在另一个时候发送这个缓冲中的数据(对于异步IO的处理毕竟不一样)。 </font>
<p><font size=2>Etwork关于这个缓冲机制的相关代码，主要集中在Buffer这个类。与Buffer相关的是一个Message机制。Buffer<br>维护了一个Message的队列(deque)。一个Message实际上是一个非常简单的结构体： </font>
<p><font size=2>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;Message&nbsp;<br><img id=Codehighlighter1_16_72_Open_Image onclick="this.style.display='none'; Codehighlighter1_16_72_Open_Text.style.display='none'; Codehighlighter1_16_72_Closed_Image.style.display='inline'; Codehighlighter1_16_72_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_16_72_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_16_72_Closed_Text.style.display='none'; Codehighlighter1_16_72_Open_Image.style.display='inline'; Codehighlighter1_16_72_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_16_72_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_16_72_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">short</span><span style="COLOR: #000000">&nbsp;offset_;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span style="COLOR: #0000ff">short</span><span style="COLOR: #000000">&nbsp;size_;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;&nbsp;</span></div>
<p></font>&nbsp;
<p><font size=2>这其实是消息头，在消息头后全部是数据。在创建消息时(new_message)，Etwork根据客户端提供的数据创建<br>足够大的缓存保存： </font>
<p><font size=2>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">Message&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;m&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(Message&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)::</span><span style="COLOR: #0000ff">operator</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">(&nbsp;size&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(&nbsp;Message&nbsp;)&nbsp;);&nbsp;</span></div>
<p></font>&nbsp;
<p><font size=2>这其实是一个很危险的做法，但是从Etwokr的源码可以看出来，作者很喜欢玩弄这个技巧。与Buffer具体相<br>关的接口包括：get_data, put_data, get_message, put_message。Buffer内部维护的数据都是以Message<br>的形式组织。但是，对于外部而言，却依然是raw data，也就是诸如char*之类的数据。几个相关函数大致<br>上的操作为：获取指定尺寸的消息(可能包含多个消息)，将一段数据加入Buffer并以消息的形式组织(可能会<br>创建多个消息)，将一个消息以raw data的形式输出，将raw data以一个消息的形式加入到Buffer。 </font>
<p><font size=2>一般情况下，Etwork的poll操作，会将套接字上的数据接收并put_data到缓冲中；发送数据时则get_data。<br>客户端要从缓冲中获取数据时，就调用get_message；发送数据时就put_message。 </font>
<p><font size=2>Etwork中还有一个比较有趣的东西：marshaller。这个东西主要就是提供将C++中各种数据类型的变量进行字<br>节编码，也就是将int long struct之类的东西转换为unsigned char，从而方便直接往网络上发送。 </font>
<p><font size=2>基本上，Buffer和marshaller可以说是一个网络库(模块)的必要部件，你可以在不同的网络库中看到类似的<br>东西。 </font>
<p><font size=2>Etwork在网络事件的处理上，除了上面的轮询外，还支持回调机制。这主要是通过INotify，以及给各个ISocket<br>注册Notify对象实现。没什么难度，基本上就是observer模式的简单实现。 </font>
<p><font size=2>其他东西就没什么好说的了，纵观一下，Etwork实现得还是比较典型的，可以作为开发网络库的一个简单例子。 </font></p>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/50687.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-05-21 21:06 <a href="http://www.cppblog.com/kevinlynx/archive/2008/05/21/50687.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>突破select的FD_SETSIZE限制</title><link>http://www.cppblog.com/kevinlynx/archive/2008/05/20/50500.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Tue, 20 May 2008 03:20:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/05/20/50500.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/50500.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/05/20/50500.html#Feedback</comments><slash:comments>12</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/50500.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/50500.html</trackback:ping><description><![CDATA[<p style="font-size: 10pt">Author : Kevin Lynx&nbsp; <p style="font-size: 10pt"><strong>前言：</strong>  <p style="font-size: 10pt">在很多比较各种网络模型的文章中，但凡提到select模型时，都会说select受限于轮询的套接字数量，这个<br>数量也就是系统头文件中定义的FD_SETSIZE值(例如64)。但事实上这个算不上真的限制。  <p style="font-size: 10pt"><strong>C语言的偏方：</strong>  <p style="font-size: 10pt">在C语言的世界里存在一个关于结构体的偏门技巧，例如：  <p style="font-size: 10pt">&nbsp;</p> <div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #000000">typedef </span><span style="color: #0000ff">struct</span><span style="color: #000000"> _str_type<br><img id="Codehighlighter1_25_57_Open_Image" onclick="this.style.display='none'; Codehighlighter1_25_57_Open_Text.style.display='none'; Codehighlighter1_25_57_Closed_Image.style.display='inline'; Codehighlighter1_25_57_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img id="Codehighlighter1_25_57_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_25_57_Closed_Text.style.display='none'; Codehighlighter1_25_57_Open_Image.style.display='inline'; Codehighlighter1_25_57_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align="top"></span><span id="Codehighlighter1_25_57_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_25_57_Open_Text"><span style="color: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">int</span><span style="color: #000000"> _len;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">char</span><span style="color: #000000"> _s[</span><span style="color: #000000">1</span><span style="color: #000000">];<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top">}</span></span><span style="color: #000000">str_type; </span></div> <p style="font-size: 10pt">&nbsp; <p style="font-size: 10pt">str_type用于保存字符串(我只是举例，事实上这个结构体没什么用处)，乍看上去str_type只能保存长度为<br>1的字符串('\0')。但是，通过写下如下的代码，你将突破这个限制：  <p style="font-size: 10pt"></p> <div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #0000ff">int</span><span style="color: #000000"> str_len </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">5</span><span style="color: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top">str_type </span><span style="color: #000000">*</span><span style="color: #000000">s </span><span style="color: #000000">=</span><span style="color: #000000"> (str_type</span><span style="color: #000000">*</span><span style="color: #000000">) malloc( </span><span style="color: #0000ff">sizeof</span><span style="color: #000000">( str_type ) </span><span style="color: #000000">+</span><span style="color: #000000"> str_len </span><span style="color: #000000">-</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">1</span><span style="color: #000000"> );<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #008000">//</span><span style="color: #008000"><img src="http://www.cppblog.com/Images/dot.gif"></span><span style="color: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #000000">free( s ); <br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span></div> <p style="font-size: 10pt"><br>这个技巧原理很简单，因为_s恰好在结构体尾部，所以可以为其分配一段连续的空间，只要注意指针的使用，<br>这个就算不上代码上的罪恶。但是这个技巧有个限制，str_type定义的变量必须是被分配在堆上，否则会破<br>坏堆栈。另外，需要动态增长的成员需要位于结构体的末尾。最后，一个忠告就是，这个是C语言里的技巧，<br>如果你的结构体包含了C++的东西，这个技巧将不再安全(&lt;Inside the C++ object model&gt;)。  <p style="font-size: 10pt"><strong>其实select也可以这样做：</strong>  <p style="font-size: 10pt">事实上，因为select涉及到的fd_set是一个完全满足上述要求的结构体：  <p style="font-size: 10pt"></p> <div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #000000">winsock2.h : <br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><br><img id="Codehighlighter1_37_167_Open_Image" onclick="this.style.display='none'; Codehighlighter1_37_167_Open_Text.style.display='none'; Codehighlighter1_37_167_Closed_Image.style.display='inline'; Codehighlighter1_37_167_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img id="Codehighlighter1_37_167_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_37_167_Closed_Text.style.display='none'; Codehighlighter1_37_167_Open_Image.style.display='inline'; Codehighlighter1_37_167_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align="top">typedef </span><span style="color: #0000ff">struct</span><span style="color: #000000"> fd_set </span><span id="Codehighlighter1_37_167_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_37_167_Open_Text"><span style="color: #000000">{<br><img id="Codehighlighter1_77_99_Open_Image" onclick="this.style.display='none'; Codehighlighter1_77_99_Open_Text.style.display='none'; Codehighlighter1_77_99_Closed_Image.style.display='inline'; Codehighlighter1_77_99_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img id="Codehighlighter1_77_99_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_77_99_Closed_Text.style.display='none'; Codehighlighter1_77_99_Open_Image.style.display='inline'; Codehighlighter1_77_99_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; u_int fd_count;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span id="Codehighlighter1_77_99_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff">/**/</span><span id="Codehighlighter1_77_99_Open_Text"><span style="color: #008000">/*</span><span style="color: #008000"> how many are SET? </span><span style="color: #008000">*/</span></span><span style="color: #000000"><br><img id="Codehighlighter1_141_165_Open_Image" onclick="this.style.display='none'; Codehighlighter1_141_165_Open_Text.style.display='none'; Codehighlighter1_141_165_Closed_Image.style.display='inline'; Codehighlighter1_141_165_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img id="Codehighlighter1_141_165_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_141_165_Closed_Text.style.display='none'; Codehighlighter1_141_165_Open_Image.style.display='inline'; Codehighlighter1_141_165_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SOCKET&nbsp; fd_array[FD_SETSIZE];&nbsp;&nbsp; </span><span id="Codehighlighter1_141_165_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff">/**/</span><span id="Codehighlighter1_141_165_Open_Text"><span style="color: #008000">/*</span><span style="color: #008000"> an array of SOCKETs </span><span style="color: #008000">*/</span></span><span style="color: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top">}</span></span><span style="color: #000000"> fd_set; <br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span></div> <p style="font-size: 10pt"><br>但是，如果使用了以上技巧来增加fd_array的数量(也就是保存的套接字数量)，那么关于fd_set的那些宏可<br>能就无法使用了，例如FD_SET。  <p style="font-size: 10pt"></p> <div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #000000">winsock2.h : <br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #0000ff">#define</span><span style="color: #000000"> FD_SET(fd, set) do { \</span><span style="color: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp; u_int __i; \<br><img id="Codehighlighter1_129_236_Open_Image" onclick="this.style.display='none'; Codehighlighter1_129_236_Open_Text.style.display='none'; Codehighlighter1_129_236_Closed_Image.style.display='inline'; Codehighlighter1_129_236_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img id="Codehighlighter1_129_236_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_129_236_Closed_Text.style.display='none'; Codehighlighter1_129_236_Open_Image.style.display='inline'; Codehighlighter1_129_236_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">for</span><span style="color: #000000"> (__i </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">; __i </span><span style="color: #000000">&lt;</span><span style="color: #000000"> ((fd_set FAR </span><span style="color: #000000">*</span><span style="color: #000000">)(</span><span style="color: #0000ff">set</span><span style="color: #000000">))</span><span style="color: #000000">-&gt;</span><span style="color: #000000">fd_count; __i</span><span style="color: #000000">++</span><span style="color: #000000">) </span><span id="Codehighlighter1_129_236_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_129_236_Open_Text"><span style="color: #000000">{ \<br><img id="Codehighlighter1_192_227_Open_Image" onclick="this.style.display='none'; Codehighlighter1_192_227_Open_Text.style.display='none'; Codehighlighter1_192_227_Closed_Image.style.display='inline'; Codehighlighter1_192_227_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img id="Codehighlighter1_192_227_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_192_227_Closed_Text.style.display='none'; Codehighlighter1_192_227_Open_Image.style.display='inline'; Codehighlighter1_192_227_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">if</span><span style="color: #000000"> (((fd_set FAR </span><span style="color: #000000">*</span><span style="color: #000000">)(</span><span style="color: #0000ff">set</span><span style="color: #000000">))</span><span style="color: #000000">-&gt;</span><span style="color: #000000">fd_array[__i] </span><span style="color: #000000">==</span><span style="color: #000000"> (fd)) </span><span id="Codehighlighter1_192_227_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_192_227_Open_Text"><span style="color: #000000">{ \<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">break</span><span style="color: #000000">; \<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></span><span style="color: #000000"> \<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp; }</span></span><span style="color: #000000"> \<br><img id="Codehighlighter1_289_484_Open_Image" onclick="this.style.display='none'; Codehighlighter1_289_484_Open_Text.style.display='none'; Codehighlighter1_289_484_Closed_Image.style.display='inline'; Codehighlighter1_289_484_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img id="Codehighlighter1_289_484_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_289_484_Closed_Text.style.display='none'; Codehighlighter1_289_484_Open_Image.style.display='inline'; Codehighlighter1_289_484_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">if</span><span style="color: #000000"> (__i </span><span style="color: #000000">==</span><span style="color: #000000"> ((fd_set FAR </span><span style="color: #000000">*</span><span style="color: #000000">)(</span><span style="color: #0000ff">set</span><span style="color: #000000">))</span><span style="color: #000000">-&gt;</span><span style="color: #000000">fd_count) </span><span id="Codehighlighter1_289_484_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_289_484_Open_Text"><span style="color: #000000">{ \<br><img id="Codehighlighter1_352_475_Open_Image" onclick="this.style.display='none'; Codehighlighter1_352_475_Open_Text.style.display='none'; Codehighlighter1_352_475_Closed_Image.style.display='inline'; Codehighlighter1_352_475_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img id="Codehighlighter1_352_475_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_352_475_Closed_Text.style.display='none'; Codehighlighter1_352_475_Open_Image.style.display='inline'; Codehighlighter1_352_475_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">if</span><span style="color: #000000"> (((fd_set FAR </span><span style="color: #000000">*</span><span style="color: #000000">)(</span><span style="color: #0000ff">set</span><span style="color: #000000">))</span><span style="color: #000000">-&gt;</span><span style="color: #000000">fd_count </span><span style="color: #000000">&lt;</span><span style="color: #000000"> FD_SETSIZE) </span><span id="Codehighlighter1_352_475_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_352_475_Open_Text"><span style="color: #000000">{ \<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ((fd_set FAR </span><span style="color: #000000">*</span><span style="color: #000000">)(</span><span style="color: #0000ff">set</span><span style="color: #000000">))</span><span style="color: #000000">-&gt;</span><span style="color: #000000">fd_array[__i] </span><span style="color: #000000">=</span><span style="color: #000000"> (fd); \<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ((fd_set FAR </span><span style="color: #000000">*</span><span style="color: #000000">)(</span><span style="color: #0000ff">set</span><span style="color: #000000">))</span><span style="color: #000000">-&gt;</span><span style="color: #000000">fd_count</span><span style="color: #000000">++</span><span style="color: #000000">; \<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></span><span style="color: #000000"> \<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp; }</span></span><span style="color: #000000"> \<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top">} </span><span style="color: #0000ff">while</span><span style="color: #000000">(</span><span style="color: #000000">0</span><span style="color: #000000">) <br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span></div> <p style="font-size: 10pt"><br>有点让人眼花缭乱，我鼓励你仔细看，其实很简单。这里有个小技巧，就是他把这些代码放到一个do...while(0)<br>里，为什么要这样做，我觉得应该是防止名字污染，也就是防止那个__i变量与你的代码相冲突。可以看出，<br>FD_SET会将fd_count与FD_SETSIZE相比较，这里主要是防止往fd_array的非法位置写数据。  <p style="font-size: 10pt">因为这个宏原理不过如此，所以我们完全可以自己写一个新的版本。例如：  <p style="font-size: 10pt"></p> <div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #0000ff">#define</span><span style="color: #000000"> MY_FD_SET( fd, set, size ) do { \</span><span style="color: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp; unsigned </span><span style="color: #0000ff">int</span><span style="color: #000000"> i </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">; \<br><img id="Codehighlighter1_124_223_Open_Image" onclick="this.style.display='none'; Codehighlighter1_124_223_Open_Text.style.display='none'; Codehighlighter1_124_223_Closed_Image.style.display='inline'; Codehighlighter1_124_223_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img id="Codehighlighter1_124_223_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_124_223_Closed_Text.style.display='none'; Codehighlighter1_124_223_Open_Image.style.display='inline'; Codehighlighter1_124_223_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">for</span><span style="color: #000000">( i </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">; i </span><span style="color: #000000">&lt;</span><span style="color: #000000"> ((fd_set</span><span style="color: #000000">*</span><span style="color: #000000">) </span><span style="color: #0000ff">set</span><span style="color: #000000">)</span><span style="color: #000000">-&gt;</span><span style="color: #000000">fd_count; </span><span style="color: #000000">++</span><span style="color: #000000"> i ) </span><span id="Codehighlighter1_124_223_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_124_223_Open_Text"><span style="color: #000000">{ \<br><img id="Codehighlighter1_179_214_Open_Image" onclick="this.style.display='none'; Codehighlighter1_179_214_Open_Text.style.display='none'; Codehighlighter1_179_214_Closed_Image.style.display='inline'; Codehighlighter1_179_214_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img id="Codehighlighter1_179_214_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_179_214_Closed_Text.style.display='none'; Codehighlighter1_179_214_Open_Image.style.display='inline'; Codehighlighter1_179_214_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">if</span><span style="color: #000000">( ((fd_set</span><span style="color: #000000">*</span><span style="color: #000000">)</span><span style="color: #0000ff">set</span><span style="color: #000000">)</span><span style="color: #000000">-&gt;</span><span style="color: #000000">fd_array[i] </span><span style="color: #000000">==</span><span style="color: #000000"> (fd) ) </span><span id="Codehighlighter1_179_214_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_179_214_Open_Text"><span style="color: #000000">{ \<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">break</span><span style="color: #000000">; \<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></span><span style="color: #000000"> \<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp; }</span></span><span style="color: #000000"> \<br><img id="Codehighlighter1_268_438_Open_Image" onclick="this.style.display='none'; Codehighlighter1_268_438_Open_Text.style.display='none'; Codehighlighter1_268_438_Closed_Image.style.display='inline'; Codehighlighter1_268_438_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img id="Codehighlighter1_268_438_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_268_438_Closed_Text.style.display='none'; Codehighlighter1_268_438_Open_Image.style.display='inline'; Codehighlighter1_268_438_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">if</span><span style="color: #000000">( i </span><span style="color: #000000">==</span><span style="color: #000000"> ((fd_set</span><span style="color: #000000">*</span><span style="color: #000000">)</span><span style="color: #0000ff">set</span><span style="color: #000000">)</span><span style="color: #000000">-&gt;</span><span style="color: #000000">fd_count ) </span><span id="Codehighlighter1_268_438_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_268_438_Open_Text"><span style="color: #000000">{ \<br><img id="Codehighlighter1_321_429_Open_Image" onclick="this.style.display='none'; Codehighlighter1_321_429_Open_Text.style.display='none'; Codehighlighter1_321_429_Closed_Image.style.display='inline'; Codehighlighter1_321_429_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img id="Codehighlighter1_321_429_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_321_429_Closed_Text.style.display='none'; Codehighlighter1_321_429_Open_Image.style.display='inline'; Codehighlighter1_321_429_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">if</span><span style="color: #000000">( ((fd_set</span><span style="color: #000000">*</span><span style="color: #000000">)</span><span style="color: #0000ff">set</span><span style="color: #000000">)</span><span style="color: #000000">-&gt;</span><span style="color: #000000">fd_count </span><span style="color: #000000">&lt;</span><span style="color: #000000"> (size) ) </span><span id="Codehighlighter1_321_429_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_321_429_Open_Text"><span style="color: #000000">{ \<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ((fd_set</span><span style="color: #000000">*</span><span style="color: #000000">)</span><span style="color: #0000ff">set</span><span style="color: #000000">)</span><span style="color: #000000">-&gt;</span><span style="color: #000000">fd_array[i] </span><span style="color: #000000">=</span><span style="color: #000000"> (fd); \<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ((fd_set</span><span style="color: #000000">*</span><span style="color: #000000">)</span><span style="color: #0000ff">set</span><span style="color: #000000">)</span><span style="color: #000000">-&gt;</span><span style="color: #000000">fd_count </span><span style="color: #000000">++</span><span style="color: #000000">; \<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></span><span style="color: #000000"> \<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp; }</span></span><span style="color: #000000"> \<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top">} </span><span style="color: #0000ff">while</span><span style="color: #000000">( </span><span style="color: #000000">0</span><span style="color: #000000"> ) <br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span></div> <p style="font-size: 10pt"><br>没什么变化，只是为FD_SET加入一个fd_array的长度参数，宏体也只是将FD_SETSIZE换成这个长度参数。<br>于是，现在你可以写下这样的代码：  <p style="font-size: 10pt"></p> <div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #000000">unsigned </span><span style="color: #0000ff">int</span><span style="color: #000000"> count </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">100</span><span style="color: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top">fd_set </span><span style="color: #000000">*</span><span style="color: #000000">read_set </span><span style="color: #000000">=</span><span style="color: #000000"> (fd_set</span><span style="color: #000000">*</span><span style="color: #000000">) malloc( </span><span style="color: #0000ff">sizeof</span><span style="color: #000000">( fd_set ) </span><span style="color: #000000">+</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">sizeof</span><span style="color: #000000">(SOCKET) </span><span style="color: #000000">*</span><span style="color: #000000"> (count </span><span style="color: #000000">-</span><span style="color: #000000"> FD_SETSIZE ) );<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top">SOCKET s </span><span style="color: #000000">=</span><span style="color: #000000"> socket( AF_INET, SOCK_STREAM, </span><span style="color: #000000">0</span><span style="color: #000000"> );<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #008000">//</span><span style="color: #008000"><img src="http://www.cppblog.com/Images/dot.gif"></span><span style="color: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #000000">MY_FD_SET( s, read_set, count );<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #008000">//</span><span style="color: #008000"><img src="http://www.cppblog.com/Images/dot.gif"></span><span style="color: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #000000">free( read_set );<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top">closesocket( s ); <br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span></div> <p style="font-size: 10pt"><strong> <p style="font-size: 10pt"><br>小提下select模型：</strong>  <p style="font-size: 10pt">这里我不会具体讲select模型，我只稍微提一下。一个典型的select轮询模型为：  <p style="font-size: 10pt"></p> <div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #0000ff">int</span><span style="color: #000000"> r </span><span style="color: #000000">=</span><span style="color: #000000"> select( </span><span style="color: #000000">0</span><span style="color: #000000">, </span><span style="color: #000000">&amp;</span><span style="color: #000000">read_set, </span><span style="color: #000000">0</span><span style="color: #000000">, </span><span style="color: #000000">0</span><span style="color: #000000">, </span><span style="color: #000000">&amp;</span><span style="color: #000000">timeout );<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #0000ff">if</span><span style="color: #000000">( r </span><span style="color: #000000">&lt;</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000"> )<br><img id="Codehighlighter1_60_82_Open_Image" onclick="this.style.display='none'; Codehighlighter1_60_82_Open_Text.style.display='none'; Codehighlighter1_60_82_Closed_Image.style.display='inline'; Codehighlighter1_60_82_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img id="Codehighlighter1_60_82_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_60_82_Closed_Text.style.display='none'; Codehighlighter1_60_82_Open_Image.style.display='inline'; Codehighlighter1_60_82_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align="top"></span><span id="Codehighlighter1_60_82_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_60_82_Open_Text"><span style="color: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span style="color: #008000">//</span><span style="color: #008000"> select error</span><span style="color: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top"></span><span style="color: #000000">}</span></span><span style="color: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #0000ff">if</span><span style="color: #000000">( r </span><span style="color: #000000">&gt;</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000"> )<br><img id="Codehighlighter1_98_246_Open_Image" onclick="this.style.display='none'; Codehighlighter1_98_246_Open_Text.style.display='none'; Codehighlighter1_98_246_Closed_Image.style.display='inline'; Codehighlighter1_98_246_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img id="Codehighlighter1_98_246_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_98_246_Closed_Text.style.display='none'; Codehighlighter1_98_246_Open_Image.style.display='inline'; Codehighlighter1_98_246_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align="top"></span><span id="Codehighlighter1_98_246_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_98_246_Open_Text"><span style="color: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">for</span><span style="color: #000000">( each sockets )<br><img id="Codehighlighter1_128_244_Open_Image" onclick="this.style.display='none'; Codehighlighter1_128_244_Open_Text.style.display='none'; Codehighlighter1_128_244_Closed_Image.style.display='inline'; Codehighlighter1_128_244_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img id="Codehighlighter1_128_244_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_128_244_Closed_Text.style.display='none'; Codehighlighter1_128_244_Open_Image.style.display='inline'; Codehighlighter1_128_244_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span id="Codehighlighter1_128_244_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_128_244_Open_Text"><span style="color: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">if</span><span style="color: #000000">( FD_ISSET( now_socket, </span><span style="color: #000000">&amp;</span><span style="color: #000000">read_set ) ) <br><img id="Codehighlighter1_187_238_Open_Image" onclick="this.style.display='none'; Codehighlighter1_187_238_Open_Text.style.display='none'; Codehighlighter1_187_238_Closed_Image.style.display='inline'; Codehighlighter1_187_238_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img id="Codehighlighter1_187_238_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_187_238_Closed_Text.style.display='none'; Codehighlighter1_187_238_Open_Image.style.display='inline'; Codehighlighter1_187_238_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span id="Codehighlighter1_187_238_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_187_238_Open_Text"><span style="color: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #008000">//</span><span style="color: #008000"> this socket can read data</span><span style="color: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top"></span><span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></span><span style="color: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp; }</span></span><span style="color: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top">}</span></span><span style="color: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span></div> <p style="font-size: 10pt"><br>轮询write时也差不多。在Etwork(一个超小型的基本用于练习网络编程的网络库,google yourself)中，作者<br>的轮询方式则有所不同：  <p style="font-size: 10pt"></p> <div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #008000">//</span><span style="color: #008000"> read_set, write_set为采用了上文所述技巧的fd_set类型的指针</span><span style="color: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #0000ff">int</span><span style="color: #000000"> r </span><span style="color: #000000">=</span><span style="color: #000000"> select( </span><span style="color: #000000">0</span><span style="color: #000000">, read_set, write_set, </span><span style="color: #000000">0</span><span style="color: #000000">, </span><span style="color: #000000">&amp;</span><span style="color: #000000">timeout );<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #008000">//</span><span style="color: #008000">&nbsp;<img src="http://www.cppblog.com/Images/dot.gif"> error handling</span><span style="color: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #0000ff">for</span><span style="color: #000000">( </span><span style="color: #0000ff">int</span><span style="color: #000000"> i </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">; i </span><span style="color: #000000">&lt;</span><span style="color: #000000"> read_set</span><span style="color: #000000">-&gt;</span><span style="color: #000000">fd_count; </span><span style="color: #000000">++</span><span style="color: #000000"> i )<br><img id="Codehighlighter1_169_245_Open_Image" onclick="this.style.display='none'; Codehighlighter1_169_245_Open_Text.style.display='none'; Codehighlighter1_169_245_Closed_Image.style.display='inline'; Codehighlighter1_169_245_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img id="Codehighlighter1_169_245_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_169_245_Closed_Text.style.display='none'; Codehighlighter1_169_245_Open_Image.style.display='inline'; Codehighlighter1_169_245_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align="top"></span><span id="Codehighlighter1_169_245_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_169_245_Open_Text"><span style="color: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span style="color: #008000">//</span><span style="color: #008000"> 轮询所有socket，这里直接采用read_set-&gt;fd_array[i] == now_socket判断，而不是FD_ISSET</span><span style="color: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top"></span><span style="color: #000000">}</span></span><span style="color: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #0000ff">for</span><span style="color: #000000">( </span><span style="color: #0000ff">int</span><span style="color: #000000"> i </span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">; i </span><span style="color: #000000">&lt;</span><span style="color: #000000"> write_set</span><span style="color: #000000">-&gt;</span><span style="color: #000000">fd_count; </span><span style="color: #000000">++</span><span style="color: #000000"> i )<br><img id="Codehighlighter1_297_345_Open_Image" onclick="this.style.display='none'; Codehighlighter1_297_345_Open_Text.style.display='none'; Codehighlighter1_297_345_Closed_Image.style.display='inline'; Codehighlighter1_297_345_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img id="Codehighlighter1_297_345_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_297_345_Closed_Text.style.display='none'; Codehighlighter1_297_345_Open_Image.style.display='inline'; Codehighlighter1_297_345_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align="top"></span><span id="Codehighlighter1_297_345_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_297_345_Open_Text"><span style="color: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span style="color: #008000">//</span><span style="color: #008000"> 轮询所有socket，检查其whether can write，判断方式同上</span><span style="color: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top"></span><span style="color: #000000">}</span></span><span style="color: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span></div> <p style="font-size: 10pt"><br>两种方式的效率从代码上看去似乎都差不多，关键在于，FD_ISSET干了什么？这个宏实际上使用了__WSAFDIsSet<br>函数，而__WSAFDIsSet做了什么则不知道。也许它会依赖于FD_SETSIZE宏，那么这在我们这里将是不安全的，<br>所以相比之下，如果我们使用了这个突破FD_SETSIZE的偏方手段，那么也许第二种方式要好些。 </p> <p><strong style="font-size: 10pt">相关下载(5.21.2008)</strong></p><span style="font-size: 10pt">随便写了一个改进的select模型的echo服务器，放上<a href="http://www.cppblog.com/Files/kevinlynx/select_echo_server_2.rar">源码</a>。</span><img src ="http://www.cppblog.com/kevinlynx/aggbug/50500.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-05-20 11:20 <a href="http://www.cppblog.com/kevinlynx/archive/2008/05/20/50500.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>tcp要点学习-断开连接</title><link>http://www.cppblog.com/kevinlynx/archive/2008/05/14/49825.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Wed, 14 May 2008 07:46:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/05/14/49825.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/49825.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/05/14/49825.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/49825.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/49825.html</trackback:ping><description><![CDATA[<p style="font-size: 10pt">Author : Kevin Lynx <p style="font-size: 10pt"><strong>主要部分，四次握手：</strong>  <p style="font-size: 10pt">断开连接其实从我的角度看不区分客户端和服务器端，任何一方都可以调用close(or closesocket)之类<br>的函数开始主动终止一个连接。这里先暂时说正常情况。当调用close函数断开一个连接时，主动断开的<br>一方发送FIN(finish报文给对方。有了之前的经验，我想你应该明白我说的FIN报文时什么东西。也就是<br>一个设置了FIN标志位的报文段。FIN报文也可能附加用户数据，如果这一方还有数据要发送时，将数据附<br>加到这个FIN报文时完全正常的。之后你会看到，这种附加报文还会有很多，例如ACK报文。我们所要把握<br>的原则是，TCP肯定会力所能及地达到最大效率，所以你能够想到的优化方法，我想TCP都会想到。  <p style="font-size: 10pt">当被动关闭的一方收到FIN报文时，它会发送ACK确认报文(对于ACK这个东西你应该很熟悉了)。这里有个<br>东西要注意，因为TCP是双工的，也就是说，你可以想象一对TCP连接上有两条数据通路。当发送FIN报文<br>时，意思是说，发送FIN的一端就不能发送数据，也就是关闭了其中一条数据通路。被动关闭的一端发送<br>了ACK后，应用层通常就会检测到这个连接即将断开，然后被动断开的应用层调用close关闭连接。  <p style="font-size: 10pt">我可以告诉你，一旦当你调用close(or closesocket)，这一端就会发送FIN报文。也就是说，现在被动<br>关闭的一端也发送FIN给主动关闭端。有时候，被动关闭端会将ACK和FIN两个报文合在一起发送。主动<br>关闭端收到FIN后也发送ACK，然后整个连接关闭(事实上还没完全关闭，只是关闭需要交换的报文发送<br>完毕)，四次握手完成。如你所见，因为被动关闭端可能会将ACK和FIN合到一起发送，所以这也算不上<br>严格的四次握手---四个报文段。  <p style="font-size: 10pt">在前面的文章中，我一直没提TCP的状态转换。在这里我还是在犹豫是不是该将那张四处通用的图拿出来，<br>不过，这里我只给出断开连接时的状态转换图，摘自&lt;The TCP/IP Guide&gt;：  <p style="font-size: 10pt"><a href="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/tcp_DDB4/tcpclose_2.png"><img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="560" alt="tcpclose" src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/tcp_DDB4/tcpclose_thumb.png" width="670" border="0"></a>  <p style="font-size: 10pt">给出一个正常关闭时的windump信息：<br></p> <div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #000000">14</span><span style="color: #000000">:</span><span style="color: #000000">00</span><span style="color: #000000">:</span><span style="color: #000000">38.819856</span><span style="color: #000000"> IP cd</span><span style="color: #000000">-</span><span style="color: #000000">zhangmin.</span><span style="color: #000000">1748</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&gt;</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">220.181</span><span style="color: #000000">.</span><span style="color: #000000">37.55</span><span style="color: #000000">.</span><span style="color: #000000">80</span><span style="color: #000000">: F </span><span style="color: #000000">1</span><span style="color: #000000">:</span><span style="color: #000000">1</span><span style="color: #000000">(</span><span style="color: #000000">0</span><span style="color: #000000">) ack </span><span style="color: #000000">1</span><span style="color: #000000"> win </span><span style="color: #000000">65535</span><span style="color: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #000000">14</span><span style="color: #000000">:</span><span style="color: #000000">00</span><span style="color: #000000">:</span><span style="color: #000000">38.863989</span><span style="color: #000000"> IP </span><span style="color: #000000">220.181</span><span style="color: #000000">.</span><span style="color: #000000">37.55</span><span style="color: #000000">.</span><span style="color: #000000">80</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&gt;</span><span style="color: #000000"> cd</span><span style="color: #000000">-</span><span style="color: #000000">zhangmin.</span><span style="color: #000000">1748</span><span style="color: #000000">: F </span><span style="color: #000000">1</span><span style="color: #000000">:</span><span style="color: #000000">1</span><span style="color: #000000">(</span><span style="color: #000000">0</span><span style="color: #000000">) ack </span><span style="color: #000000">2</span><span style="color: #000000"> win </span><span style="color: #000000">2920</span><span style="color: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #000000">14</span><span style="color: #000000">:</span><span style="color: #000000">00</span><span style="color: #000000">:</span><span style="color: #000000">38.864412</span><span style="color: #000000"> IP cd</span><span style="color: #000000">-</span><span style="color: #000000">zhangmin.</span><span style="color: #000000">1748</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&gt;</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">220.181</span><span style="color: #000000">.</span><span style="color: #000000">37.55</span><span style="color: #000000">.</span><span style="color: #000000">80</span><span style="color: #000000">: . ack </span><span style="color: #000000">2</span><span style="color: #000000"> win </span><span style="color: #000000">65535</span><span style="color: #000000">&nbsp;</span></div> <p style="font-size: 10pt">&nbsp; <p style="font-size: 10pt"><strong>补充细节：</strong>  <p style="font-size: 10pt">关于以上的四次握手，我补充下细节：<br>1. 默认情况下(不改变socket选项)，当你调用close( or closesocket，以下说close不再重复)时，如果<br>发送缓冲中还有数据，TCP会继续把数据发送完。<br>2. 发送了FIN只是表示这端不能继续发送数据(应用层不能再调用send发送)，但是还可以接收数据。<br>3. 应用层如何知道对端关闭？通常，在最简单的阻塞模型中，当你调用recv时，如果返回0，则表示对端<br>关闭。在这个时候通常的做法就是也调用close，那么TCP层就发送FIN，继续完成四次握手。如果你不调用<br>close，那么对端就会处于FIN_WAIT_2状态，而本端则会处于CLOSE_WAIT状态。这个可以写代码试试。<br>4. 在很多时候，TCP连接的断开都会由TCP层自动进行，例如你CTRL+C终止你的程序，TCP连接依然会正常关<br>闭，你可以写代码试试。  <p style="font-size: 10pt"><strong>特别的TIME_WAIT状态：</strong>  <p style="font-size: 10pt">从以上TCP连接关闭的状态转换图可以看出，主动关闭的一方在发送完对对方FIN报文的确认(ACK)报文后，<br>会进入TIME_WAIT状态。TIME_WAIT状态也称为2MSL状态。  <p style="font-size: 10pt">什么是2MSL？MSL即Maximum Segment Lifetime，也就是报文最大生存时间，引用&lt;TCP/IP详解&gt;中的话：“<br>它(MSL)是任何报文段被丢弃前在网络内的最长时间。”那么，2MSL也就是这个时间的2倍。其实我觉得没<br>必要把这个MSL的确切含义搞明白，你所需要明白的是，当TCP连接完成四个报文段的交换时，主动关闭的<br>一方将继续等待一定时间(2-4分钟)，即使两端的应用程序结束。你可以写代码试试，然后用netstat查看下。  <p style="font-size: 10pt">为什么需要2MSL？根据&lt;TCP/IP详解&gt;和&lt;The TCP/IP Guide&gt;中的说法，有两个原因：<br>其一，保证发送的ACK会成功发送到对方，如何保证？我觉得可能是通过超时计时器发送。这个就很难用<br>代码演示了。<br>其二，报文可能会被混淆，意思是说，其他时候的连接可能会被当作本次的连接。直接引用&lt;The TCP/IP Guide&gt;<br>的说法：The second is to provide a “buffering period” between the end of this connection <br>and any subsequent ones. If not for this period, it is possible that packets from different <br>connections could be mixed, creating confusion.  <p style="font-size: 10pt"><strong>TIME_WAIT状态所带来的影响：</strong>  <p style="font-size: 10pt">当某个连接的一端处于TIME_WAIT状态时，该连接将不能再被使用。事实上，对于我们比较有现实意义的<br>是，这个端口将不能再被使用。某个端口处于TIME_WAIT状态(其实应该是这个连接)时，这意味着这个TCP<br>连接并没有断开(完全断开)，那么，如果你bind这个端口，就会失败。  <p style="font-size: 10pt">对于服务器而言，如果服务器突然crash掉了，那么它将无法再2MSL内重新启动，因为bind会失败。解决这<br>个问题的一个方法就是设置socket的SO_REUSEADDR选项。这个选项意味着你可以重用一个地址。  <p style="font-size: 10pt"><strong>对于TIME_WAIT的插曲：</strong>  <p style="font-size: 10pt">当建立一个TCP连接时，服务器端会继续用原有端口监听，同时用这个端口与客户端通信。而客户端默认情况<br>下会使用一个随机端口与服务器端的监听端口通信。有时候，为了服务器端的安全性，我们需要对客户端进行<br>验证，即限定某个IP某个特定端口的客户端。客户端可以使用bind来使用特定的端口。  <p style="font-size: 10pt">对于服务器端，当设置了SO_REUSEADDR选项时，它可以在2MSL内启动并listen成功。但是对于客户端，当使<br>用bind并设置SO_REUSEADDR时，如果在2MSL内启动，虽然bind会成功，但是在windows平台上connect会失败。<br>而在linux上则不存在这个问题。(我的实验平台：winxp, ubuntu7.10)  <p style="font-size: 10pt">要解决windows平台的这个问题，可以设置SO_LINGER选项。SO_LINGER选项决定调用close时，TCP的行为。<br>SO_LINGER涉及到linger结构体，如果设置结构体中l_onoff为非0，l_linger为0，那么调用close时TCP连接<br>会立刻断开，TCP不会将发送缓冲中未发送的数据发送，而是立即发送一个RST报文给对方，这个时候TCP连<br>接就不会进入TIME_WAIT状态。  <p style="font-size: 10pt">如你所见，这样做虽然解决了问题，但是并不安全。通过以上方式设置SO_LINGER状态，等同于设置SO_DONTLINGER<br>状态。  <p style="font-size: 10pt"><strong>断开连接时的意外：</strong><br>这个算不上断开连接时的意外，当TCP连接发生一些物理上的意外情况时，例如网线断开，linux上的TCP实现<br>会依然认为该连接有效，而windows则会在一定时间后返回错误信息。  <p style="font-size: 10pt">这似乎可以通过设置SO_KEEPALIVE选项来解决，不过不知道这个选项是否对于所有平台都有效。  <p style="font-size: 10pt"><strong>总结：</strong>  <p style="font-size: 10pt">个人感觉，越写越烂。接下来会讲到TCP的数据发送，这会涉及到滑动窗口各种定时器之类的东西。我真诚<br>希望各位能够多提意见。对于TCP连接的断开，我们只要清楚：<br>1. 在默认情况下，调用close时TCP会继续将数据发送完毕；<br>2. TIME_WAIT状态会导致的问题；<br>3. 连接意外断开时可能会出现的问题。<br>4. maybe more... </p><img src ="http://www.cppblog.com/kevinlynx/aggbug/49825.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-05-14 15:46 <a href="http://www.cppblog.com/kevinlynx/archive/2008/05/14/49825.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>tcp要点学习-建立连接</title><link>http://www.cppblog.com/kevinlynx/archive/2008/05/11/49482.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Sat, 10 May 2008 17:03:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/05/11/49482.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/49482.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/05/11/49482.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/49482.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/49482.html</trackback:ping><description><![CDATA[<p style="font-size: 10pt">Author : Kevin Lynx <p style="font-size: 10pt"><strong>准备：</strong>  <p style="font-size: 10pt">在这里本文将遵循上一篇文章的风格，只提TCP协议中的要点，这样我觉得可以更容易地掌握TCP。或者<br>根本谈不上掌握，对于这种纯理论的东西，即使你现在掌握了再多的细节，一段时间后也会淡忘。  <p style="font-size: 10pt">在以后各种细节中，因为我们会涉及到分析一些TCP中的数据报，因此一个协议包截获工具必不可少。在<br>&lt;TCP/IP详解&gt;中一直使用tcpdump。这里因为我的系统是windows，所以只好使用windows平台的tcpdump，<br>也就是<a href="http://www.winpcap.org/windump/" target="_blank">WinDump</a>。在使用WinDump之前，你需要安装该程序使用的库<a href="http://www.winpcap.org/install/default.htm#Developer" target="_blank">WinpCap</a>。  <p style="font-size: 10pt">关于WinDump的具体用法你可以从网上其他地方获取，这里我只稍微提一下。要让WinDump开始监听数据，<br>首先需要确定让其监听哪一个网络设备(或者说是网络接口)。你可以:  <p style="font-size: 10pt">&nbsp;</p> <div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #000000">windump </span><span style="color: #000000">-</span><span style="color: #000000">D </span></div> <p style="font-size: 10pt">&nbsp; <p style="font-size: 10pt">获取当前机器上的网络接口。然后使用：  <p style="font-size: 10pt">&nbsp;</p> <div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #000000">windump </span><span style="color: #000000">-</span><span style="color: #000000">i </span><span style="color: #000000">2</span><span style="color: #000000">&nbsp;</span></div> <p style="font-size: 10pt">&nbsp; <p style="font-size: 10pt">开始对网络接口2的数据监听。windump如同tcpdump(其实就是tcpdump)一样支持过滤表达式，windump<br>将会根据你提供的过滤表达式过滤不需要的网络数据包，例如：  <p style="font-size: 10pt">&nbsp;</p> <div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #000000">windump </span><span style="color: #000000">-</span><span style="color: #000000">i </span><span style="color: #000000">2</span><span style="color: #000000"> port </span><span style="color: #000000">4000</span><span style="color: #000000">&nbsp;</span></div> <p style="font-size: 10pt">&nbsp; <p style="font-size: 10pt">那么windump只会显示端口号为4000的网络数据。  <p style="font-size: 10pt"><strong>序号和确认号：</strong>  <p style="font-size: 10pt">要讲解TCP的建立过程，也就是那个所谓的三次握手，就会涉及到序号和确认号这两个东西。翻书到TCP<br>的报文头，有两个很重要的域(都是32位)就是序号域和确认号域。可能有些同学会对TCP那个报文头有所<br>疑惑(能看懂我在讲什么的会产生这样的疑惑么？)，这里我可以告诉你，你可以假想TCP的报文头就是个<br>C语言结构体(假想而已，去翻翻bsd对TCP的实现，肯定没这么简单)，那么大致上，所谓的TCP报文头就是：<br></p> <div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #000000">typedef </span><span style="color: #0000ff">struct</span><span style="color: #000000"> _tcp_header<br><img id="Codehighlighter1_27_429_Open_Image" onclick="this.style.display='none'; Codehighlighter1_27_429_Open_Text.style.display='none'; Codehighlighter1_27_429_Closed_Image.style.display='inline'; Codehighlighter1_27_429_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img id="Codehighlighter1_27_429_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_27_429_Closed_Text.style.display='none'; Codehighlighter1_27_429_Open_Image.style.display='inline'; Codehighlighter1_27_429_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align="top"></span><span id="Codehighlighter1_27_429_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_27_429_Open_Text"><span style="color: #000000">{<br><img id="Codehighlighter1_33_44_Open_Image" onclick="this.style.display='none'; Codehighlighter1_33_44_Open_Text.style.display='none'; Codehighlighter1_33_44_Closed_Image.style.display='inline'; Codehighlighter1_33_44_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img id="Codehighlighter1_33_44_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_33_44_Closed_Text.style.display='none'; Codehighlighter1_33_44_Open_Image.style.display='inline'; Codehighlighter1_33_44_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span id="Codehighlighter1_33_44_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff">/**/</span><span id="Codehighlighter1_33_44_Open_Text"><span style="color: #808080">///</span><span style="color: #008000"> 16位源端口号</span><span style="color: #808080"></span></span><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"><span style="color: #000000">&nbsp;&nbsp;&nbsp; unsigned </span><span style="color: #0000ff">short</span><span style="color: #000000"> src_port;<br><img id="Codehighlighter1_78_90_Open_Image" onclick="this.style.display='none'; Codehighlighter1_78_90_Open_Text.style.display='none'; Codehighlighter1_78_90_Closed_Image.style.display='inline'; Codehighlighter1_78_90_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img id="Codehighlighter1_78_90_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_78_90_Closed_Text.style.display='none'; Codehighlighter1_78_90_Open_Image.style.display='inline'; Codehighlighter1_78_90_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span id="Codehighlighter1_78_90_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff">/**/</span><span id="Codehighlighter1_78_90_Open_Text"><span style="color: #808080">///</span><span style="color: #008000"> 16位目的端口号</span><span style="color: #808080"></span></span><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"><span style="color: #000000">&nbsp;&nbsp;&nbsp; unsigned </span><span style="color: #0000ff">short</span><span style="color: #000000"> dst_port;<br><img id="Codehighlighter1_124_133_Open_Image" onclick="this.style.display='none'; Codehighlighter1_124_133_Open_Text.style.display='none'; Codehighlighter1_124_133_Closed_Image.style.display='inline'; Codehighlighter1_124_133_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img id="Codehighlighter1_124_133_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_124_133_Closed_Text.style.display='none'; Codehighlighter1_124_133_Open_Image.style.display='inline'; Codehighlighter1_124_133_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span id="Codehighlighter1_124_133_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff">/**/</span><span id="Codehighlighter1_124_133_Open_Text"><span style="color: #808080">///</span><span style="color: #008000"> 32位序号</span><span style="color: #808080"></span></span><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"><span style="color: #000000">&nbsp;&nbsp;&nbsp; unsigned </span><span style="color: #0000ff">long</span><span style="color: #000000"> seq_num;<br><img id="Codehighlighter1_165_175_Open_Image" onclick="this.style.display='none'; Codehighlighter1_165_175_Open_Text.style.display='none'; Codehighlighter1_165_175_Closed_Image.style.display='inline'; Codehighlighter1_165_175_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img id="Codehighlighter1_165_175_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_165_175_Closed_Text.style.display='none'; Codehighlighter1_165_175_Open_Image.style.display='inline'; Codehighlighter1_165_175_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span id="Codehighlighter1_165_175_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff">/**/</span><span id="Codehighlighter1_165_175_Open_Text"><span style="color: #808080">///</span><span style="color: #008000"> 32位确认号</span><span style="color: #808080"></span></span><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"><span style="color: #000000">&nbsp;&nbsp;&nbsp; unsigned </span><span style="color: #0000ff">long</span><span style="color: #000000"> ack_num;<br><img id="Codehighlighter1_207_244_Open_Image" onclick="this.style.display='none'; Codehighlighter1_207_244_Open_Text.style.display='none'; Codehighlighter1_207_244_Closed_Image.style.display='inline'; Codehighlighter1_207_244_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img id="Codehighlighter1_207_244_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_207_244_Closed_Text.style.display='none'; Codehighlighter1_207_244_Open_Image.style.display='inline'; Codehighlighter1_207_244_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span id="Codehighlighter1_207_244_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff">/**/</span><span id="Codehighlighter1_207_244_Open_Text"><span style="color: #808080">///</span><span style="color: #008000"> 16位标志位[4位首部长度，保留6位，ACK、SYN之类的标志位]</span><span style="color: #808080"></span></span><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"><span style="color: #000000">&nbsp;&nbsp;&nbsp; unsigned </span><span style="color: #0000ff">short</span><span style="color: #000000"> flag;<br><img id="Codehighlighter1_274_285_Open_Image" onclick="this.style.display='none'; Codehighlighter1_274_285_Open_Text.style.display='none'; Codehighlighter1_274_285_Closed_Image.style.display='inline'; Codehighlighter1_274_285_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img id="Codehighlighter1_274_285_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_274_285_Closed_Text.style.display='none'; Codehighlighter1_274_285_Open_Image.style.display='inline'; Codehighlighter1_274_285_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span id="Codehighlighter1_274_285_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff">/**/</span><span id="Codehighlighter1_274_285_Open_Text"><span style="color: #808080">///</span><span style="color: #008000"> 16位窗口大小</span><span style="color: #808080"></span></span><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"><span style="color: #000000">&nbsp;&nbsp;&nbsp; unsigned </span><span style="color: #0000ff">short</span><span style="color: #000000"> win_size;<br><img id="Codehighlighter1_319_329_Open_Image" onclick="this.style.display='none'; Codehighlighter1_319_329_Open_Text.style.display='none'; Codehighlighter1_319_329_Closed_Image.style.display='inline'; Codehighlighter1_319_329_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img id="Codehighlighter1_319_329_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_319_329_Closed_Text.style.display='none'; Codehighlighter1_319_329_Open_Image.style.display='inline'; Codehighlighter1_319_329_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span id="Codehighlighter1_319_329_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff">/**/</span><span id="Codehighlighter1_319_329_Open_Text"><span style="color: #808080">///</span><span style="color: #008000"> 16位校验和</span><span style="color: #808080"></span></span><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"><span style="color: #000000">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">short</span><span style="color: #000000"> crc_sum;<br><img id="Codehighlighter1_353_364_Open_Image" onclick="this.style.display='none'; Codehighlighter1_353_364_Open_Text.style.display='none'; Codehighlighter1_353_364_Closed_Image.style.display='inline'; Codehighlighter1_353_364_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img id="Codehighlighter1_353_364_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_353_364_Closed_Text.style.display='none'; Codehighlighter1_353_364_Open_Image.style.display='inline'; Codehighlighter1_353_364_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span id="Codehighlighter1_353_364_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff">/**/</span><span id="Codehighlighter1_353_364_Open_Text"><span style="color: #808080">///</span><span style="color: #008000"> 16位紧急指针</span><span style="color: #808080"></span></span><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"><span style="color: #000000">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">short</span><span style="color: #000000"> ptr;<br><img id="Codehighlighter1_384_428_Open_Image" onclick="this.style.display='none'; Codehighlighter1_384_428_Open_Text.style.display='none'; Codehighlighter1_384_428_Closed_Image.style.display='inline'; Codehighlighter1_384_428_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img id="Codehighlighter1_384_428_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_384_428_Closed_Text.style.display='none'; Codehighlighter1_384_428_Open_Image.style.display='inline'; Codehighlighter1_384_428_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span id="Codehighlighter1_384_428_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff">/**/</span><span id="Codehighlighter1_384_428_Open_Text"><span style="color: #808080">///</span><span style="color: #008000"> 可选选项<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span style="color: #808080">///</span><span style="color: #008000"> how to implement this ?&nbsp;&nbsp;&nbsp; </span><span style="color: #808080"></span></span><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top"><span style="color: #000000">}</span></span><span style="color: #000000"> tcp_header; <br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span></div> <p style="font-size: 10pt"><br>那么，这个序号和确认号是什么？TCP报文为每一个字节都设置一个序号，觉得很奇怪？这里并不是为每一<br>字节附加一个序号(那会是多么可笑的编程手法?)，而是为一个TCP报文附加一个序号，这个序号表示报文<br>中数据的第一个字节的序号，而其他数据则是根据离第一个数据的偏移来决定序号的，例如，现在有数据：<br>abcd。如果这段数据的序号为1200，那么a的序号就是1200，b的序号就是1201。而TCP发送的下一个数据包<br>的序号就会是上一个数据包最后一个字节的序号加一。例如efghi是abcd的下一个数据包，那么它的序号就<br>是1204。通过这种看似简单的方法，TCP就实现了为每一个字节设置序号的功能(终于明白为什么书上要告诉<br>我们‘为每一个字节设置一个序号’了吧?)。注意，设置序号是一种可以让TCP成为’可靠协议‘的手段。<br>TCP中各种乱七八糟的东西都是有目的的，大部分目的还是为了’可靠‘两个字。别把TCP看高深了，如果<br>让你来设计一个网络协议，目的需要告诉你是’可靠的‘，你就会明白为什么会产生那些乱七八糟的东西了。  <p style="font-size: 10pt">接着看，确认号是什么？因为TCP会对接收到的数据包进行确认，发送确认数据包时，就会设置这个确认号，<br>确认号通常表示接收方希望接收到的下一段报文的序号。例如某一次接收方收到序号为1200的4字节数举报，<br>那么它发送确认报文给发送方时，就会设置确认号为1204。  <p style="font-size: 10pt">大部分书上在讲确认号和序号时，都会说确认号是序号加一。这其实有点误解人，所以我才在这里废话了<br>半天(高手宽容下:D)。  <p style="font-size: 10pt"><strong>开始三次握手：</strong>  <p style="font-size: 10pt">如果你还不会简单的tcp socket编程，我建议你先去学学，这就好比你不会C++基本语法，就别去研究vtable<br>之类。  <p style="font-size: 10pt">三次握手开始于客户端试图连接服务器端。当你调用诸如connect的函数时，正常情况下就会开始三次握手。<br>随便在网上找张三次握手的图：  <p style="font-size: 10pt"><a href="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/tcp_EB3/connection.jpg"><img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="210" alt="connection" src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/tcp_EB3/connection_thumb.jpg" width="458" border="0"></a>  <p style="font-size: 10pt">如前文所述，三次握手也就是产生了三个数据包。客户端主动连接，发送SYN被设置了的报文(注意序号和<br>确认号，因为这里不包含用户数据，所以序号和确认号就是加一减一的关系)。服务器端收到该报文时，正<br>常情况下就发送SYN和ACK被设置了的报文作为确认，以及告诉客户端：我想打开我这边的连接(双工)。客户<br>端于是再对服务器端的SYN进行确认，于是再发送ACK报文。然后连接建立完毕。对于阻塞式socket而言，你<br>的connect可能就返回成功给你。  <p style="font-size: 10pt">在进行了铺天盖地的罗利巴索的基础概念的讲解后，看看这个连接建立的过程，是不是简单得几近无聊？  <p style="font-size: 10pt">我们来实际点，写个最简单的客户端代码：<br></p> <div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #000000">&nbsp;&nbsp; sockaddr_in addr;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp; memset( </span><span style="color: #000000">&amp;</span><span style="color: #000000">addr, </span><span style="color: #000000">0</span><span style="color: #000000">, </span><span style="color: #0000ff">sizeof</span><span style="color: #000000">( addr ) );<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp; addr.sin_family </span><span style="color: #000000">=</span><span style="color: #000000"> AF_INET;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp; addr.sin_port </span><span style="color: #000000">=</span><span style="color: #000000"> htons( </span><span style="color: #000000">80</span><span style="color: #000000"> );<br><img id="Codehighlighter1_129_146_Open_Image" onclick="this.style.display='none'; Codehighlighter1_129_146_Open_Text.style.display='none'; Codehighlighter1_129_146_Closed_Image.style.display='inline'; Codehighlighter1_129_146_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img id="Codehighlighter1_129_146_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_129_146_Closed_Text.style.display='none'; Codehighlighter1_129_146_Open_Image.style.display='inline'; Codehighlighter1_129_146_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span id="Codehighlighter1_129_146_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff">/**/</span><span id="Codehighlighter1_129_146_Open_Text"><span style="color: #808080">///</span><span style="color: #008000"> 220.181.37.55</span><span style="color: #808080"></span></span><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #000000">&nbsp;&nbsp;&nbsp; addr.sin_addr.s_addr </span><span style="color: #000000">=</span><span style="color: #000000"> inet_addr( </span><span style="color: #000000">"</span><span style="color: #000000">220.181.37.55</span><span style="color: #000000">"</span><span style="color: #000000"> );<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp; printf( </span><span style="color: #000000">"</span><span style="color: #000000">%s : connecting to server.\n</span><span style="color: #000000">"</span><span style="color: #000000">, _str_time() );<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">int</span><span style="color: #000000"> err </span><span style="color: #000000">=</span><span style="color: #000000"> connect( s, (sockaddr</span><span style="color: #000000">*</span><span style="color: #000000">) </span><span style="color: #000000">&amp;</span><span style="color: #000000">addr, </span><span style="color: #0000ff">sizeof</span><span style="color: #000000">( addr ) ); <br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span></div> <p style="font-size: 10pt">&nbsp;<br>主要就是connect。运行程序前我们运行windump：  <p style="font-size: 10pt">&nbsp;</p> <div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #000000">windump </span><span style="color: #000000">-</span><span style="color: #000000">i </span><span style="color: #000000">2</span><span style="color: #000000"> host </span><span style="color: #000000">220.181</span><span style="color: #000000">.</span><span style="color: #000000">37.55</span><span style="color: #000000">&nbsp;</span></div> <p style="font-size: 10pt">&nbsp;</p> <div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #000000">00</span><span style="color: #000000">:</span><span style="color: #000000">38</span><span style="color: #000000">:</span><span style="color: #000000">22.979229</span><span style="color: #000000"> IP noname.domain.</span><span style="color: #000000">4397</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&gt;</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">220.181</span><span style="color: #000000">.</span><span style="color: #000000">37.55</span><span style="color: #000000">.</span><span style="color: #000000">80</span><span style="color: #000000">: S </span><span style="color: #000000">2523219966</span><span style="color: #000000">:</span><span style="color: #000000">2523219966</span><span style="color: #000000">(</span><span style="color: #000000">0</span><span style="color: #000000">) win </span><span style="color: #000000">65535</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">mss </span><span style="color: #000000">1460</span><span style="color: #000000">,nop,nop,sackOK</span><span style="color: #000000">&gt;</span><span style="color: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #000000">00</span><span style="color: #000000">:</span><span style="color: #000000">38</span><span style="color: #000000">:</span><span style="color: #000000">23.024254</span><span style="color: #000000"> IP </span><span style="color: #000000">220.181</span><span style="color: #000000">.</span><span style="color: #000000">37.55</span><span style="color: #000000">.</span><span style="color: #000000">80</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&gt;</span><span style="color: #000000"> noname.domain.</span><span style="color: #000000">4397</span><span style="color: #000000">: S </span><span style="color: #000000">1277008647</span><span style="color: #000000">:</span><span style="color: #000000">1277008647</span><span style="color: #000000">(</span><span style="color: #000000">0</span><span style="color: #000000">) ack </span><span style="color: #000000">2523219967</span><span style="color: #000000"> win </span><span style="color: #000000">2920</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">mss </span><span style="color: #000000">1440</span><span style="color: #000000">,nop,nop,sackOK</span><span style="color: #000000">&gt;</span><span style="color: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #000000">00</span><span style="color: #000000">:</span><span style="color: #000000">38</span><span style="color: #000000">:</span><span style="color: #000000">23.024338</span><span style="color: #000000"> IP noname.domain.</span><span style="color: #000000">4397</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&gt;</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">220.181</span><span style="color: #000000">.</span><span style="color: #000000">37.55</span><span style="color: #000000">.</span><span style="color: #000000">80</span><span style="color: #000000">: . ack </span><span style="color: #000000">1</span><span style="color: #000000"> win </span><span style="color: #000000">65535</span><span style="color: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span></div> <p style="font-size: 10pt">&nbsp; <p style="font-size: 10pt">如何分析windump的结果，建议参看&lt;tcp/ip详解&gt;中对于tcpdump的描述。  <p style="font-size: 10pt"><strong>建立连接的附加信息：</strong>  <p style="font-size: 10pt">虽然SYN、ACK之类的报文没有用户数据，但是TCP还是附加了其他信息。最为重要的就是附加的MSS值。这个<br>可以被协商的MSS值基本上就只在建立连接时协商。如以上数据表示，MSS为1460字节。  <p style="font-size: 10pt"><strong>连接的意外：</strong>  <p style="font-size: 10pt">连接的意外我大致分为两种情况(也许还有更多情况)：目的主机不可达、目的主机并没有在指定端口监听。<br>当目的主机不可达时，也就是说，SYN报文段根本无法到达对方(如果你的机器根本没插网线，你就不可达)，<br>那么TCP收不到任何回复报文。这个时候，你会看到TCP中的定时器机制出现了。TCP对发出的SYN报文进行<br>计时，当在指定时间内没有得到回复报文时，TCP就会重传刚才的SYN报文。通常，各种不同的TCP实现对于<br>这个超时值都不同，但是据我观察，重传次数基本上都是3次。例如，我连接一个不可达的主机：  <p style="font-size: 10pt">&nbsp;</p> <div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #000000">12</span><span style="color: #000000">:</span><span style="color: #000000">39</span><span style="color: #000000">:</span><span style="color: #000000">50.560690</span><span style="color: #000000"> IP cd</span><span style="color: #000000">-</span><span style="color: #000000">zhangmin.</span><span style="color: #000000">1573</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&gt;</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">220.181</span><span style="color: #000000">.</span><span style="color: #000000">37.55</span><span style="color: #000000">.</span><span style="color: #000000">1024</span><span style="color: #000000">: S </span><span style="color: #000000">3117975575</span><span style="color: #000000">:</span><span style="color: #000000">3117975575</span><span style="color: #000000">(</span><span style="color: #000000">0</span><span style="color: #000000">) win </span><span style="color: #000000">65535</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">mss </span><span style="color: #000000">1460</span><span style="color: #000000">,nop,nop,sackOK</span><span style="color: #000000">&gt;</span><span style="color: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #000000">12</span><span style="color: #000000">:</span><span style="color: #000000">39</span><span style="color: #000000">:</span><span style="color: #000000">53.538734</span><span style="color: #000000"> IP cd</span><span style="color: #000000">-</span><span style="color: #000000">zhangmin.</span><span style="color: #000000">1573</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&gt;</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">220.181</span><span style="color: #000000">.</span><span style="color: #000000">37.55</span><span style="color: #000000">.</span><span style="color: #000000">1024</span><span style="color: #000000">: S </span><span style="color: #000000">3117975575</span><span style="color: #000000">:</span><span style="color: #000000">3117975575</span><span style="color: #000000">(</span><span style="color: #000000">0</span><span style="color: #000000">) win </span><span style="color: #000000">65535</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">mss </span><span style="color: #000000">1460</span><span style="color: #000000">,nop,nop,sackOK</span><span style="color: #000000">&gt;</span><span style="color: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #000000">12</span><span style="color: #000000">:</span><span style="color: #000000">39</span><span style="color: #000000">:</span><span style="color: #000000">59.663726</span><span style="color: #000000"> IP cd</span><span style="color: #000000">-</span><span style="color: #000000">zhangmin.</span><span style="color: #000000">1573</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&gt;</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">220.181</span><span style="color: #000000">.</span><span style="color: #000000">37.55</span><span style="color: #000000">.</span><span style="color: #000000">1024</span><span style="color: #000000">: S </span><span style="color: #000000">3117975575</span><span style="color: #000000">:</span><span style="color: #000000">3117975575</span><span style="color: #000000">(</span><span style="color: #000000">0</span><span style="color: #000000">) win </span><span style="color: #000000">65535</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">mss </span><span style="color: #000000">1460</span><span style="color: #000000">,nop,nop,sackOK</span><span style="color: #000000">&gt;</span></div> <p style="font-size: 10pt">&nbsp; <p style="font-size: 10pt">发出了三个序号一样的SYN报文，但是没有得到一个回复报文(废话)。每一个SYN报文之间的间隔时间都是<br>有规律的，在windows上是3秒6秒9秒12秒。上面的数据你看不到12秒这个数据，因为这是第三个报文发出的<br>时间和connect返回错误信息时的时间之差。另一方面，如果连接同一个网络，这个间隔时间又不同。例如<br>直接连局域网，间隔时间就差不多为500ms。  <p style="font-size: 10pt">(我强烈建议你能运行windump去试验这里提到的每一个现象，如果你在ubuntu下使用tcpdump，记住sudo :D)  <p style="font-size: 10pt">出现意外的第二种情况是如果主机数据包可达，但是试图连接的端口根本没有监听，那么发送SYN报文的这<br>方会收到RST被设置的报文(connect也会返回相应的信息给你)，例如：  <p style="font-size: 10pt">&nbsp;</p> <div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #000000">13</span><span style="color: #000000">:</span><span style="color: #000000">37</span><span style="color: #000000">:</span><span style="color: #000000">22.202532</span><span style="color: #000000"> IP cd</span><span style="color: #000000">-</span><span style="color: #000000">zhangmin.</span><span style="color: #000000">1658</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&gt;</span><span style="color: #000000"> 7AURORA</span><span style="color: #000000">-</span><span style="color: #000000">CCTEST.</span><span style="color: #000000">7100</span><span style="color: #000000">: S </span><span style="color: #000000">2417354281</span><span style="color: #000000">:</span><span style="color: #000000">2417354281</span><span style="color: #000000">(</span><span style="color: #000000">0</span><span style="color: #000000">) win </span><span style="color: #000000">65535</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">mss </span><span style="color: #000000">1460</span><span style="color: #000000">,nop,nop,sackOK</span><span style="color: #000000">&gt;</span><span style="color: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #000000">13</span><span style="color: #000000">:</span><span style="color: #000000">37</span><span style="color: #000000">:</span><span style="color: #000000">22.202627</span><span style="color: #000000"> IP 7AURORA</span><span style="color: #000000">-</span><span style="color: #000000">CCTEST.</span><span style="color: #000000">7100</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&gt;</span><span style="color: #000000"> cd</span><span style="color: #000000">-</span><span style="color: #000000">zhangmin.</span><span style="color: #000000">1658</span><span style="color: #000000">: R </span><span style="color: #000000">0</span><span style="color: #000000">:</span><span style="color: #000000">0</span><span style="color: #000000">(</span><span style="color: #000000">0</span><span style="color: #000000">) ack </span><span style="color: #000000">2417354282</span><span style="color: #000000"> win </span><span style="color: #000000">0</span><span style="color: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #000000">13</span><span style="color: #000000">:</span><span style="color: #000000">37</span><span style="color: #000000">:</span><span style="color: #000000">22.711415</span><span style="color: #000000"> IP cd</span><span style="color: #000000">-</span><span style="color: #000000">zhangmin.</span><span style="color: #000000">1658</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&gt;</span><span style="color: #000000"> 7AURORA</span><span style="color: #000000">-</span><span style="color: #000000">CCTEST.</span><span style="color: #000000">7100</span><span style="color: #000000">: S </span><span style="color: #000000">2417354281</span><span style="color: #000000">:</span><span style="color: #000000">2417354281</span><span style="color: #000000">(</span><span style="color: #000000">0</span><span style="color: #000000">) win </span><span style="color: #000000">65535</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">mss </span><span style="color: #000000">1460</span><span style="color: #000000">,nop,nop,sackOK</span><span style="color: #000000">&gt;</span><span style="color: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #000000">13</span><span style="color: #000000">:</span><span style="color: #000000">37</span><span style="color: #000000">:</span><span style="color: #000000">22.711498</span><span style="color: #000000"> IP 7AURORA</span><span style="color: #000000">-</span><span style="color: #000000">CCTEST.</span><span style="color: #000000">7100</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&gt;</span><span style="color: #000000"> cd</span><span style="color: #000000">-</span><span style="color: #000000">zhangmin.</span><span style="color: #000000">1658</span><span style="color: #000000">: R </span><span style="color: #000000">0</span><span style="color: #000000">:</span><span style="color: #000000">0</span><span style="color: #000000">(</span><span style="color: #000000">0</span><span style="color: #000000">) ack </span><span style="color: #000000">1</span><span style="color: #000000"> win </span><span style="color: #000000">0</span><span style="color: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #000000">13</span><span style="color: #000000">:</span><span style="color: #000000">37</span><span style="color: #000000">:</span><span style="color: #000000">23.367733</span><span style="color: #000000"> IP cd</span><span style="color: #000000">-</span><span style="color: #000000">zhangmin.</span><span style="color: #000000">1658</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&gt;</span><span style="color: #000000"> 7AURORA</span><span style="color: #000000">-</span><span style="color: #000000">CCTEST.</span><span style="color: #000000">7100</span><span style="color: #000000">: S </span><span style="color: #000000">2417354281</span><span style="color: #000000">:</span><span style="color: #000000">2417354281</span><span style="color: #000000">(</span><span style="color: #000000">0</span><span style="color: #000000">) win </span><span style="color: #000000">65535</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">mss </span><span style="color: #000000">1460</span><span style="color: #000000">,nop,nop,sackOK</span><span style="color: #000000">&gt;</span><span style="color: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #000000">13</span><span style="color: #000000">:</span><span style="color: #000000">37</span><span style="color: #000000">:</span><span style="color: #000000">23.367826</span><span style="color: #000000"> IP 7AURORA</span><span style="color: #000000">-</span><span style="color: #000000">CCTEST.</span><span style="color: #000000">7100</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&gt;</span><span style="color: #000000"> cd</span><span style="color: #000000">-</span><span style="color: #000000">zhangmin.</span><span style="color: #000000">1658</span><span style="color: #000000">: R </span><span style="color: #000000">0</span><span style="color: #000000">:</span><span style="color: #000000">0</span><span style="color: #000000">(</span><span style="color: #000000">0</span><span style="color: #000000">) ack </span><span style="color: #000000">1</span><span style="color: #000000"> win </span><span style="color: #000000">0</span><span style="color: #000000">&nbsp;</span></div> <p style="font-size: 10pt">&nbsp; <p style="font-size: 10pt">可以看出，7AURORA-CCTEST.7100返回了RST报文给我，但是我这边根本不在乎这个报文，继续发送SYN报文。<br>三次过后connect就返回了。(数据反映的事实是这样)  <p style="font-size: 10pt"><strong>关于listen:</strong>  <p style="font-size: 10pt">TCP服务器端会维护一个新连接的队列。当新连接上的客户端三次握手完成时，就会将其放入这个队列。这个队  <p style="font-size: 10pt">列的大小是通过listen设置的。当这个队列满时，如果有新的客户端试图连接（发送SYN），服务器端丢弃报文，  <p style="font-size: 10pt">同时不做任何回复。  <p style="font-size: 10pt"><strong>总结：</strong><br>TCP连接的建立的相关要点就是这些(or more?)。正常情况下就是三次握手，非正常情况下就是SYN三次超时，<br>以及收到RST报文却被忽略。 </p><img src ="http://www.cppblog.com/kevinlynx/aggbug/49482.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-05-11 01:03 <a href="http://www.cppblog.com/kevinlynx/archive/2008/05/11/49482.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>