﻿<?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-随笔分类-game develop</title><link>http://www.cppblog.com/kevinlynx/category/6338.html</link><description>低调做技术__
C/C++\MMORPG服务器\模块架构__ TODO：linux env/read more books __Kevin Lynx</description><language>zh-cn</language><lastBuildDate>Wed, 29 Aug 2012 07:02:34 GMT</lastBuildDate><pubDate>Wed, 29 Aug 2012 07:02:34 GMT</pubDate><ttl>60</ttl><item><title>MMO聊天服务器设计</title><link>http://www.cppblog.com/kevinlynx/archive/2012/08/29/188604.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Wed, 29 Aug 2012 03:37:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2012/08/29/188604.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/188604.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2012/08/29/188604.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/188604.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/188604.html</trackback:ping><description><![CDATA[<div>
<div>
<p>MMO中的聊天服务主要功能就是做客户端之间的聊天内容转发。但是聊天的形式有很多，例如私聊、同场景聊、队伍内聊、工会内聊、全服务器聊、甚至临 时组建房间聊。这些逻辑功能其实都是可以做在逻辑服务器上的，最多改改世界服务器，但是这样完成功能的话，不免将聊天本身的逻辑与游戏逻辑关联起来。我们 希望做得更上一层，将聊天服务本身脱离开来。但是独立聊天服务还不够，因为就算独立出来了，也有可能在实现上与具体的游戏逻辑相关联。所以，我们做了进一 步的抽象，想实现一个更为通用的聊天服务器。</p>
<h2>设计实现</h2>
<h3>实体设计</h3>
<p>聊天这个过程，我们将其抽象为实体(entity)与实体间的对话。这个实体概念其实很宽泛。任何可接收聊天消息的都算做实体，例如单个玩家、一个 场景、一个队伍、一个房间、一个工会、甚至整个服务器。这个思想其实就是支持整个聊天服务器设计的最根本思想。最开始，我将聊天服务器分为个体和组两个概 念，其实这个抽象程度都太低，并且会导致实现上的复杂。相反，将整个系统完全使用实体这个概念来组装，就简单很多。当然，实体是有很多种类的，在处理接收 聊天消息这个动作时，其处理方式就不同。例如单个玩家实体仅做消息的发送，场景实体则是将消息发给场景内的所有玩家，队伍实体就是将消息发给队伍内的所有 玩家。从这一点来看，我们的实体种类其实并不多，因为场景、队伍这些，都是组实体(group entity)。用C++来描述：</p>
<div>
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;Entity&nbsp;{<br /></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;send&nbsp;text&nbsp;to&nbsp;this&nbsp;entity</span><span style="color: #008000;"><br /></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">virtual</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">bool</span><span style="color: #000000;">&nbsp;Send(Entity&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">sender,&nbsp;</span><span style="color: #0000ff;">const</span><span style="color: #000000;">&nbsp;std::</span><span style="color: #0000ff;">string</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">text)&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;<br /><br /></span><span style="color: #0000ff;">protected</span><span style="color: #000000;">:<br />&nbsp;&nbsp;&nbsp;&nbsp;GUID&nbsp;m_id;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;m_type;<br />};<br /><br /></span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;SockEntity&nbsp;:&nbsp;pubilc&nbsp;Entity&nbsp;{<br /></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">virtual</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">bool</span><span style="color: #000000;">&nbsp;Send(Entity&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">sender,&nbsp;</span><span style="color: #0000ff;">const</span><span style="color: #000000;">&nbsp;std::</span><span style="color: #0000ff;">string</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">text)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;find&nbsp;the&nbsp;map&nbsp;socket&nbsp;and&nbsp;send&nbsp;text&nbsp;to&nbsp;the&nbsp;socket</span><span style="color: #008000;"><br /></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">long</span><span style="color: #000000;">&nbsp;socket&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;FindSocket(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Message&nbsp;msg(MSG_CS2E_SENDTEXT);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;msg.Add(sender</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">ID());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;msg.Add(text);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;msg.SendToSocket(socket);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">true</span><span style="color: #000000;">;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />};<br /><br /></span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;GroupEntity&nbsp;:&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Entity&nbsp;{<br /></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">virtual</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">bool</span><span style="color: #000000;">&nbsp;Send(Entity&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">sender,&nbsp;</span><span style="color: #0000ff;">const</span><span style="color: #000000;">&nbsp;std::</span><span style="color: #0000ff;">string</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">text)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(std::list</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Entity</span><span style="color: #000000;">*&gt;</span><span style="color: #000000;">::const_iterator&nbsp;it&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;m_mems.begin();&nbsp;it&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;m_mems.end();&nbsp;</span><span style="color: #000000;">++</span><span style="color: #000000;">it)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(</span><span style="color: #000000;">*</span><span style="color: #000000;">it)</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">Send(sender,&nbsp;text);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">true</span><span style="color: #000000;">;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /></span><span style="color: #0000ff;">private</span><span style="color: #000000;">:<br />&nbsp;&nbsp;&nbsp;&nbsp;std::list</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Entity</span><span style="color: #000000;">*&gt;</span><span style="color: #000000;">&nbsp;m_mems;<br />};<br /><br /></span></div><pre><code>
</code></pre>
</div>
<p><code>SockEntity</code>用于表示物理上聊天服务器的客户端，例如游戏客户端。</p>
<h3>网络拓扑</h3>
<p>实际上，除了转发聊天内容外(Entity::Send)，实体还有很多其他行为，例如最起码的，创建组实体，往组实体里添加成员等。在设计上，组 实体的创建由逻辑服务器或者其他服务器来完成，目前游戏客户端是没有创建组实体的权限的（实现上我们还为实体添加了权限验证机制）。在网络拓扑上，聊天服 务器始终是作为服务器角色，而它的客户端则包括游戏客户端、逻辑服务器、甚至其他服务器，这样聊天服务器在提供了固定的协议后，它就是完全独立的，不依赖 任何其他组件：</p>
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CS<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">/</span><span style="color: #000000;">&nbsp;&nbsp;</span><span style="color: #000000;">|</span><span style="color: #000000;">&nbsp;&nbsp;\<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">/</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">|</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;\<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">/</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">|</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;\<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GC&nbsp;&nbsp; GC&nbsp;&nbsp;&nbsp;GS<br /></span></div><pre><code>
</code></pre>
<p>(CS: Chat Server, GC: Game Client, GS: Game Server)</p>
<p>基于此，我们扩充了Entity的类体系：</p>
<div>
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;ClientEntity&nbsp;:&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;SockEntity&nbsp;{<br /><br /></span><span style="color: #0000ff;">private</span><span style="color: #000000;">:<br />&nbsp;&nbsp;&nbsp;&nbsp;GUID&nbsp;m_gsEntity;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;标示该客户端实体位于哪个逻辑服务器实体上</span><span style="color: #008000;"><br /></span><span style="color: #000000;">};<br /><br /></span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;GSEntity&nbsp;:&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;SockEntity&nbsp;{<br />};<br /></span></div>
</div>
<h3>消息协议</h3>
<p>聊天服务器的核心实现，其实就是针对以上实体做操作。因此，聊天服务器的消息协议方面，也主要是针对这些实体的操作，包括：</p>
<ul>
     <li>
     <p>创建</p>
     <p> 实体的创建很简单，不同的实体其创建所需的参数都不一样。例如客户端实体创建时需要传入一个逻辑服务器实体的ID，组实体的创建可以携带组成员实体列表。 为了处理权限和安全问题，在具体实现上，逻辑服务器实体的创建是由聊天服务器本地的配置决定，即聊天服务器启动则根据配置创建好逻辑服务器实体；客户端实 体是当角色进入逻辑服务器后，由服务器创建，客户端无法创建实体。</p>
     </li>
     <li>
     <p>删除</p>
     <p> 实体的删除为了处理方便，约定删除请求必须由实体的创建者发起。因为从逻辑上将，某个模块如果可以创建一个实体，那么其必然知道什么时候该删除这个实体。</p>
     </li>
     <li>
     <p>修改</p>
     <p> 修改指的是修改实体内部实现的一些属性，例如组实体修改其组成员。这个操作是非常重要的。对于<code>SockEntity</code>而 言，修改意味着修改其连接状态，例如当逻辑服务器在聊天服务器上创建了客户端实体后，实际上此时客户端并没有在网络方面连接聊天服务器，此时这个<code>Entity</code>实 际上是不可用的，因为它无法用于发送消息。这个时候我们标志该实体的状态为非连接状态。当客户端主动连接上聊天服务器后，客户端就主动发起修改自己对应的 客户端实体请求，该请求将自己的状态修改为连接状态。当客户端关闭时，聊天服务器网络层接收到连接断开通知，该通知肯定是早于逻辑服务器发来的删除实体通 知的，此时将该客户端实体状态修改为断开状态，并在接收到逻辑服务器删除实体通知时将其真正删除。这里展示的这种状态修改策略，实际上在整个系统中是非常 重要的。它用于指导网络连接和上层逻辑之间的关系，因为整个聊天系统中，各个进程的状态是不可预料的（随时可能宕掉），当某个进程尤其是逻辑服务器宕掉 后，聊天服务器是得不到任何正常逻辑通知的，它只能得到网络连接的通知。</p>
     </li>
</ul>
<h2>总结</h2>
<p>整个系统实现下来，实际上是非常简单的，代码量也很少。当然还有很多细节问题，例如聊天信息中携带物品信息，这涉及到异步预处理聊天内容，这里就不 方便细说了。</p>
<p> 原文地址： <a href="http://codemacro.com/2012/08/29/mmo-chat-server/">http://codemacro.com/2012/08/29/mmo-chat-server/</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>
</div><img src ="http://www.cppblog.com/kevinlynx/aggbug/188604.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> 2012-08-29 11:37 <a href="http://www.cppblog.com/kevinlynx/archive/2012/08/29/188604.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>谈谈我们的游戏逻辑服务器实现（二）</title><link>http://www.cppblog.com/kevinlynx/archive/2012/04/25/172741.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Wed, 25 Apr 2012 08:55:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2012/04/25/172741.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/172741.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2012/04/25/172741.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/172741.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/172741.html</trackback:ping><description><![CDATA[<p>原文链接：<a href="http://codemacro.com/2012/04/25/game-server-info-2/">http://codemacro.com/2012/04/25/game-server-info-2/</a></p>
<p>上一篇谈了一些关键技术的实现方案。本篇描述一些遇到的问题。</p>
<h2>一</h2>
<p>在策划制作完了几个职业后（主要是技能制作），大概去年年底公司内部进行了一次混战测试。30个角色在一个场景进行混战，测试结果从技术上来说非常不理想。首先是客户端和服务器都巨卡无比。服务器CPU一直是满负载状态。而客户端又频繁宕机。</p>
<p>我们关注的主要问题，是服务器CPU满负载问题。最开始，我通过日志初步定位为网络模块问题，因为逻辑线程表现不是那么差。然后考虑到技能过程中的特效、动作都是通过服务器消息驱动，并且本身特效和动作就比一般网游复杂，通过逐一屏蔽这一部分功能，最终确认确为网络模块导致。然后团队决定从两方面努力：重写网络模块，改善性能；改善技能实现机制，将表现类逻辑移到客户端。</p>
<p>至于网络模块，在后来才发现，虽然网络流量过高，但导致网络线程CPU满的原因竟然是网络模块自身的流量限制导致。而技能实现机制的改善，考虑到改动的成本，最终使用了一种RPC机制，让服务器脚本可以调用客户端脚本，并且支持传入复杂参数。然后策划通过一些关键数据在客户端计算出特效、动作之类。</p>
<p>此外，程序将更多的技能属性广播给客户端，一个客户端上保存了周围角色的技能数据，从而可以进行更多的客户端逻辑。这一块具体的修改当然还是策划在做（我们的脚本策划基本就是半个程序员）。后经测试，效果改善显著。</p>
<h2>二</h2>
<p>在策划制作了一个PVP竞技副本后，服务器在10V10测试过程中又表现出CPU负载较高的情况。这个问题到目前为止依然存在，只不过情况略微不同。</p>
<p>首先是触发器生命周期的问题。触发器自身包含最大触发次数、存留时间等需求，即当触发一定次数，或超过存留时间后，需要由程序自动删除；另一方面，触发器可以是定时器类型，而定时器也决定了触发器的生命周期。这一块代码写的非常糟糕，大概就是管理职责划分不清，导致出现对象自己删除自己，而删除后还在依赖自己做逻辑。</p>
<p>但这样的逻辑，最多就是导致野指针的出现。不过，这种混乱的代码，也更容易导致BUG。例如，在某种情况下触发器得不到自动删除了。但这个BUG并不是直接暴露的，直接暴露的，是CPU满了。我们的怪物AI在脚本中是通过定时器类触发器驱动的，每次AI帧完了后就注册一个触发器，以驱动下一次AI帧。由于这个BUG导致触发器没有被删除，从而导致服务器上触发器的数量急剧增加。但，这也就导致内存增长吧？</p>
<p>另一个巧合的原因在于，在当时的版本中，触发器是保存一个表里的，即定时器类触发器、属性类触发器、移动类触发器等都在一个表里。每次任意触发器事件发生时，例如属性改变，都会遍历这个表，检查其是否触发。</p>
<p>基于以上原因，悲剧就发生了。在这个怪物的AI脚本里，有行代码设置了怪物的属性。这会导致程序遍历该怪物的所有触发器。而这个怪物的触发器数量一直在增长。然后就出现了在很多游戏帧里出现过长的遍历操作，CPU就上去了。</p>
<p>找到这个问题了几乎花了我一天的时间。因为脚本代码不是我写的，触发器的最初版本也不是我写的。通过逐一排除可能的代码，最终竟然发现是一行毫不起眼的属性改变导致。这个问题的查找流程，反映了将大量逻辑放在脚本中的不便之处：查起问题来实在吃力不讨好。</p>
<p>修复了这个BUG后，我又对触发器管理做了简单的优化。将触发器列表改成二级表，将触发器按照类型保存成几个列表。每次触发事件时，找出对应类型的表遍历。</p>
<h3>改进</h3>
<p>除了修改触发器的维护数据结构外，程序还实现了一套性能统计机制，大概就是统计某个函数在一段时间内的执行时间情况。最初这套机制仅用于程序，但考虑到脚本代码在整个项目中的比例，又决定将其应用到脚本中。</p>
<p>这个统计需要在函数进入退出时做一些事情，C++中可以通过类对象的构建和析构完成，但lua中没有类似机制。最初，我使用了lua的调试库来捕获函数进入/退出事件，但后来又害怕这种方式本身存在效率消耗，就取消了。我们使用lua的方式，不仅仅是全局函数，还包括函数对象。而函数对象是没有名字标示的，这对于日志记录不是什么好事。为了解决这个问题，我只好对部分功能做了封装，并让策划显示填入函数对于的字符串标示。</p>
<p>除此之外，因为触发器是一种重要的敏感资源，我又加入了一个专门的触发器统计模块，分别统计触发器的类型数量、游戏对象拥有的触发器数量等。</p>
<h2>END</h2>
<p>到目前为止，导致服务器CPU负载过高，一般都是由BUG导致。这些BUG通常会造成一个过长的列表，然后有针对这个列表的遍历操作，从而导致CPU负载过高。更重要的，我们使用了这么多的脚本去开发这个游戏，如何找到一个更有效合理的监测方法，如何让程序框架更稳定，则是接下来更困难而又必须去面对的事情。</p><img src ="http://www.cppblog.com/kevinlynx/aggbug/172741.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> 2012-04-25 16:55 <a href="http://www.cppblog.com/kevinlynx/archive/2012/04/25/172741.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>谈谈我们的游戏逻辑服务器实现（一）</title><link>http://www.cppblog.com/kevinlynx/archive/2012/04/25/172739.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Wed, 25 Apr 2012 08:54:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2012/04/25/172739.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/172739.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2012/04/25/172739.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/172739.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/172739.html</trackback:ping><description><![CDATA[<p>原文链接：<a href="http://codemacro.com/2012/04/23/game-server-info-1/">http://codemacro.com/2012/04/23/game-server-info-1/</a></p>
<p>我们的逻辑服务器(Game Server，以下简称GS)主要逻辑大概是从去年夏天开始写的。因为很多基础模块，包括整体结构沿用了上个项目的代码，所以算不上从头开始做。转眼又快一年，我觉得回头总结下对于经验的积累太有必要。</p>
<h2>整体架构</h2>
<p>GS的架构很大程度取决于游戏的功能需求，当然更受限于上个项目的基础架构。基础架构包括场景、对象的关系管理，消息广播等。</p>
<h3>需求</h3>
<p>这一回，程序员其实已经不需要太过关心需求。因为我们决定大量使用脚本。到目前为止整个项目主要还是集中在技能开发上。而这个使用脚本的度，就是技能全部由策划使用脚本制作，程序员不会去编写某个具体技能，也不会提供某种配置方式去让策划通过配置来开发技能。这真是一个好消息，不管对于程序员而言，还是对于策划而言。但后来，我觉得对于这一点还是带来了很多问题。</p>
<h3>实现</h3>
<p>基于以上需求，程序员所做的就是开发框架，制定功能实现方案。脚本为了与整个游戏框架交互，我们制定了&#8220;触发器&#8220;这个概念，大概就是一种事件系统。</p>
<p>这个触发器系统，简单来说，就是提供一种&#8220;关心&#8220;、&#8221;通知&#8220;的交互方式，也就是一般意义上的事件机制。例如，脚本中告诉程序它关心某个对象的移动，那么当程序中该对象产生移动时，就通知脚本。脚本中可以关心很多东西，包括对象属性，其关心的方式包括属性值改变、变大、变小，各种变化形式；对象开始移动，移动停止；对象碰撞，这个会单独谈谈；定时器等。</p>
<p>除了触发器系统外，还有个较大的系统是游戏对象的属性系统。对象的属性必然是游戏逻辑中策划最关心最容易改动的模块。既然我们程序的大方向是尽可能地不关心策划需求，所以对象属性在设计上就不可能去编写某个具体属性，更不会编写这个属性相关的逻辑功能。简单来说，程序为每个对象维护一个key-value表，也就是属性名、属性值表。该表的内容由脚本填入，脚本享有存取权限。然后脚本中就可以围绕某个属性来编写功能，而程序仅起存储作用。</p>
<p>第三，怪物AI模块。AI模块的设计在开发周期上靠后。同样，程序不会去编写某类AI的实现。程序提供了另一种简单的事件系统，这个系统其实就是一个调用脚本的方案。当关于某个怪物发生了某个事件时，程序调用脚本，传入事件类型和事件参数。这个事件分为两类：程序类和脚本类。脚本类程序不需关心，仅提供事件触发API。程序类事件非常有限：怪物创建、出生、删除。</p>
<p>除了以上三块之外，还有很多零散的脚本交互。例如游戏对象属性初始化，角色进入游戏，角色进入场景等。这些都无关痛痒。</p>
<p>接下来谈一些关键模块的实现。</p>
<p><strong>定时器</strong></p>
<p>整个GS的很多逻辑模块都基于这个定时器来实现。这个定时器接收逻辑模块的注册，在主循环中传入系统时间，定时器模块检查哪些定时器实例超时，然后触发调用之。这个主循环以每帧5ms的速率运行，也即帧率1000/5。</p>
<p>这个定时器是基于操作系统的时间。随着帧率的不同，它在触发逻辑功能时，就必然不精确。游戏客户端（包括单机游戏）在帧率这块的实现上，一般逻辑功能的计算都会考虑到一个dt（也就是一帧的时间差），例如移动更新，一般都是x = last_x + speed * dt。但，我们这里并没有这样做。我们的几乎所有逻辑功能，都没有考虑这个时间差。</p>
<p>例如，我们的移动模块注册了一个固定时间值的定时器，假设是200ms。理想情况下，定时器模块每200ms回调移动模块更新坐标。但现实情况肯定是大于200ms的更新频率，悲剧的是，移动模块每次更新坐标都更新一个固定偏移。这显然是不够精确的。</p>
<p>更悲剧的是，定时器的实现中，还可能出现跳过一些更新帧。例如，理论情况下，定时器会在系统时间点t1/t2/t3/t4分别回调某个逻辑模块。某一帧里，定时器大概在t1回调了某逻辑模块，而当该帧耗时严重时，下一帧定时器模块在计算时，其时间值为t，而t大于t4，此时定时器模块跳过t2/t3。相当于该逻辑模块少了2次更新。这对于移动模块而言，相当于某个对象本来在1秒的时间里该走5格，但实际情况却走了1格。</p>
<p>当然，当游戏帧率无法保证时，逻辑模块运行不理想也是情有可原的。但，不理想并不包含BUG。而我觉得，这里面是可能出现BUG的。如何改善这块，目前为止我也没什么方案。</p>
<p><strong>移动</strong></p>
<p>有很多更上层的模块依赖移动。我们的移动采用了一种分别模拟的实现。客户端将复杂的移动路径拆分为一条一条的线段，然后每个线段请求服务器移动。然后服务器上使用定时器来模拟在该线段上的移动。因为服务器上的阻挡是二维格子，这样服务器的模拟也很简单。当然，这个模块在具体实现上复杂很多，这里不细谈。</p>
<p><strong>碰撞检测</strong></p>
<p>我们的技能要求有碰撞检测，这主要包括对象与对象之间的碰撞。在最早的实现中，当脚本关心某个对象的碰撞情况时，程序就为该对象注册定时器，然后周期触发检测与周围对象的距离关系，这个周期低于100ms。这个实现很简单，维护起来也就很简单。但它是有问题的。因为它基于了一个不精确的定时器，和一个不精确的移动模块。</p>
<p>首先，这个检测是基于对象的当前坐标。前面分析过在帧率掉到移动更新帧都掉帧的情况下，服务器的对象坐标和理论情况差距会很大，而客户端基本上是接近正确情况的，这个时候做的距离检测，就不可能正确。另一方面，就算移动精确了，这个碰撞检测还是会带来BUG。例如现在检测到了碰撞，触发了脚本，脚本中注册了关心离开的事件。但不幸的是，在这个定时器开始检测前，这两个对象已经经历了碰撞、离开、再碰撞的过程，而定时器开始检测的时候，因为它基于了当前的对象坐标，它依然看到的是两个对象处于碰撞状态。</p>
<p>最开始，我们直觉这样的实现是费时的，是不精确的。然后有了第二种实现。这个实现基于了移动的实现。因为对象的移动是基于直线的（服务器上）。我们就在对象开始移动时，根据移动方向、速度预测两个对象会在未来的某个时间点发生碰撞。当然，对于频繁的小距离移动而言，这个预测从直觉上来说也是费时的。然后实现代码写了出来，一看，挺复杂，维护难度不小。如果效果好这个维护成本也就算了，但是，它依然是不精确的。因为，它也依赖了这个定时器。</p>
<p>例如，在某个对象开始移动时，我们预测到在200ms会与对象B发生碰撞。然后注册了一个200ms的定时器。但定时器不会精确地在未来200ms触发，随着帧率的下降，400ms触发都有可能。即便不考虑帧率下降的情况，它还是有问题。前面说过，我们游戏帧保证每帧至少5ms，本来这是一个限帧手段，目的当然是避免busy-loop。这导致定时器最多出现5ms的延迟。如果策划使用这个碰撞检测去做飞行道具的实现，例如一个快速飞出去的火球，当这个飞行速度很快的时候，这5ms相对于这个预测碰撞时间就不再是个小数目。真悲剧。</p>
<p><strong>技能</strong></p>
<p>虽然具体的技能不是程序写的，但正如把几乎所有具体逻辑交给策划写带来的悲剧一样：这事不是你干的，但你得负责它的性能。所以有必要谈谈技能的实现。</p>
<p>技能的实现里，只有一个技能使用入口，程序只需要在客户端发出使用技能的消息时，调用这个入口脚本函数。然后脚本中会通过注册一些触发器来驱动整个技能运作。程序员一直希望策划能把技能用一个统一的、具体的框架统一起来，所谓的变动都是基于这个框架来变的。但策划也一直坚持，他们心目中的技能是无法统一的。</p>
<p>我们的技能确实很复杂。一个技能的整个过程中，服务器可能会和客户端发生多次消息交互。在最初的实现中，服务器甚至会控制客户端的技能特效、释放动作等各种细节；甚至于服务器会在这个过程中依赖客户端的若干次输入。</p>
<hr />
<p>下一篇我将谈谈一些遇到的问题。</p><img src ="http://www.cppblog.com/kevinlynx/aggbug/172739.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> 2012-04-25 16:54 <a href="http://www.cppblog.com/kevinlynx/archive/2012/04/25/172739.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MMO游戏对象属性设计</title><link>http://www.cppblog.com/kevinlynx/archive/2011/05/02/145504.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Mon, 02 May 2011 11:19:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2011/05/02/145504.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/145504.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2011/05/02/145504.html#Feedback</comments><slash:comments>18</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/145504.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/145504.html</trackback:ping><description><![CDATA[<div class="document" id="mmo">
<h1 class="title">MMO游戏对象属性设计</h1>
<table class="docinfo" frame="void" rules="none">
<col class="docinfo-name" />
<col class="docinfo-content" />
<tbody valign="top">
<tr><th class="docinfo-name">Author:</th>
<td>Kevin Lynx</td></tr>
<tr><th class="docinfo-name">Date:</th>
<td>5.2.2011</td></tr>
</tbody>
</table>
<p>一般的MMORPG中，游戏对象主要包括怪物和玩家。这两类对象在经过游戏性方面的不断&#8220;进化&#8221;后，其属性数量及与之相关的逻辑往往会变得很巨大。如何将这一块做得既不损失效率，又能保证结构的灵活、清晰、可维护？本文将提供一种简单的结构。</p>
<div class="section" id="id1">
<h1>原始结构</h1>
<p>最原始的结构，极有可能为这样:</p>
<pre class="literal-block">
Player:     +---------------+
            | property-1    |
            +---------------+
            | property-2    |
            +---------------+
            |     ...       |
            +---------------+
            | operator-1    |
            +---------------+
            | operator-2    |
            +---------------+
            | ...           |
            +---------------+
</pre>
<p>也就是，一个对象为一个C++类，然后里面直接塞满了各种属性名，然后是针对这个属性的逻辑操作（函数）。其结果就是Player成为巨类。针对这个情况，一直以来我觉得可以使用一种简单的方法来拆分这个类。冠以官腔，称之为Entity-Component-based Desgin。产生这种想法和我的个人技术积累有一定关系，见下文。</p>
</div>
<div class="section" id="policy-based-design">
<h1>Policy-based Design</h1>
<p>Policy-based Design，基于决策的设计。这个概念来源于&lt;Modern C++ Design&gt;。虽然这本书讲述的是针对C++模板的使用及设计技巧。但这种思想依然被我潜意识般地用在其他地方。Policy大致来说就是一个小的组件(Component)。它努力不依赖于其他东西，它可能就是个简单的类，它拥有极少的数据结构，及针对这些数据的极少操作接口。举例而言，玩家MP的自动回复功能，就可封装为一个Policy。将许多Policy组合起来，就可完成一个复杂的功能。</p>
<p>这种思想还可指导很多程序结构方面的设计。例如在做功能的接口拆分时，就将每个函数设计得足够小，小到单纯地完成一个功能。一个功能的入口函数，就将之前实现的小函数全部组合起来，然后共同完成功能点。</p>
<p>当然，&lt;Modern C++ Design&gt;里的Policy在表现形式上有所不同。但其核心思想相同，主要体现在 <strong>组合</strong> 特点上。</p>
</div>
<div class="section" id="entity-component-based-design">
<h1>Entity-Component-based Design</h1>
<p>Entity-Component-based Design按照google到的文章，严格来说算是与OOP完全不同的软件设计方法。不过在这里它将按照我的意思重新被解释。</p>
<p>如果说Policy-based Design极大可能地影响着我们平时的细节编码，那么Entity-Component则是直接对游戏对象的结构设计做直接的说明。 <strong>一个游戏对象就是一个Entity。</strong> Entity拥有很少的属性，也许仅包含一个全局标示的ID。 <strong>一个Component则是Entity的某个行为、或者说某个组成部分。</strong> 其实说白了，以玩家为例，一个玩家对象就是一个Entity，而一个MP的自动回复功能就可被包装为一个Component。这个Component可能包含若干与该功能相关的数据，例如回复时间间隔，每次的回复量等。我们往玩家对象这个Entity添加各种Component，也就是给玩家添加各种逻辑功能。</p>
<p>但是，Component之间可能会涉及到交互，玩家对象之外的模块可能也会与玩家内的某个Component交互。子功能点的拆分，不得不涉及到更多的胶水代码，这也算一种代价。</p>
</div>
<div class="section" id="id2">
<h1>游戏对象属性设计</h1>
<p>这份属性结构设计，基本就是参考了上面提到的设计思想。整个系统有如下组件:</p>
<pre class="literal-block">
Entity:    +-------------------+
           | property-table    |
           +-------------------+
           | component-table   |
           +-------------------+
Property:  +-------------------+
           | observer-list     |
           +-------------------+
Component: +--------------------+
           | logic-related data |
           +--------------------+
           | logic-related func |
           +--------------------+
</pre>
<p>意即，所有Entity都包含一个属性表和组件表。这里的属性表并非硬编码的属性数据成员集合，而是一个key-value形式的表。Property包含一个观察者列表，其实就是一系列回调函数，但是这些观察者本质上也是组件，后面会提到。Component正如上文描述，仅包含Component本身实现的功能所需要的数据和函数。整个结构大致的代码如下:</p>
<pre class="literal-block">
class Entity {
private:
    GUID id;
    std::map&lt;std::string, IComponent*&gt; components;
    std::map&lt;std::string, Property*&gt; properties;
};
class Property {
private:
    std::string name;
    Value val;
    std::vector&lt;IComponent*&gt; observers;
};
class IComponent {
public:
    virtual bool Operate (const Args &amp;args) { return false; }
    virtual void OnNotify (const Property &amp;property, const Args &amp;args) {}
protected:
    std::string name;
    Entity *entity;
};
</pre>
<p>属性本身是抽象的，这完全是因为我们将属性统一地放在了一个表里。从而又导致属性的值也需要<a href="http://codemacro.com/blog/display?id=8">继续阅读</a></p>
</div>
</div><img src ="http://www.cppblog.com/kevinlynx/aggbug/145504.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> 2011-05-02 19:19 <a href="http://www.cppblog.com/kevinlynx/archive/2011/05/02/145504.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>网游中的玩家移动</title><link>http://www.cppblog.com/kevinlynx/archive/2010/06/22/118497.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Tue, 22 Jun 2010 13:27:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2010/06/22/118497.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/118497.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2010/06/22/118497.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/118497.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/118497.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: MMORPG中，玩家的移动主要逻辑都放在客户端进行，包括自动寻路和响应玩家的操作，服务<br>器在这里担当一个被动角色。但是服务器端的玩家数据却是真正的被其他逻辑模块参考的数<br>据。&nbsp;&nbsp;<a href='http://www.cppblog.com/kevinlynx/archive/2010/06/22/118497.html'>阅读全文</a><img src ="http://www.cppblog.com/kevinlynx/aggbug/118497.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> 2010-06-22 21:27 <a href="http://www.cppblog.com/kevinlynx/archive/2010/06/22/118497.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏资源包简单设计</title><link>http://www.cppblog.com/kevinlynx/archive/2010/06/19/118242.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Sat, 19 Jun 2010 06:59:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2010/06/19/118242.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/118242.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2010/06/19/118242.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/118242.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/118242.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 一般的资源包文件格式基本上是由包文件头和包内容组成。文件头描述资源包内打包的文件<br>信息，例如文件名、在资源包里的偏移、大小、压缩加密相关信息等；包内容则是实际文件<br>打包在一起后的内容，可能直接是未打包前文件连续存放在一起的内容，也可能是相同类型<br>文件除掉文件头的内容（例如某个资源包里打包的全部是相同格式的图片文件，那么这些图<br>片文件被打包后包内只需要保存一个图片文件头，包内容全部是直接的图片数据）。 &nbsp;&nbsp;<a href='http://www.cppblog.com/kevinlynx/archive/2010/06/19/118242.html'>阅读全文</a><img src ="http://www.cppblog.com/kevinlynx/aggbug/118242.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> 2010-06-19 14:59 <a href="http://www.cppblog.com/kevinlynx/archive/2010/06/19/118242.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SGI STL的内存池</title><link>http://www.cppblog.com/kevinlynx/archive/2008/06/12/53054.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Thu, 12 Jun 2008 13:26:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/06/12/53054.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/53054.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/06/12/53054.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/53054.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/53054.html</trackback:ping><description><![CDATA[<p><font size=2>stl中各种容器都有一个可选的模板参数：allocator，也就是一个负责内存分配的组件。STL标准规定的allcator<br>被定义在memory文件中。STL标准规定的allocator只是单纯地封装operator new，效率上有点过意不去。 </font>
<p><font size=2>SGI实现的STL里，所有的容器都使用SGI自己定义的allocator。这个allocator实现了一个small object的内存池。<br>Loki里为了处理小对象的内存分配，也实现了类似的内存管理机制。 </font>
<p><font size=2>该内存池大致上，就是一大块一大块地从系统获取内存，然后将其分成很多小块以链表的形式链接起来。其内部<br>有很多不同类型的链表，不同的链表维护不同大小的内存块。每一次客户端要求分配内存时，allcator就根据请求<br>的大小找到相应的链表(最接近的尺寸)，然后从链表里取出内存。当客户端归还内存时，allocator就将这块内存<br>放回到对应的链表里。 </font>
<p><font size=2>我简单地画了幅图表示整个结构：</font>
<p><a href="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/SGISTL_12DE6/allocator.jpg"><img style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height=352 alt=allocator src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/SGISTL_12DE6/allocator_thumb.jpg" width=434 border=0></a>
<p><font size=2>allocator内部维护一个链表数组，数组元素全部是链表头指针。链表A每一个节点维护一个8bytes的内存块，链表<br>B每一个节点维护一个16bytes的内存块。 </font>
<p><font size=2>当客户端请求分配10bytes的内存时，allocator将10调整为最接近的16bytes(只能大于10bytes)，然后发现16bytes<br>这个链表(链表B)里有可用内存块，于是从B里取出一块内存返回。当客户端归还时，allocator找到对应的链表，将<br>内存重新放回链表B即可。 </font>
<p><font size=2>大致过程就这么简单，也许有人要说用链表维护一块内存，链表本身就会浪费一些内存(在我很早前接触内存池时，<br>总会看到类似的论点= =|)，其实通过一些简单的技巧是完全可以避免的。例如，这里allocator维护了很多内存块，<br>反正这些内存本身就是闲置的，因此我们就可以直接在这些内存里记录链表的信息(下一个元素)。 </font>
<p><font size=2>还是写点代码详细说下这个小技巧：</font>
<p>&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 src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;Obj<br><img id=Codehighlighter1_15_40_Open_Image onclick="this.style.display='none'; Codehighlighter1_15_40_Open_Text.style.display='none'; Codehighlighter1_15_40_Closed_Image.style.display='inline'; Codehighlighter1_15_40_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_15_40_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_15_40_Closed_Text.style.display='none'; Codehighlighter1_15_40_Open_Image.style.display='inline'; Codehighlighter1_15_40_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_15_40_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_15_40_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;Obj&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">next;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&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>&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">mem&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;malloc(&nbsp;</span><span style="COLOR: #000000">100</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Obj&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">header&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(Obj</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)&nbsp;mem;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Obj&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">cur_obj&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;header;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Obj&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">next_obj&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;cur_obj;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;i&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;;&nbsp;</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">&nbsp;i&nbsp;)<br><img id=Codehighlighter1_195_441_Open_Image onclick="this.style.display='none'; Codehighlighter1_195_441_Open_Text.style.display='none'; Codehighlighter1_195_441_Closed_Image.style.display='inline'; Codehighlighter1_195_441_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_195_441_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_195_441_Closed_Text.style.display='none'; Codehighlighter1_195_441_Open_Image.style.display='inline'; Codehighlighter1_195_441_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_195_441_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_195_441_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;cur_obj&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;next_obj;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;next_obj&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(Obj</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)((</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)next_obj&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">10</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;i&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">9</span><span style="COLOR: #000000">&nbsp;)<br><img id=Codehighlighter1_304_364_Open_Image onclick="this.style.display='none'; Codehighlighter1_304_364_Open_Text.style.display='none'; Codehighlighter1_304_364_Closed_Image.style.display='inline'; Codehighlighter1_304_364_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_304_364_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_304_364_Closed_Text.style.display='none'; Codehighlighter1_304_364_Open_Image.style.display='inline'; Codehighlighter1_304_364_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_304_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"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_304_364_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;cur_obj</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">next&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;&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;&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_387_435_Open_Image onclick="this.style.display='none'; Codehighlighter1_387_435_Open_Text.style.display='none'; Codehighlighter1_387_435_Closed_Image.style.display='inline'; Codehighlighter1_387_435_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_387_435_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_387_435_Closed_Text.style.display='none'; Codehighlighter1_387_435_Open_Image.style.display='inline'; Codehighlighter1_387_435_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_387_435_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_387_435_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;cur_obj</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">next&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;next_obj;<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/ExpandedBlockEnd.gif" align=top>&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;free(&nbsp;mem&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p>&nbsp;
<p><font size=2>这样，通过header指针和next域，就可以逐块(这里是10byts)地访问mem所指向的内存，而这些链表的节点，都<br>是直接保存在这块内存里的，所以完全没有额外消耗。 </font>
<p><font size=2>我用C模仿着SGI的这个allocator写了个可配置的内存池，在其上按照STL的标准包装了一个allocator，可以直接<br>用于VC自带的STL里。</font><a href="http://www.cppblog.com/Files/kevinlynx/alloc.rar" target=_blank><font size=2>测试代码</font></a><font size=2>稍微测试了下，发现在不同的机器上有明显的差距。 </font></p>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/53054.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-12 21:26 <a href="http://www.cppblog.com/kevinlynx/archive/2008/06/12/53054.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>11</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>学生时代做的东西-留个纪念</title><link>http://www.cppblog.com/kevinlynx/archive/2008/05/14/49783.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Wed, 14 May 2008 01:23:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/05/14/49783.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/49783.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/05/14/49783.html#Feedback</comments><slash:comments>15</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/49783.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/49783.html</trackback:ping><description><![CDATA[<p>可能我这个人比较怀旧，对什么东西都想做个记录，方便日后回忆。可能很多认识我的朋友都是通过GameRes那个作品专</p> <p>区。我对于当年那种疯狂编程的干劲很是自豪，现在差了很多，以前帮别人做小学生系列游戏外包的时候，可以12小时出</p> <p>个弱智的小游戏，那些日子一度被我称为’12小时编程挑战赛‘，只是自己跟自己比赛。</p> <p>&nbsp;</p> <p>每一次发布在GameRes(排除早期的那些垃圾玩意），在写简介时我都要把自己开发用的时间写上，可是脾气好的sea_bug</p> <p>每次都给我删掉了。我自己汇总一下：</p> <p>&nbsp;</p> <p>1. 最让我自豪的一个游戏引擎，耗尽了我当时所有的设计能力。我努力把它做得很具扩展性，可是忽略了功能性。现在基本不维护了，可能是用户群太少了。我想我还是没做好吧：</p> <p><a href="http://code.google.com/p/edge2d/" target="_blank">edge2d google code page</a></p> <p>托sea_bug的忙搞了个论坛，冷清得让我心寒:<a title="http://bbs.gameres.com/showforum.asp?forumid=91" href="http://bbs.gameres.com/showforum.asp?forumid=91">http://bbs.gameres.com/showforum.asp?forumid=91</a></p> <p>&nbsp;</p> <p>2. PacShooter3d:</p> <p><a title="http://data.gameres.com/showmessage.asp?TopicID=90655" href="http://data.gameres.com/showmessage.asp?TopicID=90655">http://data.gameres.com/showmessage.asp?TopicID=90655</a></p> <p>不知道怎么的被人放到一个网站上了：<a title="http://noyes.cn/Software.Asp?id=9667" href="http://noyes.cn/Software.Asp?id=9667">http://noyes.cn/Software.Asp?id=9667</a></p> <p><a href="http://www.cppblog.com/Files/kevinlynx/PacShooterSrc.rar" target="_blank">源代码下载。</a></p> <p>&nbsp;</p> <p>3. Space Demon demo</p> <p>当初看到dophi写的俄罗斯方块营造的那种感觉觉得很不错，于是决定认真地做个游戏出来。结果后来做的东西让我很失望。这是一个在代码上过度设计的东西。我虽然对这个游戏不满意，但是我对代码还基本满意。后来这个游戏的代码被我游戏学院的一个朋友拿给金山的一个主程（在他们学校教书？）看，还得到了表扬。;D</p> <p>这个游戏我是直接开源了的：<a title="http://www.gameres.com/showmessage.asp?TopicID=73123" href="http://www.gameres.com/showmessage.asp?TopicID=73123">http://www.gameres.com/showmessage.asp?TopicID=73123</a></p> <p>&nbsp;</p> <p>4. Crazy Eggs Clone</p> <p>&lt;Crazy Eggs&gt;是小林子他们工作室做的东西，属于casual games，拿到国外去卖的。我当时也觉得casual games市场不错，还找了个美工，大谈特谈，吹嘘了很多，最终在写策划案的时候失败了。我当时心也懒了，最终失败。</p> <p>同样是在GameRes上：<a title="http://www.gameres.com/showmessage.asp?TopicID=72351" href="http://www.gameres.com/showmessage.asp?TopicID=72351">http://www.gameres.com/showmessage.asp?TopicID=72351</a></p> <p><a href="http://www.cppblog.com/Files/kevinlynx/CrazeEggs_Src.zip" target="_blank">源代码下载。</a></p> <p>后来我为了宣传edge2d，特地把这个游戏移植到我的引擎上。我从来很自豪自己代码的模块性，所以移植起来很容易。除了edge2d版本，我还做了HGE版本，不过HGE版本是做给别人的外包：</p> <p><a href="http://edge2d.googlecode.com/files/EdgeExample-CrazyEggsEdge_v3.zip" target="_blank">edge2d版本下载</a></p> <p>&nbsp;</p> <p>5. Brick Shooter Jr</p> <p>这个游戏也是我翻版别人的，用的别人的美术+音乐资源，自己重写代码。后来网上有个人又用我的资源翻作了个，做的比我好。</p> <p><a title="http://data.gameres.com/showmessage.asp?TopicID=65654" href="http://data.gameres.com/showmessage.asp?TopicID=65654">http://data.gameres.com/showmessage.asp?TopicID=65654</a></p> <p><a href="http://www.cppblog.com/Files/kevinlynx/BrickShooterJrSrc.rar" target="_blank">源代码下载</a></p> <p>6. Feeding Frenzy</p> <p>Popcap的经典游戏，我做的垃圾东西，不提其他的了：</p> <p><a title="http://data.gameres.com/showmessage.asp?TopicID=62796" href="http://data.gameres.com/showmessage.asp?TopicID=62796">http://data.gameres.com/showmessage.asp?TopicID=62796</a></p> <p><a href="http://www.cppblog.com/Files/kevinlynx/FFSrc.zip" target="_blank">源代码下载</a></p> <p>&nbsp;</p> <p>7.是男人就下一百层</p> <p>超级古老的东西，这个东西当初还和上海一家广告公司合作过。我签署了长这么大的第一份合同，结果后来一分钱没捞到。他们公司现在也不做这个了。和我合作的产品经理现在貌似在搞棋牌。</p> <p><a title="http://data.gameres.com/showmessage.asp?TopicID=54475" href="http://data.gameres.com/showmessage.asp?TopicID=54475">http://data.gameres.com/showmessage.asp?TopicID=54475</a></p> <p><a href="http://www.cppblog.com/Files/kevinlynx/Down100sourceCode.rar" target="_blank">源代码下载</a></p> <p>8. 所谓的雷电，一个我最早做的东西，现在你开baidu搜索 kevin lynx，出来最多的链接就是&lt;雷电kevinlynx版&gt;，别信那</p> <p>些，全是流氓软件。</p> <p><a title="http://data.gameres.com/showmessage.asp?TopicID=54474" href="http://data.gameres.com/showmessage.asp?TopicID=54474">http://data.gameres.com/showmessage.asp?TopicID=54474</a></p> <p><a href="http://www.cppblog.com/Files/kevinlynx/DSSourceCode.rar" target="_blank">源代码下载</a></p> <p>&nbsp;</p> <p>其他还给别人做了一些外包，在此特别感谢哆啦G梦老大，给我找了很多工作。他这个人四处跳巢，还给我说了几次工作。</p> <p>只是我还想暂时留在成都，所以都拒绝了。那些外包做的都比较垃圾，做到后来基本有个小游戏框架了。版权问题可能不</p> <p>能发布出来吧。</p><img src ="http://www.cppblog.com/kevinlynx/aggbug/49783.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 09:23 <a href="http://www.cppblog.com/kevinlynx/archive/2008/05/14/49783.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><item><title>lua和python谁更适用于嵌入MMORPG？</title><link>http://www.cppblog.com/kevinlynx/archive/2008/05/06/49023.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Tue, 06 May 2008 09:37:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/05/06/49023.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/49023.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/05/06/49023.html#Feedback</comments><slash:comments>14</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/49023.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/49023.html</trackback:ping><description><![CDATA[<p>&nbsp;</p> <p>预计新项目会选择lua或python之一作为游戏的脚本语言。以前草草地接触过这两门语言，对于语法，以及嵌入进C/C++程序都有点感性上的认识。可能是受《UNIX编程艺术》中KISS原则的影响，现在总喜欢简洁的东西。所以我个人比较偏向于使用lua。</p> <p>&nbsp;</p> <p>这两天翻了下网络上的资料，在lua的wiki上看到一篇比较lua和python的<a href="http://lua-users.org/wiki/LuaVersusPython">文章</a>，草草地翻译出要点：</p> <p>Python:<br>1. 扩展库很多，资料很多<br>2. 数值计算比较强大，支持多维数组，而lua没有数组类型<br>3. 本身带的c类型(?)支持处理动态链接库，不需要进行C封装(C扩展)<br>4. 远程调试器，似乎lua扩展工具支持<br>5. 自然语言似的语法<br>6. 对于string和list的支持，lua可以通过扩展库实现<br>7. 对unicode的支持<br>8. 空格敏感(代码不忽略空格)，这其实可以使python的代码风格看起来更好一点<br>9. 内建位操作，lua可以通过扩展库支持<br>10.语言本身对错误的处理要好些，可以有效减少程序错误<br>11.初级文档比lua多<br>12.对面向对象支持更好  <p>Lua:<br>1. 比python小巧很多(包括编译出来的运行时库)<br>2. 占用更小的内存<br>3. 解释器速度更快<br>4. 比python更容易集成到C语言中<br>5. 对于对象不使用引用计数(引用计数会导致更多的问题？)<br>6. lua早期定位于一种配置语言(作为配置文件)，因此比起python来更容易配置数据<br>7. 语言更漂亮(nice)、简单(simple)、强大(powerful)。<br>8. lua支持多线程，每个线程可以配置独立的解释器，因此lua更适合于集成进多线程程序<br>9. 对空格不敏感，不用担心编辑器会将tab替换成空格  <p>Useful Comments:<br>1. Everything is an object allocated on the heap in Python, including numbers. (So 123+456 creates a new heap object).<br>2. lua对于coroutine的支持更适用于嵌入进游戏，虽然python也有，但是并没有包含进核心模块  <p>3.Python was a language better suited to Game AI</p> <p>&nbsp;</p> <p>本来想去找点对于python的正面资料(嵌入进游戏这方面），但是居然没找到。客观地说如果单独用python做应用，python还是很有优势。现在心意已决，应该向leader推荐lua。</p> <p>&nbsp;</p> <p>ps，希望能补充以上两种语言的特点。</p><img src ="http://www.cppblog.com/kevinlynx/aggbug/49023.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-06 17:37 <a href="http://www.cppblog.com/kevinlynx/archive/2008/05/06/49023.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TCP/IP Concepts 1</title><link>http://www.cppblog.com/kevinlynx/archive/2008/04/18/47471.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Fri, 18 Apr 2008 02:02:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/04/18/47471.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/47471.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/04/18/47471.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/47471.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/47471.html</trackback:ping><description><![CDATA[<p><br><font style="FONT-SIZE: 10pt; BACKGROUND-COLOR: #ffffff">最近在草草地看&lt;TCP/IP详解&gt;TCP那一部分，之所以草草地看是因为觉得早晚一天会回过头去细看。手头上<br>有工作要做，所以先草草地把之前随便摘抄的TCP/IP相关概念贴出来：<br><br>继续草草地贴:<br><br>--------------------------------------------------------------------------------------------------------------------------------------------------------<br>TCP segment:<br>Thus, we have simply &#8220;passed the buck&#8221; to TCP, which must take the stream from the application <br>and divide it into discrete messages for IP. These messages are called TCP segments.</font></p>
<p><font style="FONT-SIZE: 10pt; BACKGROUND-COLOR: #ffffff">On regular intervals, it forms segments to be transmitted using IP. The size of the segment is <br>controlled by two primary factors. The first issue is that there is an overall limit to the size <br>of a segment, chosen to prevent unnecessary fragmentation at the IP layer. This is governed by a <br>parameter called the maximum segment size (MSS), which is determined during connection establishment. <br>The second is that TCP is designed so that once a connection is set up, each of the devices tells the <br>other how much data it is ready to accept at any given time. If this is lower than the MSS value, a <br>smaller segment must be sent. This is part of the sliding window system described in the next topic.</font></p>
<p><font style="FONT-SIZE: 10pt; BACKGROUND-COLOR: #ffffff">Since TCP works with individual bytes of data rather than discrete messages, it must use an <br>identification scheme that works at the byte level to implement its data transmission and tracking <br>system. This is accomplished by assigning each byte TCP processes a sequence number.</font></p>
<p><font style="FONT-SIZE: 10pt; BACKGROUND-COLOR: #ffffff"><br>Since applications send data to TCP as a stream of bytes and not prepackaged messages, each <br>application must use its own scheme to determine where one application data element ends and the <br>next begins.<br><br>--------------------------------------------------------------------------------------------------------------------------------------------------------<br></p>
<p>TCP MSS:<br><a href="http://www.tcpipguide.com/free/t_TCPMaximumSegmentSizeMSSandRelationshiptoIPDatagra.htm">http://www.tcpipguide.com/free/t_TCPMaximumSegmentSizeMSSandRelationshiptoIPDatagra.htm</a></p>
<p>In addition to the dictates of the current window size, each TCP device also has associated <br>with it a ceiling on TCP size—a segment size that will never be exceeded regardless of how<br>&nbsp;large the current window is. This is called the maximum segment size (MSS). When deciding <br>how much data to put into a segment, each device in the TCP connection will choose the amount<br>&nbsp;based on the current window size, in conjunction with the various algorithms described in <br>the reliability section, but it will never be so large that the amount of data exceeds the<br>&nbsp;MSS of the device to which it is sending. </p>
<p><br>Note: I need to point out that the name &#8220;maximum segment size&#8221; is in fact misleading. The<br>&nbsp;value actually refers to the maximum amount of data that a segment can hold—it does not <br>include the TCP headers. So if the MSS is 100, the actual maximum segment size could be 120 <br>(for a regular TCP header) or larger (if the segment includes TCP options). </p>
<p>This was computed by starting with the minimum MTU for IP networks of 576. </p>
<p>Devices can indicate that they wish to use a different MSS value from the default by including <br>a Maximum Segment Size option in the SYN message they use to establish a connection. Each <br>device in the connection may use a different MSS value.<br><br>--------------------------------------------------------------------------------------------------------------------------------------------------------<br><br>delayed ACK algorithm<br><br><a href="http://tangentsoft.net/wskfaq/intermediate.html#delayed-ack">http://tangentsoft.net/wskfaq/intermediate.html#delayed-ack</a></p>
<p>In a simpleminded implementation of TCP, every data packet that comes in is immediately acknowledged<br>&nbsp;with an ACK packet. (ACKs help to provide the reliability TCP promises.)</p>
<p>In modern stacks, ACKs are delayed for a short time (up to 200ms, typically) for three reasons: a) <br>to avoid the silly window syndrome; b) to allow ACKs to piggyback on a reply frame if one is ready <br>to go when the stack decides to do the ACK; and c) to allow the stack to send one ACK for several <br>frames, if those frames arrive within the delay period.</p>
<p>The stack is only allowed to delay ACKs for up to 2 frames of data.</p>
<p><br>&nbsp;</p>
<p>--------------------------------------------------------------------------------------------------------------------------------------------------------<br>Nagle algorithm:</p>
<p>Nagle's algorithm, named after John Nagle, is a means of improving the efficiency of TCP/IP networks by reducing the number of packets that need to be sent over the network.</p>
<p>Nagle's document, Congestion Control in IP/TCP Internetworks (RFC896) describes what he called the 'small packet problem', where an application repeatedly emits data in small chunks, frequently only 1 byte in size. Since TCP packets have a 40 byte header (20 bytes for TCP, 20 bytes for IPv4), this results in a 41 byte packet for 1 byte of useful information, a huge overhead. This situation often occurs in Telnet sessions, where most keypresses generate a single byte of data which is transmitted immediately. Worse, over slow links, many such packets can be in transit at the same time, potentially leading to congestion collapse.</p>
<p>Nagle's algorithm works by coalescing a number of small outgoing messages, and sending them all at once. Specifically, as long as there is a sent packet for which the sender has received no acknowledgment, the sender should keep buffering its output until it has a full packet's worth of output, so that output can be sent all at once.</p>
<p><br>[edit] Algorithm<br>if there is new data to send<br>&nbsp; if the window size &gt;= MSS and available data is &gt;= MSS<br>&nbsp;&nbsp;&nbsp; send complete MSS segment now<br>&nbsp; else<br>&nbsp;&nbsp;&nbsp; if there is unconfirmed data still in the pipe<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; enqueue data in the buffer until an acknowledge is received<br>&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; send data immediately<br>&nbsp;&nbsp;&nbsp; end if<br>&nbsp; end if<br>end if<br>where MSS = Maximum segment size</p>
<p>This algorithm interacts badly with TCP delayed acknowledgments, a feature introduced into TCP at roughly the same time in the early 1980s, but by a different group. With both algorithms enabled, applications which do two successive writes to a TCP connection, followed by a read, experience a constant delay of up to 500 milliseconds, the "ACK delay". For this reason, TCP implementations usually provide applications with an interface to disable the Nagle algorithm. This is typically called the TCP_NODELAY option. The first major application to run into this problem was the X Window System.</p>
<p>The tinygram problem and silly window syndrome are sometimes confused. The tinygram problem occurs when the window is almost empty. Silly window syndrome occurs when the window is almost full</p>
<p>===================================================================================================================================<br>3.17 - What is the Nagle algorithm?<br>The Nagle algorithm is an optimization to TCP that makes the stack wait until all data is acknowledged on the connection before it sends more data. The exception is that Nagle will not cause the stack to wait for an ACK if it has enough enqueued data that it can fill a network frame. (Without this exception, the Nagle algorithm would effectively disable TCP's sliding window algorithm.) For a full description of the Nagle algorithm, see RFC 896.</p>
<p>So, you ask, what's the purpose of the Nagle algorithm?</p>
<p>The ideal case in networking is that each program always sends a full frame of data with each call to send(). That maximizes the percentage of useful program data in a packet.</p>
<p>The basic TCP and IPv4 headers are 20 bytes each. The worst case protocol overhead percentage, therefore, is 40/41, or 98%. Since the maximum amount of data in an Ethernet frame is 1500 bytes, the best case protocol overhead percentage is 40/1500, less than 3%.</p>
<p>While the Nagle algorithm is causing the stack to wait for data to be ACKed by the remote peer, the local program can make more calls to send(). Because TCP is a stream protocol, it can coalesce the data in those send() calls into a single TCP packet, increasing the percentage of useful data.</p>
<p>Imagine a simple Telnet program: the bulk of a Telnet conversation consists of sending one character, and receiving an echo of that character back from the remote host. Without the Nagle algorithm, this results in TCP's worst case: one byte of user data wrapped in dozens of bytes of protocol overhead. With the Nagle algorithm enabled, the TCP stack won't send that one Telnet character out until the previous characters have all been acknowledged. By then, the user may well have typed another character or two, reducing the relative protocol overhead.</p>
<p>This simple optimization interacts with other features of the TCP protocol suite, too:</p>
<p>Most stacks implement the delayed ACK algorithm: this causes the remote stack to delay ACKs under certain circumstances, which allows the local stack a bit of time to "Nagle" some more bytes into a single packet. </p>
<p>The Nagle algorithm tends to improve the percentage of useful data in packets more on slow networks than on fast networks, because ACKs take longer to come back. </p>
<p>TCP allows an ACK packet to also contain data. If the local stack decides it needs to send out an ACK packet and the Nagle algorithm has caused data to build up in the output buffer, the enqueued data will go out along with the ACK packet. <br>The Nagle algorithm is on by default in Winsock, but it can be turned off on a per-socket basis with the TCP_NODELAY option of setsockopt(). This option should not be turned off except in a very few situations.</p>
<p>Beware of depending on the Nagle algorithm too heavily. send() is a kernel function, so every call to send() takes much more time than for a regular function call. Your application should coalesce its own data as much as is practical to minimize the number of calls to send().</p>
<p><br>--------------------------------------------------------------------------------------------------------------------------------------------------------<br><br>Sliding Window Acknowledgment System :<br><a href="http://www.tcpipguide.com/free/t_TCPSlidingWindowAcknowledgmentSystemForDataTranspo.htm">http://www.tcpipguide.com/free/t_TCPSlidingWindowAcknowledgmentSystemForDataTranspo.htm</a><br>--------------------------------------------------------------------------------------------<br>A basic technique for ensuring reliability in communications uses a rule that requires a <br>device to send back an acknowledgment each time it successfully receives a transmission. <br>If a transmission is not acknowledged after a period of time, it is retransmitted by its <br>sender. This system is called positive acknowledgment with retransmission (PAR). One<br>&nbsp;drawback with this basic scheme is that the transmitter cannot send a second message <br>until the first has been acknowledged.<br>--------------------------------------------------------------------------------------------</p>
<p><a href="http://www.ssfnet.org/Exchange/tcp/tcpTutorialNotes.html">http://www.ssfnet.org/Exchange/tcp/tcpTutorialNotes.html</a></p>
<p>The sliding window serves several purposes:<br>(1) it guarantees the reliable delivery of data<br>(2) it ensures that the data is delivered in order,<br>(3) it enforces flow control between the sender and the receiver.</p>
<p><br><br><br>------------------to be continued<br></p>
</font>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/47471.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-04-18 10:02 <a href="http://www.cppblog.com/kevinlynx/archive/2008/04/18/47471.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WoW服务器模拟器Ascent网络模块分析</title><link>http://www.cppblog.com/kevinlynx/archive/2008/04/02/46097.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Wed, 02 Apr 2008 13:22:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/04/02/46097.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/46097.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/04/02/46097.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/46097.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/46097.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 10pt">Ascent网络模块<o:p></o:p></p>
<p style="FONT-SIZE: 10pt">Author: Kevin Lynx</p>
<p style="FONT-SIZE: 10pt"><o:p>&nbsp;</o:p></p>
<p style="FONT-SIZE: 10pt">Ascent是WoW的服务器模拟器，你可以从<a href="http://wiki.ascentemu.com/index.php?title=Checkout">它的SVN</a>上获取它的全部代码，并从它的WIKI页面获取架构起整个服务器的相关步骤。</p>
<p style="FONT-SIZE: 10pt"><strong>基本架构：<o:p></o:p></strong></p>
<p style="FONT-SIZE: 10pt">Ascent网络模块核心的几个类关系如下图所示：<br></p>
<p style="FONT-SIZE: 10pt"><img height=275 alt="" src="http://www.cppblog.com/images/cppblog_com/kevinlynx/ascent_net.jpg" width=403 border=0><br><v:shapetype id=_x0000_t75 stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><v:stroke joinstyle="miter"></v:stroke><v:formulas><v:f eqn="if lineDrawn pixelLineWidth 0"></v:f><v:f eqn="sum @0 1 0"></v:f><v:f eqn="sum 0 0 @1"></v:f><v:f eqn="prod @2 1 2"></v:f><v:f eqn="prod @3 21600 pixelWidth"></v:f><v:f eqn="prod @3 21600 pixelHeight"></v:f><v:f eqn="sum @0 0 1"></v:f><v:f eqn="prod @6 1 2"></v:f><v:f eqn="prod @7 21600 pixelWidth"></v:f><v:f eqn="sum @8 21600 0"></v:f><v:f eqn="prod @7 21600 pixelHeight"></v:f><v:f eqn="sum @10 21600 0"></v:f></v:formulas><v:path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></v:path><o:lock aspectratio="t" v:ext="edit"></o:lock></v:shapetype><v:shape id=_x0000_i1025 o:ole="" type="#_x0000_t75"><v:imagedata o:title="" src="file:///C:\DOCUME~1\zhangmin\LOCALS~1\Temp\msohtmlclip1\01\clip_image001.emz"></v:imagedata></v:shape></p>
<p style="FONT-SIZE: 10pt">ThreadBase属于Ascent线程池模块中的类，它实现了一个job类，当其被加入到线程池中开始执行时，线程池管理器会为其分配一个线程（如果有线程资源）并多态调用到ThreadBase派生类的run函数。</p>
<p style="FONT-SIZE: 10pt">SocketWorkerThread用以代表IOCP网络模型中的一个工作者线程，它会从IOCP结果队列里取出异步IO的操作结果。这里的IOCP使用的完成键是Socket对象指针。SocketWorkerThread获取到IO操作结果后，根据获得的完成键将结果通知给具体的Socket对象。（Socket的说明见后面）</p>
<p style="FONT-SIZE: 10pt">ListenSocket代表一个监听套接字。该网络模块其实只是简单地将socket中的概念加以封装。也就说，它依然把一个套接字分为两种类型：监听套接字和数据套接字（代表一个网络连接）。所谓的监听套接字，是指只可以在该套接字上进行监听操作；而数据套接字则只可以在此套接字上进行发送、接收数据的操作。</p>
<p style="FONT-SIZE: 10pt">Socket代表我上面说的数据套接字。ListenSocket是一个类模板，为这个模板指定的模板参数通常是派生于Socket的类。其实这里使用了这个小技巧隐藏了工厂模式的细节。因为ListenSocket被放在一个单独的线程里运作，当其接受到一个新的网络连接时，就创建一个Socket派生类对象。（ListenSocket类如何知道这个派生类的类名？这就是通过类模板的那个模板参数）</p>
<p style="FONT-SIZE: 10pt">上层模块通常会派生Socket类，实现一些IO操作的回调。也就说，当某个IO操作完成后，会通过Socket基类让上层模块获取通知。</p>
<p style="FONT-SIZE: 10pt">SocketMgr是一个全局单件类。它主要负责一些网络库的全局操作（例如winsock库的初始化），它还维护了一个容器，保存所有的Socket对象。这其实是它的主要作用。</p>
<p style="FONT-SIZE: 10pt"><strong>运作之一，接收新的连接</strong>：<o:p></o:p></p>
<p style="FONT-SIZE: 10pt">接收新的网络连接是通过ListenSocket实现的。在创建一个ListenSocket对象时，你需要指定它的模板参数。这个参数通常是一个派生于Socket的类。如下：</p>
<p style="FONT-SIZE: 10pt" align=left>ascent-logonserver/Main.cpp<o:p></o:p></p>
<p style="FONT-SIZE: 10pt" align=left>&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 src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">ListenSocket</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">AuthSocket</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;cl&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;ListenSocket</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">AuthSocket</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">(host.c_str(),&nbsp;cport);</span></div>
<p style="FONT-SIZE: 10pt" align=left>&nbsp;</p>
<p style="FONT-SIZE: 10pt">AuthSocket派生于Socket。创建ListenSocket时构造函数指定监听IP和监听端口。</p>
<p style="FONT-SIZE: 10pt"><o:p>&nbsp;</o:p></p>
<p style="FONT-SIZE: 10pt">因为ListenSocket派生于ThreadBase，属于线程池job，因此要让ListenSocket工作起来，只需要将其加入到线程池管理器：</p>
<p style="FONT-SIZE: 10pt" align=left>ascent-logonserver/Main.cpp<o:p></o:p></p>
<p style="FONT-SIZE: 10pt"><o:p>&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">ThreadPool.ExecuteTask(cl);</span></div>
<p style="FONT-SIZE: 10pt"></o:p><o:p>&nbsp;</o:p></p>
<p style="FONT-SIZE: 10pt">ListenSocket开始运作起来后，会阻塞式地WSAAccept。如果WSAAccept返回一个有效的套接字，ListenSocket就创建一个Socket派生类对象（类型由模板参数指定），在上面举的例子中，也就是AuthSocket：</p>
<p style="FONT-SIZE: 10pt">ascent-logonserver/ ListenSocketWin32.h</p>
<p style="FONT-SIZE: 10pt" align=left>&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 src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;socket&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;T(aSocket);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">创建AuthSocket并保存网络套接字aSocket</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></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;socket</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">SetCompletionPort(m_cp);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">保存完成端口对象</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></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;socket</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">Accept(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">m_tempAddress);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">关联到完成端口等<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p style="FONT-SIZE: 10pt" align=left>Accept函数最终会将新创建的Socket对象保存到SocketMgr对象内部维护的容器里。在这里，还会回调到上层模块的OnConnect函数，从而实现信息捕获。</p>
<p style="FONT-SIZE: 10pt"><strong>运作之二，接收数据<o:p></o:p></strong></p>
<p style="FONT-SIZE: 10pt">在windows平台下，该网络模块使用的是IOCP模型，属于异步IO。当接收新的连接时，即发出WSARecv的IO操作。在工作者线程中，也就是SocketWorkerThread中，会根据IOCP完成键得到Socket对象指针，然后根据不同的IO操作结果多态回调到Socket派生类对应的函数。例如如果是WSARecv完成，则调用到AuthSocket::OnRead函数（上述例子）。OnRead函数直接可以获取到保存数据的缓冲区指针。事实上，每一个Socket对象在被创建时，就会自动创建接收缓冲区以及发送缓冲区。</p>
<p style="FONT-SIZE: 10pt"><strong>运作之三，发送数据<o:p></o:p></strong></p>
<p style="FONT-SIZE: 10pt">分析到这里，我们可以看出，该网络模块实现得很一般。在接受数据部分，网络工作者线程回调到对应的Socket对象，Socket直接对数据进行上层逻辑处理。更好的做法是当工作者线程回调到上层Socket（Socket的派生类）时，这里应该简单地将数据组织成上层数据包并放入上层数据包队列，让上层逻辑稍后处理，而不是让网络模块自己去处理。这样做主要是考虑到多线程模型。</p>
<p style="FONT-SIZE: 10pt">同样，该网络模块的发送模块也是一样，没有缓冲机制。当要发送数据时，直接调用到Socket的Send函数。该函数拷贝用户数据到自己维护的发送缓冲区，然后将自己的缓冲区指针直接提交给IOCP，WSASend发送。</p>
<p style="FONT-SIZE: 10pt"><o:p>&nbsp;</o:p></p>
<p style="FONT-SIZE: 10pt"><strong>结束<o:p></o:p></strong></p>
<p style="FONT-SIZE: 10pt">该网络模块实现的似乎有点简陋，在该模块之上也没有数据校验、数据加密的模块（这些动作散乱地分布在最上层逻辑）。在架构上也没能很好地将概念区分开来，Socket套用了原始socket中的数据套接字，而不是我所希望的NetSession。可以圈点的地方在于该模块很多地方使用了回调函数表，从而方便地实现事件传送。</p>
<p style="FONT-SIZE: 10pt"><o:p>&nbsp;</o:p></p>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/46097.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-04-02 21:22 <a href="http://www.cppblog.com/kevinlynx/archive/2008/04/02/46097.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>探究CRC32算法实现原理-why table-driven implemention</title><link>http://www.cppblog.com/kevinlynx/archive/2008/04/01/45952.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Tue, 01 Apr 2008 13:22:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/04/01/45952.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/45952.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/04/01/45952.html#Feedback</comments><slash:comments>13</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/45952.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/45952.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 探究CRC32算法实现原理-why table-driven implementionAuthor : Kevin Lynxemail&nbsp; : zmhn320@163.comPreface基于不重造轮子的原则，本文尽量不涉及网络上遍地都是的资料。What's CRC ?简而言之，CRC是一个数值。该数值被用于校验数据的正确性。CRC数值简单地说就是通过让你需要做处理的数...&nbsp;&nbsp;<a href='http://www.cppblog.com/kevinlynx/archive/2008/04/01/45952.html'>阅读全文</a><img src ="http://www.cppblog.com/kevinlynx/aggbug/45952.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-04-01 21:22 <a href="http://www.cppblog.com/kevinlynx/archive/2008/04/01/45952.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>