﻿<?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++博客-twzheng's cppblog-文章分类-网络编程</title><link>http://www.cppblog.com/twzheng/category/3937.html</link><description>『站在风口浪尖紧握住鼠标旋转！』  人在台北心在汉</description><language>zh-cn</language><lastBuildDate>Mon, 15 Mar 2010 04:03:35 GMT</lastBuildDate><pubDate>Mon, 15 Mar 2010 04:03:35 GMT</pubDate><ttl>60</ttl><item><title>求windows xp 繁体版</title><link>http://www.cppblog.com/twzheng/articles/109710.html</link><dc:creator>谭文政</dc:creator><author>谭文政</author><pubDate>Sun, 14 Mar 2010 16:05:00 GMT</pubDate><guid>http://www.cppblog.com/twzheng/articles/109710.html</guid><wfw:comment>http://www.cppblog.com/twzheng/comments/109710.html</wfw:comment><comments>http://www.cppblog.com/twzheng/articles/109710.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/twzheng/comments/commentRss/109710.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/twzheng/services/trackbacks/109710.html</trackback:ping><description><![CDATA[求windows xp 繁体版，最好是台湾版的，要能一步步的手动安装(想装双系统)<br><br>找了好几天了，很多资源都下不动<br><br>今天好不容易下了一个，但是香港版的，且安装到选择盘符时，键盘所有键都不能使用，所以还是无法安装<br><br>谢谢各位大哥大姐帮帮忙，小弟感激不尽！<img src ="http://www.cppblog.com/twzheng/aggbug/109710.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/twzheng/" target="_blank">谭文政</a> 2010-03-15 00:05 <a href="http://www.cppblog.com/twzheng/articles/109710.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>说说大型高并发高负载网站的系统架构</title><link>http://www.cppblog.com/twzheng/articles/39950.html</link><dc:creator>谭文政</dc:creator><author>谭文政</author><pubDate>Sat, 29 Dec 2007 11:58:00 GMT</pubDate><guid>http://www.cppblog.com/twzheng/articles/39950.html</guid><wfw:comment>http://www.cppblog.com/twzheng/comments/39950.html</wfw:comment><comments>http://www.cppblog.com/twzheng/articles/39950.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/twzheng/comments/commentRss/39950.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/twzheng/services/trackbacks/39950.html</trackback:ping><description><![CDATA[<p align=center><strong><span>说说大型高并发高负载网站的系统架构</span></strong><strong></strong></p>
<p align=center>摘自 CSDN<span></span></p>
<div>
<p><span>我在</span><span>Cernet</span><span>做过拨号接入平台的搭建，而后在</span><span>Yahoo3721</span><span>负载搜索引擎前端平台开发，又在猫扑处理过大型社区猫扑大杂烩的架构升级等工作，同时自己接触和开发过不少大中型网站的模块，因此在大型网站应对高负载和并发的解决方案上有一些积累和经验，可以和大家一起探讨一下。</span></p>
</div>
<div>
<p><span><SCRIPT type=text/javascript>
show_ads_zone(13);
</SCRIPT></span><span>我在</span><span>Cernet</span><span>做过拨号接入平台的搭建，而后在</span><span>Yahoo3721</span><span>负载搜索引擎前端平台开发，又在猫扑处理过大型社区猫扑大杂烩的架构升级等工作，同时自己接触和开发过不少大中型网站的模块，因此在大型网站应对高负载和并发的解决方案上有一些积累和经验，可以和大家一起探讨一下。</span></p>
<p><span><br></span><span>一个小型的网站，比如个人网站，可以使用最简单的</span><span>html</span><span>静态页面就实现了，配合一些图片达到美化效果，所有的页面均存放在一个目录下，这样的网站对系统架构、性能的要求都很简单，随着互联网业务的不断丰富，网站相关的技术经过这些年的发展，已经细分到很细的方方面面，尤其对于大型网站来说，所采用的技术更是涉及面非常广，从硬件到软件、编程语言、数据库、</span><span>WebServer</span><span>、防火墙等各个领域都有了很高的要求，已经不是原来简单的</span><span>html</span><span>静态网站所能比拟的。</span></p>
<p><span>大型网站，比如门户网站。在面对大量用户访问、高并发请求方面，基本的解决方案集中在这样几个环节：使用高性能的服务器、高性能的数据库、高效率的编程语言、还有高性能的</span><span>Web</span><span>容器。但是除了这几个方面，还没法根本解决大型网站面临的高负载和高并发问题。</span></p>
<p><span>上面提供的几个解决思路在一定程度上也意味着更大的投入，并且这样的解决思路具备瓶颈，没有很好的扩展性，下面我从低成本、高性能和高扩张性的角度来说说我的一些经验。</span></p>
<p><span>1</span><span>、</span><span>HTML</span><span>静态化</span><span><br></span><span>其实大家都知道，效率最高、消耗最小的就是纯静态化的</span><span>html</span><span>页面，所以我们尽可能使我们的网站上的页面采用静态页面来实现，这个最简单的方法其实也是最有效的方法。但是对于大量内容并且频繁更新的网站，我们无法全部手动去挨个实现，于是出现了我们常见的信息发布系统</span><span>CMS</span><span>，像我们常访问的各个门户站点的新闻频道，甚至他们的其他频道，都是通过信息发布系统来管理和实现的，信息发布系统可以实现最简单的信息录入自动生成静态页面，还能具备频道管理、权限管理、自动抓取等功能，对于一个大型网站来说，拥有一套高效、可管理的</span><span>CMS</span><span>是必不可少的。</span></p>
<p><span>除了门户和信息发布类型的网站，对于交互性要求很高的社区类型网站来说，尽可能的静态化也是提高性能的必要手段，将社区内的帖子、文章进行实时的静态化，有更新的时候再重新静态化也是大量使用的策略，像</span><span>Mop</span><span>的大杂烩就是使用了这样的策略，网易社区等也是如此。</span></p>
<p><span>同时，</span><span>html</span><span>静态化也是某些缓存策略使用的手段，对于系统中频繁使用数据库查询但是内容更新很小的应用，可以考虑使用</span><span>html</span><span>静态化来实现，比如论坛中论坛的公用设置信息，这些信息目前的主流论坛都可以进行后台管理并且存储再数据库中，这些信息其实大量被前台程序调用，但是更新频率很小，可以考虑将这部分内容进行后台更新的时候进行静态化，这样避免了大量的数据库访问请求。</span></p>
<p><span>2</span><span>、图片服务器分离</span><span><br></span><span>大家知道，对于</span><span>Web</span><span>服务器来说，不管是</span><span>Apache</span><span>、</span><span>IIS</span><span>还是其他容器，图片是最消耗资源的，于是我们有必要将图片与页面进行分离，这是基本上大型网站都会采用的策略，他们都有独立的图片服务器，甚至很多台图片服务器。这样的架构可以降低提供页面访问请求的服务器系统压力，并且可以保证系统不会因为图片问题而崩溃，在应用服务器和图片服务器上，可以进行不同的配置优化，比如</span><span>apache</span><span>在配置</span><span>ContentType</span><span>的时候可以尽量少支持，尽可能少的</span><span>LoadModule</span><span>，保证更高的系统消耗和执行效率。</span></p>
<p><span>3</span><span>、数据库集群和库表散列</span><span><br></span><span>大型网站都有复杂的应用，这些应用必须使用数据库，那么在面对大量访问的时候，数据库的瓶颈很快就能显现出来，这时一台数据库将很快无法满足应用，于是我们需要使用数据库集群或者库表散列。</span></p>
<p><span>在数据库集群方面，很多数据库都有自己的解决方案，</span><span>Oracle</span><span>、</span><span>Sybase</span><span>等都有很好的方案，常用的</span><span>MySQL</span><span>提供的</span><span>Master/Slave</span><span>也是类似的方案，您使用了什么样的</span><span>DB</span><span>，就参考相应的解决方案来实施即可。</span></p>
<p><span>上面提到的数据库集群由于在架构、成本、扩张性方面都会受到所采用</span><span>DB</span><span>类型的限制，于是我们需要从应用程序的角度来考虑改善系统架构，库表散列是常用并且最有效的解决方案。我们在应用程序中安装业务和应用或者功能模块将数据库进行分离，不同的模块对应不同的数据库或者表，再按照一定的策略对某个页面或者功能进行更小的数据库散列，比如用户表，按照用户</span><span>ID</span><span>进行表散列，这样就能够低成本的提升系统的性能并且有很好的扩展性。</span><span>sohu</span><span>的论坛就是采用了这样的架构，将论坛的用户、设置、帖子等信息进行数据库分离，然后对帖子、用户按照板块和</span><span>ID</span><span>进行散列数据库和表，最终可以在配置文件中进行简单的配置便能让系统随时增加一台低成本的数据库进来补充系统性能。</span></p>
<p><span>4</span><span>、缓存</span><span><br></span><span>缓存一词搞技术的都接触过，很多地方用到缓存。网站架构和网站开发中的缓存也是非常重要。这里先讲述最基本的两种缓存。高级和分布式的缓存在后面讲述。</span><span><br></span><span>架构方面的缓存，对</span><span>Apache</span><span>比较熟悉的人都能知道</span><span>Apache</span><span>提供了自己的缓存模块，也可以使用外加的</span><span>Squid</span><span>模块进行缓存，这两种方式均可以有效的提高</span><span>Apache</span><span>的访问响应能力。</span><span><br></span><span>网站程序开发方面的缓存，</span><span>Linux</span><span>上提供的</span><span>Memory Cache</span><span>是常用的缓存接口，可以在</span><span>web</span><span>开发中使用，比如用</span><span>Java</span><span>开发的时候就可以调用</span><span>MemoryCache</span><span>对一些数据进行缓存和通讯共享，一些大型社区使用了这样的架构。另外，在使用</span><span>web</span><span>语言开发的时候，各种语言基本都有自己的缓存模块和方法，</span><span>PHP</span><span>有</span><span>Pear</span><span>的</span><span>Cache</span><span>模块，</span><span>Java</span><span>就更多了，</span><span>.net</span><span>不是很熟悉，相信也肯定有。</span></p>
<p><span>5</span><span>、镜像</span><span><br></span><span>镜像是大型网站常采用的提高性能和数据安全性的方式，镜像的技术可以解决不同网络接入商和地域带来的用户访问速度差异，比如</span><span>ChinaNet</span><span>和</span><span>EduNet</span><span>之间的差异就促使了很多网站在教育网内搭建镜像站点，数据进行定时更新或者实时更新。在镜像的细节技术方面，这里不阐述太深，有很多专业的现成的解决架构和产品可选。也有廉价的通过软件实现的思路，比如</span><span>Linux</span><span>上的</span><span>rsync</span><span>等工具。</span></p>
<p><span>6</span><span>、负载均衡</span><span><br></span><span>负载均衡将是大型网站解决高负荷访问和大量并发请求采用的终极解决办法。</span><span><br></span><span>负载均衡技术发展了多年，有很多专业的服务提供商和产品可以选择，我个人接触过一些解决方法，其中有两个架构可以给大家做参考。</span><span><br></span><span>硬件四层交换</span><span><br></span><span>第四层交换使用第三层和第四层信息包的报头信息，根据应用区间识别业务流，将整个区间段的业务流分配到合适的应用服务器进行处理。　第四层交换功能就象是虚</span><span>IP</span><span>，指向物理服务器。它传输的业务服从的协议多种多样，有</span><span>HTTP</span><span>、</span><span>FTP</span><span>、</span><span>NFS</span><span>、</span><span>Telnet</span><span>或其他协议。这些业务在物理服务器基础上，需要复杂的载量平衡算法。在</span><span>IP</span><span>世界，业务类型由终端</span><span>TCP</span><span>或</span><span>UDP</span><span>端口地址来决定，在第四层交换中的应用区间则由源端和终端</span><span>IP</span><span>地址、</span><span>TCP</span><span>和</span><span>UDP</span><span>端口共同决定。</span><span><br></span><span>在硬件四层交换产品领域，有一些知名的产品可以选择，比如</span><span>Alteon</span><span>、</span><span>F5</span><span>等，这些产品很昂贵，但是物有所值，能够提供非常优秀的性能和很灵活的管理能力。</span><span>Yahoo</span><span>中国当初接近</span><span>2000</span><span>台服务器使用了三四台</span><span>Alteon</span><span>就搞定了。</span></p>
<p><span>软件四层交换</span><span><br></span><span>大家知道了硬件四层交换机的原理后，基于</span><span>OSI</span><span>模型来实现的软件四层交换也就应运而生，这样的解决方案实现的原理一致，不过性能稍差。但是满足一定量的压力还是游刃有余的，有人说软件实现方式其实更灵活，处理能力完全看你配置的熟悉能力。</span><span><br></span><span>软件四层交换我们可以使用</span><span>Linux</span><span>上常用的</span><span>LVS</span><span>来解决，</span><span>LVS</span><span>就是</span><span>Linux Virtual Server</span><span>，他提供了基于心跳线</span><span>heartbeat</span><span>的实时灾难应对解决方案，提高系统的鲁棒性，同时可供了灵活的虚拟</span><span>VIP</span><span>配置和管理功能，可以同时满足多种应用需求，这对于分布式的系统来说必不可少。</span></p>
<p><span>一个典型的使用负载均衡的策略就是，在软件或者硬件四层交换的基础上搭建</span><span>squid</span><span>集群，这种思路在很多大型网站包括搜索引擎上被采用，这样的架构低成本、高性能还有很强的扩张性，随时往架构里面增减节点都非常容易。这样的架构我准备空了专门详细整理一下和大家探讨。</span></p>
<p><span>对于大型网站来说，前面提到的每个方法可能都会被同时使用到，我这里介绍得比较浅显，具体实现过程中很多细节还需要大家慢慢熟悉和体会，有时一个很小的</span><span>squid</span><span>参数或者</span><span>apache</span><span>参数设置，对于系统性能的影响就会很大，希望大家一起讨论，达到抛砖引玉之效。</span></p>
<p><span>原文链接：</span><span><a href="http://sunjiuchen.spaces.live.com/Blog/cns!4FF388881B3FE073!113.entry">http://sunjiuchen.spaces.live.com/Blog/cns!4FF388881B3FE073!113.entry</a></span></p>
</div>
<img src ="http://www.cppblog.com/twzheng/aggbug/39950.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/twzheng/" target="_blank">谭文政</a> 2007-12-29 19:58 <a href="http://www.cppblog.com/twzheng/articles/39950.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>详述Windows 2003 SP2入门IDS构建过程</title><link>http://www.cppblog.com/twzheng/articles/35898.html</link><dc:creator>谭文政</dc:creator><author>谭文政</author><pubDate>Mon, 05 Nov 2007 10:16:00 GMT</pubDate><guid>http://www.cppblog.com/twzheng/articles/35898.html</guid><wfw:comment>http://www.cppblog.com/twzheng/comments/35898.html</wfw:comment><comments>http://www.cppblog.com/twzheng/articles/35898.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/twzheng/comments/commentRss/35898.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/twzheng/services/trackbacks/35898.html</trackback:ping><description><![CDATA[<span class=t18><strong style="FONT-SIZE: 18pt">&nbsp;详述Windows 2003 SP2入门IDS构建过程</strong>
<p>　　IDS的技术手段其实并不很神秘，接下来本文会用一种&#8220;顺藤摸瓜&#8221;的脉络，给大家介绍一个较简单的IDS入门级构架。从市场分布、入手难易的角度来看，选择NIDS作为范例进行部署，比较地恰当。本文以完全的Windows平台来贯穿整个入侵<nobr>检测</nobr>流程，由于篇幅所限，以定性<nobr>分析</nobr>角度来陈述。 </p>
<p>　　预备知识 </p>
<p>　　IDS：Intrusion Detection System（入侵检测<nobr>系统</nobr>），通过收集网络系统信息来进行入侵检测分析的软件与硬件的智能组合。 </p>
<p>　　对IDS进行标准化工作的两个组织：作为国际互联网标准的制定者IETF的Intrusion Detection working Group（IDWG，入侵检测工作组）和Common Intrusion Detection Framework（CIDF，通用入侵检测框架）。 </p>
<p>　　IDS分类：Network IDS（基于网络）、Host-based IDS（基于主机）、Hybrid IDS（混合式）、Consoles IDS（控制台）、File Integrity Checkers（文件完整性检查器）、Honeypots（蜜罐）。事件产生系统 </p>
<p>　　根据CIDF阐述入侵检测系统（IDS）的通用模型思想，具备所有要素、最简单的入侵检测组件如图所示。根据CIDF规范，将IDS需要分析的数据统称为Event（事件），Event既可能是网络中的Data Packets（数据包），也可能是从System Log等其他方式得到的Information（信息）。 </p>
<p>　　没有数据流进（或数据被采集），IDS就是无根之木，完全无用武之地。 </p>
<p>　　作为IDS的基层组织，事件产生系统大可施展拳脚，它收集被定义的所有事件，然后一股脑地传到其它组件里。在Windows环境下，目前比较基本的做法是使用Winpcap和WinDump。 </p>
<p>　　大家知道，对于事件产生和事件分析系统来说，眼下流行采用Linux和Unix平台的软件和程序；其实在Windows平台中，也有类似Libpcap（是Unix或Linux从内核捕获网络数据包的必备软件）的工具即Winpcap。 </p>
<p>　　Winpcap是一套免费的， 基于Windows的网络接口API，把网卡设置为&#8220;混杂&#8221;模式，然后循环处理网络捕获的数据包。其技术实现简单，可移植性强，与网卡无关，但效率不高，适合在100 Mbps以下的网络 </p>
<p>　　相应的基于Windows的网络嗅探工具是WinDump（是Linux/Unix平台的Tcpdump在Windows上的移植版），这个软件必须基于Winpcap接口（这里有人形象地称Winpcap为：数据嗅探驱动程序）。使用WinDump，它能把匹配规则的数据包的包头给显示出来。你能使用这个工具去查找网络问题或者去监视网络上的状况，可以在一定程度上有效监控来自网络上的安全和不安全的行为。 </p>
<p>　　这两个软件在网上都可以免费地找到，读者还可以查看相关软件使用教程。 </p>
<p>　　下面大略介绍一下建立事件探测及采集的步骤 </p>
<p>　　1、装配软件和硬件系统。根据网络繁忙程度决定是否采用普通兼容机或性能较高的专用服务器；安装NT核心的Windows<nobr>操作系统</nobr>，推荐使用<nobr>Windows Server</nobr> 2003企业版，如果条件不满足也可使用Windows 2000 Advanced Server。分区格式建议为NTFS格式。 </p>
<p>　　2、服务器的空间划分要合理有效，执行程序的安装、数据日志的存储，两者空间最好分别放置在不同分区。 </p>
<p>　　3、Winpcap的简单实现。首先安装它的驱动程序，可以到它的主页或镜像站点下载WinPcap auto-installer (Driver+DLLs)，直接安装。 </p>
<p>　　注：如果用Winpcap做开发，还需要下载 Developer＇s pack。 </p>
<p>　　WinPcap 包括三个<nobr>模块</nobr>：第一个模块NPF（Netgroup Packet Filter），是一个VxD（虚拟设备驱动程序）文件。其功能是过滤数据包，并把这些包完好无损地传给用户态模块。第二个模块packet.dll为Win32平台提供了一个公共接口，架构在packet.dll之上，提供了更方便、更直接的编程方法。第三个模块 Wpcap.dll不依赖于任何操作系统，是底层的动态链接库，提供了高层、抽象的函数。具体使用说明在各大网站上都有涉及，如何更好利用Winpcap需要较强的C环境编程能力。 </p>
<p>　　4、WinDump的创建。安装后，在Windows命令提示符模式下运行，用户自己可以查看网络状态，恕不赘述。 </p>
<p>　　如果没有软件兼容性问题、安装和配置正确的话，事件探测及采集已能实现。</p>
<p>　　事件分析系统 <br>　　由于我们的网络大都用交换式以太网交换机连接，所以建立事件分析系统的目的是实现对多种网络防火墙设备的探测，以及多种采集方式（如基于Snmp、Syslog数据信息的采集）日志的支持，并提供一定的事件日志处理，统计、分析和查询功能。 </p>
<p>　　事件分析系统是IDS的核心模块，主要功能是对各种事件进行分析，从中发现违反安全策略的行为，如何建立是重点也是难点。如果自己能或与人合作编写软件系统，就需要做好严谨的前期开发准备，如对网络协议、黑客攻击、系统漏洞有着比较清晰的认识，接着开始制定规则和策略，它应该基于标准的技术标准和规范，然后优化算法以提高执行效率，建立检测模型，可以模拟进行攻击及分析过程。 </p>
<p>　　事件分析系统把检测引擎驻留在监视网段中，一般通过三种技术手段进行分析：模式匹配、协议分析和行为分析。当检测到某种误用模式时，产生对应的警告信息并发送给响应系统。目前来看，使用协议分析是实时检测的最好方式。 </p>
<p>　　这个系统一种可能的方式是由协议分析器作为主体，可以在现成的、开放式的协议分析工具包基础上来构建；协议分析器可以显示分组级网络传输流，基于网络协议规则的警告进行自动分析来快速探测攻击的存在；由此，网络程序员和管理员可监控并分析网络活动，从而主动检测并定位故障。用户可以尝试一下一个叫Ethereal的免费网络协议分析器，它支持Windows系统。用户可以对由事件产生系统抓取后保存在硬盘上的数据进行分析。你能交互式地浏览抓取到的数据包，查看每一个数据包的摘要和详细信息。Ethereal有多种强大的特征，如支持几乎所有的协议、丰富的过滤语言、易于查看TCP会话经重构后的数据流等。 </p>
<p>　　响应系统 </p>
<p>　　响应系统是面向人、物的交互系统，可以说是整个系统的中转站和协调站。人即是系统管理员、物是其他所有组件。详细说来，响应系统这个协调员要做的事很多：按照预置定义的方式，记录安全事件、产生报警信息（如E-mail形式）、记录附加日志、隔离入侵者、终止进程、禁止受害者的端口和服务、甚至反戈一击；可以采取人工响应和自动响应（基于机器的响应），两者结合起来会比较好。 </p>
<p>　　响应系统的设计要素 </p>
<p>　　(1) 接受自事件产生系统经事件分析系统过滤、分析、重建后的事件警报信息，然后交互给用户（管理员）查询并做出规则判断和采取管理行为。 </p>
<p>　　(2) 给管理员提供管理事件<nobr>数据库</nobr>系统的一个接口，可以修改规则库、根据不同网络环境情况配置安全策略、读写数据库系统。 </p>
<p>　　(3)作用于前端系统时，可管理事件产生、分析系统（合称事件探测器），对该系统采集、探测、分析的事件进行分类、筛选，可针对不同安全状况，重新对安全规则进行洗牌。 </p>
<p>　　响应系统和事件探测器通常是以应用程序的形式实现。 </p>
<p>　　设计思路：响应系统可分为两个程序部分，监听和控制。 监听部分绑定某个空闲端口，接收从事件探测器发出的分析结果和其他信息，并转化存储文件到事件数据库系统中，作为管理员可根据用户权限调用来只读、修改以及特别的操作。控制部分可用GTK＋来编写GUI，开发出较为直观的图形用户界面，目的主要是给用户一个更方便友好的界面来浏览警告信息。 </p>
<p>　　事件数据库系统 </p>
<p>　　在Windows平台下，虽然Access更易掌握，但采用SQL Server 2000构建会比Access有效，而且并不是很难入手，此系统主要功能：记录、存储、重排事件信息，可供管理员调用查看和对攻击审查取证使用。 </p>
<p>　　此系统构造相对简单，只需利用到数据库软件的一些基本功能。 </p>
<p>　　要协调各组件之间的有目的通信，各组件就必须能正确理解相互之间传递的各种数据的语义。可参考CIDF的通信机制，构建3层模型。注意各个组件之间的互操作性，保证安全、高效、顺畅。 </p>
<p>　　整合在后续的工作中会不断进行，各个组件的功能也会不断完善。一个基本的、基于Windows平台的IDS框架就构建完毕。满足网络条件的话，试试亲手做做自己的奶酪吧，有一种不可名状的劳作后的甜蜜。</p>
</span><!--正文内容结束-->
<img src ="http://www.cppblog.com/twzheng/aggbug/35898.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/twzheng/" target="_blank">谭文政</a> 2007-11-05 18:16 <a href="http://www.cppblog.com/twzheng/articles/35898.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用键盘钩子在Windows平台捕获键盘动作</title><link>http://www.cppblog.com/twzheng/articles/25536.html</link><dc:creator>谭文政</dc:creator><author>谭文政</author><pubDate>Mon, 04 Jun 2007 16:29:00 GMT</pubDate><guid>http://www.cppblog.com/twzheng/articles/25536.html</guid><wfw:comment>http://www.cppblog.com/twzheng/comments/25536.html</wfw:comment><comments>http://www.cppblog.com/twzheng/articles/25536.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/twzheng/comments/commentRss/25536.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/twzheng/services/trackbacks/25536.html</trackback:ping><description><![CDATA[<strong style="FONT-SIZE: 18pt">用键盘钩子在Windows平台捕获键盘动作</strong><br><span style="FONT-SIZE: 10pt">摘自：好易教程网</span><br><span style="FONT-SIZE: 10pt">信息产业部电子第二十二研究所青岛分所 郎锐</span><br><br>一、引言 <br>我们可以在应用程序中毫不费力的捕获在本程序窗口上所进行的键盘操作，但如果我们想要将此程序作成一个监控程序，捕获在Windows平台下任意窗口上的键盘操作，就需要借助于全局钩子来实现了。 <br>二、系统钩子和DLL <br>钩子的本质是一段用以处理系统消息的程序，通过系统调用，将其挂入系统。钩子的种类有很多，每种钩子可以截获并处理相应的消息，每当特定的消息发出，在到达目的窗口之前，钩子程序先行截获该消息、得到对此消息的控制权。此时在钩子函数中就可以对截获的消息进行加工处理，甚至可以强制结束消息的传递。 <br>在本程序中我们需要捕获在任意窗口上的键盘输入，这就需要采用全局钩子以便拦截整个系统的消息，而全局钩子函数必须以DLL（动态连接库）为载体进行封装，VC6中有三种形式的MFC DLL可供选择，即Regular statically linked to MFC DLL（标准静态链接MFC DLL）、Regular using the shared MFC DLL（标准动态链接MFC DLL）以及Extension MFC DLL（扩展MFC DLL）。 在本程序中为方便起见采用了标准静态连接MFC DLL。 <br>三、键盘钩子程序示例 <br>本示例程序用到全局钩子函数，程序分两部分：可执行程序KeyHook和动态连接库LaunchDLL。 <br>1、首先编制MFC扩展动态连接库LaunchDLL.dll: <br>（1）选择MFC AppWizard(DLL)创建项目LaunchDLL；在接下来的选项中选择Regular statically linked to MFC DLL（标准静态链接MFC DLL）。 <br>（2）在LaunchDLL.h中添加宏定义和待导出函数的声明： <br>#define DllExport __declspec(dllexport) <br>&#8230;&#8230; <br>DllExport void WINAPI InstallLaunchEv(); <br>&#8230;&#8230; <br>class CLaunchDLLApp : public CWinApp <br>{ <br>public: <br>CLaunchDLLApp(); <br> <br>//{{AFX_VIRTUAL(CLaunchDLLApp) <br>//}}AFX_VIRTUAL <br> <br>//{{AFX_MSG(CLaunchDLLApp) <br>// NOTE - the ClassWizard will add and remove member functions here. <br>// DO NOT EDIT what you see in these blocks of generated code ! <br>//}}AFX_MSG <br>DECLARE_MESSAGE_MAP() <br>}; <br>（3）在LaunchDLL.cpp中添加全局变量Hook和全局函数LauncherHook、SaveLog： <br>HHOOK Hook; <br>LRESULT CALLBACK LauncherHook(int nCode,WPARAM wParam,LPARAM lParam); <br>void SaveLog(char* c); <br>（4）完成以上提到的这几个函数的实现部分： <br>&#8230;&#8230; <br>CLaunchDLLApp theApp; <br>&#8230;&#8230; <br>DllExport void WINAPI InstallLaunchEv() <br>{ <br>Hook=(HHOOK)SetWindowsHookEx(WH_KEYBOARD, <br>(HOOKPROC)LauncherHook, <br>theApp.m_hInstance, <br>0); <br>} <br>在此我们实现了Windows的系统钩子的安装，首先要调用SDK中的API函数SetWindowsHookEx来安装这个钩子函数，其原型是： <br>HHOOK SetWindowsHookEx(int idHook, <br>HOOKPROC lpfn, <br>HINSTANCE hMod, <br>DWORD dwThreadId); <br>其中，第一个参数指定钩子的类型，常用的有WH_MOUSE、WH_KEYBOARD、WH_GETMESSAGE等，在此我们只关心键盘操作所以设定为WH_KEYBOARD；第二个参数标识钩子函数的入口地址，当钩子钩到任何消息后便调用这个函数,即当不管系统的哪个窗口有键盘输入马上会引起LauncherHook的动作；第三个参数是钩子函数所在模块的句柄，我们可以很简单的设定其为本应用程序的实例句柄；最后一个参数是钩子相关函数的ID用以指定想让钩子去钩哪个线程，为0时则拦截整个系统的消息，在本程序中钩子需要为全局钩子，故设定为0。 <br>&#8230;&#8230; <br>LRESULT CALLBACK LauncherHook(int nCode,WPARAM wParam,LPARAM lParam) <br>{ <br>LRESULT Result=CallNextHookEx(Hook,nCode,wParam,lParam); <br>if(nCode==HC_ACTION) <br>{ <br>if(lParam &amp; 0x80000000) <br>{ <br>char c[1]; <br>c[0]=wParam; <br>SaveLog(c); <br>} <br>} <br>return Result; <br>} <br>虽然调用CallNextHookEx()是可选的，但调用此函数的习惯是很值得推荐的；否则的话，其他安装了钩子的应用程序将不会接收到钩子的通知而且还有可能产生不正确的结果，所以我们应尽量调用该函数除非绝对需要阻止其他程序获取通知。 <br>&#8230;&#8230; <br>void SaveLog(char* c) <br>{ <br>CTime tm=CTime::GetCurrentTime(); <br>CString name; <br>name.Format("c:\\Key_%d_%d.log",tm.GetMonth(),tm.GetDay()); <br>CFile file; <br>if(!file.Open(name,CFile::modeReadWrite)) <br>{ <br>file.Open(name,CFile::modeCreate|CFile::modeReadWrite); <br>} <br>file.SeekToEnd(); <br>file.Write(c,1); <br>file.Close(); <br>} <br>当有键弹起的时候就通过此函数将刚弹起的键保存到记录文件中从而实现对键盘进行监控记录的目的。 <br>编译完成便可得到运行时所需的键盘钩子的动态连接库LaunchDLL.dll和进行静态链接时用到的LaunchDLL.lib。 <br>2、下面开始编写调用此动态连接库的主程序，并实现最后的集成： <br>（1）用MFC的AppWizard(EXE)创建项目KeyHook； <br>（2）选择单文档，其余几步可均为确省； <br>（3）把LaunchDLL.h和LaunchDLL.lib复制到KeyHook工程目录中，LaunchDLL.dll复制到Debug目录下。 <br>（4）链接DLL库，即在"Project","Settings&#8230;"的"Link"属性页内，在"Object/librarymodules:"中填入"LaunchDLL.lib"。再通过"Project"，"Add To Project","Files&#8230;"将LaunchDLL.h添加到工程中来，最后在视类的源文件KeyHook.cpp中加入对其的引用： <br>#include "LaunchDLL.h" <br>这样我们就可以象使用本工程内的 函数一样使用动态连接库LaunchDLL.dll中的所有导出函数了。 <br>（5）在视类中添加虚函数OnInitialUpdate()，并添加代码完成对键盘钩子的安装： <br>&#8230;&#8230; <br>InstallLaunchEv(); <br>&#8230;&#8230; <br>（6）到此为止其实已经完成了所有的功能，但作为一个后台监控软件，运行时并不希望有界面，可以在应用程序类CkeyHookApp的InitInstance()函数中将m_pMainWnd-&gt;ShowWindow(SW_SHOW);改为m_pMainWnd-&gt;ShowWindow(SW_HIDE);即可。 <br>四、运行与检测 <br>编译运行程序，运行起来之后并无什么现象，但通过Alt+Ctrl+Del在关闭程序对话框内可以找到我们刚编写完毕的程序"KeyHook",随便在什么程序中通过键盘输入字符，然后打开记录文件，我们会发现：通过键盘钩子，我们刚才输入的字符都被记录到记录文件中了。 <br>小结：系统钩子具有相当强大的功能，通过这种技术可以对几乎所有的Windows系统消息进行拦截、监视、处理。这种技术广泛应用于各种自动监控系统中。<br>
<img src ="http://www.cppblog.com/twzheng/aggbug/25536.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/twzheng/" target="_blank">谭文政</a> 2007-06-05 00:29 <a href="http://www.cppblog.com/twzheng/articles/25536.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>完成端口中的单句柄数据结构与单IO数据结构的理解与设计</title><link>http://www.cppblog.com/twzheng/articles/25359.html</link><dc:creator>谭文政</dc:creator><author>谭文政</author><pubDate>Sat, 02 Jun 2007 15:19:00 GMT</pubDate><guid>http://www.cppblog.com/twzheng/articles/25359.html</guid><wfw:comment>http://www.cppblog.com/twzheng/comments/25359.html</wfw:comment><comments>http://www.cppblog.com/twzheng/articles/25359.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/twzheng/comments/commentRss/25359.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/twzheng/services/trackbacks/25359.html</trackback:ping><description><![CDATA[<span id=ArticleTitle1_ArticleTitle1_lblTitle><strong>完成端口中的单句柄数据结构与单IO数据结构的理解与设计</strong></span><br><br>本文作者：sodme<br>本文出处：<font style="COLOR: #000000" color=#0000ff>http://blog.csdn.net/sodme</font><br>声明：本文可以不经作者同意任意转载、复制、传播，但任何对本文的引用均须保留本文的作者、出处及本行声明信息！谢谢！<br><br>　　完成端口模型，针对于WIN平台的其它异步网络模型而言，最大的好处，除了性能方面的卓越外，还在于完成端口在传递网络事件的通知时，可以一并传递与此事件相关的应用层数据。这个应用层数据，体现在两个方面：一是单句柄数据，二是单IO数据。<br><br>　　GetQueuedCompletionStatus函数的原型如下：<br>　　WINBASEAPI<br>　　BOOL<br>　　WINAPI<br>　　GetQueuedCompletionStatus(<br>&nbsp;&nbsp;&nbsp; 　　IN&nbsp; HANDLE CompletionPort,<br>&nbsp;&nbsp;&nbsp;　　 OUT LPDWORD lpNumberOfBytesTransferred,<br>&nbsp;&nbsp;&nbsp; 　　OUT PULONG_PTR lpCompletionKey,<br>&nbsp;&nbsp;&nbsp; 　　OUT LPOVERLAPPED *lpOverlapped,<br>&nbsp;&nbsp;&nbsp; 　　IN&nbsp; DWORD dwMilliseconds<br>&nbsp;&nbsp;&nbsp; 　);<br>　　其中，我们把第三个参数lpCompletionKey称为完成键，由它传递的数据称为单句柄数据。我们把第四个参数lpOverlapped称为重叠结构体，由它传递的数据称为单IO数据。<br><br>　　以字面的意思来理解，lpCompletionKey内包容的东西应该是与各个socket一一对应的，而lpOverlapped是与每一次的wsarecv或wsasend操作一一对应的。<br><br>　　在网络模型的常见设计中，当一个客户端连接到服务器后，服务器会通过accept或AcceptEx创建一个socket，而应用层为了保存与此socket相关的其它信息（比如：该socket所对应的sockaddr_in结构体数据，该结构体内含客户端IP等信息，以及为便于客户端的逻辑包整理而准备的数据整理缓冲区等），往往需要创建一个与该socket一一对应的客户端底层通信对象，这个对象可以负责保存仅在网络层需要处理的数据成员和方法，然后我们需要将此客户端底层通信对象放入一个类似于list或map的容器中，待到需要使用的时候，使用容器的查找算法根据socket值找到它所对应的对象然后进行我们所需要的操作。<br><br>　　让人非常高兴的是，完成端口&#8220;体贴入微&#8221;，它已经帮我们在每次的完成事件通知时，稍带着把该socket所对应的底层通信对象的指针送给了我们，这个指针就是lpCompletionKey。也就是说，当我们从GetQueuedCompletionStatus函数取得一个数据接收完成的通知，需要将此次收到的数据放到该socket所对应的通信对象整理缓冲区内对数据进行整理时，我们已经不需要去执行list或map等的查找算法，而是可以直接定位这个对象了，当客户端连接量很大时，频繁查表还是很影响效率的。哇哦，太帅了，不是吗？呵呵。<br><br>　　基于以上的认识，我们的lpCompletionKey对象可以设计如下：<br>　　typedef struct PER_HANDLE_DATA<br>　　{<br>　　　　SOCKET socket;　　　　　　　　　　&nbsp;&nbsp; //本结构体对应的socket值<br>　　　　sockaddr_in addr;　　　　　　　　　　//用于存放客户端IP等信息<br>　　　　char DataBuf[ 2*MAX_BUFFER_SIZE ];　　//整理缓冲区,用于存放每次整理时的数据<br>　　}<br><br>　　PER_HANDLE_DATA与socket的绑定，通过CreateIOCompletionPort完成，将该结构体地址作为该函数的第三个参数传入即可。而PER_HANDLE_DATA结构体中addr成员，是在accept执行成功后进行赋值的。DataBuf则可以在每次WSARecv操作完成，需要整理缓冲区数据时使用。<br><br>　　下面我们再来看看完成端口的收、发操作中所使用到的重叠结构体OVERLAPPED。<br><br>　　关于重叠IO的知识，请自行GOOGLE相关资料。简单地说，OVERLAPPED是应用层与核心层交互共享的数据单元，如果要执行一个重叠IO操作，必须带有OVERLAPPED结构。在完成端口中，它允许应用层对OVERLAPPED结构进行扩展和自定义，允许应用层根据自己的需要在OVERLAPPED的基础上形成新的扩展OVERLAPPED结构。一般地，扩展的OVERLAPPED结构中，要求放在第一个的数据成员是原OVERLAPPED结构。我们可以形如以下方式定义自己的扩展OVERLAPPED结构：<br>　　typedef struct PER_IO_DATA<br>　　{<br>　　　　OVERLAPPED ovl;<br>　　　　WSABUF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf;<br>　　　　char&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RecvDataBuf[&nbsp;MAX_BUFFER_SIZE ];&nbsp;&nbsp; //接收缓冲区<br>　　　　char&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SendDataBuf[ MAX_BUFFER_SIZE&nbsp;];&nbsp;&nbsp; //发送缓冲区<br>　　　　OpType&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; opType;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //操作类型：发送、接收或关闭等<br>　　}<br>　　<br>　　在执行WSASend和WSARecv操作时，应用层会将扩展OVERLAPPED结构的地址传给核心，核心完成相应的操作后，仍然通过原有的这个结构传递操作结果，比如&#8220;接收&#8221;操作完成后，RecvDataBuf里存放便是此次接收下来的数据。<br><br>　　根据各自应用的不同，不同的完成端口设计者可能会设计出不同的PER_HANDLE_DATA<br>和PER_IO_DATA，我这里给出的设计也只是针对自己的应用场合的，不一定就适合你。但我想，最主要的还是要搞明白PER_HANDLE_DATA和PER_IO_DATA两种结构体的含义、用途，以及调用流程。<br>
<img src ="http://www.cppblog.com/twzheng/aggbug/25359.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/twzheng/" target="_blank">谭文政</a> 2007-06-02 23:19 <a href="http://www.cppblog.com/twzheng/articles/25359.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows Sockets 2.0:使用完成端口高性能，可扩展性Winsock服务程序</title><link>http://www.cppblog.com/twzheng/articles/25140.html</link><dc:creator>谭文政</dc:creator><author>谭文政</author><pubDate>Wed, 30 May 2007 09:24:00 GMT</pubDate><guid>http://www.cppblog.com/twzheng/articles/25140.html</guid><wfw:comment>http://www.cppblog.com/twzheng/comments/25140.html</wfw:comment><comments>http://www.cppblog.com/twzheng/articles/25140.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/twzheng/comments/commentRss/25140.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/twzheng/services/trackbacks/25140.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Windows Sockets 2.0:使用完成端口高性能，可扩展性Winsock服务程序[摘自]http://blog.csdn.net/vcbear/翻译说明： 完成端口基本上公认为一种在windows服务平台上比较成熟和高效的IO方法，理解和编写程序都不是很困难。目前我正在进行这方面的实践，代码还没有完全调试和评价，只有这一篇拙劣的学习翻译文摘，见笑见笑。翻译这个文章，是因为我近期在...&nbsp;&nbsp;<a href='http://www.cppblog.com/twzheng/articles/25140.html'>阅读全文</a><img src ="http://www.cppblog.com/twzheng/aggbug/25140.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/twzheng/" target="_blank">谭文政</a> 2007-05-30 17:24 <a href="http://www.cppblog.com/twzheng/articles/25140.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>微软msn服务器设计思想初步理解</title><link>http://www.cppblog.com/twzheng/articles/24929.html</link><dc:creator>谭文政</dc:creator><author>谭文政</author><pubDate>Sun, 27 May 2007 04:02:00 GMT</pubDate><guid>http://www.cppblog.com/twzheng/articles/24929.html</guid><wfw:comment>http://www.cppblog.com/twzheng/comments/24929.html</wfw:comment><comments>http://www.cppblog.com/twzheng/articles/24929.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/twzheng/comments/commentRss/24929.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/twzheng/services/trackbacks/24929.html</trackback:ping><description><![CDATA[<font color=#0000ff><strong>[转]微软msn服务器设计思想</strong></font><font color=#0000ff><strong>初步理解<br></strong><span style="FONT-SIZE: 10pt">来源不明<br></span></font><br>由于工作需要，我用了近2个月的时间去了解msn的协议，通过长时间的抓包和试图实现，我将我了解了的msn的服务器端的部分设计思想总结如下。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;作为服务器设计，比较重要的几个问题是：(不妥之处，希望大家修正）<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1.安全性<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2.并发服务能力<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3.性能的可线性提高<br><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;一、安全性<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;服务器的安全性包括两部分，一是服务器本身软硬件配置上的安全性，比如防止系统漏洞；二是服务器和客户端通讯协议的安全性设计，防止通过协议本身导致密码泄露、服务器被非法攻击等。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在协议上，msn的密码是通过ssl传送到服务器的；我对ssl的内部细节不是很了解，但是显然，密码经过ssl传输过程到服务器端后，是被明文解出的， 因此安全性依赖于ssl本身提供。在这方面，我倾向于yahoo的设计，密码不通过自身的明文或者任何本身的加密后密文传输，而是同服务器返回的 session和password结合，进行混合的不可反向解密的md5密文进行传输。这样的加密结果被任何第三方截获都是没有意义的；因为不可能从这样 的密文中分析出原来的密码。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我认为，传输协议中，密码必须和服务器端协商的一个随机seesion结合，通过不可恢复的加密方式进行加密，传送到服务器，服务器端也是按照session和passowrd进行同样方式的加密，比较加密结果，验证用户的合法性。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 就软硬件系统本身的安全性而言，我认为尽量把系统中不需要的软件和其它模块去除，保留服务器系统运行需要的最小内核；同时一台服务器应该只提供该服务器需 要提供的服务，不开多余的网络端口，对telnet方式禁止，而使用更安全的ssh进行远程管理。<br><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;二、并发服务能力<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;服务器的并发服务能力是服务器程序设计的一个重要内容。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1、在单台服务器上，服务器软件的性能设计应考虑以下问题：<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;数据拷贝<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;内存管理<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;线程间的锁控制<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;数据拷贝：<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 通常来说，避免数据拷贝是个非常头疼的问题。我在平时的工作中，尽量把缓冲区的指针使用范围限定在一定的作用域内，如果需要在作用域外使用，我通常会通过 数据拷贝的方式进行，这样可以避免令人头疼的内存泄露问题，一块内存一旦在多个作用域使用或者在分配该内存的作用域之外使用时，很容易搞不清楚何时该释放 该内存。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一个比较好的办法是利用在COM里使用的引用计数技术，把该内存的释放时机交给内存自己管理；也就是说把内存封装进一个结构体或者类里，本身对自己被使用进行管理，一旦发现自己没有人使用了，就释放自己。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;内存管理：<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 内存的处理也是很需要注意的一部分，频繁的new /delete内存会让内存出现大量碎片，对服务器软件的性能也是有不小的影响的；通常的做法我们可以一开始申请一个比较大的内存区域，然后自己负责管 理，把这块内存划分成很多小块（64B/ 128B/ 256B)，然后按照申请内存的需要，分配合适的内存区域。这样可以不用每次都到系统申请内存，也把内存泄露的可能性限制在很小的范围内（内存泄露应该被 解决）。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;另外，对于一些对象，在我们使用完后，可以暂时不把它真正的从内存里释放掉，而是把它挂到一个list上去，下次对于通用的对象，完全可以重用这快内存。这也是减少内存分配次数的一个办法。但是这可能会导致使用很耗的加解锁。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;线程间的锁控制：<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;涉及到锁控制的，主要是因为共享问题。共享分为两种：一是代码共享部分；一是数据共享部分。其中做主要的还是数据共享部分。但是没有什么好的解决办法，唯一的办法就是检查这个共享是不是真正必要的，这些数据可不可以分成两部分以形成不是共享的。<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当然，这部分为软老大做了什么我不清楚。<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;三、性能的可线性提高<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这主要指服务器群组的服务能力可以通过增加服务器的方式线性提高性能。这就要求服务器的服务能力分担是均衡的，即实现良好的负载平衡。新加入的服务器能均衡的被负载平衡服务器分配服务。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当然，这也设计到服务器集群、数据库服务器的集群，我想找个时间专门研究这些问题。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在这方面，微软的设计思想很好的体现了这个原则，能够把负载均衡的交给新服务器。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;msn 的认证服务器、聊天服务器分开的。即每次聊天时，都需要向认证服务器申请一个聊天服务器地址，然后在通过认证服务器邀请对方加入到这个聊天服务器，这就 保证了聊天的人会在同一台服务器上，不用再到数据库服务器查找对方的地址，也避免了头疼的服务器数据同步问题。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果新加入一个服务器，那么这太服务器只要在负载平衡服务器注册，就可以和其他服务器不相干的为客户端提供可靠的服务，当然群组的服务能力就线性提升了。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;写文专为与朋友们交流，对于内容中不妥之处，请多多指教：）<br>
<img src ="http://www.cppblog.com/twzheng/aggbug/24929.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/twzheng/" target="_blank">谭文政</a> 2007-05-27 12:02 <a href="http://www.cppblog.com/twzheng/articles/24929.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>类似于QQ游戏百万人同时在线的服务器架构实现 </title><link>http://www.cppblog.com/twzheng/articles/24801.html</link><dc:creator>谭文政</dc:creator><author>谭文政</author><pubDate>Thu, 24 May 2007 18:58:00 GMT</pubDate><guid>http://www.cppblog.com/twzheng/articles/24801.html</guid><wfw:comment>http://www.cppblog.com/twzheng/comments/24801.html</wfw:comment><comments>http://www.cppblog.com/twzheng/articles/24801.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/twzheng/comments/commentRss/24801.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/twzheng/services/trackbacks/24801.html</trackback:ping><description><![CDATA[<span style="FONT-SIZE: 18pt">类似于QQ游戏百万人同时在线的服务器架构实现</span><br><br>本文作者：sodme　本文出处：http://blog.csdn.net/sodme<br>版权声明：本文可以不经作者同意任意转载，但转载时烦请保留文章开始前两行的版权、作者及出处信息。<br><br>　　QQ游戏于前几日终于突破了百万人同时在线的关口，向着更为远大的目标迈进，这让其它众多传统的棋牌休闲游戏平台黯然失色，相比之下，联众似乎已经根本不是QQ的对手，因为QQ除了这100万的游戏在线人数外，它还拥有3亿多的注册量（当然很多是重复注册的）以及QQ聊天软件900万的同时在线率，我们已经可以预见未来由QQ构建起来的强大棋牌休闲游戏帝国。<br>　　那么，在技术上，QQ游戏到底是如何实现百万人同时在线并保持游戏高效率的呢？<br>　　事实上，针对于任何单一的网络服务器程序，其可承受的同时连接数目是有理论峰值的，通过C＋＋中对TSocket的定义类型：word，我们可以判定这个连接理论峰值是65535，也就是说，你的单个服务器程序，最多可以承受6万多的用户同时连接。但是，在实际应用中，能达到一万人的同时连接并能保证正常的数据交换已经是很不容易了，通常这个值都在2000到5000之间，据说QQ的单台服务器同时连接数目也就是在这个值这间。<br>　　如果要实现2000到5000用户的单服务器同时在线，是不难的。在windows下，比较成熟的技术是采用IOCP－－完成端口。与完成端口相关的资料在网上和CSDN论坛里有很多，感兴趣的朋友可以自己搜索一下。只要运用得当，一个完成端口服务器是完全可以达到2K到5K的同时在线量的。但，5K这样的数值离百万这样的数值实在相差太大了，所以，百万人的同时在线是单台服务器肯定无法实现的。<br>　　要实现百万人同时在线，首先要实现一个比较完善的完成端口服务器模型，这个模型要求至少可以承载2K到5K的同时在线率（当然，如果你MONEY多，你也可以只开发出最多允许100人在线的服务器）。在构建好了基本的完成端口服务器之后，就是有关服务器组的架构设计了。之所以说这是一个服务器组，是因为它绝不仅仅只是一台服务器，也绝不仅仅是只有一种类型的服务器。<br>　　简单地说，实现百万人同时在线的服务器模型应该是：登陆服务器＋大厅服务器＋房间服务器。当然，也可以是其它的模型，但其基本的思想是一样的。下面，我将逐一介绍这三类服务器的各自作用。<br>　　登陆服务器：一般情况下，我们会向玩家开放若干个公开的登陆服务器，就如QQ登陆时让你选择的从哪个QQ游戏服务器登陆一样，QQ登陆时让玩家选择的六个服务器入口实际上就是登陆服务器。登陆服务器主要完成负载平衡的作用。详细点说就是，在登陆服务器的背后，有N个大厅服务器，登陆服务器只是用于为当前的客户端连接选择其下一步应该连接到哪个大厅服务器，当登陆服务器为当前的客户端连接选择了一个合适的大厅服务器后，客户端开始根据登陆服务器提供的信息连接到相应的大厅上去，同时客户端断开与登陆服务器的连接，为其他玩家客户端连接登陆服务器腾出套接字资源。在设计登陆服务器时，至少应该有以下功能：N个大厅服务器的每一个大厅服务器都要与所有的登陆服务器保持连接，并实时地把本大厅服务器当前的同时在线人数通知给各个登陆服务器，这其中包括：用户进入时的同时在线人数增加信息以及用户退出时的同时在线人数减少信息。这里的各个大厅服务器同时在线人数信息就是登陆服务器为客户端选择某个大厅让其登陆的依据。举例来说，玩家A通过登陆服务器1连接到登陆服务器，登陆服务器开始为当前玩家在众多的大厅服务器中根据哪一个大厅服务器人数比较少来选择一个大厅，同时把这个大厅的连接IP和端口发给客户端，客户端收到这个IP和端口信息后，根据这个信息连接到此大厅，同时，客户端断开与登陆服务器之间的连接，这便是用户登陆过程中，在登陆服务器这一块的处理流程。<br>　　大厅服务器：大厅服务器，是普通玩家看不到的服务器，它的连接IP和端口信息是登陆服务器通知给客户端的。也就是说，在QQ游戏的本地文件中，具体的大厅服务器连接IP和端口信息是没有保存的。大厅服务器的主要作用是向玩家发送游戏房间列表信息，这些信息包括：每个游戏房间的类型，名称，在线人数，连接地址以及其它如游戏帮助文件URL的信息。从界面上看的话，大厅服务器就是我们输入用户名和密码并校验通过后进入的游戏房间列表界面。大厅服务器，主要有以下功能：一是向当前玩家广播各个游戏房间在线人数信息；二是提供游戏的版本以及下载地址信息；三是提供各个游戏房间服务器的连接IP和端口信息；四是提供游戏帮助的URL信息；五是提供其它游戏辅助功能。但在这众多的功能中，有一点是最为核心的，即：为玩家提供进入具体的游戏房间的通道，让玩家顺利进入其欲进入的游戏房间。玩家根据各个游戏房间在线人数，判定自己进入哪一个房间，然后双击服务器列表中的某个游戏房间后玩家开始进入游戏房间服务器。<br>　　游戏房间服务器：游戏房间服务器，具体地说就是如&#8220;斗地主1&#8221;，&#8220;斗地主2&#8221;这样的游戏房间。游戏房间服务器才是具体的负责执行游戏相关逻辑的服务器。这样的游戏逻辑分为两大类：一类是通用的游戏房间逻辑，如：进入房间，离开房间，进入桌子，离开桌子以及在房间内说话等；第二类是游戏桌子逻辑，这个就是各种不同类型游戏的主要区别之处了，比如斗地主中的叫地主或不叫地主的逻辑等，当然，游戏桌子逻辑里也包括有通用的各个游戏里都存在的游戏逻辑，比如在桌子内说话等。总之，游戏房间服务器才是真正负责执行游戏具体逻辑的服务器。<br>　　这里提到的三类服务器，我均采用的是完成端口模型，每个服务器最多连接数目是5000人，但是，我在游戏房间服务器上作了逻辑层的限定，最多只允许300人同时在线。其他两个服务器仍然允许最多5000人的同时在线。如果按照这样的结构来设计，那么要实现百万人的同时在线就应该是这样：首先是大厅，1000000/5000＝200。也就是说，至少要200台大厅服务器，但通常情况下，考虑到实际使用时服务器的处理能力和负载情况，应该至少准备250台左右的大厅服务器程序。另外，具体的各种类型的游戏房间服务器需要多少，就要根据当前玩各种类型游戏的玩家数目分别计算了，比如斗地主最多是十万人同时在线，每台服务器最多允许300人同时在线，那么需要的斗地主服务器数目就应该不少于：100000/300=333，准备得充分一点，就要准备350台斗地主服务器。<br>　　除正常的玩家连接外，还要考虑到：<br>　　对于登陆服务器，会有250台大厅服务器连接到每个登陆服务器上，这是始终都要保持的连接；<br>　　而对于大厅服务器而言，如果仅仅有斗地主这一类的服务器，就要有350多个连接与各个大厅服务器始终保持着。所以从这一点看，我的结构在某些方面还存在着需要改进的地方，但核心思想是：尽快地提供用户登陆的速度，尽可能方便地让玩家进入游戏中。
<img src ="http://www.cppblog.com/twzheng/aggbug/24801.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/twzheng/" target="_blank">谭文政</a> 2007-05-25 02:58 <a href="http://www.cppblog.com/twzheng/articles/24801.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>A Reusable Windows Socket Server Class With C++</title><link>http://www.cppblog.com/twzheng/articles/SSC.html</link><dc:creator>谭文政</dc:creator><author>谭文政</author><pubDate>Tue, 22 May 2007 16:12:00 GMT</pubDate><guid>http://www.cppblog.com/twzheng/articles/SSC.html</guid><wfw:comment>http://www.cppblog.com/twzheng/comments/24653.html</wfw:comment><comments>http://www.cppblog.com/twzheng/articles/SSC.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/twzheng/comments/commentRss/24653.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/twzheng/services/trackbacks/24653.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: A Reusable Windows Socket Server Class With C++ Contributed by Len Holgate 摘自：http://www.devarticles.com&nbsp;Ever thought of writing your own Windows socket server class? In this article Len sh...&nbsp;&nbsp;<a href='http://www.cppblog.com/twzheng/articles/SSC.html'>阅读全文</a><img src ="http://www.cppblog.com/twzheng/aggbug/24653.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/twzheng/" target="_blank">谭文政</a> 2007-05-23 00:12 <a href="http://www.cppblog.com/twzheng/articles/SSC.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基于SPI的数据报过滤原理与实现</title><link>http://www.cppblog.com/twzheng/articles/24524.html</link><dc:creator>谭文政</dc:creator><author>谭文政</author><pubDate>Mon, 21 May 2007 03:36:00 GMT</pubDate><guid>http://www.cppblog.com/twzheng/articles/24524.html</guid><wfw:comment>http://www.cppblog.com/twzheng/comments/24524.html</wfw:comment><comments>http://www.cppblog.com/twzheng/articles/24524.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/twzheng/comments/commentRss/24524.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/twzheng/services/trackbacks/24524.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 基于SPI的数据报过滤原理与实现作者：TOo2y [摘自] VC 知识库 （http://www.vckbase.com/）一. 个人防火墙技术概述二. Winsock 2 SPI介绍三. 相关程序代码分析四. 小结与后记五. 附录之源代码 一、个人防火墙技术概述&nbsp;&nbsp;&nbsp;&nbsp;随着网络安全问题日益严重，广大用户对网络安全产品也越来越关注。防火墙作为一种网络安全工具...&nbsp;&nbsp;<a href='http://www.cppblog.com/twzheng/articles/24524.html'>阅读全文</a><img src ="http://www.cppblog.com/twzheng/aggbug/24524.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/twzheng/" target="_blank">谭文政</a> 2007-05-21 11:36 <a href="http://www.cppblog.com/twzheng/articles/24524.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>最通俗浅显的“IO模式”解析</title><link>http://www.cppblog.com/twzheng/articles/24508.html</link><dc:creator>谭文政</dc:creator><author>谭文政</author><pubDate>Sun, 20 May 2007 17:46:00 GMT</pubDate><guid>http://www.cppblog.com/twzheng/articles/24508.html</guid><wfw:comment>http://www.cppblog.com/twzheng/comments/24508.html</wfw:comment><comments>http://www.cppblog.com/twzheng/articles/24508.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/twzheng/comments/commentRss/24508.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/twzheng/services/trackbacks/24508.html</trackback:ping><description><![CDATA[<table style="TABLE-LAYOUT: fixed">
    <tbody>
        <tr>
            <td>
            <div class=cnt>
            <div class=articleTitle><strong>[转] 最通俗浅显的&#8220;IO模式&#8221;解析<br></strong>[源] ：http://hi.baidu.com/firebird/blog/item/f592b3193a02814542a9adeb.html<br><br>&nbsp;<font size=2>&nbsp;&nbsp;&nbsp; 一：select模型<br>&nbsp;&nbsp;&nbsp; 二：WSAAsyncSelect模型<br>&nbsp;&nbsp;&nbsp; 三：WSAEventSelect模型<br>&nbsp;&nbsp;&nbsp; 四：Overlapped I/O 事件通知模型<br>&nbsp;&nbsp;&nbsp; 五：Overlapped I/O 完成例程模型<br>&nbsp;&nbsp;&nbsp; 六：IOCP模型</font><br><br>原文名：《基于Delphi的Socket&nbsp;I/O模型全接触&nbsp;》 <br>老陈有一个在外地工作的女儿，不能经常回来，老陈和她通过信件联系。他们的信会被邮递员投递到他们的信箱里。&nbsp; <br><br>这和Socket模型非常类似。下面我就以老陈接收信件为例讲解Socket&nbsp;I/O模型。&nbsp; <br><br>一：select模型&nbsp; <br><br>老陈非常想看到女儿的信。以至于他每隔10分钟就下楼检查信箱，看是否有女儿的信，在这种情况下，&#8220;下楼检查信箱&#8221;然后回到楼上耽误了老陈太多的时间，以至于老陈无法做其他工作。&nbsp; <br><br>select模型和老陈的这种情况非常相似：周而复始地去检查......如果有数据......接收/发送.......&nbsp; <br><br>使用线程来select应该是通用的做法：&nbsp; <br><br>procedure&nbsp;TListenThread.Execute;&nbsp; <br>var&nbsp; <br>　&nbsp;addr&nbsp;:&nbsp;TSockAddrIn;&nbsp; <br>　&nbsp;fd_read&nbsp;:&nbsp;TFDSet;&nbsp; <br>　&nbsp;timeout&nbsp;:&nbsp;TTimeVal;&nbsp; <br>　&nbsp;ASock,&nbsp; <br>　&nbsp;MainSock&nbsp;:&nbsp;TSocket;&nbsp; <br>　&nbsp;len,&nbsp;i&nbsp;:&nbsp;Integer;&nbsp; <br>begin&nbsp; <br>　&nbsp;MainSock&nbsp;:=&nbsp;socket(&nbsp;AF_INET,&nbsp;SOCK_STREAM,&nbsp;IPPROTO_TCP&nbsp;);&nbsp; <br>　&nbsp;addr.sin_family&nbsp;:=&nbsp;AF_INET;&nbsp; <br>　&nbsp;addr.sin_port&nbsp;:=&nbsp;htons(5678);&nbsp; <br>　&nbsp;addr.sin_addr.S_addr&nbsp;:=&nbsp;htonl(INADDR_ANY);&nbsp; <br>　&nbsp;bind(&nbsp;MainSock,&nbsp;@addr,&nbsp;sizeof(addr)&nbsp;);&nbsp; <br>　&nbsp;listen(&nbsp;MainSock,&nbsp;5&nbsp;);&nbsp; <br><br>　&nbsp;while&nbsp;(not&nbsp;Terminated)&nbsp;do&nbsp; <br>　&nbsp;begin&nbsp; <br>　　&nbsp;FD_ZERO(&nbsp;fd_read&nbsp;);&nbsp; <br>　　&nbsp;FD_SET(&nbsp;MainSock,&nbsp;fd_read&nbsp;);&nbsp; <br>　　&nbsp;timeout.tv_sec&nbsp;:=&nbsp;0;&nbsp; <br>　　&nbsp;timeout.tv_usec&nbsp;:=&nbsp;500;&nbsp; <br>　　&nbsp;if&nbsp;select(&nbsp;0,&nbsp;@fd_read,&nbsp;nil,&nbsp;nil,&nbsp;@timeout&nbsp;)&nbsp;&gt;&nbsp;0&nbsp;then&nbsp;//至少有1个等待Accept的connection&nbsp; <br>　　&nbsp;begin&nbsp; <br>　　　&nbsp;if&nbsp;FD_ISSET(&nbsp;MainSock,&nbsp;fd_read&nbsp;)&nbsp;then&nbsp; <br>　　　&nbsp;begin&nbsp; <br>　　　&nbsp;for&nbsp;i:=0&nbsp;to&nbsp;fd_read.fd_count-1&nbsp;do&nbsp;//注意，fd_count&nbsp;&lt;=&nbsp;64， <br>&nbsp;&nbsp;&nbsp;也就是说select只能同时管理最多64个连接&nbsp; <br>　　　&nbsp;begin&nbsp; <br>　　　　&nbsp;len&nbsp;:=&nbsp;sizeof(addr);&nbsp; <br>　　　　&nbsp;ASock&nbsp;:=&nbsp;accept(&nbsp;MainSock,&nbsp;addr,&nbsp;len&nbsp;);&nbsp; <br>　　　　&nbsp;if&nbsp;ASock&nbsp;&lt;&gt;&nbsp;INVALID_SOCKET&nbsp;then&nbsp; <br>　　　　　&nbsp;....//为ASock创建一个新的线程，在新的线程中再不停地select&nbsp; <br>　　　　&nbsp;end;&nbsp; <br>　　　&nbsp;end;&nbsp;　　&nbsp; <br>　　&nbsp;end;&nbsp; <br>　&nbsp;end;&nbsp;//while&nbsp;(not&nbsp;self.Terminated)&nbsp; <br><br>　&nbsp;shutdown(&nbsp;MainSock,&nbsp;SD_BOTH&nbsp;);&nbsp; <br>　&nbsp;closesocket(&nbsp;MainSock&nbsp;);&nbsp; <br>end; <br>&nbsp; <br><br><br>二：WSAAsyncSelect模型&nbsp; <br><br>后来，老陈使用了微软公司的新式信箱。这种信箱非常先进，一旦信箱里有新的信件，盖茨就会给老陈打电话：喂，大爷，你有新的信件了！从此，老陈再也不必频繁上下楼检查信箱了，牙也不疼了，你瞅准了，蓝天......不是，微软......&nbsp; <br><br>微软提供的WSAAsyncSelect模型就是这个意思。&nbsp; <br><br>WSAAsyncSelect模型是Windows下最简单易用的一种Socket&nbsp;I/O模型。使用这种模型时，Windows会把网络事件以消息的形势通知应用程序。&nbsp; <br><br>首先定义一个消息标示常量：&nbsp; <br><br>const&nbsp;WM_SOCKET&nbsp;=&nbsp;WM_USER&nbsp;+&nbsp;55; <br>&nbsp; <br><br><br>再在主Form的private域添加一个处理此消息的函数声明：&nbsp; <br><br>private&nbsp; <br>procedure&nbsp;WMSocket(var&nbsp;Msg:&nbsp;TMessage);&nbsp;message&nbsp;WM_SOCKET; <br>&nbsp; <br>　&nbsp; <br><br>然后就可以使用WSAAsyncSelect了：&nbsp; <br><br>var&nbsp; <br>　&nbsp;addr&nbsp;:&nbsp;TSockAddr;&nbsp; <br>　&nbsp;sock&nbsp;:&nbsp;TSocket;&nbsp; <br><br>　&nbsp;sock&nbsp;:=&nbsp;socket(&nbsp;AF_INET,&nbsp;SOCK_STREAM,&nbsp;IPPROTO_TCP&nbsp;);&nbsp; <br>　&nbsp;addr.sin_family&nbsp;:=&nbsp;AF_INET;&nbsp; <br>　&nbsp;addr.sin_port&nbsp;:=&nbsp;htons(5678);&nbsp; <br>　&nbsp;addr.sin_addr.S_addr&nbsp;:=&nbsp;htonl(INADDR_ANY);&nbsp; <br>　&nbsp;bind(&nbsp;m_sock,&nbsp;@addr,&nbsp;sizeof(SOCKADDR)&nbsp;);&nbsp; <br><br>　&nbsp;WSAAsyncSelect(&nbsp;m_sock,&nbsp;Handle,&nbsp;WM_SOCKET,&nbsp;FD_ACCEPT&nbsp;or&nbsp;FD_CLOSE&nbsp;);&nbsp; <br><br>　&nbsp;listen(&nbsp;m_sock,&nbsp;5&nbsp;);&nbsp; <br>　&nbsp;.... <br>&nbsp; <br><br><br>应用程序可以对收到WM_SOCKET消息进行分析，判断是哪一个socket产生了网络事件以及事件类型：&nbsp; <br><br>procedure&nbsp;TfmMain.WMSocket(var&nbsp;Msg:&nbsp;TMessage);&nbsp; <br>var&nbsp; <br>　&nbsp;sock&nbsp;:&nbsp;TSocket;&nbsp; <br>　&nbsp;addr&nbsp;:&nbsp;TSockAddrIn;&nbsp; <br>　&nbsp;addrlen&nbsp;:&nbsp;Integer;&nbsp; <br>　&nbsp;buf&nbsp;:&nbsp;Array&nbsp;[0..4095]&nbsp;of&nbsp;Char;&nbsp; <br>begin&nbsp; <br>　&nbsp;//Msg的WParam是产生了网络事件的socket句柄，LParam则包含了事件类型&nbsp; <br>　&nbsp;case&nbsp;WSAGetSelectEvent(&nbsp;Msg.LParam&nbsp;)&nbsp;of&nbsp; <br>　&nbsp;FD_ACCEPT&nbsp;:&nbsp; <br>　　&nbsp;begin&nbsp; <br>　　　&nbsp;addrlen&nbsp;:=&nbsp;sizeof(addr);&nbsp; <br>　　　&nbsp;sock&nbsp;:=&nbsp;accept(&nbsp;Msg.WParam,&nbsp;addr,&nbsp;addrlen&nbsp;);&nbsp; <br>　　　&nbsp;if&nbsp;sock&nbsp;&lt;&gt;&nbsp;INVALID_SOCKET&nbsp;then&nbsp; <br>　　　　&nbsp;WSAAsyncSelect(&nbsp;sock,&nbsp;Handle,&nbsp;WM_SOCKET,&nbsp;FD_READ&nbsp;or&nbsp;FD_WRITE&nbsp;or&nbsp;FD_CLOSE&nbsp;);&nbsp; <br>　　&nbsp;end;&nbsp; <br><br>　　&nbsp;FD_CLOSE&nbsp;:&nbsp;closesocket(&nbsp;Msg.WParam&nbsp;);&nbsp; <br>　　&nbsp;FD_READ&nbsp;:&nbsp;recv(&nbsp;Msg.WParam,&nbsp;buf[0],&nbsp;4096,&nbsp;0&nbsp;);&nbsp; <br>　　&nbsp;FD_WRITE&nbsp;:&nbsp;;&nbsp; <br>　&nbsp;end;&nbsp; <br>end; <br>&nbsp; <br><br><br>三：WSAEventSelect模型&nbsp; <br><br>后来，微软的信箱非常畅销，购买微软信箱的人以百万计数......以至于盖茨每天24小时给客户打电话，累得腰酸背痛，喝蚁力神都不好使。微软改进了他们的信箱：在客户的家中添加一个附加装置，这个装置会监视客户的信箱，每当新的信件来临，此装置会发出&#8220;新信件到达&#8221;声，提醒老陈去收信。盖茨终于可以睡觉了。&nbsp; <br><br>同样要使用线程：&nbsp; <br><br>procedure&nbsp;TListenThread.Execute;&nbsp; <br>var&nbsp; <br>　&nbsp;hEvent&nbsp;:&nbsp;WSAEvent;&nbsp; <br>　&nbsp;ret&nbsp;:&nbsp;Integer;&nbsp; <br>　&nbsp;ne&nbsp;:&nbsp;TWSANetworkEvents;&nbsp; <br>　&nbsp;sock&nbsp;:&nbsp;TSocket;&nbsp; <br>　&nbsp;adr&nbsp;:&nbsp;TSockAddrIn;&nbsp; <br>　&nbsp;sMsg&nbsp;:&nbsp;String;&nbsp; <br>　&nbsp;Index,&nbsp; <br>　&nbsp;EventTotal&nbsp;:&nbsp;DWORD;&nbsp; <br>　&nbsp;EventArray&nbsp;:&nbsp;Array&nbsp;[0..WSA_MAXIMUM_WAIT_EVENTS-1]&nbsp;of&nbsp;WSAEVENT;&nbsp; <br>begin&nbsp; <br>　&nbsp;...socket...bind...&nbsp; <br>　&nbsp;hEvent&nbsp;:=&nbsp;WSACreateEvent();&nbsp; <br>　&nbsp;WSAEventSelect(&nbsp;ListenSock,&nbsp;hEvent,&nbsp;FD_ACCEPT&nbsp;or&nbsp;FD_CLOSE&nbsp;);&nbsp; <br>　&nbsp;...listen...&nbsp; <br><br>　&nbsp;while&nbsp;(&nbsp;not&nbsp;Terminated&nbsp;)&nbsp;do&nbsp; <br>　&nbsp;begin&nbsp; <br>　　&nbsp;Index&nbsp;:=&nbsp;WSAWaitForMultipleEvents(&nbsp;EventTotal,&nbsp;@EventArray[0],&nbsp;FALSE,&nbsp; <br>&nbsp;&nbsp;WSA_INFINITE,&nbsp;FALSE&nbsp;);&nbsp; <br>　　&nbsp;FillChar(&nbsp;ne,&nbsp;sizeof(ne),&nbsp;0&nbsp;);&nbsp; <br>　　&nbsp;WSAEnumNetworkEvents(&nbsp;SockArray[Index-WSA_WAIT_EVENT_0],&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;EventArray <br>&nbsp;&nbsp;[Index-WSA_WAIT_EVENT_0],&nbsp;@ne&nbsp;);&nbsp; <br><br>　　&nbsp;if&nbsp;(&nbsp;ne.lNetworkEvents&nbsp;and&nbsp;FD_ACCEPT&nbsp;)&nbsp;&gt;&nbsp;0&nbsp;then&nbsp; <br>　　&nbsp;begin&nbsp; <br>　　　&nbsp;if&nbsp;ne.iErrorCode[FD_ACCEPT_BIT]&nbsp;&lt;&gt;&nbsp;0&nbsp;then&nbsp; <br>　　　　&nbsp;continue;&nbsp; <br><br>　　　&nbsp;ret&nbsp;:=&nbsp;sizeof(adr);&nbsp; <br>　　　&nbsp;sock&nbsp;:=&nbsp;accept(&nbsp;SockArray[Index-WSA_WAIT_EVENT_0],&nbsp;adr,&nbsp;ret&nbsp;);&nbsp; <br>　　　&nbsp;if&nbsp;EventTotal&nbsp;&gt;&nbsp;WSA_MAXIMUM_WAIT_EVENTS-1&nbsp;then <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//这里WSA_MAXIMUM_WAIT_EVENTS同样是64&nbsp; <br>　　　&nbsp;begin&nbsp; <br>　　　　&nbsp;closesocket(&nbsp;sock&nbsp;);&nbsp; <br>　　　　&nbsp;continue;&nbsp; <br>　　　&nbsp;end;&nbsp; <br><br>　　　&nbsp;hEvent&nbsp;:=&nbsp;WSACreateEvent();&nbsp; <br>　　　&nbsp;WSAEventSelect(&nbsp;sock,&nbsp;hEvent,&nbsp;FD_READ&nbsp;or&nbsp;FD_WRITE&nbsp;or&nbsp;FD_CLOSE&nbsp;);&nbsp; <br>　　　&nbsp;SockArray[EventTotal]&nbsp;:=&nbsp;sock;&nbsp; <br>　　　&nbsp;EventArray[EventTotal]&nbsp;:=&nbsp;hEvent;&nbsp; <br>　　　&nbsp;Inc(&nbsp;EventTotal&nbsp;);&nbsp; <br>　　&nbsp;end;&nbsp; <br><br>　　&nbsp;if&nbsp;(&nbsp;ne.lNetworkEvents&nbsp;and&nbsp;FD_READ&nbsp;)&nbsp;&gt;&nbsp;0&nbsp;then&nbsp; <br>　　&nbsp;begin&nbsp; <br>　　　&nbsp;if&nbsp;ne.iErrorCode[FD_READ_BIT]&nbsp;&lt;&gt;&nbsp;0&nbsp;then&nbsp; <br>　　　　&nbsp;continue;&nbsp; <br>　　　　&nbsp;FillChar(&nbsp;RecvBuf[0],&nbsp;PACK_SIZE_RECEIVE,&nbsp;0&nbsp;);&nbsp; <br>　　　　&nbsp;ret&nbsp;:=&nbsp;recv(&nbsp;SockArray[Index-WSA_WAIT_EVENT_0],&nbsp;RecvBuf[0],&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;PACK_SIZE_RECEIVE,&nbsp;0&nbsp;);&nbsp; <br>　　　　&nbsp;......&nbsp; <br>　　　&nbsp;end;&nbsp; <br>　　&nbsp;end;&nbsp; <br>end; <br>&nbsp; <br><br><br><br>四：Overlapped&nbsp;I/O&nbsp;事件通知模型&nbsp; <br><br>后来，微软通过调查发现，老陈不喜欢上下楼收发信件，因为上下楼其实很浪费时间。于是微软再次改进他们的信箱。新式的信箱采用了更为先进的技术，只要用户告诉微软自己的家在几楼几号，新式信箱会把信件直接传送到用户的家中，然后告诉用户，你的信件已经放到你的家中了！老陈很高兴，因为他不必再亲自收发信件了！&nbsp; <br><br>Overlapped&nbsp;I/O&nbsp;事件通知模型和WSAEventSelect模型在实现上非常相似，主要区别在"Overlapped&#8221;，Overlapped模型是让应用程序使用重叠数据结构(WSAOVERLAPPED)，一次投递一个或多个Winsock&nbsp;I/O请求。这些提交的请求完成后，应用程序会收到通知。什么意思呢？就是说，如果你想从socket上接收数据，只需要告诉系统，由系统为你接收数据，而你需要做的只是为系统提供一个缓冲区~~~~~&nbsp; <br><br>Listen线程和WSAEventSelect模型一模一样，Recv/Send线程则完全不同：&nbsp; <br><br>procedure&nbsp;TOverlapThread.Execute;&nbsp; <br>var&nbsp; <br>　&nbsp;dwTemp&nbsp;:&nbsp;DWORD;&nbsp; <br>　&nbsp;ret&nbsp;:&nbsp;Integer;&nbsp; <br>　&nbsp;Index&nbsp;:&nbsp;DWORD;&nbsp; <br>begin&nbsp; <br>　&nbsp;......&nbsp; <br><br>　&nbsp;while&nbsp;(&nbsp;not&nbsp;Terminated&nbsp;)&nbsp;do&nbsp; <br>　&nbsp;begin&nbsp; <br>　　&nbsp;Index&nbsp;:=&nbsp;WSAWaitForMultipleEvents <br>&nbsp;&nbsp;&nbsp;(&nbsp;FLinks.Count,&nbsp;@FLinks.Events[0],&nbsp;FALSE,&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;RECV_TIME_OUT,&nbsp;FALSE&nbsp;);&nbsp; <br>　　&nbsp;Dec(&nbsp;Index,&nbsp;WSA_WAIT_EVENT_0&nbsp;);&nbsp; <br>　　&nbsp;if&nbsp;Index&nbsp;&gt;&nbsp;WSA_MAXIMUM_WAIT_EVENTS-1&nbsp;then&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;//超时或者其他错误&nbsp; <br>　　　&nbsp;continue;&nbsp; <br><br>　　&nbsp;WSAResetEvent <br>&nbsp;&nbsp;&nbsp;(&nbsp;FLinks.Events[Index]&nbsp;);&nbsp; <br>　　&nbsp;WSAGetOverlappedResult(&nbsp;FLinks.Sockets[Index],&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FLinks.pOverlaps[Index],&nbsp;@dwTemp,&nbsp;FALSE,FLinks. <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pdwFlags[Index]^&nbsp;);&nbsp; <br><br>　　&nbsp;if&nbsp;dwTemp&nbsp;=&nbsp;0&nbsp;then&nbsp;//连接已经关闭&nbsp; <br>　　&nbsp;begin&nbsp; <br>　　　&nbsp;......&nbsp; <br>　　　&nbsp;continue;&nbsp; <br>　　&nbsp;end&nbsp;else&nbsp; <br>　&nbsp;begin&nbsp; <br>　　&nbsp;fmMain.ListBox1.Items.Add(&nbsp;FLinks.pBufs[Index]^.buf&nbsp;);&nbsp; <br>　&nbsp;end;&nbsp; <br><br>　&nbsp;//初始化缓冲区&nbsp; <br>　&nbsp;FLinks.pdwFlags[Index]^&nbsp;:=&nbsp;0;&nbsp; <br>　&nbsp;FillChar(&nbsp;FLinks.pOverlaps[Index]^,&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;sizeof(WSAOVERLAPPED),&nbsp;0&nbsp;);&nbsp; <br>　&nbsp;FLinks.pOverlaps[Index]^. <br>&nbsp;&nbsp;&nbsp;hEvent&nbsp;:=&nbsp;FLinks.Events[Index];&nbsp; <br>　&nbsp;FillChar(&nbsp;FLinks.pBufs[Index]^.buf^,&nbsp; <br>&nbsp;&nbsp;&nbsp;BUFFER_SIZE,&nbsp;0&nbsp;);&nbsp; <br><br>　&nbsp;//递一个接收数据请求&nbsp; <br>　&nbsp;WSARecv(&nbsp;FLinks.Sockets[Index],&nbsp;FLinks.pBufs[Index],&nbsp;1, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FLinks.pdwRecvd[Index]^,&nbsp;FLinks.pdwFlags[Index]^,&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FLinks.pOverlaps[Index],&nbsp;nil&nbsp;);&nbsp; <br>end;&nbsp; <br>end; <br>&nbsp; <br><br><br>五：Overlapped&nbsp;I/O&nbsp;完成例程模型&nbsp; <br><br>老陈接收到新的信件后，一般的程序是：打开信封----掏出信纸----阅读信件----回复信件......为了进一步减轻用户负担，微软又开发了一种新的技术：用户只要告诉微软对信件的操作步骤，微软信箱将按照这些步骤去处理信件，不再需要用户亲自拆信/阅读/回复了！老陈终于过上了小资生活！&nbsp; <br><br>Overlapped&nbsp;I/O&nbsp;完成例程要求用户提供一个回调函数，发生新的网络事件的时候系统将执行这个函数：&nbsp; <br><br>procedure&nbsp;WorkerRoutine(&nbsp;const&nbsp;dwError,&nbsp;cbTransferred&nbsp;:&nbsp;DWORD;&nbsp; <br>const&nbsp; <br>lpOverlapped&nbsp;:&nbsp;LPWSAOVERLAPPED;&nbsp;const&nbsp;dwFlags&nbsp;:&nbsp;DWORD&nbsp;);&nbsp;stdcall; <br>&nbsp; <br><br><br>然后告诉系统用WorkerRoutine函数处理接收到的数据：&nbsp; <br><br>WSARecv(&nbsp;m_socket,&nbsp;@FBuf,&nbsp;1,&nbsp;dwTemp,&nbsp;dwFlag,&nbsp;@m_overlap,&nbsp;WorkerRoutine&nbsp;);&nbsp; <br>　　&nbsp;然后......没有什么然后了，系统什么都给你做了！微软真实体贴！&nbsp; <br><br>while&nbsp;(&nbsp;not&nbsp;Terminated&nbsp;)&nbsp;do//这就是一个Recv/Send线程要做的事情......什么都不用做啊！！！&nbsp; <br>begin&nbsp; <br>　&nbsp;if&nbsp;SleepEx(&nbsp;RECV_TIME_OUT,&nbsp;True&nbsp;)&nbsp;=&nbsp;WAIT_IO_COMPLETION&nbsp;then&nbsp;//&nbsp; <br>　&nbsp;begin&nbsp; <br>　　&nbsp;;&nbsp; <br>　&nbsp;end&nbsp;else&nbsp; <br>　&nbsp;begin&nbsp; <br>　　&nbsp;continue;&nbsp; <br>　&nbsp;end;&nbsp; <br>end; <br>&nbsp; <br><br><br>六：IOCP模型&nbsp; <br><br>微软信箱似乎很完美，老陈也很满意。但是在一些大公司情况却完全不同！这些大公司有数以万计的信箱，每秒钟都有数以百计的信件需要处理，以至于微软信箱经常因超负荷运转而崩溃！需要重新启动！微软不得不使出杀手锏......&nbsp; <br><br>微软给每个大公司派了一名名叫&#8220;Completion&nbsp;Port&#8221;的超级机器人，让这个机器人去处理那些信件！&nbsp; <br><br>&#8220;Windows&nbsp;NT小组注意到这些应用程序的性能没有预料的那么高。特别的，处理很多同时的客户请求意味着很多线程并发地运行在系统中。因为所有这些线程都是可运行的[没有被挂起和等待发生什么事]，Microsoft意识到NT内核花费了太多的时间来转换运行线程的上下文[Context]，线程就没有得到很多CPU时间来做它们的工作。大家可能也都感觉到并行模型的瓶颈在于它为每一个客户请求都创建了一个新线程。创建线程比起创建进程开销要小，但也远不是没有开销的。我们不妨设想一下：如果事先开好N个线程，让它们在那hold[堵塞]，然后可以将所有用户的请求都投递到一个消息队列中去。然后那N个线程逐一从消息队列中去取出消息并加以处理。就可以避免针对每一个用户请求都开线程。不仅减少了线程的资源，也提高了线程的利用率。理论上很不错，你想我等泛泛之辈都能想出来的问题，Microsoft又怎会没有考虑到呢?&#8221;-----摘自nonocast的《理解I/O&nbsp;Completion&nbsp;Port》&nbsp; <br><br>先看一下IOCP模型的实现：&nbsp; <br><br>//创建一个完成端口&nbsp; <br>FCompletPort&nbsp;:=&nbsp;CreateIoCompletionPort(&nbsp;INVALID_HANDLE_VALUE,&nbsp;0,0,0&nbsp;);&nbsp; <br><br>//接受远程连接，并把这个连接的socket句柄绑定到刚才创建的IOCP上&nbsp; <br>AConnect&nbsp;:=&nbsp;accept(&nbsp;FListenSock,&nbsp;addr,&nbsp;len);&nbsp; <br>CreateIoCompletionPort(&nbsp;AConnect,&nbsp;FCompletPort,&nbsp;nil,&nbsp;0&nbsp;);&nbsp; <br><br>//创建CPU数*2&nbsp;+&nbsp;2个线程&nbsp; <br>for&nbsp;i:=1&nbsp;to&nbsp;si.dwNumberOfProcessors*2+2&nbsp;do&nbsp; <br>begin&nbsp; <br>　&nbsp;AThread&nbsp;:=&nbsp;TRecvSendThread.Create(&nbsp;false&nbsp;);&nbsp; <br>　&nbsp;AThread.CompletPort&nbsp;:=&nbsp;FCompletPort;//告诉这个线程，你要去这个IOCP去访问数据&nbsp; <br>end; <br>&nbsp; <br><br><br>就这么简单，我们要做的就是建立一个IOCP，把远程连接的socket句柄绑定到刚才创建的IOCP上，最后创建n个线程，并告诉这n个线程到这个IOCP上去访问数据就可以了。&nbsp; <br><br>再看一下TRecvSendThread线程都干些什么：&nbsp; <br><br>procedure&nbsp;TRecvSendThread.Execute;&nbsp; <br>var&nbsp; <br>　&nbsp;......&nbsp; <br>begin&nbsp; <br>　&nbsp;while&nbsp;(not&nbsp;self.Terminated)&nbsp;do&nbsp; <br>　&nbsp;begin&nbsp; <br>　　&nbsp;//查询IOCP状态（数据读写操作是否完成）&nbsp; <br>　　&nbsp;GetQueuedCompletionStatus(&nbsp;CompletPort,&nbsp;BytesTransd,&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;CompletKey,&nbsp;POVERLAPPED(pPerIoDat),&nbsp;TIME_OUT&nbsp;);&nbsp; <br><br>　　&nbsp;if&nbsp;BytesTransd&nbsp;&lt;&gt;&nbsp;0&nbsp;then&nbsp; <br>　　　&nbsp;....;//数据读写操作完成&nbsp; <br>　　&nbsp; <br>　　　&nbsp;//再投递一个读数据请求&nbsp; <br>　　　&nbsp;WSARecv(&nbsp;CompletKey,&nbsp;@(pPerIoDat^.BufData),&nbsp;1,&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;BytesRecv,&nbsp;Flags,&nbsp;@(pPerIoDat^.Overlap),&nbsp;nil&nbsp;);&nbsp; <br>　　&nbsp;end;&nbsp; <br>end; <br>&nbsp; <br><br><br>读写线程只是简单地检查IOCP是否完成了我们投递的读写操作，如果完成了则再投递一个新的读写请求。&nbsp; <br><br>应该注意到，我们创建的所有TRecvSendThread都在访问同一个IOCP（因为我们只创建了一个IOCP），并且我们没有使用临界区！难道不会产生冲突吗？不用考虑同步问题吗？&nbsp; <br><br>这正是IOCP的奥妙所在。IOCP不是一个普通的对象，不需要考虑线程安全问题。它会自动调配访问它的线程：如果某个socket上有一个线程A正在访问，那么线程B的访问请求会被分配到另外一个socket。这一切都是由系统自动调配的，我们无需过问。</div>
            </div>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/twzheng/aggbug/24508.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/twzheng/" target="_blank">谭文政</a> 2007-05-21 01:46 <a href="http://www.cppblog.com/twzheng/articles/24508.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]AfxBeginThread函数初探</title><link>http://www.cppblog.com/twzheng/articles/23440.html</link><dc:creator>谭文政</dc:creator><author>谭文政</author><pubDate>Sat, 05 May 2007 05:42:00 GMT</pubDate><guid>http://www.cppblog.com/twzheng/articles/23440.html</guid><wfw:comment>http://www.cppblog.com/twzheng/comments/23440.html</wfw:comment><comments>http://www.cppblog.com/twzheng/articles/23440.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/twzheng/comments/commentRss/23440.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/twzheng/services/trackbacks/23440.html</trackback:ping><description><![CDATA[<center><strong><font size=3>AfxBeginThread函数初探</font></strong><br>版权所有 codesky.net 2003-2005</center>
<center>发表时间：2005-1-16&nbsp;&nbsp;&nbsp;&nbsp;关键字：不详</center>
<p>
<blockquote><font class=b1>在进行多线程程序设计的时候,我们经常用到AfxBeginThread函数来启动一条线程<br>该函数使用起来非常的简单方便,其定义如下
<p>CWinThread* AfxBeginThread(<br>&nbsp;&nbsp; AFX_THREADPROC pfnThreadProc,//线程函数地址<br>&nbsp;&nbsp; LPVOID pParam,//线程参数<br>&nbsp;&nbsp; int nPriority = THREAD_PRIORITY_NORMAL,//线程优先级<br>&nbsp;&nbsp; UINT nStackSize = 0,//线程堆栈大小,默认为1M<br>&nbsp;&nbsp; DWORD dwCreateFlags = 0,//<br>&nbsp;&nbsp; LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL <br>);</p>
<p>CWinThread* AfxBeginThread(<br>&nbsp;&nbsp; CRuntimeClass* pThreadClass,<br>&nbsp;&nbsp; int nPriority = THREAD_PRIORITY_NORMAL,<br>&nbsp;&nbsp; UINT nStackSize = 0,<br>&nbsp;&nbsp; DWORD dwCreateFlags = 0,<br>&nbsp;&nbsp; LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL <br>);</p>
<p>参数说明:<br>pfnThreadProc:线程函数的地址,该参数不能设置为NULL,线程函数必须定义成全局函数或者类的静态成员函数<br>例如:<br>UINT myThreadFunc(LPVOID lparam)<br>或者<br>class A<br>{<br>public:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static UINT __stdcall myThreadFunc(LPVOID lparam);<br>}<br>之所以要定义成类的静态成员函数,是因为类的静态成员函数不属于某个类对象,这样在调用函数<br>的时候就不用传递一个额外的this指针.</p>
<p>pThreadClass:指向从CWinThread派生的子类对象的RUNTIME_CLASS</p>
<p>pParam:要传递给线程函数的参数</p>
<p>nPriority:要启动的线程的优先级,默认优先级为THREAD_PRIORITY_NORMAL(普通优先级),关于线程<br>&nbsp;优先级的详细说明请参考Platform SDK SetThreadPriority函数说明</p>
<p>nStackSize:新线程的堆栈大小,如果设置为0,则使用默认大小,在应用程序中一般情况下线程的默认堆栈大小<br>&nbsp;为1M</p>
<p>dwCreateFlags:线程创建标志,该参数可以指定为下列标志<br>&nbsp;CREATE_SUSPENDED:以挂起方式启动线程,如果你在线程启动之前想初始化一些CWinThread类中的一些成员变量<br>&nbsp;比如:m_bAutoDelete或者你的派生类中的成员变量,当初始化完成之后,你可以使用CWinThread类的ResumeThread<br>&nbsp;成员函数来恢复线程的运行<br>&nbsp;如果把该标志设置为0,则表示立即启动线程<br>lpSecurityAttrs:指向安全描述符的指针,如果使用默认的安全级别只要讲该参数设置为NULL就可以了!</p>
<p>上面就是AfxBeginThread函数的简单说明,我们在使用的时候一般情况下只要指定前两个参数,其他<br>参数使用默认值就可以.嗯,的确,使用起来是很简单,只要这个函数一被调用,就创建了一个线程.<br>但是大家有没有想过,AfxBeginThread函数究竟是如何启动的线程呢?它的内部是如何实现的呢?</p>
<p>下面我们就来看一下AfxBeginThread函数的内部实现</p>
<p>//启动worker线程<br>CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam,<br>&nbsp;int nPriority, UINT nStackSize, DWORD dwCreateFlags,<br>&nbsp;LPSECURITY_ATTRIBUTES lpSecurityAttrs)<br>{<br>#ifndef _MT<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pfnThreadProc;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pParam;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nPriority;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nStackSize;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dwCreateFlags;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lpSecurityAttrs;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return NULL;<br>#else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ASSERT(pfnThreadProc != NULL);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CWinThread* pThread = DEBUG_NEW CWinThread(pfnThreadProc, pParam);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ASSERT_VALID(pThread);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!pThread-&gt;CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lpSecurityAttrs))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pThread-&gt;Delete();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return NULL;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;VERIFY(pThread-&gt;SetThreadPriority(nPriority));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!(dwCreateFlags &amp; CREATE_SUSPENDED))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;VERIFY(pThread-&gt;ResumeThread() != (DWORD)-1);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return pThread;<br>#endif //!_MT)<br>}</p>
<p dir=ltr style="MARGIN-RIGHT: 0px">//启动UI线程<br>CWinThread* AFXAPI AfxBeginThread(CRuntimeClass* pThreadClass,<br>&nbsp;int nPriority, UINT nStackSize, DWORD dwCreateFlags,<br>&nbsp;LPSECURITY_ATTRIBUTES lpSecurityAttrs)<br>{<br>#ifndef _MT<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pThreadClass;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nPriority;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;nStackSize;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dwCreateFlags;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lpSecurityAttrs;</p>
<p dir=ltr style="MARGIN-RIGHT: 0px">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return NULL;<br>#else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ASSERT(pThreadClass != NULL);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ASSERT(pThreadClass-&gt;IsDerivedFrom(RUNTIME_CLASS(CWinThread)));</p>
<p dir=ltr style="MARGIN-RIGHT: 0px">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CWinThread* pThread = (CWinThread*)pThreadClass-&gt;CreateObject();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (pThread == NULL)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AfxThrowMemoryException();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ASSERT_VALID(pThread);</p>
<p dir=ltr style="MARGIN-RIGHT: 0px">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pThread-&gt;m_pThreadParams = NULL;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!pThread-&gt;CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lpSecurityAttrs))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pThread-&gt;Delete();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return NULL;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; VERIFY(pThread-&gt;SetThreadPriority(nPriority));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!(dwCreateFlags &amp; CREATE_SUSPENDED))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;VERIFY(pThread-&gt;ResumeThread() != (DWORD)-1);</p>
<p dir=ltr style="MARGIN-RIGHT: 0px">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return pThread;<br>#endif //!_MT<br>}</p>
<p>从上面的代码中可以看出AfxBeginThread所做的事情主要有以下几点:</p>
<p>1.在heap中配置一个新的CWinThread对象(worker线程)<br>代码如:CWinThread* pThread = DEBUG_NEW CWinThread(pfnThreadProc, pParam);<br>调用CRuntimeClass结构中的CreateObject函数创建CWinThread对象<br>CWinThread* pThread = (CWinThread*)pThreadClass-&gt;CreateObject();<br>CRuntimeClass以及MFC相关类的内部实现,详情请参考<br>《深入浅出MFC》侯捷著</p>
<p>2.调用CWinThread::CreateThread()并设定属性,使线程以挂起状态产生<br>pThread-&gt;CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,lpSecurityAttrs);</p>
<p>3.设定线程的优先权<br>pThread-&gt;SetThreadPriority(nPriority);</p>
<p>4.调用CWinThread::ResumeThread<br>pThread-&gt;ResumeThread();<br><br>通过上面的说明,我想大家对该函数到底在内部都做了什么,应该有一个初步的了解了!<br>对于VC老手来说,这篇文章可能并没有什么可读之处,但是对于初学者来说,还是有一定的<br>价值的!<br>总之,希望这篇文章能给各位一点点的帮助!</p>
</font></blockquote>
<img src ="http://www.cppblog.com/twzheng/aggbug/23440.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/twzheng/" target="_blank">谭文政</a> 2007-05-05 13:42 <a href="http://www.cppblog.com/twzheng/articles/23440.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CreateEvent 函数</title><link>http://www.cppblog.com/twzheng/articles/23439.html</link><dc:creator>谭文政</dc:creator><author>谭文政</author><pubDate>Sat, 05 May 2007 05:25:00 GMT</pubDate><guid>http://www.cppblog.com/twzheng/articles/23439.html</guid><wfw:comment>http://www.cppblog.com/twzheng/comments/23439.html</wfw:comment><comments>http://www.cppblog.com/twzheng/articles/23439.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/twzheng/comments/commentRss/23439.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/twzheng/services/trackbacks/23439.html</trackback:ping><description><![CDATA[<strong>CreateEvent 函数</strong><span class=doc><br><br>函数功能描述：创建或打开一个命名的或无名的事件对象<br>函数原型：<br>HANDLE CreateEvent(<br>&nbsp;&nbsp;LPSECURITY_ATTRIBUTES lpEventAttributes,&nbsp;&nbsp;&nbsp;// 安全属性<br>&nbsp;&nbsp;BOOL bManualReset,&nbsp;&nbsp;&nbsp;// 复位方式<br>&nbsp;&nbsp;BOOL bInitialState,&nbsp;&nbsp;&nbsp;// 初始状态<br>&nbsp;&nbsp;LPCTSTR lpName&nbsp;&nbsp;&nbsp;// 对象名称<br>);<br><br>参数：<br><br>lpEventAttributes：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[输入]一个指向SECURITY_ATTRIBUTES结构的指针，确定返回的句柄是否可被子进程继承。如果lpEventAttributes是NULL，此句柄不能被继承。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Windows NT/2000：lpEventAttributes的结构中的成员为新的事件指定了一个安全符。如果lpEventAttributes是NULL，事件将获得一个默认的安全符。<br><br>bManualReset：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[输入]指定将事件对象创建成手动复原还是自动复原。如果是TRUE，那么必须用ResetEvent函数来手工将事件的状态复原到无信号状态。如果设置为FALSE，当事件被一个等待线程释放以后，系统将会自动将事件状态复原为无信号状态。<br><br>bInitialState：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[输入]指定事件对象的初始状态。如果为TRUE，初始状态为有信号状态；否则为无信号状态。<br><br>lpName：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[输入]指定事件的对象的名称，是一个以0结束的字符串指针。名称的字符格式限定在MAX_PATH之内。名字是对大小写敏感的。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果lpName指定的名字，与一个存在的命名的事件对象的名称相同，函数将请求EVENT_ALL_ACCESS来访问存在的对象。这时候，由于bManualReset和bInitialState参数已经在创建事件的进程中设置，这两个参数将被忽略。如果lpEventAttributes是参数不是NULL，它将确定此句柄是否可以被继承，但是其安全描述符成员将被忽略。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果lpName为NULL，将创建一个无名的事件对象。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果lpName的和一个存在的信号、互斥、等待计时器、作业或者是文件映射对象名称相同，函数将会失败，在GetLastError函数中将返回ERROR_INVALID_HANDLE。造成这种现象的原因是这些对象共享同一个命名空间。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;终端服务(Terminal Services)：名称中可以加入"Global\"或是"Local\"的前缀，这样可以明确的将对象创建在全局的或事务的命名空间。名称的其它部分除了反斜杠(\)，可以使用任意字符。详细内容可参考Kernel Object Name Spaces。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Windows 2000：在Windows 2000系统中，没有终端服务运行，"Global\"和"Local\"前缀将被忽略。名称的其它部分除了反斜杠(\)，可以使用任意字符。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Windows NT 4.0以及早期版本, Windows 95/98：名称中除了反斜杠(\)，可以使用任意字符。<br><br>返回值：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果函数调用成功，函数返回事件对象的句柄。如果对于命名的对象，在函数调用前已经被创建，函数将返回存在的事件对象的句柄，而且在GetLastError函数中返回ERROR_ALREADY_EXISTS。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果函数失败，函数返回值为NULL，如果需要获得详细的错误信息，需要调用GetLastError。<br><br>备注：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;调用CreateEvent函数返回的句柄，该句柄具有EVENT_ALL_ACCESS权限去访问新的事件对象，同时它可以在任何有此事件对象句柄的函数中使用。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在调用的过程中，所有线程都可以在一个等待函数中指定事件对象句柄。当指定的对象的状态被置为有信号状态时，单对象等待函数将返回。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;对于多对象等待函数，可以指定为任意或所有指定的对象被置为有信号状态。当等待函数返回时，等待线程将被释放去继续运行。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;初始状态在bInitialState参数中进行设置。使用SetEvent函数将事件对象的状态置为有信号状态。使用ResetEvent函数将事件对象的状态置为无信号状态。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当一个手动复原的事件对象的状态被置为有信号状态时，该对象状态将一直保持有信号状态，直至明确调用ResetEvent函数将其置为无符号状态。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当事件的对象被置为有信号状态时，任意数量的等待中线程，以及随后开始等待的线程均会被释放。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当一个自动复原的事件对象的状态被置为有信号状态时，该对象状态将一直保持有信号状态，直至一个等待线程被释放;系统将自动将此函数置为无符号状态。如果没有等待线程正在等待，事件对象的状态将保持有信号状态。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;多个进程可持有同一个事件对象的多个句柄，可以通过使用此对象来实现进程间的同步。下面的对象共享机制是可行的：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#183;在CreateEvent函数中，lpEventAttributes参数指定句柄可被继承时，通过CreateProcess函数创建的子进程继承的事件对象句柄。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#183;一个进程可以在DuplicateHandle函数中指定事件对象句柄，从而获得一个复制的句柄，此句柄可以被其它进程使用。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#183;一个进程可以在OpenEvent或CreateEvent函数中指定一个名字，从而获得一个有名的事件对象句柄。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;使用CloseHandle函数关闭句柄。当进程停止时，系统将自动关闭句柄。当最后一个句柄被关闭后，事件对象将被销毁。<br><br>使用环境：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Windows NT/2000：需要3.1或更高版本<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Windows 95/98：需要Windows 95或更高版本<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;头文件：定义在Winbase.h；需要包含 Windows.h。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;导入库：user32.lib<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Unicode：在Windows NT/2000中，以 Unicode 和 ANSI 执行<br><br>参考：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Synchronization Overview, Synchronization Functions, CloseHandle, CreateProcess, DuplicateHandle, OpenEvent, ResetEvent, SECURITY_ATTRIBUTES, SetEvent, Object Names<br><br>示例代码：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 创建一个有名的，不能被继承的，手动复原，初始状态是无信号状态的事件对象<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Handle h = CreateEvent(NULL,TRUE,FALSE,&#8220;MyEvent&#8221;);<br><br></span>
<img src ="http://www.cppblog.com/twzheng/aggbug/23439.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/twzheng/" target="_blank">谭文政</a> 2007-05-05 13:25 <a href="http://www.cppblog.com/twzheng/articles/23439.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WaitForSingleObject的用法</title><link>http://www.cppblog.com/twzheng/articles/21029.html</link><dc:creator>谭文政</dc:creator><author>谭文政</author><pubDate>Sat, 31 Mar 2007 16:04:00 GMT</pubDate><guid>http://www.cppblog.com/twzheng/articles/21029.html</guid><wfw:comment>http://www.cppblog.com/twzheng/comments/21029.html</wfw:comment><comments>http://www.cppblog.com/twzheng/articles/21029.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/twzheng/comments/commentRss/21029.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/twzheng/services/trackbacks/21029.html</trackback:ping><description><![CDATA[<h2 class=diaryTitle>WaitForSingleObject的用法&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </h2>
<p>&nbsp;</p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><strong><span lang=en style="FONT-SIZE: 8.5pt; COLOR: black; FONT-FAMILY: Verdana">WaitForSingleObject</span> </strong><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:=""><font size=3>的用法</font> </span></p>
<pre style="BACKGROUND: #dddddd">				<strong>
<span lang=en style="FONT-SIZE: 8.5pt; COLOR: black">DWORD</span></strong><span lang=en style="FONT-SIZE: 8.5pt; COLOR: black">
<strong>WaitForSingleObject(</strong></span><span lang=en style="FONT-SIZE: 8.5pt; COLOR: black"><span style="mso-spacerun: yes"> </span>
<strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HANDLE </strong><em><span style="mso-field-code: " hyperlink="" ????=""><span class=MsoHyperlink><span style="COLOR: #0040ff; TEXT-DECORATION: none; text-underline: none"><u>hHandle</u></span></span></span></em><strong>,</strong></span><span lang=en style="FONT-SIZE: 8.5pt; COLOR: black"><span style="mso-spacerun: yes">&nbsp; </span>
<strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DWORD </strong><u><em><span style="mso-field-code: " hyperlink="" ????=""><span class=MsoHyperlink><span style="COLOR: #0040ff; TEXT-DECORATION: none; text-underline: none">dwMilliseconds</span></span></span></em></u></span>				<strong>
<span lang=en style="FONT-SIZE: 8.5pt; COLOR: black">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;);</span></strong></pre>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:=""><font size=3>参数</font> </span><em><span lang=en style="FONT-SIZE: 8.5pt; COLOR: black; FONT-FAMILY: Verdana">hHandle</span> </em><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:=""><font size=3>是一个事件的句柄，第二个参数</font> </span><em><span lang=en style="FONT-SIZE: 8.5pt; COLOR: black; FONT-FAMILY: Verdana">dwMilliseconds</span> </em><font size=3><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:="">是时间间隔。如果时间是有信号状态返回</span> <span lang=en><font face="Times New Roman">WAIT_OBJECT_0</font> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:="">，如果时间超过</span> </font><em><span lang=en style="FONT-SIZE: 8.5pt; COLOR: black; FONT-FAMILY: Verdana">dwMilliseconds</span> </em><font size=3><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:="">值但时间事件还是无信号状态则返回</span> <span lang=en><font face="Times New Roman">WAIT_TIMEOUT</font> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:="">。<br></span></font><em><span lang=en style="FONT-SIZE: 8.5pt; COLOR: black; FONT-FAMILY: Verdana"><br>hHandle</span> </em><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:=""><font size=3>可以是下列对象的句柄：<br></font></span><span lang=en style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Arial">Change&nbsp;notification&nbsp;<br>Console&nbsp;input&nbsp;<br>Event&nbsp;<br>Job&nbsp;<br>Memory&nbsp;resource&nbsp;notification&nbsp;<br>Mutex&nbsp;<br>Process&nbsp;<br>Semaphore&nbsp;<br>Thread&nbsp;<br>Waitable&nbsp;timer&nbsp;</span>&nbsp;</p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 0.05pt"><span lang=en style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Arial">WaitForSingleObject</span> <span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:=""><font size=3>函数用来检测</font> </span><em><span lang=en style="FONT-SIZE: 8.5pt; COLOR: black; FONT-FAMILY: Verdana">hHandle</span> </em><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:=""><font size=3>事件的信号状态，当函数的执行时间超过</font> </span><em><span lang=en style="FONT-SIZE: 8.5pt; COLOR: black; FONT-FAMILY: Verdana">dwMilliseconds</span> </em><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:=""><font size=3>就返回，但如果参数</font> </span><em><span lang=en style="FONT-SIZE: 8.5pt; COLOR: black; FONT-FAMILY: Verdana">dwMilliseconds</span> </em><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:=""><font size=3>为</font> </span><em><span lang=en style="FONT-SIZE: 8.5pt; COLOR: black; FONT-FAMILY: Verdana">INFINITE</span> </em><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:=""><font size=3>时函数将直到相应时间事件变成有信号状态才返回，否则就一直等待下去，直到</font> </span><span lang=en style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Arial">WaitForSingleObject</span> <span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:=""><font size=3>有返回直才执行后面的代码。在这里举个例子：</font> </span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 21pt; TEXT-INDENT: -21pt; mso-char-indent-count: -2.0; tab-stops: 96.75pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:=""><font size=3>先创建一个全局</font> </span><span lang=en style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Arial">Event</span> <font size=3><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:="">对象</span> <span lang=en><font face="Times New Roman">g_event:</font> </span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 21pt; TEXT-INDENT: -21pt; mso-char-indent-count: -2.0; tab-stops: 96.75pt"><span lang=en><font size=3><font face="Times New Roman"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>CEvent g_event;</font> </font></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 21pt; TEXT-INDENT: -21pt; mso-char-indent-count: -2.0; tab-stops: 96.75pt"><font size=3><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:="">在程序中可以通过调用</span> <span lang=en><font face="Times New Roman">CEvent::SetEvent</font> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:="">设置事件为有信号状态。</span> </font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 21pt; tab-stops: 96.75pt; mso-para-margin-left: 2.0gd"><font size=3><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:="">下面是一个线程函数</span> <span lang=en><font face="Times New Roman">MyThreadPro()</font> </span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><span lang=en style="FONT-SIZE: 9pt; FONT-FAMILY: 新宋体; mso-hansi-font-family: ; mso-font-kerning: 0pt" times="" new="" roman='??=""'>UINT CFlushDlg::MyThreadProc( LPVOID pParam )&nbsp;<br></span><span lang=en style="FONT-SIZE: 9pt; FONT-FAMILY: 新宋体; mso-hansi-font-family: ; mso-font-kerning: 0pt" times="" new="" roman='??=""'>{</span>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><span lang=en style="FONT-SIZE: 9pt; FONT-FAMILY: 新宋体; mso-hansi-font-family: ; mso-font-kerning: 0pt" times="" new="" roman='??=""'><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp; </span>WaitForSingleObject(g_event,INFINITE);<br></span><span lang=en style="FONT-SIZE: 9pt; FONT-FAMILY: 新宋体; mso-hansi-font-family: ; mso-font-kerning: 0pt" times="" new="" roman='??=""'><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp; </span>For(;;)<br></span><span lang=en style="FONT-SIZE: 9pt; FONT-FAMILY: 新宋体; mso-hansi-font-family: ; mso-font-kerning: 0pt" times="" new="" roman='??=""'><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{&nbsp;<br></span><span lang=en style="FONT-SIZE: 9pt; FONT-FAMILY: 新宋体; mso-hansi-font-family: ; mso-font-kerning: 0pt" times="" new="" roman='??=""'><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang=en style="FONT-SIZE: 9pt; mso-ascii-font-family: 新宋体; mso-font-kerning: 0pt; mso-fareast-font-family: 新宋体"><font face="Times New Roman">&#8230;&#8230;&#8230;&#8230;</font> </span><span lang=en style="FONT-SIZE: 9pt; FONT-FAMILY: 新宋体; mso-hansi-font-family: ; mso-font-kerning: 0pt" times="" new="" roman='??=""'>.</span>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><span lang=en style="FONT-SIZE: 9pt; FONT-FAMILY: 新宋体; mso-hansi-font-family: ; mso-font-kerning: 0pt" times="" new="" roman='??=""'><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}&nbsp;<br></span><span lang=en style="FONT-SIZE: 9pt; FONT-FAMILY: 新宋体; mso-hansi-font-family: ; mso-font-kerning: 0pt" times="" new="" roman='??=""'><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="COLOR: blue">return</span> 0;&nbsp;<br></span><span lang=en style="FONT-SIZE: 9pt; FONT-FAMILY: 新宋体; mso-hansi-font-family: ; mso-font-kerning: 0pt" times="" new="" roman='??=""'>}&nbsp;</span>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; tab-stops: 96.75pt"><font size=3><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:="">在这个线程函数中只有设置</span> <span lang=en><font face="Times New Roman">g_event</font> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:="">为有信号状态时才执行下面的</span> <span lang=en><font face="Times New Roman">for</font> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:="">循环，因为</span> <span lang=en><font face="Times New Roman">g_event</font> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:="">是全局变量，所以我们可以在别的线程中通过</span> <span lang=en><font face="Times New Roman">g_event. SetEvent</font> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:="">控制这个线程。</span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; tab-stops: 96.75pt"><font size=3><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:="">还有一种用法就是我们可以通过</span> <span lang=en><font face="Times New Roman">WaitForSingleObject</font> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:="">函数来间隔的执行一个线程函数的函数体</span> </font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align=left><span lang=en><span style="mso-spacerun: yes"><font face="Times New Roman" size=3>&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span lang=en style="FONT-SIZE: 9pt; FONT-FAMILY: 新宋体; mso-hansi-font-family: ; mso-font-kerning: 0pt" times="" new="" roman='??=""'>UINT CFlushDlg::MyThreadProc( LPVOID pParam )<br></span><span lang=en style="FONT-SIZE: 9pt; FONT-FAMILY: 新宋体; mso-hansi-font-family: ; mso-font-kerning: 0pt" times="" new="" roman='??=""'>{&nbsp;<br></span><span lang=en style="FONT-SIZE: 9pt; FONT-FAMILY: 新宋体; mso-hansi-font-family: ; mso-font-kerning: 0pt" times="" new="" roman='??=""'><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="COLOR: blue">while</span>(WaitForSingleObject(g_event,MT_INTERVAL)!=WAIT_OBJECT_0)<br></span><span lang=en style="FONT-SIZE: 9pt; FONT-FAMILY: 新宋体; mso-hansi-font-family: ; mso-font-kerning: 0pt" times="" new="" roman='??=""'><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp; </span>{<br></span><span lang=en style="FONT-SIZE: 9pt; FONT-FAMILY: 新宋体; mso-hansi-font-family: ; mso-font-kerning: 0pt" times="" new="" roman='??=""'><span style="mso-tab-count: 2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><font face="Times New Roman"><span lang=en style="FONT-SIZE: 9pt; COLOR: blue; mso-ascii-font-family: 新宋体; mso-font-kerning: 0pt; mso-fareast-font-family: 新宋体">&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;</span>&nbsp;</font>&nbsp;<br><span lang=en style="FONT-SIZE: 9pt; FONT-FAMILY: 新宋体; mso-hansi-font-family: ; mso-font-kerning: 0pt" times="" new="" roman='??=""'><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp; </span>}&nbsp;<br></span><span lang=en style="FONT-SIZE: 9pt; FONT-FAMILY: 新宋体; mso-hansi-font-family: ; mso-font-kerning: 0pt" times="" new="" roman='??=""'><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="COLOR: blue">return</span> 0;<br></span><span lang=en style="FONT-SIZE: 9pt; FONT-FAMILY: 新宋体; mso-hansi-font-family: ; mso-font-kerning: 0pt" times="" new="" roman='??=""'>}&nbsp;</span>&nbsp;
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; tab-stops: 96.75pt"><font size=3><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:="">在这个线程函数中可以可以通过设置</span> <span lang=en><font face="Times New Roman">MT_INTERVAL</font> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:="">来控制这个线程的函数体多久执行一次，当事件为无信号状态时函数体隔</span> <span lang=en><font face="Times New Roman">MT_INTERVAL</font> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: " ?="" times="" new="" roman='?;=""' mso-hansi-font-family:="">执行一次，当设置事件为有信号状态时，线程就执行完毕了。</span> </font></p>
<img src ="http://www.cppblog.com/twzheng/aggbug/21029.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/twzheng/" target="_blank">谭文政</a> 2007-04-01 00:04 <a href="http://www.cppblog.com/twzheng/articles/21029.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[摘录]Windows完成端口编程</title><link>http://www.cppblog.com/twzheng/articles/20520.html</link><dc:creator>谭文政</dc:creator><author>谭文政</author><pubDate>Sat, 24 Mar 2007 03:37:00 GMT</pubDate><guid>http://www.cppblog.com/twzheng/articles/20520.html</guid><wfw:comment>http://www.cppblog.com/twzheng/comments/20520.html</wfw:comment><comments>http://www.cppblog.com/twzheng/articles/20520.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/twzheng/comments/commentRss/20520.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/twzheng/services/trackbacks/20520.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;目录<br>一 基本概念<br>二 OVERLAPPED数据结构<br>三 完成端口的内部机制<br>&nbsp;&nbsp;&nbsp;1、创建完成端口<br>&nbsp;&nbsp;&nbsp;2、完成端口线程的工作原理<br>&nbsp;&nbsp;&nbsp;3、&nbsp;线程间数据传递<br>&nbsp;&nbsp;&nbsp;4、线程的安全退出<br><br>一 基本概念<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;设备---windows操作系统上允许通信的任何东西，比如文件、目录、串行口、并行口、邮件槽、命名管道、无名管道、套接字、控制台、逻辑磁盘、物理磁盘等。绝大多数与设备打交道的函数都是CreateFile/ReadFile/WriteFile等。所以我们不能看到**File函数就只想到文件设备。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;与设备通信有两种方式，同步方式和异步方式。同步方式下，当调用ReadFile函数时，函数会等待系统执行完所要求的工作，然后才返回；异步方式下，ReadFile这类函数会直接返回，系统自己去完成对设备的操作，然后以某种方式通知完成操作。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;重叠I/O----顾名思义，当你调用了某个函数（比如ReadFile）就立刻返回做自己的其他动作的时候，同时系统也在对I/0设备进行你要求的操作，在这段时间内你的程序和系统的内部动作是重叠的，因此有更好的性能。所以，重叠I/O是用于异步方式下使用I/O设备的。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 重叠I/O需要使用的一个非常重要的数据结构OVERLAPPED。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;完成端口---是一种WINDOWS内核对象。完成端口用于异步方式的重叠I/0情况下，当然重叠I/O不一定非使用完成端口不可，还有设备内核对象、事件对象、告警I/0等。但是完成端口内部提供了线程池的管理，可以避免反复创建线程的开销，同时可以根据CPU的个数灵活的决定线程个数，而且可以让减少线程调度的次数从而提高性能。<br><br>二 OVERLAPPED数据结构<br>typedef struct _OVERLAPPED {<br>&nbsp;&nbsp;&nbsp;&nbsp;ULONG_PTR Internal;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//被系统内部赋值，用来表示系统状态<br>&nbsp;&nbsp;&nbsp;&nbsp;ULONG_PTR InternalHigh;&nbsp;&nbsp;&nbsp;// 被系统内部赋值，传输的字节数<br>&nbsp;&nbsp;&nbsp;&nbsp;union {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DWORD Offset;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//和OffsetHigh合成一个64位的整数，用来表示从文件头部的多少字节开始<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DWORD OffsetHigh;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//操作，如果不是对文件I/O来操作，则必须设定为0<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PVOID Pointer;<br>&nbsp;&nbsp;&nbsp;&nbsp;};<br>&nbsp;&nbsp;&nbsp;&nbsp;HANDLE&nbsp;&nbsp;hEvent;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//如果不使用，就务必设为0,否则请赋一个有效的Event句柄<br>} OVERLAPPED, *LPOVERLAPPED;<br><br>下面是异步方式使用ReadFile的一个例子<br>OVERLAPPED Overlapped;<br>Overlapped.Offset=345;<br>Overlapped.OffsetHigh=0;<br>Overlapped.hEvent=0;<br>//假定其他参数都已经被初始化<br>ReadFile(hFile,buffer,sizeof(buffer),&amp;dwNumBytesRead,&amp;Overlapped);<br><br>这样就完成了异步方式读文件的操作，然后ReadFile函数返回，由操作系统做自己的事情吧<br>下面介绍几个与OVERLAPPED结构相关的函数<br>等待重叠I/0操作完成的函数<br>BOOL GetOverlappedResult (<br>HANDLE hFile,<br>LPOVERLAPPED lpOverlapped,//接受返回的重叠I/0结构<br>LPDWORD lpcbTransfer,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//成功传输了多少字节数<br>BOOL fWait&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//TRUE只有当操作完成才返回，FALSE直接返回，如果操作没有完成，通过调//用GetLastError ( )函数会返回ERROR_IO_INCOMPLETE<br>);<br>宏HasOverlappedIoCompleted可以帮助我们测试重叠I/0操作是否完成，该宏对OVERLAPPED结构的Internal成员进行了测试，查看是否等于STATUS_PENDING值。<br><br>三 完成端口的内部机制<br>&nbsp;&nbsp;&nbsp;1、创建完成端口<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;完成端口是一个内核对象，使用时他总是要和至少一个有效的设备句柄进行关联，完成端口是一个复杂的内核对象，创建它的函数是：<br>HANDLE CreateIoCompletionPort(<br>&nbsp;&nbsp;&nbsp;&nbsp;IN HANDLE FileHandle,<br>&nbsp;&nbsp;&nbsp;&nbsp;IN HANDLE ExistingCompletionPort,<br>&nbsp;&nbsp;&nbsp;&nbsp;IN ULONG_PTR CompletionKey,<br>&nbsp;&nbsp;&nbsp;&nbsp;IN DWORD NumberOfConcurrentThreads<br>&nbsp;&nbsp;&nbsp;&nbsp;);<br>通常创建工作分两步：<br>第一步，创建一个新的完成端口内核对象，可以使用下面的函数：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HANDLE CreateNewCompletionPort(DWORD dwNumberOfThreads)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,NULL,dwNumberOfThreads);<br>};&nbsp;&nbsp;<br>第二步，将刚创建的完成端口和一个有效的设备句柄关联起来，可以使用下面的函数：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bool AssicoateDeviceWithCompletionPort(HANDLE hCompPort,HANDLE hDevice,DWORD dwCompKey)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HANDLE h=CreateIoCompletionPort(hDevice,hCompPort,dwCompKey,0);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return h==hCompPort;<br>};<br>说明<br>1）&nbsp;&nbsp;CreateIoCompletionPort函数也可以一次性的既创建完成端口对象，又关联到一个有效的设备句柄<br>2）&nbsp;&nbsp;CompletionKey是一个可以自己定义的参数，我们可以把一个结构的地址赋给它，然后在合适的时候取出来使用，最好要保证结构里面的内存不是分配在栈上，除非你有十分的把握内存会保留到你要使用的那一刻。<br>3）&nbsp;&nbsp;NumberOfConcurrentThreads通常用来指定要允许同时运行的的线程的最大个数。通常我们指定为0，这样系统会根据CPU的个数来自动确定。<br>创建和关联的动作完成后，系统会将完成端口关联的设备句柄、完成键作为一条纪录加入到这个完成端口的设备列表中。如果你有多个完成端口，就会有多个对应的设备列表。如果设备句柄被关闭，则表中自动删除该纪录。<br><br>&nbsp;&nbsp;&nbsp;2、完成端口线程的工作原理<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;完成端口可以帮助我们管理线程池，但是线程池中的线程需要我们使用_beginthreadex来创建，凭什么通知完成端口管理我们的新线程呢？答案在函数GetQueuedCompletionStatus。该函数原型：<br>BOOL GetQueuedCompletionStatus(<br>&nbsp;&nbsp;&nbsp;&nbsp;IN&nbsp;&nbsp;HANDLE CompletionPort,<br>&nbsp;&nbsp;&nbsp;&nbsp;OUT LPDWORD lpNumberOfBytesTransferred,<br>&nbsp;&nbsp;&nbsp;&nbsp;OUT PULONG_PTR lpCompletionKey,<br>&nbsp;&nbsp;&nbsp;&nbsp;OUT LPOVERLAPPED *lpOverlapped,<br>&nbsp;&nbsp;&nbsp;&nbsp;IN&nbsp;&nbsp;DWORD dwMilliseconds<br>);<br>这个函数试图从指定的完成端口的I/0完成队列中抽取纪录。只有当重叠I/O动作完成的时候，完成队列中才有纪录。凡是调用这个函数的线程将被放入到完成端口的等待线程队列中，因此完成端口就可以在自己的线程池中帮助我们维护这个线程。<br>完成端口的I/0完成队列中存放了当重叠I/0完成的结果---- 一条纪录，该纪录拥有四个字段，前三项就对应GetQueuedCompletionStatus函数的2、3、4参数，最后一个字段是错误信息dwError。我们也可以通过调用PostQueudCompletionStatus模拟完成了一个重叠I/0操作。<br>当I/0完成队列中出现了纪录，完成端口将会检查等待线程队列，该队列中的线程都是通过调用GetQueuedCompletionStatus函数使自己加入队列的。等待线程队列很简单，只是保存了这些线程的ID。完成端口会按照后进先出的原则将一个线程队列的ID放入到释放线程列表中，同时该线程将从等待GetQueuedCompletionStatus函数返回的睡眠状态中变为可调度状态等待CPU的调度。<br>基本上情况就是如此，所以我们的线程要想成为完成端口管理的线程，就必须要调用<br>GetQueuedCompletionStatus函数。出于性能的优化，实际上完成端口还维护了一个暂停线程列表，具体细节可以参考《Windows高级编程指南》，我们现在知道的知识，已经足够了。<br><br>&nbsp;&nbsp;&nbsp;3、线程间数据传递<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;线程间传递数据最常用的办法是在_beginthreadex函数中将参数传递给线程函数，或者使用全局变量。但是完成端口还有自己的传递数据的方法，答案就在于CompletionKey和OVERLAPPED参数。<br>CompletionKey被保存在完成端口的设备表中，是和设备句柄一一对应的，我们可以将与设备句柄相关的数据保存到CompletionKey中，或者将CompletionKey表示为结构指针，这样就可以传递更加丰富的内容。这些内容只能在一开始关联完成端口和设备句柄的时候做，因此不能在以后动态改变。<br><br>OVERLAPPED参数是在每次调用ReadFile这样的支持重叠I/0的函数时传递给完成端口的。我们可以看到，如果我们不是对文件设备做操作，该结构的成员变量就对我们几乎毫无作用。我们需要附加信息，可以创建自己的结构，然后将OVERLAPPED结构变量作为我们结构变量的第一个成员，然后传递第一个成员变量的地址给ReadFile函数。因为类型匹配，当然可以通过编译。当GetQueuedCompletionStatus函数返回时，我们可以获取到第一个成员变量的地址，然后一个简单的强制转换，我们就可以把它当作完整的自定义结构的指针使用，这样就可以传递很多附加的数据了。太好了！只有一点要注意，如果跨线程传递，请注意将数据分配到堆上，并且接收端应该将数据用完后释放。我们通常需要将ReadFile这样的异步函数的所需要的缓冲区放到我们自定义的结构中，这样当GetQueuedCompletionStatus被返回时，我们的自定义结构的缓冲区变量中就存放了I/0操作的数据。<br>CompletionKey和OVERLAPPED参数，都可以通过GetQueuedCompletionStatus函数获得。<br>&nbsp;&nbsp;&nbsp;4、线程的安全退出<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;很多线程为了不止一次的执行异步数据处理，需要使用如下语句<br>while (true)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.。。。。。。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetQueuedCompletionStatus(...);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;。。。。。。<br>}<br>那么如何退出呢，答案就在于上面曾提到的PostQueudCompletionStatus函数，我们可以用它发送一个自定义的包含了OVERLAPPED成员变量的结构地址，里面包含一个状态变量，当状态变量为退出标志时，线程就执行清除动作然后退出。 
<img src ="http://www.cppblog.com/twzheng/aggbug/20520.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/twzheng/" target="_blank">谭文政</a> 2007-03-24 11:37 <a href="http://www.cppblog.com/twzheng/articles/20520.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>