﻿<?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++博客-C小加-随笔分类-网络编程</title><link>http://www.cppblog.com/cxiaojia/category/19331.html</link><description>厚德 博学 求真 至善
The bright moon and breeze</description><language>zh-cn</language><lastBuildDate>Sat, 13 Apr 2013 04:13:39 GMT</lastBuildDate><pubDate>Sat, 13 Apr 2013 04:13:39 GMT</pubDate><ttl>60</ttl><item><title>ACE前摄器Proactor最好的讲解（转载）</title><link>http://www.cppblog.com/cxiaojia/archive/2013/04/12/199385.html</link><dc:creator>C小加</dc:creator><author>C小加</author><pubDate>Fri, 12 Apr 2013 09:43:00 GMT</pubDate><guid>http://www.cppblog.com/cxiaojia/archive/2013/04/12/199385.html</guid><wfw:comment>http://www.cppblog.com/cxiaojia/comments/199385.html</wfw:comment><comments>http://www.cppblog.com/cxiaojia/archive/2013/04/12/199385.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/cxiaojia/comments/commentRss/199385.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/cxiaojia/services/trackbacks/199385.html</trackback:ping><description><![CDATA[<p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">转自：<a href="http://blog.csdn.net/caisini_vc/article/details/4474910">http://blog.csdn.net/caisini_vc/article/details/4474910</a><br />把这两天做Proactor的一些经验和心得写一下，可能会给一些人帮助。<br />&nbsp;&nbsp;&nbsp; Proactor是异步模式的网络处理器，ACE中叫做&#8220;前摄器&#8221;。<br />&nbsp;&nbsp;&nbsp; 先讲几个概念：<br />&nbsp;&nbsp;&nbsp; 前摄器（Proactor）－异步的事件多路分离器、处理器，是核心处理类。启动后由3个线程组成（你不需要关心这三个线程，我只是让你知道一下有这回事存在）。<br />&nbsp;&nbsp;&nbsp; 接受器（Acceptor）－用于服务端，监听在一个端口上，接受用户的请求。<br />&nbsp;&nbsp;&nbsp; 连接器（Connector）－用于客户端，去连接远程的监听。当然，如果远程是ACE写的，就是Acceptor。<br />&nbsp;&nbsp;&nbsp; 异步模式－即非阻塞模式。网络的传输速度一般来讲为10Mbps、100Mbps、1000Mbps。拿千兆网来说，实际的传输速度为1000Mbps/8大概为128KB左右。我们的CPU一般为P4 3.0GHZ，如果是32位的处理器，一秒钟大概可以处理6G的字节，那么，128KB的网络速度是远远及不上处理器的速度的。网络发送数据是一位一位发送出去的，如果CPU等在这里，发送完成函数才结束，那么，处理器浪费了大量时间在网络传输上。<br />&nbsp;&nbsp;&nbsp; 操作系统提供了异步的模式来传输网络数据，工作模式即：应用程序把要发送的数据交给操作系统，操作系统把数据放在系统缓冲区后就告诉应用程序OK了，我帮你发，应用程序该干嘛干嘛去。操作系统发送完成后，会给应用系统一个回执，告诉应用程序：刚才那个包发送完成了！<br />&nbsp;&nbsp; 举个例子：你有几封邮件和包裹要发，最有效率的办法是什么？你把邮件和包裹及交给总台，总台MM说，好了，你帮你发，你忙去吧！然后你去工作了。过了一会，总台MM打电话告诉你：&#8220;刚才我叫快递公司的人来了，把你的包裹发出去了。邮局的人也来了，取走了邮件，放心好了&#8221;。同样，如果你知道今天会有包裹来，比如你在淘宝上购物了，你能成天等在总台？你应该告诉总台MM：&#8220;今天可能有我的一个快递，你帮我收一下，晚上请你肯德基！&#8221;。MM：&#8220;看在肯得基的面子上，帮你收了&#8221;。某个时间，MM打电话来了：&#8220;帅哥，你的包裹到了，我帮你签收了，快来拿吧。&#8221;<br />&nbsp;&nbsp; 因为操作系统是很有效率的，所有，他在后台收发是很快的。应用程序也很简单。Proactor就是这种异步模式的。Proactor就是总台MM；ACE_Service_Handle就是总台代为收发邮件的公司流程。</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">我们看一个实例：</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><br /></span><span style="color: #008000; ">//</span><span style="color: #008000; ">***********************************************************</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;TPTCPAsynchServerImpl&nbsp;:&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;ACE_Service_Handler<br />{<br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">:<br />&nbsp;TPTCPAsynchServerImpl(</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">);<br />&nbsp;</span><span style="color: #000000; ">~</span><span style="color: #000000; ">TPTCPAsynchServerImpl(</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">);<br />&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;open&nbsp;(ACE_HANDLE&nbsp;handle,&nbsp;ACE_Message_Block&nbsp;</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">message_block);&nbsp;<br />&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;handle_read_stream&nbsp;(</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;ACE_Asynch_Read_Stream::Result&nbsp;</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">result);<br />&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;handle_write_stream&nbsp;(</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;ACE_Asynch_Write_Stream::Result&nbsp;</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">result);<br />&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;&nbsp;handle_time_out&nbsp;(</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;ACE_Time_Value&nbsp;</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">tv,&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">act</span><span style="color: #000000; ">=</span><span style="color: #000000; ">0</span><span style="color: #000000; ">);<br /></span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">:<br />&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;initiate_read_stream&nbsp;(</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;ACE_Asynch_Read_Stream::Result&nbsp;</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">result);<br />&nbsp;ACE_Asynch_Read_Stream&nbsp;rs_;<br />&nbsp;ACE_Asynch_Write_Stream&nbsp;ws_;<br />};</span></div></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><br />这个例子从ACE_Service_Handler继承过来，ACE_Service_Handle主要就是定义了一些回调函数。<br />1、 virtual void open (ACE_HANDLE handle, ACE_Message_Block &amp;message_block);<br />&nbsp; 当有客户端连接上来，连接建立成功后Proactor会调用这个方法。</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">2、 virtual void handle_read_stream (const ACE_Asynch_Read_Stream::Result &amp;result);<br />当用户要读的数据读好了后，调用这个方法</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">3、virtual void handle_write_stream (const ACE_Asynch_Write_Stream::Result &amp;result);<br />当用户要写的数据在网卡上发送成功后，Proactor会回调这个方法</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">4、 virtual void&nbsp; handle_time_out (const ACE_Time_Value &amp;tv, const void *act=0);<br />当用户设定的时钟到期了，这个方法会被调用。</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">这跟和总台MM的联络方法是不是一样的？</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">对还缺点东西，缺少怎么向总台MM交待任务的方法。下面看看：</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">首先，创建一个监听器。</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;ACE_Asynch_Acceptor&lt;TPTCPAsynchServerImpl&gt; acceptor_;<br />看到没，就是我们刚才写的类，因为他继承了回调接口，并实现了自已的代码，模板中ACE_Asynch_Acceptor会在合适的时候回调这些方法。</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">//创建一个地址对象<br />&nbsp;ACE_INET_Addr addr(port, ip);<br />acceptor_.open (addr, 8 * 1024, 1);<br />&nbsp;&nbsp;&nbsp; Open后，就开始监听了。其它的，向Proactor注册一些事件的事模板类中都替你做了，你不需要做很多事。<br />&nbsp;&nbsp;&nbsp; 那么，已经开始监听了，我的程序从哪里开始呢？对于一个服务程序来讲，程序是被用户的连接驱动的，一个用户程序想和通讯，必须先创建连接，就是Socket中的connect操作。这个操作Proactor会替我们做一些工作，当连接创建完成后，上面讲的Open方法会被调用，我们看看Open方法中都有些什么代码：</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;TPTCPAsynchServerImpl::open&nbsp;(ACE_HANDLE&nbsp;handle,&nbsp;ACE_Message_Block&nbsp;</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">message_block)<br />{<br />&nbsp;ACE_DEBUG&nbsp;((LM_DEBUG,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%N:%l:TPTCPAsynchServerImpl::open()<img src="http://www.cppblog.com/Images/dot.gif"  alt="" />&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">构造读流</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(rs_.open&nbsp;(</span><span style="color: #000000; ">*</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">,&nbsp;handle)&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">1</span><span style="color: #000000; ">)<br />&nbsp;{<br />&nbsp;&nbsp;ACE_ERROR&nbsp;((LM_ERROR,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%N:%l:&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">TPTCPAsynchServerImpl::open()&nbsp;Error</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">构造写流</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(ws_.open(</span><span style="color: #000000; ">*</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">,&nbsp;handle)&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">1</span><span style="color: #000000; ">)<br />&nbsp;{<br />&nbsp;&nbsp;ACE_ERROR&nbsp;((LM_ERROR,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%N:%l:&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">TPTCPAsynchServerImpl::open()&nbsp;Error</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">获取客户端连接地址和端口</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;ACE_INET_Addr&nbsp;addr;&nbsp;<br />&nbsp;ACE_SOCK_SEQPACK_Association&nbsp;ass</span><span style="color: #000000; ">=</span><span style="color: #000000; ">ACE_SOCK_SEQPACK_Association(handle);&nbsp;<br />&nbsp;size_t&nbsp;addr_size</span><span style="color: #000000; ">=</span><span style="color: #000000; ">1</span><span style="color: #000000; ">;&nbsp;<br />&nbsp;ass.get_local_addrs(</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">addr,addr_size);<br />&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">server_</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">onClientConnect((</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">)handle,&nbsp;addr.get_ip_address(),&nbsp;addr.get_port_number());<br /><br />&nbsp;<br /><br />&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">如果客户连接时同时提交了数据，需要伪造一个结果，然后呼叫读事件</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(message_block.length&nbsp;()&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">)<br />&nbsp;{<br />&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;ACE_DEBUG((LM_DEBUG,&nbsp;"message_block.length()&nbsp;!=&nbsp;0&nbsp;"));<br />&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;复制消息块</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;ACE_Message_Block&nbsp;</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">duplicate&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">message_block.duplicate&nbsp;();<br />&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;伪造读结果，以便进行读完成回调</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;ACE_Asynch_Read_Stream_Result_Impl&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">fake_result&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ACE_Proactor::instance&nbsp;()</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">create_asynch_read_stream_result&nbsp;(</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">proxy&nbsp;(),<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;&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;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">handle_,<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;&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;duplicate,<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;&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;</span><span style="color: #000000; ">1024</span><span style="color: #000000; ">,<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;&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;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">,<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;&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;ACE_INVALID_HANDLE,<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;&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;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">,<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;&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;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;size_t&nbsp;bytes_transferred&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;message_block.length&nbsp;();<br />&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Accept事件处理完成，wr_ptr指针会被向前移动，将其移动到开始位置</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;duplicate.wr_ptr&nbsp;(duplicate.wr_ptr&nbsp;()&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">&nbsp;bytes_transferred);<br />&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;这个方法将调用回调函数</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;fake_result</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">complete&nbsp;(message_block.length&nbsp;(),&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;销毁伪造的读结果</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;delete&nbsp;fake_result;<br />&nbsp;}<br /><br />&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;否则，通知底层，准备读取用户数据<br />&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">创建一个消息块。这个消息块将用于从套接字中异步读&nbsp;</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;ACE_Message_Block&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">mb&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;ACE_NEW&nbsp;(mb,&nbsp;ACE_Message_Block&nbsp;(_bufSize));<br />&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(rs_.read&nbsp;(</span><span style="color: #000000; ">*</span><span style="color: #000000; ">mb,&nbsp;mb</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">size&nbsp;()&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">)&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">1</span><span style="color: #000000; ">)<br />&nbsp;{<br />&nbsp;&nbsp;delete&nbsp;mb;<br />&nbsp;&nbsp;ACE_ERROR&nbsp;((LM_ERROR,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%N:%l:open&nbsp;init&nbsp;read&nbsp;failed!</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">;<br />&nbsp;}<br />}</span></div></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">我们看到，首先创建了两个流，就是前面类定义中定义的一个异步写流，一个异步读流。以后对网络的读和写就通过这两个流进行。我还给出了一段读客户端地址和端口的代码。然后是读取客户Connect可能附带的数据，那段代码不用看懂，以后使用照抄就行。然后就是</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><br /><span style="color: #0000FF; ">if</span>&nbsp;(rs_.read&nbsp;(*mb,&nbsp;mb-&gt;size&nbsp;()&nbsp;-&nbsp;1)&nbsp;==&nbsp;-1)<br />&nbsp;{<br />&nbsp;&nbsp;delete&nbsp;mb;<br />&nbsp;&nbsp;ACE_ERROR&nbsp;((LM_ERROR,&nbsp;"%N:%l:open&nbsp;init&nbsp;read&nbsp;failed!"));<br />&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>;<br />&nbsp;}</div><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">这段代码使用读流读一段数据。这段代码就是向总台MM交待：我要收包裹，收好了叫我！<br />也就是说，这段代码99%的可能是读不出数据的，只是向Proactor注册读的事件，具体的等待、读取操作由Proactor读，读到了，就回调Handle_Read_Stream方法。ACE_Message_Block是消息块，数据就是存放在消息块中的。<br />下面看看Handle_Read_Stream方法的代码：</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;TPTCPAsynchServerImpl::handle_read_stream&nbsp;(</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;ACE_Asynch_Read_Stream::Result&nbsp;</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">result)<br />{<br />&nbsp;result.message_block&nbsp;().rd_ptr&nbsp;()[result.bytes_transferred&nbsp;()]&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">'</span><span style="color: #000000; ">/0</span><span style="color: #000000; ">'</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;ACE_DEBUG&nbsp;((LM_DEBUG,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">********************/n</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />&nbsp;&nbsp;ACE_DEBUG&nbsp;((LM_DEBUG,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%s&nbsp;=&nbsp;%d/n</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">bytes_to_read</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;result.bytes_to_read&nbsp;()));<br />&nbsp;&nbsp;ACE_DEBUG&nbsp;((LM_DEBUG,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%s&nbsp;=&nbsp;%d/n</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">handle</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;result.handle&nbsp;()));<br />&nbsp;&nbsp;ACE_DEBUG&nbsp;((LM_DEBUG,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%s&nbsp;=&nbsp;%d/n</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">bytes_transfered</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;result.bytes_transferred&nbsp;()));<br />&nbsp;&nbsp;ACE_DEBUG&nbsp;((LM_DEBUG,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%s&nbsp;=&nbsp;%d/n</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">act</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;(u_long)&nbsp;result.act&nbsp;()));<br />&nbsp;&nbsp;ACE_DEBUG&nbsp;((LM_DEBUG,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%s&nbsp;=&nbsp;%d/n</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">success</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;result.success&nbsp;()));<br />&nbsp;&nbsp;ACE_DEBUG&nbsp;((LM_DEBUG,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%s&nbsp;=&nbsp;%d/n</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">completion_key</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;(u_long)&nbsp;result.completion_key&nbsp;()));<br />&nbsp;&nbsp;ACE_DEBUG&nbsp;((LM_DEBUG,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%s&nbsp;=&nbsp;%d/n</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">error</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;result.error&nbsp;()));<br />&nbsp;&nbsp;ACE_DEBUG&nbsp;((LM_DEBUG,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">********************/n</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />&nbsp;result.message_block().release();<br />&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">initiate_read_stream&nbsp;(result)&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">1</span><span style="color: #000000; ">)<br />&nbsp;{<br />&nbsp;&nbsp;ACE_ERROR((LM_ERROR,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%N:%l:read&nbsp;stream&nbsp;failed!connection&nbsp;closed,&nbsp;remove&nbsp;it:%d/n</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;result.handle()));<br />&nbsp;&nbsp;closeConnection(result.handle());<br />&nbsp;}&nbsp;<br />}<br />&nbsp;</span></div></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">这个函数被调用，就表明有数据已经读好了，包裹已经在总台了。Proactor比总台MM还好，给你送上门了，数据就在Result里，上面演示了Result中的数据。然后把消息块释放了，然后调用initiate_read_stream继续监听网络上可能到来的数据。看看initiate_read_stream好了：</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;TPTCPAsynchServerImpl::initiate_read_stream&nbsp;(</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;ACE_Asynch_Read_Stream::Result&nbsp;</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">result)<br />{<br />&nbsp;ACE_DEBUG((LM_TRACE,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%N:%l:TPTCPAsynchServerImpl::initiate_read_stream()&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">创建一个消息块。这个消息块将用于从套接字中异步读&nbsp;</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;ACE_Message_Block&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">mb&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ACE_Message_Block(_bufSize);<br />&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(mb&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;NULL)<br />&nbsp;{<br />&nbsp;&nbsp;ACE_DEBUG((LM_ERROR,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%N:%l:can't&nbsp;allock&nbsp;ACE_Message_Block.&nbsp;&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));&nbsp;<br />&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">1</span><span style="color: #000000; ">;<br />&nbsp;}<br />&nbsp;<br />&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(rs_.read&nbsp;(</span><span style="color: #000000; ">*</span><span style="color: #000000; ">mb,&nbsp;mb</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">size&nbsp;()&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">)&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">1</span><span style="color: #000000; ">)<br />&nbsp;{<br />&nbsp;&nbsp;delete&nbsp;mb;<br />&nbsp;&nbsp;ACE_ERROR_RETURN&nbsp;((LM_ERROR,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%N:%l:rs-&gt;read()&nbsp;failed,&nbsp;clientID=%d&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;result.handle()),&nbsp;&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">1</span><span style="color: #000000; ">);<br />&nbsp;}<br />&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">;<br />}</span></div></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">代码很简单，就是创建一个新的消息块，然后使用读流注册一个读消息就可以了。</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">到此为止，Proactor的读流程很清楚了吧？</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">下面再说一个写流程。</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">写流程其实更简单，在任意想向客户端写数据的地方，调用相应代码就行了，比如，我们提供了SendData方法来发送数据，在任意想发送数据的地方调用SendData就行了，SendData的代码如下：</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;TPTCPAsynchServerImpl::sendData(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;clientID,&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">char</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">data,&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;dataLen,&nbsp;unsigned&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">id)<br />{<br />&nbsp;ACE_DEBUG((LM_DEBUG,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">TPTCPAsynchServerImpl::sendData(void)&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />&nbsp;ACE_Message_Block&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">mb;&nbsp;<br />&nbsp;ACE_NEW_RETURN(mb,&nbsp;ACE_Message_Block(dataLen&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">),&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">1</span><span style="color: #000000; ">);<br />&nbsp;mb</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">wr_ptr((</span><span style="color: #0000FF; ">char</span><span style="color: #000000; ">*</span><span style="color: #000000; ">)data);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;ACE_OS::memcpy(mb</span><span style="color: #000000; ">-&gt;</span><span style="color: #0000FF; ">base</span><span style="color: #000000; ">(),(</span><span style="color: #0000FF; ">char</span><span style="color: #000000; ">*</span><span style="color: #000000; ">)data,&nbsp;dataLen);<br />&nbsp;id&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;GlobleSingleton::instance()</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">getIndex();<br />&nbsp;mb</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">msg_type((</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">)id);<br />&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">向操作系统发送数据</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(connection</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">ws</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">write&nbsp;(</span><span style="color: #000000; ">*</span><span style="color: #000000; ">mb&nbsp;,&nbsp;dataLen&nbsp;)&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">1</span><span style="color: #000000; ">)<br />&nbsp;{<br />&nbsp;&nbsp;ACE_ERROR_RETURN((LM_ERROR,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%N:%l:sendData&nbsp;failed!&nbsp;clientID=%d&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;clientID),</span><span style="color: #000000; ">-</span><span style="color: #000000; ">1</span><span style="color: #000000; ">);<br />&nbsp;}<br />&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">;<br />}</span></div></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">简单说，就是创建了一个消息块，把用户数据拷贝进来，然后调用写流WS向Proactor发送一个Write事件就可以了，发送成功后，Handle_write_handle会被调用，看一下：</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">void</span><span style="color: #000000; "><br />TPTCPAsynchServerImpl::handle_write_stream&nbsp;(</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;ACE_Asynch_Write_Stream::Result&nbsp;</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">result)<br />{<br />&nbsp;&nbsp;ACE_DEBUG&nbsp;((LM_DEBUG,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">handle_write_stream&nbsp;called&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Reset&nbsp;pointers.</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;result.message_block&nbsp;().rd_ptr&nbsp;(result.message_block&nbsp;().rd_ptr&nbsp;()&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">&nbsp;result.bytes_transferred&nbsp;());<br />&nbsp;&nbsp;ACE_DEBUG&nbsp;((LM_DEBUG,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">********************&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />&nbsp;&nbsp;ACE_DEBUG&nbsp;((LM_DEBUG,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%s&nbsp;=&nbsp;%d&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">bytes_to_write</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;result.bytes_to_write&nbsp;()));<br />&nbsp;&nbsp;ACE_DEBUG&nbsp;((LM_DEBUG,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%s&nbsp;=&nbsp;%d&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">handle</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;result.handle&nbsp;()));<br />&nbsp;&nbsp;ACE_DEBUG&nbsp;((LM_DEBUG,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%s&nbsp;=&nbsp;%d&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">bytes_transfered</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;result.bytes_transferred&nbsp;()));<br />&nbsp;&nbsp;ACE_DEBUG&nbsp;((LM_DEBUG,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%s&nbsp;=&nbsp;%d&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">act</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;(u_long)&nbsp;result.act&nbsp;()));<br />&nbsp;&nbsp;ACE_DEBUG&nbsp;((LM_DEBUG,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%s&nbsp;=&nbsp;%d&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">success</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;result.success&nbsp;()));<br />&nbsp;&nbsp;ACE_DEBUG&nbsp;((LM_DEBUG,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%s&nbsp;=&nbsp;%d&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">completion_key</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;(u_long)&nbsp;result.completion_key&nbsp;()));<br />&nbsp;&nbsp;ACE_DEBUG&nbsp;((LM_DEBUG,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%s&nbsp;=&nbsp;%d&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">error</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;result.error&nbsp;()));<br />&nbsp;&nbsp;ACE_DEBUG&nbsp;((LM_DEBUG,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">********************&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br /></span><span style="color: #0000FF; ">#if</span><span style="color: #000000; ">&nbsp;0</span><span style="color: #000000; "><br />&nbsp;&nbsp;ACE_DEBUG&nbsp;((LM_DEBUG,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">%s&nbsp;=&nbsp;%s&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">message_block</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;result.message_block&nbsp;().rd_ptr&nbsp;()));<br /></span><span style="color: #0000FF; ">#endif</span><span style="color: #000000; "><br />&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Release&nbsp;message&nbsp;block.</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;result.message_block&nbsp;().release&nbsp;();<br />}</span></div></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">代码中使用了result中发数据，然后把消息块释放了，就这么简单。</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><br />////////////////////////////////////////////////////////////////////////////////////////////////////</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">这是简单的proactor用法，当然，复杂也基本就这样用。所谓不基本的不是Proactor的内容，而是服务器编程本身的麻烦。比如说，多个连接的管理、重发机制、发送队列等等，这都不是ACE的内容。这些要大家自己思考了，并添加。</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">在这里，我要说几个重要的问题：连接的管理。Acceptor是一个类，但是在每一个连接，Proactor都用了某种办法创建了一个实例，所以，连接管理的群集类一定不能在Acceptor类中，不然得到的结果就是始终只有一条记录。因为每个Acceptor都有一个实例，实例对应一个连接，群集类也就每个实例一个了。要采取的方法是一个全局的容器对象就可以了。比如我这个类：</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><br />typedef&nbsp;ACE_Map_Manager&nbsp;</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">ACE_HANDLE,&nbsp;ConnectionBean&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">,&nbsp;ACE_Null_Mutex</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;ConnectionMap;<br />typedef&nbsp;ACE_Map_Iterator</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">ACE_HANDLE,&nbsp;ConnectionBean&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">,&nbsp;ACE_Null_Mutex</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;ConnectionIterator;<br />typedef&nbsp;ACE_Map_Entry&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">ACE_HANDLE,&nbsp;ConnectionBean&nbsp;</span><span style="color: #000000; ">*&gt;</span><span style="color: #000000; ">&nbsp;ConnectionEntry;<br /></span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;Globle<br />{<br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">:<br />&nbsp;Globle(</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">);<br />&nbsp;</span><span style="color: #000000; ">~</span><span style="color: #000000; ">Globle(</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">);<br />&nbsp;ITPServer</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;server_;<br />&nbsp;ConnectionMap&nbsp;_connections;<br />&nbsp;unsigned&nbsp;getIndex(</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">);&nbsp;<br />&nbsp;</span><span style="color: #0000FF; ">long</span><span style="color: #000000; ">&nbsp;getTimerId(</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">);<br /></span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">:<br />&nbsp;unsigned&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;index_;<br />&nbsp;</span><span style="color: #0000FF; ">long</span><span style="color: #000000; ">&nbsp;timerId_;<br />};<br />typedef&nbsp;ACE_Singleton</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Globle,&nbsp;ACE_Null_Mutex</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;GlobleSingleton;</span></div></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">我使用ACE的Singleton模板创建这个类，每一个Acceptor要使用ConnectionMap，都使用这里的_connections，方法如下 ：<br />&nbsp; GlobleSingleton::instance()-&gt;connection.bind()......</p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">这个问题可是我花费了2天时间找出来的，诸位同仁不可不戒啊，给点掌声：）</p><img src ="http://www.cppblog.com/cxiaojia/aggbug/199385.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/cxiaojia/" target="_blank">C小加</a> 2013-04-12 17:43 <a href="http://www.cppblog.com/cxiaojia/archive/2013/04/12/199385.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ACE网络编程笔记（1）：ACE自适配通信环境</title><link>http://www.cppblog.com/cxiaojia/archive/2013/04/08/199198.html</link><dc:creator>C小加</dc:creator><author>C小加</author><pubDate>Mon, 08 Apr 2013 01:48:00 GMT</pubDate><guid>http://www.cppblog.com/cxiaojia/archive/2013/04/08/199198.html</guid><wfw:comment>http://www.cppblog.com/cxiaojia/comments/199198.html</wfw:comment><comments>http://www.cppblog.com/cxiaojia/archive/2013/04/08/199198.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/cxiaojia/comments/commentRss/199198.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/cxiaojia/services/trackbacks/199198.html</trackback:ping><description><![CDATA[<p style="font-family: Simsun; font-size: medium; line-height: normal;">介绍<br /></p><hr />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ACE自适配通信环境（ADAPTIVE Communication Environment）是可以自由使用、开放源码的面向对象（OO）构架（Framework），在其中实现了许多用于并发通信软件的核心模式。ACE提供了一组丰富的可复用C++Wrapper Facade（包装外观）和构架组件，可跨越多种平台完成通用的通信软件任务，其中包括：事件多路分离和事件处理器分派、信号处理、服务初始化、进程间通信、共享内存管理、消息路由、分布式服务动态（重）配置、并发执行和同步，等等。<br />&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;ACE的目标用户是高性能和实时通信服务和应用的开发者。它简化了使用进程间通信、事件多路分离、显式动态链接和并发的OO网络应用和服务的开发。此外，通过服务在运行时与应用的动态链接，ACE还使系统的配置和重配置得以自动化。<br /><p style="font-family: Simsun; font-size: medium; line-height: normal; text-indent: 21pt;"><br /><br />好处<br /></p><hr /><strong style="text-indent: -24px;">增强可移植性</strong>：在ACE组件的帮助下，很容易在一种OS平台上编写并发网络应用，然后快速地将它们移植到各种其他的OS平台上。而且，因为ACE是开放源码的自由软件，你无需担心被锁定在特定的操作系统平台或编译器上。<br /><strong style="text-indent: -24px;">更好的软件质量：</strong>ACE的设计使用了许多可提高软件质量的关键模式，这些质量因素包括通信软件灵活性、可扩展性、可复用性和模块性。<br /><span lang="EN-US" style="text-indent: -24px; font-family: Wingdings;"><span style="font-size: 7pt; font-family: 'Times New Roman';">&nbsp;</span></span><strong style="text-indent: -24px;">更高的效率和可预测性：</strong>ACE经仔细设计，支持广泛的应用服务质量（QoS）需求，包括延迟敏感应用的低响应等待时间、高带宽应用的高性能，以及实时应用的可预测性。<br /><strong style="text-indent: -24px;">更容易转换到标准的高级中间件：</strong>TAO使用了ACE提供的可复用组件和模式。它是CORBA的开发源码、遵循标准的实现，并为高性能和实时系统作了优化。为此，ACE和TAO被设计为能良好地协同工作，以提供全面的中间件解决方案。<br /><br /><span style="font-family: Simsun; font-size: medium; line-height: normal; text-indent: 28px;">体系结构</span><br /><hr /><br /><div style="text-align: center;"><img src="http://www.cppblog.com/images/cppblog_com/cxiaojia/Image1.gif" width="615" height="409" alt="" style="text-indent: 0px;" /></div><strong>OS适配层：</strong>ACE底层，和OS的API相关的部分。这部分ACE把不同类型的OS接口统一起来，让ACE的高层摆脱平台的依赖性。适配层增强了代码的可移植性。<br /><strong>C++包装层：</strong>ACE中间层，主要功能的实现部分。包括并发和同步、IPC、内存管理组件、定时器类、容器类、信号处理、文件系统组件和线程管理。<br /><strong>构架和模式层：</strong>ACE高层，架构和模式部分。它们的基础是若干针对特定通信软件领域的设计模式。包括事件处理、连接或服务初始化组件、流组件和服务配置组件。<br /><p>&nbsp;</p><img src ="http://www.cppblog.com/cxiaojia/aggbug/199198.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/cxiaojia/" target="_blank">C小加</a> 2013-04-08 09:48 <a href="http://www.cppblog.com/cxiaojia/archive/2013/04/08/199198.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ACE网络编程之ACE_NEW_RETURN，ACE_NEW，ACE_NEW_NORETURN</title><link>http://www.cppblog.com/cxiaojia/archive/2013/03/26/198838.html</link><dc:creator>C小加</dc:creator><author>C小加</author><pubDate>Tue, 26 Mar 2013 07:26:00 GMT</pubDate><guid>http://www.cppblog.com/cxiaojia/archive/2013/03/26/198838.html</guid><wfw:comment>http://www.cppblog.com/cxiaojia/comments/198838.html</wfw:comment><comments>http://www.cppblog.com/cxiaojia/archive/2013/03/26/198838.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/cxiaojia/comments/commentRss/198838.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/cxiaojia/services/trackbacks/198838.html</trackback:ping><description><![CDATA[ACE源码<span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">OS_Memory.h</span>是这样定义的。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->#&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;defined&nbsp;(ACE_HAS_NEW_NOTHROW)<br />#&nbsp;&nbsp;&nbsp;&nbsp;define&nbsp;ACE_NEW_RETURN(POINTER,CONSTRUCTOR,RET_VAL)&nbsp;\<br />&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">do</span>&nbsp;{&nbsp;POINTER&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;(ACE_nothrow)&nbsp;CONSTRUCTOR;&nbsp;\<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(POINTER&nbsp;==&nbsp;0)&nbsp;{&nbsp;errno&nbsp;=&nbsp;ENOMEM;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;RET_VAL;&nbsp;}&nbsp;\<br />&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">while</span>&nbsp;(0)<br />#&nbsp;&nbsp;&nbsp;&nbsp;define&nbsp;ACE_NEW(POINTER,CONSTRUCTOR)&nbsp;\<br />&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">do</span>&nbsp;{&nbsp;POINTER&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>(ACE_nothrow)&nbsp;CONSTRUCTOR;&nbsp;\<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(POINTER&nbsp;==&nbsp;0)&nbsp;{&nbsp;errno&nbsp;=&nbsp;ENOMEM;&nbsp;<span style="color: #0000FF; ">return</span>;&nbsp;}&nbsp;\<br />&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">while</span>&nbsp;(0)<br />#&nbsp;&nbsp;&nbsp;&nbsp;define&nbsp;ACE_NEW_NORETURN(POINTER,CONSTRUCTOR)&nbsp;\<br />&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">do</span>&nbsp;{&nbsp;POINTER&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>(ACE_nothrow)&nbsp;CONSTRUCTOR;&nbsp;\<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(POINTER&nbsp;==&nbsp;0)&nbsp;{&nbsp;errno&nbsp;=&nbsp;ENOMEM;&nbsp;}&nbsp;\<br />&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">while</span>&nbsp;(0)<br /><br />#&nbsp;&nbsp;<span style="color: #0000FF; ">else</span><br /><br />#&nbsp;&nbsp;&nbsp;&nbsp;define&nbsp;ACE_NEW_RETURN(POINTER,CONSTRUCTOR,RET_VAL)&nbsp;\<br />&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">do</span>&nbsp;{&nbsp;<span style="color: #0000FF; ">try</span>&nbsp;{&nbsp;POINTER&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;CONSTRUCTOR;&nbsp;}&nbsp;\<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">catch</span>&nbsp;(ACE_bad_alloc)&nbsp;{&nbsp;ACE_del_bad_alloc&nbsp;errno&nbsp;=&nbsp;ENOMEM;&nbsp;POINTER&nbsp;=&nbsp;0;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;RET_VAL;&nbsp;}&nbsp;\<br />&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">while</span>&nbsp;(0)<br /><br />#&nbsp;&nbsp;&nbsp;&nbsp;define&nbsp;ACE_NEW(POINTER,CONSTRUCTOR)&nbsp;\<br />&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">do</span>&nbsp;{&nbsp;<span style="color: #0000FF; ">try</span>&nbsp;{&nbsp;POINTER&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;CONSTRUCTOR;&nbsp;}&nbsp;\<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">catch</span>&nbsp;(ACE_bad_alloc)&nbsp;{&nbsp;ACE_del_bad_alloc&nbsp;errno&nbsp;=&nbsp;ENOMEM;&nbsp;POINTER&nbsp;=&nbsp;0;&nbsp;<span style="color: #0000FF; ">return</span>;&nbsp;}&nbsp;\<br />&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">while</span>&nbsp;(0)<br /><br />#&nbsp;&nbsp;&nbsp;&nbsp;define&nbsp;ACE_NEW_NORETURN(POINTER,CONSTRUCTOR)&nbsp;\<br />&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">do</span>&nbsp;{&nbsp;<span style="color: #0000FF; ">try</span>&nbsp;{&nbsp;POINTER&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;CONSTRUCTOR;&nbsp;}&nbsp;\<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">catch</span>&nbsp;(ACE_bad_alloc)&nbsp;{&nbsp;ACE_del_bad_alloc&nbsp;errno&nbsp;=&nbsp;ENOMEM;&nbsp;POINTER&nbsp;=&nbsp;0;&nbsp;}&nbsp;\<br />&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">while</span>&nbsp;(0)<br />#&nbsp;&nbsp;endif&nbsp;<span style="color: #008000; ">/*</span><span style="color: #008000; ">&nbsp;ACE_HAS_NEW_NOTHROW&nbsp;</span><span style="color: #008000; ">*/</span><br /><br /><span style="color: #0000FF; ">#else</span>&nbsp;/*&nbsp;ACE_NEW_THROWS_EXCEPTIONS&nbsp;*/<br /><br />#&nbsp;define&nbsp;ACE_NEW_RETURN(POINTER,CONSTRUCTOR,RET_VAL)&nbsp;\<br />&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">do</span>&nbsp;{&nbsp;POINTER&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;CONSTRUCTOR;&nbsp;\<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(POINTER&nbsp;==&nbsp;0)&nbsp;{&nbsp;errno&nbsp;=&nbsp;ENOMEM;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;RET_VAL;&nbsp;}&nbsp;\<br />&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">while</span>&nbsp;(0)<br />#&nbsp;define&nbsp;ACE_NEW(POINTER,CONSTRUCTOR)&nbsp;\<br />&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">do</span>&nbsp;{&nbsp;POINTER&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;CONSTRUCTOR;&nbsp;\<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(POINTER&nbsp;==&nbsp;0)&nbsp;{&nbsp;errno&nbsp;=&nbsp;ENOMEM;&nbsp;<span style="color: #0000FF; ">return</span>;&nbsp;}&nbsp;\<br />&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">while</span>&nbsp;(0)<br />#&nbsp;define&nbsp;ACE_NEW_NORETURN(POINTER,CONSTRUCTOR)&nbsp;\<br />&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">do</span>&nbsp;{&nbsp;POINTER&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;CONSTRUCTOR;&nbsp;\<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(POINTER&nbsp;==&nbsp;0)&nbsp;{&nbsp;errno&nbsp;=&nbsp;ENOMEM;&nbsp;}&nbsp;\<br />&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">while</span>&nbsp;(0)</div><br />以上是用宏定义替换的表达式，功能都是用一个POINTER指针new一个CONSTRUCTOR实例，内部做了一些错误信息的标志和异常处理。<br />宏ifelse对平台的不同进行了分离，每个平台下都有三种new的方法，他们的区别是return。<br />每个宏定义都用到了do{}while(0)，是因为宏只能替换一句表达式，而你用do{}while(0)可以方便的把多个表达式集中起来。<img src ="http://www.cppblog.com/cxiaojia/aggbug/198838.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/cxiaojia/" target="_blank">C小加</a> 2013-03-26 15:26 <a href="http://www.cppblog.com/cxiaojia/archive/2013/03/26/198838.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>socket阻塞与非阻塞，同步与异步、I/O模型(转)</title><link>http://www.cppblog.com/cxiaojia/archive/2013/03/26/198831.html</link><dc:creator>C小加</dc:creator><author>C小加</author><pubDate>Tue, 26 Mar 2013 05:36:00 GMT</pubDate><guid>http://www.cppblog.com/cxiaojia/archive/2013/03/26/198831.html</guid><wfw:comment>http://www.cppblog.com/cxiaojia/comments/198831.html</wfw:comment><comments>http://www.cppblog.com/cxiaojia/archive/2013/03/26/198831.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/cxiaojia/comments/commentRss/198831.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/cxiaojia/services/trackbacks/198831.html</trackback:ping><description><![CDATA[<h2><p bk-sidecatalog-title"="" style="margin: 0px 0px 10px; padding: 0px 0px 6px; border-bottom-color: #dedfe1; border-bottom-width: 1px; border-bottom-style: solid; clear: both;"><span style="margin: 0px; padding: 0px; color: #336699;"><span style="margin: 0px; padding: 0px; line-height: 36px; font-size: 22px;">转自：<a href="http://blog.csdn.net/hguisu/article/details/7453390">http://blog.csdn.net/hguisu/article/details/7453390</a><br /><br /><br />1. 概念理解</span></span></p></h2><p style="margin: 0px; padding: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><p style="padding: 0px; color: #333333; background-color: #ffffff; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 18px;"><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; font-size: 10.5pt; line-height: 21px;">&nbsp; &nbsp; &nbsp;在进行网络编程时，我们常常见到<span style="margin: 0px; padding: 0px; font-family: Arial, Helvetica, sans-serif, 宋体; font-size: 12px; line-height: 16px; text-indent: 24px;">同步(Sync)/异步(Async)，阻塞(Block)/非阻塞(Unblock)</span>四种调用方式：</span><span style="margin: 0px; padding: 0px; font-family: Verdana; font-size: 10.5pt; line-height: 21px;"><br style="margin: 0px; padding: 0px;" /></span><span style="margin: 0px; padding: 0px; font-size: 10.5pt; line-height: 21px;"><strong style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; color: #3333ff;">同步：</span></strong></span><span style="margin: 0px; padding: 0px; font-family: Verdana; font-size: 10.5pt; line-height: 21px;"><br style="margin: 0px; padding: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="margin: 0px; padding: 0px; line-height: 21px;"><span style="margin: 0px; padding: 0px; font-size: 10.5pt;">所谓同步，就是在发出一个功能调用时，在没有得到结果之前，该调用就不返回。</span><span style="margin: 0px; padding: 0px; font-size: 12px;"><span style="margin: 0px; padding: 0px; font-family: 宋体; color: #666666; text-indent: 32px; line-height: 24px;"><strong style="margin: 0px; padding: 0px;">也就是必须一件一件事做</strong></span><span style="margin: 0px; padding: 0px; font-family: 宋体, Arial; color: #666666; line-height: 18px; text-indent: 32px;"><span style="margin: 0px; padding: 0px; line-height: 24px;"><span style="margin: 0px; padding: 0px; font-family: 'Times New Roman';">,</span></span><span style="margin: 0px; padding: 0px; font-family: 宋体; line-height: 24px;">等前一件做完了才能做下一件事。</span></span></span></span></span></p><p style="padding: 0px; color: #333333; background-color: #ffffff; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 18px;"><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; line-height: 21px;"><span style="margin: 0px; padding: 0px; font-size: 12px;"><span style="margin: 0px; padding: 0px; font-family: 宋体, Arial; color: #666666; line-height: 18px; text-indent: 32px;"></span></span></span></span></p><p align="left" style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff; color: #666666;">例如普通<span style="margin: 0px; padding: 0px; font-family: 'Times New Roman';">B/S</span>模式（同步）：提交请求<span style="margin: 0px; padding: 0px; font-family: 'Times New Roman';">-&gt;</span>等待服务器处理<span style="margin: 0px; padding: 0px; font-family: 'Times New Roman';">-&gt;</span>处理完毕返回&nbsp;这个期间客户端浏览器不能干任何事</p><p style="padding: 0px; color: #333333; font-family: Arial; background-color: #ffffff; font-size: 12px; line-height: 18px;"><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 21px; font-size: 10.5pt;"><strong style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; color: #3333ff;">异步：</span></strong></span><span style="margin: 0px; padding: 0px; font-family: Verdana; line-height: 21px; font-size: 10.5pt;"><br style="margin: 0px; padding: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="margin: 0px; padding: 0px; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 21px; font-size: 10.5pt;">异步的概念和同步相对。当一个异步过程调用发出后，调用者不能立刻得到结果。实际处理这个调用的部件在完成后，通过状态、通知和回调来通知调用者。</span></span></p><p style="padding: 0px; color: #333333; font-family: Arial; background-color: #ffffff; line-height: 18px;"><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; line-height: 21px;"><span style="margin: 0px; padding: 0px; font-size: 10.5pt;">&nbsp; &nbsp; &nbsp;</span><span style="margin: 0px; padding: 0px; font-family: 宋体; font-size: 12px; color: #666666; line-height: 24px; text-indent: 32px;">例如 ajax请求（</span><span style="margin: 0px; padding: 0px; font-size: 12px;"><span style="margin: 0px; padding: 0px; font-family: 宋体; color: #666666; text-indent: 32px; line-height: 24px;">异步）</span><span style="margin: 0px; padding: 0px; font-family: 宋体, Arial; color: #666666; text-indent: 32px; line-height: 24px;"><span style="margin: 0px; padding: 0px; font-family: 'Times New Roman';">:&nbsp;</span></span><span style="margin: 0px; padding: 0px; font-family: 宋体; color: #666666; text-indent: 32px; line-height: 24px;">请求通过事件触发</span><span style="margin: 0px; padding: 0px; font-family: 宋体, Arial; color: #666666; text-indent: 32px; line-height: 24px;"><span style="margin: 0px; padding: 0px; font-family: 'Times New Roman';">-&gt;</span></span><span style="margin: 0px; padding: 0px; font-family: 宋体; color: #666666; text-indent: 32px; line-height: 24px;">服务器处理（这是浏览器仍然可以作其他事情）</span><span style="margin: 0px; padding: 0px; font-family: 宋体, Arial; color: #666666; text-indent: 32px; line-height: 24px;"><span style="margin: 0px; padding: 0px; font-family: 'Times New Roman';">-&gt;</span></span><span style="margin: 0px; padding: 0px; font-family: 宋体; color: #666666; text-indent: 32px; line-height: 24px;">处理完毕</span></span></span></span></p><p style="padding: 0px; color: #333333; background-color: #ffffff; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; font-size: 12px; line-height: 18px;"><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; line-height: 21px; font-size: 10.5pt;"><span style="margin: 0px; padding: 0px; color: #3333ff;"><strong style="margin: 0px; padding: 0px;">阻塞</strong></span></span><span style="margin: 0px; padding: 0px; font-family: Verdana; line-height: 21px; font-size: 10.5pt;"><br style="margin: 0px; padding: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="margin: 0px; padding: 0px; line-height: 21px; font-size: 10.5pt;">阻塞调用是指调用结果返回之前，当前线程会被挂起（线程进入非可执行状态，在这个状态下，cpu不会给线程分配时间片，即线程暂停运行）。函数只有在得到结果之后才会返回。</span></span></p><p style="padding: 0px; color: #333333; background-color: #ffffff; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; font-size: 12px; line-height: 18px;"><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; line-height: 21px; font-size: 10.5pt;">&nbsp; &nbsp; &nbsp;有人也许会把阻塞调用和同步调用等同起来，实际上他是不同的。对于同</span><span style="margin: 0px; padding: 0px; line-height: 21px; font-size: 10.5pt;">步调用来说，很多时候当前线程还是激活的，只是从逻辑上当前函数没有返回而已。</span><span style="margin: 0px; padding: 0px; font-size: 10.5pt; line-height: 21px;">&nbsp;例如，我们在s</span><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; font-family: Verdana; line-height: 21px; font-size: 10.5pt;">ocket</span></span><span style="margin: 0px; padding: 0px; font-size: 10.5pt; line-height: 21px;">中调用r</span><span style="margin: 0px; padding: 0px; font-family: Verdana; font-size: 10.5pt; line-height: 21px;">ecv</span><span style="margin: 0px; padding: 0px; font-size: 10.5pt; line-height: 21px;">函数，如果缓冲区中没有数</span><span style="margin: 0px; padding: 0px; font-size: 10.5pt; line-height: 21px;">据，这个函数就会一直等待，直到有数据才返回。而此时，当前线程还会继续处理各种各样的消息。</span></span></p><p style="padding: 0px; color: #333333; line-height: 26px; background-color: #ffffff; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; font-size: 12px;"><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; line-height: 21px; font-size: 10.5pt;"><span style="margin: 0px; padding: 0px; color: #3333ff;"><strong style="margin: 0px; padding: 0px;">非阻塞</strong></span></span><span style="margin: 0px; padding: 0px; font-family: Verdana; line-height: 21px; font-size: 10.5pt;"><br style="margin: 0px; padding: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="margin: 0px; padding: 0px; line-height: 21px; font-size: 10.5pt;">非阻塞和阻塞的概念相对应，指在不能立刻得到结果之前，该函数不会阻塞当前线程，而会立刻返回。</span><span style="margin: 0px; padding: 0px; font-family: Verdana; line-height: 21px; font-size: 10.5pt;"><br style="margin: 0px; padding: 0px;" /></span><span style="margin: 0px; padding: 0px; line-height: 21px; font-size: 10.5pt;">对象的阻塞模式和阻塞函数调用</span><span style="margin: 0px; padding: 0px; font-family: Verdana; line-height: 21px; font-size: 10.5pt;"><br style="margin: 0px; padding: 0px;" /></span><span style="margin: 0px; padding: 0px; line-height: 21px; font-size: 10.5pt;">对象是否处于阻塞模式和函数是不是阻塞调用有很强的相关性，但是并不是一一对应的。阻塞对象上可以有非阻塞的调用方式，我们可以通过一定的</span><span style="margin: 0px; padding: 0px; line-height: 18px;"><span style="margin: 0px; padding: 0px; font-family: Verdana; line-height: 21px; font-size: 10.5pt;">API</span></span><span style="margin: 0px; padding: 0px; line-height: 21px; font-size: 10.5pt;">去轮询状</span>&nbsp;<span style="margin: 0px; padding: 0px; line-height: 21px; font-size: 10.5pt;">态，在适当的时候调用阻塞函数，就可以避免阻塞。而对于非阻塞对象，调用特殊的函数也可以进入阻塞调用。函数</span><span style="margin: 0px; padding: 0px; line-height: 18px;"><span style="margin: 0px; padding: 0px; font-family: Verdana; line-height: 21px; font-size: 10.5pt;">select</span></span><span style="margin: 0px; padding: 0px; line-height: 21px; font-size: 10.5pt;">就是这样的一个例子。</span></span></p><p style="padding: 0px; color: #333333; background-color: #ffffff; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; font-size: 12px; line-height: 18px;">&nbsp;</p><p style="padding: 0px; color: #333333; line-height: 26px; background-color: #ffffff; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; font-size: 12px;"><span style="margin: 0px; padding: 0px; line-height: 20px;"><span style="margin: 0px; padding: 0px; font-family: Arial; font-size: 14px; line-height: 21px;">1. 同步，就是我调用一个功能，该功能没有结束前，我死等结果。</span><br style="margin: 0px; padding: 0px; font-family: Arial; font-size: 14px; line-height: 21px;" /><span style="margin: 0px; padding: 0px; font-family: Arial; font-size: 14px; line-height: 21px;">2. 异步，就是<span style="margin: 0px; padding: 0px; line-height: 20px;">我调用一个功能，</span>不需要知道该功能结果，该功能有结果后通知我（回调通知）</span><br style="margin: 0px; padding: 0px; font-family: Arial; font-size: 14px; line-height: 21px;" /><span style="margin: 0px; padding: 0px; font-family: Arial; font-size: 14px; line-height: 21px;">3. 阻塞， &nbsp; &nbsp; &nbsp;就是调用我（函数），我<span style="margin: 0px; padding: 0px; line-height: 20px;">（函数）</span>没有接收完数据或者没有得到结果之前，我不会返回。</span><br style="margin: 0px; padding: 0px; font-family: Arial; font-size: 14px; line-height: 21px;" /><span style="margin: 0px; padding: 0px; font-family: Arial; font-size: 14px; line-height: 21px;">4. 非阻塞， &nbsp;就是调用我<span style="margin: 0px; padding: 0px; line-height: 20px;">（函数）</span>，我<span style="margin: 0px; padding: 0px; line-height: 20px;">（函数）</span>立即返回，<span style="margin: 0px; padding: 0px; line-height: 20px;">通过select通知调用者</span></span></span></p><p style="padding: 0px; color: #333333; line-height: 26px; background-color: #ffffff; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; font-size: 12px;"><span style="margin: 0px; padding: 0px; line-height: 20px;"></span></p><p style="margin: 0px; padding: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">同步IO和异步IO的区别就在于：<span style="margin: 0px; padding: 0px; color: #ff0000;">数据拷贝的时候进程是否阻塞！</span></p><p style="margin: 0px; padding: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="margin: 0px; padding: 0px; color: #000000;">阻塞IO和非阻塞IO的区别就在于：</span><span style="margin: 0px; padding: 0px; color: #ff0000;">应用程序的调用是否立即返回！</span></p><br style="margin: 0px; padding: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><p style="padding: 0px; color: #333333; line-height: 26px; background-color: #ffffff; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; font-size: 12px;"><span style="margin: 0px; padding: 0px; line-height: 20px;"><span style="margin: 0px; padding: 0px; font-family: Arial; font-size: 14px; line-height: 21px;">对于举个简单c/s 模式：</span></span></p><p style="padding: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; font-size: 12px;"></p><div style="margin: 0px; color: #333333; font-family: Arial; background-color: #ffffff;"><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; color: #ff0000;">同步：</span>提交请求-&gt;等待服务器处理-&gt;处理完毕返回这个期间客户端浏览器不能干任何事<br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; color: #ff0000;">异步：</span>请求通过事件触发-&gt;服务器处理（这是浏览器仍然可以作其他事情）-&gt;处理完毕</span></div><div style="margin: 0px; color: #333333; font-family: Arial; background-color: #ffffff;"><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; font-family: Helvetica, Arial, sans-serif; font-size: 12px; line-height: 19px;">同步和异步都只针对于本机SOCKET而言的。</span><br style="margin: 0px; padding: 0px;" /></span></div><div style="margin: 0px; color: #333333; font-family: Arial; background-color: #ffffff;"><p style="padding: 0px; line-height: 25px;"><span style="margin: 0px; padding: 0px;">同步和异步,阻塞和非阻塞,有些混用,其实它们完全不是一回事,而且它们修饰的对象也不相同。<br style="margin: 0px; padding: 0px;" />阻塞和非阻塞是指当进程访问的数据如果尚未就绪,进程是否需要等待,</span><span style="margin: 0px; padding: 0px; background-color: #ffcc00;">简单说这相当于函数内部的实现区别</span><span style="margin: 0px; padding: 0px;">,也就是未就绪时是直接返回还是等待就绪;</span></p><p style="padding: 0px; line-height: 25px;"><span style="margin: 0px; padding: 0px;">而同步和异步是</span><span style="margin: 0px; padding: 0px; background-color: #ffcc00;">指访问数据的机制</span><span style="margin: 0px; padding: 0px;">,同步一般指主动请求并等待I/O操作完毕的方式,当数据就绪后在读写的时候必须阻塞(区别就绪与读写二个阶段,同步的读写必须阻塞),异步则指主动请求数据后便可以继续处理其它任务,随后等待I/O,操作完毕的通知,这可以使进程在数据读写时也不阻塞。(等待"通知")</span></p></div><h2><a name="t1" style="margin: 0px; padding: 0px; color: rgb(51, 102, 153);"></a><p bk-sidecatalog-title"="" style="margin: 0px 0px 10px; padding: 0px 0px 6px; border-bottom-color: #dedfe1; border-bottom-width: 1px; border-bottom-style: solid; clear: both;"><span style="margin: 0px; padding: 0px; color: #336699;"><span style="margin: 0px; padding: 0px; line-height: 36px; font-size: 22px;">1. Linux下的五种I/O模型</span></span></p></h2><div style="margin: 0px; color: #333333; font-family: Arial; background-color: #ffffff;"><p style="margin: 0px 0px 15px; padding: 0px; font-family: 微软雅黑, Verdana, sans-serif, 宋体; line-height: 24px;"><span style="margin: 0px; padding: 0px;">1)阻塞I/O（blocking I/O）<br style="margin: 0px; padding: 0px;" />2)非阻塞I/O&nbsp;<span style="margin: 0px; padding: 0px;">（nonblocking I/O）</span><br style="margin: 0px; padding: 0px;" />3) I/O复用(select 和poll)&nbsp;<span style="margin: 0px; padding: 0px;">（I/O multiplexing）</span><br style="margin: 0px; padding: 0px;" />4)信号驱动I/O&nbsp;<span style="margin: 0px; padding: 0px;">（signal driven I/O (SIGIO)）</span><br style="margin: 0px; padding: 0px;" />5)异步I/O&nbsp;<span style="margin: 0px; padding: 0px;">（asynchronous I/O (the POSIX aio_functions)）</span><br style="margin: 0px; padding: 0px;" /></span></p><p style="margin: 0px 0px 15px; padding: 0px; font-family: 微软雅黑, Verdana, sans-serif, 宋体; line-height: 24px;"></p><p style="margin: 0px; padding: 0px;">前四种都是同步，只有最后一种才是异步IO。</p><br style="margin: 0px; padding: 0px;" /><h3><a name="t2" style="margin: 0px; padding: 0px; color: rgb(51, 102, 153);"></a><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; font-family: 微软雅黑, Verdana, sans-serif, 宋体; color: #3333ff; line-height: 24px;"><strong style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; font-size: 14px;">阻塞I/O模型：</span></strong></span></span></h3><p style="margin: 0px; padding: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="margin: 0px; padding: 0px; background-color: #ffcc00;">简介：进程会</span><span style="margin: 0px; padding: 0px; color: #ff0000; background-color: #ffcc00;">一直阻塞</span><span style="margin: 0px; padding: 0px; background-color: #ffcc00;">，直到数据拷贝完成</span><br style="margin: 0px; padding: 0px;" /></p><p style="margin: 0px 0px 15px; padding: 0px; font-family: 微软雅黑, Verdana, sans-serif, 宋体; line-height: 24px;"><span style="margin: 0px; padding: 0px; font-family: tahoma, 宋体; line-height: 22px;">&nbsp;&nbsp;&nbsp;&nbsp; 应用程序调用一个IO函数，导致应用程序阻塞，等待数据准备好。 如果数据没有准备好，一直等待&#8230;.数据准备好了，从内核拷贝到用户空间,IO函数返回成功指示。</span></p><p style="margin: 0px 0px 15px; padding: 0px; font-family: 微软雅黑, Verdana, sans-serif, 宋体; line-height: 24px;"><span style="margin: 0px; padding: 0px;"><strong style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; color: #ff6600;">阻塞I/O模型图：</span></strong><span style="margin: 0px; padding: 0px; font-family: Simsun; font-size: 16px;">在调用recv()/recvfrom（）函数时，发生在内核中等待数据和复制数据的过程。</span></span></p><p style="margin: 0px 0px 15px; padding: 0px; font-family: 微软雅黑, Verdana, sans-serif, 宋体; line-height: 24px;"><span style="margin: 0px; padding: 0px;"><img src="http://my.csdn.net/uploads/201204/12/1334216532_9745.jpg" alt="" style="margin: 0px; padding: 0px; border: none;" /><br style="margin: 0px; padding: 0px;" /></span></p><p style="margin: 0px 0px 15px; padding: 0px; font-family: 微软雅黑, Verdana, sans-serif, 宋体; line-height: 24px;"><span style="margin: 0px; padding: 0px; font-family: Simsun; font-size: 16px;">&nbsp; &nbsp; 当调用recv()函数时，系统首先查是否有准备好的数据。如果数据没有准备好，那么系统就处于等待状态。当数据准备好后，将数据从系统缓冲区复制到用户空间，然后该函数返回。在套接应用程序中，当调用recv()函数时，未必用户空间就已经存在数据，那么此时recv()函数就会处于等待状态。<br style="margin: 0px; padding: 0px;" /></span></p><p style="margin: 0px 0px 15px; padding: 0px; font-family: 微软雅黑, Verdana, sans-serif, 宋体; line-height: 24px;"></p><p style="margin: 10px 0px; padding: 0px; font-family: Simsun; font-size: 16px;"><span style="margin: 0px; padding: 0px;">&nbsp; &nbsp; &nbsp;当使用socket()函数和WSASocket()函数创建套接字时，默认的套接字都是阻塞的。这意味着当调用Windows Sockets API不能立即完成时，线程处于等待状态，直到操作完成。</span></p><p style="margin: 10px 0px; padding: 0px; font-family: Simsun; font-size: 16px;"><span style="margin: 0px; padding: 0px;">&nbsp; &nbsp; 并不是所有Windows Sockets API以阻塞套接字为参数调用都会发生阻塞。例如，以阻塞模式的套接字为参数调用bind()、listen()函数时，函数会立即返回。将可能阻塞套接字的Windows Sockets API调用分为以下四种:</span></p><p style="margin: 10px 0px; padding: 0px; font-family: Simsun; font-size: 16px;"><span style="margin: 0px; padding: 0px;">&nbsp; &nbsp; 1．输入操作：&nbsp;recv()、recvfrom()、WSARecv()和WSARecvfrom()函数。以阻塞套接字为参数调用该函数接收数据。如果此时套接字缓冲区内没有数据可读，则调用线程在数据到来前一直睡眠。</span></p><p style="margin: 10px 0px; padding: 0px; font-family: Simsun; font-size: 16px;"><span style="margin: 0px; padding: 0px;">&nbsp; &nbsp; 2．输出操作：&nbsp;send()、sendto()、WSASend()和WSASendto()函数。以阻塞套接字为参数调用该函数发送数据。如果套接字缓冲区没有可用空间，线程会一直睡眠，直到有空间。</span></p><p style="margin: 10px 0px; padding: 0px; font-family: Simsun; font-size: 16px;"><span style="margin: 0px; padding: 0px;">&nbsp; &nbsp; 3．接受连接：accept()和WSAAcept()函数。以阻塞套接字为参数调用该函数，等待接受对方的连接请求。如果此时没有连接请求，线程就会进入睡眠状态。</span></p><p style="margin: 10px 0px; padding: 0px; font-family: Simsun; font-size: 16px;"><span style="margin: 0px; padding: 0px;">&nbsp; &nbsp;4．外出连接：connect()和WSAConnect()函数。对于TCP连接，客户端以阻塞套接字为参数，调用该函数向服务器发起连接。该函数在收到服务器的应答前，不会返回。这意味着TCP连接总会等待至少到服务器的一次往返时间。</span></p><p style="margin: 10px 0px; padding: 0px; font-family: Simsun; font-size: 16px;"><span style="margin: 0px; padding: 0px;">　　使用阻塞模式的套接字，开发网络程序比较简单，容易实现。当希望能够立即发送和接收数据，且处理的套接字数量比较少的情况下，使用阻塞模式来开发网络程序比较合适。</span></p><p style="margin: 10px 0px; padding: 0px; font-family: Simsun; font-size: 16px;"><span style="margin: 0px; padding: 0px;">&nbsp; &nbsp; 阻塞模式套接字的不足表现为，在大量建立好的套接字线程之间进行通信时比较困难。当使用&#8220;生产者-消费者&#8221;模型开发网络程序时，为每个套接字都分别分配一个读线程、一个处理数据线程和一个用于同步的事件，那么这样无疑加大系统的开销。其最大的缺点是当希望同时处理大量套接字时，将无从下手，其扩展性很差</span></p><h3><a name="t3" style="margin: 0px; padding: 0px; color: rgb(51, 102, 153);"></a><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; font-family: 微软雅黑, Verdana, sans-serif, 宋体; color: #3333ff; line-height: 24px;"><strong style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; font-size: 14px;">非阻塞IO模型</span></strong></span></span>&nbsp;</h3></div><p style="margin: 0px; padding: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><div style="margin: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="margin: 0px; padding: 0px; background-color: #ffcc00;">简介：非阻塞IO通过进程反复调用IO函数（</span><span style="margin: 0px; padding: 0px; color: #ff0000; background-color: #ffcc00;">多次系统调用，并马上返回</span><span style="margin: 0px; padding: 0px; background-color: #ffcc00;">）；</span><span style="margin: 0px; padding: 0px; color: #ff0000; background-color: #ffcc00;">在数据拷贝的过程中，进程是阻塞的</span><span style="margin: 0px; padding: 0px; background-color: #ffcc00;">；</span></div><p style="margin: 0px; padding: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><p style="margin: 0px; padding: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="margin: 0px; padding: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br style="margin: 0px; padding: 0px;" /></span></p><p style="margin: 0px; padding: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="margin: 0px; padding: 0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们把一个SOCKET接口设置为非阻塞就是告诉内核，当所请求的I/O操作无法完成时，不要将进程睡眠，而是返回一个错误。这样我们的I/O操作函数将不断的测试数据是否已经准备好，如果没有准备好，继续测试，直到数据准备好为止。在这个不断测试的过程中，会大量的占用CPU的时间。</span></p><p style="margin: 0px 0px 15px; padding: 0px; color: #333333; background-color: #ffffff; font-family: 微软雅黑, Verdana, sans-serif, 宋体; line-height: 24px;"><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; font-family: Simsun; font-size: 16px;">&nbsp; &nbsp; 把<span style="margin: 0px; padding: 0px; font-size: 14px;">SOCKET</span>设置为非阻塞模式，即通知系统内核：在调用Windows Sockets API时，不要让线程睡眠，而应该让函数立即返回。在返回时，该函数返回一个错误代码。图所示，一个非阻塞模式套接字多次调用recv()函数的过程。前三次调用recv()函数时，内核数据还没有准备好。因此，该函数立即返回WSAEWOULDBLOCK错误代码。第四次调用recv()函数时，数据已经准备好，被复制到应用程序的缓冲区中，recv()函数返回成功指示，应用程序开始处理数据。</span><br style="margin: 0px; padding: 0px;" /></span></p><p style="margin: 0px 0px 15px; padding: 0px; color: #333333; background-color: #ffffff; font-family: 微软雅黑, Verdana, sans-serif, 宋体; line-height: 24px;"><span style="margin: 0px; padding: 0px;"><img src="http://my.csdn.net/uploads/201204/12/1334216607_3004.jpg" alt="" style="margin: 0px; padding: 0px; border: none;" /><br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /></span></p><p style="margin: 0px 0px 15px; padding: 0px; color: #333333; background-color: #ffffff; font-family: 微软雅黑, Verdana, sans-serif, 宋体; line-height: 24px;"><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; font-family: Simsun; font-size: 16px;">&nbsp; &nbsp; &nbsp;当使用socket()函数和WSASocket()函数创建套接字时，默认都是阻塞的。在创建套接字之后，通过调用ioctlsocket()函数，将该套接字设置为非阻塞模式。Linux下的函数是:fcntl().</span><br style="margin: 0px; padding: 0px; font-family: Simsun; font-size: 16px;" /><span style="margin: 0px; padding: 0px; font-family: Simsun; font-size: 16px;">&nbsp;&nbsp;&nbsp; 套接字设置为非阻塞模式后，在调用Windows Sockets API函数时，调用函数会立即返回。大多数情况下，这些函数调用都会调用&#8220;失败&#8221;，并返回WSAEWOULDBLOCK错误代码。说明请求的操作在调用期间内没有时间完成。通常，应用程序需要重复调用该函数，直到获得成功返回代码。</span><br style="margin: 0px; padding: 0px; font-family: Simsun; font-size: 16px;" /></span></p><p style="margin: 10px 0px; padding: 0px; color: #333333; line-height: 26px; background-color: #ffffff; font-family: Simsun; font-size: 16px;"><span style="margin: 0px; padding: 0px;">&nbsp;&nbsp;&nbsp; 需要说明的是并非所有的Windows Sockets API在非阻塞模式下调用，都会返回WSAEWOULDBLOCK错误。例如，以非阻塞模式的套接字为参数调用bind()函数时，就不会返回该错误代码。当然，在调用WSAStartup()函数时更不会返回该错误代码，因为该函数是应用程序第一调用的函数，当然不会返回这样的错误代码。</span></p><p style="margin: 10px 0px; padding: 0px; color: #333333; line-height: 26px; background-color: #ffffff; font-family: Simsun; font-size: 16px;"><span style="margin: 0px; padding: 0px;">&nbsp;&nbsp;&nbsp; 要将套接字设置为非阻塞模式，除了使用ioctlsocket()函数之外，还可以使用WSAAsyncselect()和WSAEventselect()函数。当调用该函数时，套接字会自动地设置为非阻塞方式。<br style="margin: 0px; padding: 0px;" /></span></p><p style="margin: 10px 0px; padding: 0px; color: #333333; line-height: 26px; background-color: #ffffff; font-family: Simsun; font-size: 16px;"><span style="margin: 0px; padding: 0px;">　　由于使用非阻塞套接字在调用函数时，会经常返回WSAEWOULDBLOCK错误。所以在任何时候，都应仔细检查返回代码并作好对&#8220;失败&#8221;的准备。应用程序连续不断地调用这个函数，直到它返回成功指示为止。上面的程序清单中，在While循环体内不断地调用recv()函数，以读入1024个字节的数据。这种做法很浪费系统资源。</span></p><p style="margin: 10px 0px; padding: 0px; color: #333333; line-height: 26px; background-color: #ffffff; font-family: Simsun; font-size: 16px;"><span style="margin: 0px; padding: 0px;">&nbsp;&nbsp;&nbsp; 要完成这样的操作，有人使用MSG_PEEK标志调用recv()函数查看缓冲区中是否有数据可读。同样，这种方法也不好。因为该做法对系统造成的开销是很大的，并且应用程序至少要调用recv()函数两次，才能实际地读入数据。较好的做法是，使用套接字的&#8220;I/O模型&#8221;来判断非阻塞套接字是否可读可写。</span></p><p style="margin: 10px 0px; padding: 0px; color: #333333; line-height: 26px; background-color: #ffffff; font-family: Simsun; font-size: 16px;"><span style="margin: 0px; padding: 0px;">&nbsp;&nbsp;&nbsp; 非阻塞模式套接字与阻塞模式套接字相比，不容易使用。使用非阻塞模式套接字，需要编写更多的代码，以便在每个Windows Sockets API函数调用中，对收到的WSAEWOULDBLOCK错误进行处理。因此，非阻塞套接字便显得有些难于使用。</span></p><p style="margin: 10px 0px; padding: 0px; color: #333333; line-height: 26px; background-color: #ffffff; font-family: Simsun; font-size: 16px;"><span style="margin: 0px; padding: 0px;">&nbsp;&nbsp;&nbsp; 但是，非阻塞套接字在控制建立的多个连接，在数据的收发量不均，时间不定时，明显具有优势。这种套接字在使用上存在一定难度，但只要排除了这些困难，它在功能上还是非常强大的。通常情况下，可考虑使用套接字的&#8220;I/O模型&#8221;，它有助于应用程序通过异步方式，同时对一个或多个套接字的通信加以管理。</span></p><span style="margin: 0px; padding: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><br style="margin: 0px; padding: 0px;" /></span><h3><a name="t4" style="margin: 0px; padding: 0px; color: rgb(51, 102, 153);"></a><span style="margin: 0px; padding: 0px;"><strong style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; color: #3333ff;"><span style="margin: 0px; padding: 0px; font-size: 14px;">IO复用模型：</span></span></strong></span></h3><p style="margin: 0px; padding: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="margin: 0px; padding: 0px;">&nbsp;<span style="margin: 0px; padding: 0px; background-color: #ffcc00;">&nbsp;简介：主要是select和epoll；对一个IO端口，两次调用，两次返回，比阻塞IO并没有什么优越性；关键是能实现同时对多个IO端口进行监听；</span></span><br style="margin: 0px; padding: 0px;" /></p><p style="margin: 0px 0px 15px; padding: 0px; color: #333333; background-color: #ffffff; font-family: 微软雅黑, Verdana, sans-serif, 宋体; line-height: 24px;"><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; color: #3333ff;">&nbsp; &nbsp;</span><span style="margin: 0px; padding: 0px;">&nbsp; &nbsp;I/O复用模型会用到select、poll、epoll函数，这几个函数也会使进程阻塞，但是和阻塞I/O所不同的的，这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作，多个写操作的I/O函数进行检测，直到有数据可读或可写时，才真正调用I/O操作函数</span><span style="margin: 0px; padding: 0px; color: #3333ff;">。</span></span></p><p style="margin: 0px 0px 15px; padding: 0px; color: #333333; background-color: #ffffff; font-family: 微软雅黑, Verdana, sans-serif, 宋体; line-height: 24px;"><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;"><img src="http://my.csdn.net/uploads/201204/12/1334216620_6310.jpg" alt="" style="margin: 0px; padding: 0px; border: none;" /></span><br style="margin: 0px; padding: 0px;" /></span></p><h3><a name="t5" style="margin: 0px; padding: 0px; color: rgb(51, 102, 153);"></a><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; color: #3333ff;"><strong style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; font-size: 14px;">信号驱动IO</span></strong></span></span></h3><p style="margin: 0px; padding: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><p style="margin: 0px 0px 15px; padding: 0px; color: #333333; background-color: #ffffff; font-family: 微软雅黑, Verdana, sans-serif, 宋体; line-height: 24px;"><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; color: #3333ff;">&nbsp; &nbsp;&nbsp;<span style="margin: 0px; padding: 0px; background-color: #ffcc00;">简介：<span style="margin: 0px; padding: 0px;">两次调用，两次返回；</span></span><br style="margin: 0px; padding: 0px;" /></span></span></p><p style="margin: 0px 0px 15px; padding: 0px; color: #333333; background-color: #ffffff; font-family: 微软雅黑, Verdana, sans-serif, 宋体; line-height: 24px;"><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; color: #3333ff;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="margin: 0px; padding: 0px;">首先我们允许套接口进行信号驱动I/O,并安装一个信号处理函数，进程继续运行并不阻塞。当数据准备好时，进程会收到一个SIGIO信号，可以在信号处理函数中调用I/O操作函数处理数据。</span></span></p><p style="margin: 0px 0px 15px; padding: 0px; color: #333333; background-color: #ffffff; font-family: 微软雅黑, Verdana, sans-serif, 宋体; line-height: 24px;"><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;"><img src="http://my.csdn.net/uploads/201204/12/1334216632_6025.jpg" alt="" style="margin: 0px; padding: 0px; border: none;" /></span><br style="margin: 0px; padding: 0px;" /></span></p><h3><a name="t6" style="margin: 0px; padding: 0px; color: rgb(51, 102, 153);"></a><span style="margin: 0px; padding: 0px;"><strong style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; color: #3333ff;"><span style="margin: 0px; padding: 0px; font-size: 14px;">异步IO模型</span></span></strong></span></h3><p style="margin: 0px; padding: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; background-color: #ffcc00;">&nbsp; 简介：数据拷贝的时候进程无需阻塞。</span></span><br style="margin: 0px; padding: 0px;" /></p><p style="margin: 0px 0px 15px; padding: 0px; color: #333333; background-color: #ffffff; font-family: 微软雅黑, Verdana, sans-serif, 宋体; line-height: 24px;"><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; color: #3333ff;"><strong style="margin: 0px; padding: 0px;">&nbsp; &nbsp; &nbsp;</strong></span><span style="margin: 0px; padding: 0px;">当一个异步过程调用发出后，调用者不能立刻得到结果。实际处理这个调用的部件在完成后，通过状态、通知和回调来通知调用者的输入输出操作</span></span></p><span style="margin: 0px; padding: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; line-height: 21px;"><img src="http://my.csdn.net/uploads/201204/12/1334216641_7821.jpg" alt="" style="margin: 0px; padding: 0px; border: none;" /></span></span><br style="margin: 0px; padding: 0px;" /></span><p style="padding: 0px; color: #333333; line-height: 26px; background-color: #ffffff; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; font-size: 12px;"><span style="margin: 0px; padding: 0px; line-height: 20px;"><span style="margin: 0px; padding: 0px; font-family: Arial; font-size: 14px; line-height: 21px;">同步IO引起进程阻塞，直至IO操作完成。<br style="margin: 0px; padding: 0px;" />异步IO不会引起进程阻塞。<br style="margin: 0px; padding: 0px;" />IO复用是先通过select调用阻塞。<br style="margin: 0px; padding: 0px;" /></span></span></p><p style="padding: 0px; color: #333333; line-height: 26px; background-color: #ffffff; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; font-size: 12px;"><span style="margin: 0px; padding: 0px; line-height: 20px;"><span style="margin: 0px; padding: 0px; font-family: Arial; font-size: 14px; line-height: 21px;"><br style="margin: 0px; padding: 0px;" /></span></span></p><h3><a name="t7" style="margin: 0px; padding: 0px; color: rgb(51, 102, 153);"></a><span style="margin: 0px; padding: 0px; line-height: 20px;"><span style="margin: 0px; padding: 0px; line-height: 21px;"><span style="margin: 0px; padding: 0px; color: #3333ff;"><strong style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; font-size: 14px;">5个I/O模型的比较：</span></strong></span></span></span></h3><p style="padding: 0px; color: #333333; line-height: 26px; background-color: #ffffff; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; font-size: 12px;"><span style="margin: 0px; padding: 0px; line-height: 20px;"><span style="margin: 0px; padding: 0px; font-family: Arial; font-size: 14px; line-height: 21px;"><img src="http://my.csdn.net/uploads/201204/12/1334216724_2405.jpg" alt="" style="margin: 0px; padding: 0px; border: none;" /><br style="margin: 0px; padding: 0px;" /></span></span></p><p style="padding: 0px; color: #333333; line-height: 26px; background-color: #ffffff; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; font-size: 12px;"><span style="margin: 0px; padding: 0px; line-height: 20px;"><span style="margin: 0px; padding: 0px; font-family: Arial; font-size: 14px; line-height: 21px;"><br style="margin: 0px; padding: 0px;" /></span></span></p><p style="padding: 0px; color: #333333; line-height: 26px; background-color: #ffffff; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; font-size: 12px;"><span style="margin: 0px; padding: 0px; line-height: 20px;"></span></p><h2><a name="t8" style="margin: 0px; padding: 0px; color: rgb(51, 102, 153);"></a><p bk-sidecatalog-title"="" style="margin: 0px 0px 10px; padding: 0px 0px 6px; border-bottom-color: #dedfe1; border-bottom-width: 1px; border-bottom-style: solid; clear: both;"><span style="margin: 0px; padding: 0px; color: #336699;"><span style="margin: 0px; padding: 0px; line-height: 36px; font-size: 22px;">1. select、poll、epoll简介<br style="margin: 0px; padding: 0px;" /></span></span></p></h2><p style="margin: 0px; padding: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">epoll跟select都能提供多路I/O复用的解决方案。在现在的Linux内核里有都能够支持，其中epoll是Linux所特有，而select则应该是POSIX所规定，一般操作系统均有实现</p><p style="padding: 0px; color: #333333; line-height: 26px; background-color: #ffffff; font-family: verdana, 'ms song', 宋体, Arial, 微软雅黑, Helvetica, sans-serif; font-size: 12px;"><span style="margin: 0px; padding: 0px; line-height: 20px;"></span></p><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; color: #333333; line-height: 25px; background-color: #ffffff; font-family: Georgia, 'Times new roman', Times, san-serif;"><strong style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; line-height: 20px;"><span style="margin: 0px; padding: 0px; font-family: Arial; line-height: 21px;">select：</span></span></strong></p><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; color: #333333; line-height: 25px; background-color: #ffffff; font-family: Georgia, 'Times new roman', Times, san-serif;">select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理。这样所带来的缺点是：</p><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; color: #333333; font-family: Arial; background-color: #ffffff; line-height: 1.8;">1、 单个进程可监视的fd数量被限制，即能监听端口的大小有限。<br style="margin: 0px; padding: 0px;" /></p><p style="margin: 0px; padding: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一般来说这个数目和系统内存关系很大，具体数目可以cat /proc/sys/fs/file-max察看。32位机默认是1024个。64位机默认是2048.</p><p style="margin: 0px; padding: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">2、 对socket进行扫描时是线性扫描，即采用轮询的方法，效率较低：<br style="margin: 0px; padding: 0px;" /></p><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; color: #333333; font-family: Arial; background-color: #ffffff; line-height: 1.8;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当套接字比较多的时候，每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。这会浪费很多CPU时间。如果能给套接字注册某个回调函数，当他们活跃时，自动完成相关操作，那就避免了轮询，这正是epoll与kqueue做的。</p><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; color: #333333; font-family: Arial; background-color: #ffffff; line-height: 1.8;">3、需要维护一个用来存放大量fd的数据结构，这样会使得用户空间和内核空间在传递该结构时复制开销大</p><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; color: #333333; font-family: Arial; background-color: #ffffff; line-height: 1.8;"><strong style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; line-height: 20px;"><span style="margin: 0px; padding: 0px; line-height: 21px;">poll：</span></span></strong></p><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; color: #333333; font-family: Arial; background-color: #ffffff; line-height: 1.8;"><span style="margin: 0px; padding: 0px; line-height: 20px;"><span style="margin: 0px; padding: 0px; line-height: 21px;"></span></span>poll本质上和select没有区别，它将用户传入的数组拷贝到内核空间，然后查询每个fd对应的设备状态，如果设备就绪则在设备等待队列中加入一项并继续遍历，如果遍历完所有fd后没有发现就绪设备，则挂起当前进程，直到设备就绪或者主动超时，被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。</p><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; color: #333333; font-family: Arial; background-color: #ffffff; line-height: 1.8;">它没有最大连接数的限制，原因是它是基于链表来存储的，但是同样有一个缺点：</p><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; color: #333333; font-family: Arial; background-color: #ffffff; line-height: 1.8;">1、大量的fd的数组被整体复制于用户态和内核地址空间之间，而不管这样的复制是不是有意义。&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;&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;&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; 2、poll还有一个特点是&#8220;水平触发&#8221;，如果报告了fd后，没有被处理，那么下次poll时会再次报告该fd。</p><strong style="margin: 0px; padding: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="margin: 0px; padding: 0px; line-height: 20px;"><span style="margin: 0px; padding: 0px; line-height: 21px;">epoll:</span></span></strong><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; color: #333333; font-family: Arial; background-color: #ffffff; line-height: 1.8;">epoll支持水平触发和边缘触发，最大的特点在于边缘触发，它只告诉进程哪些fd刚刚变为就需态，并且只会通知一次。还有一个特点是，epoll使用&#8220;事件&#8221;的就绪通知方式，通过epoll_ctl注册fd，一旦该fd就绪，内核就会采用类似callback的回调机制来激活该fd，epoll_wait便可以收到通知<br style="margin: 0px; padding: 0px;" /></p><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; color: #333333; font-family: Arial; background-color: #ffffff; line-height: 1.8;"><span style="margin: 0px; padding: 0px; color: #000000; background-color: #ffcc00;">epoll的优点：</span><br style="margin: 0px; padding: 0px;" /></p><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; color: #333333; font-family: Arial; background-color: #ffffff; line-height: 1.8;"></p><div style="margin: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><strong style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; color: #3333ff;">1、没有最大并发连接的限制，</span></strong>能打开的FD的上限远大于1024（1G的内存上能监听约10万个端口）；<br style="margin: 0px; padding: 0px;" /><span style="margin: 0px; padding: 0px; color: #3333ff;"><strong style="margin: 0px; padding: 0px;">2、效率提升</strong></span>，不是轮询的方式，不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数；<br style="margin: 0px; padding: 0px;" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="margin: 0px; padding: 0px; background-color: #ffcc00;">&nbsp;即Epoll最大的优点就在于它只管你&#8220;活跃&#8221;的连接，而跟连接总数无关，因此在实际的网络环境中，Epoll的效率就会远远高于select和poll。</span></div><div style="margin: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"><strong style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; color: #3333ff;">3、&nbsp;内存拷贝</span></strong>，利用mmap()文件映射内存加速与内核空间的消息传递；即epoll使用mmap减少复制开销。<br style="margin: 0px; padding: 0px;" /><br style="margin: 0px; padding: 0px;" /><strong style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; font-size: 18px;">select、poll、epoll 区别总结：</span></strong></div><p style="margin: 0px; padding: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; color: #333333; line-height: 25px; background-color: #ffffff; font-family: Georgia, 'Times new roman', Times, san-serif;">1、支持一个进程所能打开的最大连接数</p><table border="1" cellpadding="0" cellspacing="0" style="margin: 0px; padding: 0px; color: #333333; font-size: 14px; border-style: solid; border-color: #c0c0c0; line-height: 25px; background-color: #ffffff; border-collapse: collapse; font-family: Georgia, 'Times new roman', Times, san-serif;"><tbody style="margin: 0px; padding: 0px;"><tr style="margin: 0px; padding: 0px;"><td width="111" style="margin: 0px; padding: 3px; border-style: solid; border-color: #c0c0c0; border-collapse: collapse;"><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; line-height: 1.8;">select</p></td><td width="457" style="margin: 0px; padding: 3px; border-style: solid; border-color: #c0c0c0; border-collapse: collapse;"><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; line-height: 1.8;">单个进程所能打开的最大连接数有FD_SETSIZE宏定义，其大小是32个整数的大小（在32位的机器上，大小就是32*32，同理64位机器上FD_SETSIZE为32*64），当然我们可以对进行修改，然后重新编译内核，但是性能可能会受到影响，这需要进一步的测试。</p></td></tr><tr style="margin: 0px; padding: 0px;"><td width="111" style="margin: 0px; padding: 3px; border-style: solid; border-color: #c0c0c0; border-collapse: collapse;"><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; line-height: 1.8;">poll</p></td><td width="457" style="margin: 0px; padding: 3px; border-style: solid; border-color: #c0c0c0; border-collapse: collapse;"><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; line-height: 1.8;">poll本质上和select没有区别，但是它没有最大连接数的限制，原因是它是基于链表来存储的</p></td></tr><tr style="margin: 0px; padding: 0px;"><td width="111" style="margin: 0px; padding: 3px; border-style: solid; border-color: #c0c0c0; border-collapse: collapse;"><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; line-height: 1.8;">epoll</p></td><td width="457" style="margin: 0px; padding: 3px; border-style: solid; border-color: #c0c0c0; border-collapse: collapse;"><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; line-height: 1.8;">虽然连接数有上限，但是很大，1G内存的机器上可以打开10万左右的连接，2G内存的机器可以打开20万左右的连接</p></td></tr></tbody></table><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; color: #333333; line-height: 25px; background-color: #ffffff; font-family: Georgia, 'Times new roman', Times, san-serif;">2、FD剧增后带来的IO效率问题</p><table border="1" cellpadding="0" cellspacing="0" style="margin: 0px; padding: 0px; color: #333333; font-size: 14px; border-style: solid; border-color: #c0c0c0; line-height: 25px; background-color: #ffffff; border-collapse: collapse; font-family: Georgia, 'Times new roman', Times, san-serif;"><tbody style="margin: 0px; padding: 0px;"><tr style="margin: 0px; padding: 0px;"><td width="111" style="margin: 0px; padding: 3px; border-style: solid; border-color: #c0c0c0; border-collapse: collapse;"><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; line-height: 1.8;">select</p></td><td width="457" style="margin: 0px; padding: 3px; border-style: solid; border-color: #c0c0c0; border-collapse: collapse;"><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; line-height: 1.8;">因为每次调用时都会对连接进行线性遍历，所以随着FD的增加会造成遍历速度慢的&#8220;线性下降性能问题&#8221;。</p></td></tr><tr style="margin: 0px; padding: 0px;"><td width="111" style="margin: 0px; padding: 3px; border-style: solid; border-color: #c0c0c0; border-collapse: collapse;"><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; line-height: 1.8;">poll</p></td><td width="457" style="margin: 0px; padding: 3px; border-style: solid; border-color: #c0c0c0; border-collapse: collapse;"><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; line-height: 1.8;">同上</p></td></tr><tr style="margin: 0px; padding: 0px;"><td width="111" style="margin: 0px; padding: 3px; border-style: solid; border-color: #c0c0c0; border-collapse: collapse;"><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; line-height: 1.8;">epoll</p></td><td width="457" style="margin: 0px; padding: 3px; border-style: solid; border-color: #c0c0c0; border-collapse: collapse;"><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; line-height: 1.8;">因为epoll内核中实现是根据每个fd上的callback函数来实现的，只有活跃的socket才会主动调用callback，所以在活跃socket较少的情况下，使用epoll没有前面两者的线性下降的性能问题，但是所有socket都很活跃的情况下，可能会有性能问题。</p></td></tr></tbody></table><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; color: #333333; line-height: 25px; background-color: #ffffff; font-family: Georgia, 'Times new roman', Times, san-serif;">3、 消息传递方式</p><table border="1" cellpadding="0" cellspacing="0" style="margin: 0px; padding: 0px; color: #333333; font-size: 14px; border-style: solid; border-color: #c0c0c0; line-height: 25px; background-color: #ffffff; border-collapse: collapse; font-family: Georgia, 'Times new roman', Times, san-serif;"><tbody style="margin: 0px; padding: 0px;"><tr style="margin: 0px; padding: 0px;"><td width="111" style="margin: 0px; padding: 3px; border-style: solid; border-color: #c0c0c0; border-collapse: collapse;"><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; line-height: 1.8;">select</p></td><td width="457" style="margin: 0px; padding: 3px; border-style: solid; border-color: #c0c0c0; border-collapse: collapse;"><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; line-height: 1.8;">内核需要将消息传递到用户空间，都需要内核拷贝动作</p></td></tr><tr style="margin: 0px; padding: 0px;"><td width="111" style="margin: 0px; padding: 3px; border-style: solid; border-color: #c0c0c0; border-collapse: collapse;"><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; line-height: 1.8;">poll</p></td><td width="457" style="margin: 0px; padding: 3px; border-style: solid; border-color: #c0c0c0; border-collapse: collapse;"><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; line-height: 1.8;">同上</p></td></tr><tr style="margin: 0px; padding: 0px;"><td width="111" style="margin: 0px; padding: 3px; border-style: solid; border-color: #c0c0c0; border-collapse: collapse;"><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; line-height: 1.8;">epoll</p></td><td width="457" style="margin: 0px; padding: 3px; border-style: solid; border-color: #c0c0c0; border-collapse: collapse;"><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; line-height: 1.8;">epoll通过内核和用户空间共享一块内存来实现的。</p></td></tr></tbody></table><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; color: #333333; line-height: 25px; background-color: #ffffff; font-family: Georgia, 'Times new roman', Times, san-serif;"><strong style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; color: #ff0000;">总结：</span></strong><br style="margin: 0px; padding: 0px;" /></p><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; color: #333333; line-height: 25px; background-color: #ffffff; font-family: Georgia, 'Times new roman', Times, san-serif;">综上，在选择select，poll，epoll时要根据具体的使用场合以及这三种方式的自身特点。</p><p style="margin-top: 12px; margin-bottom: 12px; padding: 0px; color: #333333; line-height: 25px; background-color: #ffffff; font-family: Georgia, 'Times new roman', Times, san-serif;">1、表面上看epoll的性能最好，但是在连接数少并且连接都十分活跃的情况下，select和poll的性能可能比epoll好，毕竟epoll的通知机制需要很多函数回调。</p><p style="margin: 0px; padding: 0px; color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">2、<span style="margin: 0px; padding: 0px; color: #000000; background-color: #ffcc00;">select低效是因为每次它都需要轮询。但低效也是相对的，视情况而定，也可通过良好的设计改善</span></p><img src ="http://www.cppblog.com/cxiaojia/aggbug/198831.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/cxiaojia/" target="_blank">C小加</a> 2013-03-26 13:36 <a href="http://www.cppblog.com/cxiaojia/archive/2013/03/26/198831.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windowsSocket编程学习（3）：面向连接的Socket编程步骤</title><link>http://www.cppblog.com/cxiaojia/archive/2012/06/04/177444.html</link><dc:creator>C小加</dc:creator><author>C小加</author><pubDate>Mon, 04 Jun 2012 04:24:00 GMT</pubDate><guid>http://www.cppblog.com/cxiaojia/archive/2012/06/04/177444.html</guid><wfw:comment>http://www.cppblog.com/cxiaojia/comments/177444.html</wfw:comment><comments>http://www.cppblog.com/cxiaojia/archive/2012/06/04/177444.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/cxiaojia/comments/commentRss/177444.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/cxiaojia/services/trackbacks/177444.html</trackback:ping><description><![CDATA[<div><p style="text-indent:20.0pt"><span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">面向连接的</span>Socket<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">通信是基于</span>TCP<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">的。网络中的两个进程以客户机</span>/<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">服务器模式进行通信<br /><br /><strong>&nbsp; &nbsp;服务器程序要先于客户机程序启动，每个步骤中调用的</strong></span><strong>Socket</strong><strong><span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">函数如下：</span></strong></p>  <p style="text-indent:20.0pt"><span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">（</span>1<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">）调用</span>WSAStartup()<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">函数加载</span>Windows Sockets<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">动态库，然后调用</span>socket()<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">函数创建一个流式套接字，返回套接字号</span>s<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">。</span></p>  <p style="text-indent:20.0pt"><span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">（</span>2<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">）调用</span>bind()<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">函数将套接字</span>s<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">绑定到一个已知的地址，通常为本地</span>IP<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">地址。</span></p>  <p style="text-indent:20.0pt"><span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">（</span>3<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">）调用</span>listen()<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">函数将套接字</span>s<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">设置为侦听模式，准备好接收来自各个客户机的连接请求。</span></p>  <p style="text-indent:20.0pt"><span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">（</span>4<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">）调用</span>accept()<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">函数等待接受客户端的连接请求。</span></p>  <p style="text-indent:20.0pt"><span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">（</span>5<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">）如果接收到客户端的请求，则</span>accept()<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">函数返回，得到新的套接字</span>ns<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">。</span></p>  <p style="text-indent:20.0pt"><span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">（</span>6<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">）调用</span>recv()<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">函数接收来自客户端的数据，调用</span>send()<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">函数向客户端发送数据。</span></p>  <p style="text-indent:20.0pt"><span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">（</span>7<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">）与客户端的通信结束后，服务器程序可以调用</span>shutdown()<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">函数通知对方不再发送或接收数据，也可以由客户端程序断开连接。断开连接后，服务器进程调用</span>closesocket()<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">函数关闭套接字</span>ns<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">。此后服务器程序返回第</span>4<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">步，继续等待客户端进程的连接。</span></p>  <p style="text-indent:20.0pt"><span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">（</span>8<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">）如果要退出服务器程序，则调用</span>closesocket()<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">函数关闭最初的套接字</span>s<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">。</span></p>  <p style="text-indent:20.0pt"><strong><span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">客户端程序在每一步骤中使用的函数如下：</span></strong></p>  <p style="text-indent:20.0pt"><span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">（</span>1<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">）调用</span>WSAStartup()<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">函数加载</span>Windows Sockets<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">动态库，然后调用</span>socket()<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">函数创建一个流式套接字，返回套接字号</span>s<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">。</span></p>  <p style="text-indent:20.0pt"><span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">（</span>2<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">）调用</span>connect()<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">函数将套接字</span>s<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">连接到服务器。</span></p>  <p style="text-indent:20.0pt"><span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">（</span>3<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">）调用</span>send()<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">函数向服务器发送数据，调用</span>recv()<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">函数接收来自服务器的数据。</span></p>  <p style="text-indent:20.0pt"><span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">（</span>4<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">）与服务器的通信结束后，客户端程序可以调用</span>shutdown()<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">函数通知对方不再发送或接收数据，也可以由服务器程序断开连接。断开连接后，客户端进程调用</span>closesocket()<span style="font-family:宋体;Times New Roman&quot;;Times New Roman&quot;">函数关闭套接字。</span></p>  <p>&nbsp;</p></div><img src ="http://www.cppblog.com/cxiaojia/aggbug/177444.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/cxiaojia/" target="_blank">C小加</a> 2012-06-04 12:24 <a href="http://www.cppblog.com/cxiaojia/archive/2012/06/04/177444.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windowsSocket编程学习（2）：一个简单的Socket程序</title><link>http://www.cppblog.com/cxiaojia/archive/2012/05/21/175548.html</link><dc:creator>C小加</dc:creator><author>C小加</author><pubDate>Mon, 21 May 2012 01:14:00 GMT</pubDate><guid>http://www.cppblog.com/cxiaojia/archive/2012/05/21/175548.html</guid><wfw:comment>http://www.cppblog.com/cxiaojia/comments/175548.html</wfw:comment><comments>http://www.cppblog.com/cxiaojia/archive/2012/05/21/175548.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/cxiaojia/comments/commentRss/175548.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/cxiaojia/services/trackbacks/175548.html</trackback:ping><description><![CDATA[<div><p><span style="font-family:宋体;">通过一个控制台应用程序实例来演示初始化</span>Windows Sockets<span style="font-family:宋体;">并输出得到的版本信息。<br /></span><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->#include&lt;iostream&gt;<br />#include&lt;winsock2.h&gt;<span style="color: #008000; ">//</span><span style="color: #008000; ">使用Winsock&nbsp;2.2&nbsp;版本</span><span style="color: #008000; "><br /></span>#pragma&nbsp;comment&nbsp;(lib,"ws2_32.lib")<br />#include&lt;stdlib.h&gt;<br /><span style="color: #0000FF; ">using</span>&nbsp;<span style="color: #0000FF; ">namespace</span>&nbsp;std;<br /><br /><span style="color: #0000FF; ">int</span>&nbsp;main()<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;WSADATA&nbsp;wsadata;<span style="color: #008000; ">//</span><span style="color: #008000; ">WSADATA结构体中主要包含了系统所支持的Winsock版本信息</span><span style="color: #008000; "><br /></span><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>(&nbsp;WSAStartup(&nbsp;MAKEWORD(2,2),&amp;wsadata&nbsp;)!=0&nbsp;)<span style="color: #008000; ">//</span><span style="color: #008000; ">初始化Winsock&nbsp;2.2</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&lt;&lt;"WSAStartup无法初始化！"&lt;&lt;endl;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;0;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">使用Winsock实现网络通信<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">显示wsadata中的数据</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;printf("Version:&nbsp;%d.%d\n",LOBYTE(wsadata.wVersion),HIBYTE(wsadata.wVersion));<span style="color: #008000; ">//</span><span style="color: #008000; ">注释1</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;printf("High&nbsp;Version:&nbsp;%d.%d\n",&nbsp;LOBYTE(wsadata.wHighVersion),HIBYTE(wsadata.wHighVersion)&nbsp;);<span style="color: #008000; ">//</span><span style="color: #008000; ">注释2</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;printf("Description:&nbsp;%s\n",wsadata.szDescription&nbsp;);<span style="color: #008000; ">//</span><span style="color: #008000; ">注释3</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;printf("System&nbsp;Status:&nbsp;%s\n",wsadata.szSystemStatus&nbsp;);<span style="color: #008000; ">//</span><span style="color: #008000; ">注释4<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">最后应该做的清理工作</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>(WSACleanup()==SOCKET_ERROR)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("WSACleanup出错\n");<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;system("pause");<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;0;<br />}</div><span style="font-family:宋体;"><br /><img src="http://www.cppblog.com/images/cppblog_com/cxiaojia/QQ截图20120520102552.png" width="489" height="102" alt="" /><br /><br /></span><div><p>注释1：</p>  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 字段wVersion：Windows Sockets DLL期望调用者使用的Windows Sockets规范的版本，为WORD类型。高位字节中存储副版本号，地位字节中存储主版本号。调用LOBYTE()函数可以返回WORD类型数据的地位字节，从而获取主版本号；调用HIBYTE()函数可以返回WORD类型数据的高位字节，从而获取副版本号。</p>  <p>注释2：</p>  <p style="text-indent:21.0pt">字段wHighVersion：Windows Sockets DLL 可以支持的Windows Sockets 规范的最高版本。</p>  <p>注释3：</p>  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 字段szDescription：以null结尾的ASCLL字符串。Windows Sockets DLL将对Windows Sockets实现的描述复制到该字符串中，最多可以包含256个字符。</p>  <p>注释4：</p>  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 字段szSystemStatus：以null结尾的ASCLL字符串，Windows Sockets DLL将有关状态或配置信息复制到该字符串中。</p>  <p>&nbsp;</p>  <p>其他字段及其含义：</p>  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 字段iMaxSockets：单个进程可以打开的最大Socket数量。Windows Sockets可以提供一个全局的Socket，为每个进程分配Socket资源。程序员可以使用该数字作为Windows Sockets是否可以被应用程序使用的原始依据。</p>  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 字段iMaxUdpDg：Windows Sockets应用程序能够发送或接受的最大UDP数据包大小，单位为字节。如果实现方式没有限制，则iMaxUdpDg等于0。</p>  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 字段lpVendorInfo：指向销售商数据结构的指针。</p></div><span style="font-family:宋体;"><br /><br /><br /></span></p></div><img src ="http://www.cppblog.com/cxiaojia/aggbug/175548.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/cxiaojia/" target="_blank">C小加</a> 2012-05-21 09:14 <a href="http://www.cppblog.com/cxiaojia/archive/2012/05/21/175548.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windowsSocket编程学习（1）：Socket简介和应用程序框架</title><link>http://www.cppblog.com/cxiaojia/archive/2012/05/20/175432.html</link><dc:creator>C小加</dc:creator><author>C小加</author><pubDate>Sun, 20 May 2012 02:17:00 GMT</pubDate><guid>http://www.cppblog.com/cxiaojia/archive/2012/05/20/175432.html</guid><wfw:comment>http://www.cppblog.com/cxiaojia/comments/175432.html</wfw:comment><comments>http://www.cppblog.com/cxiaojia/archive/2012/05/20/175432.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/cxiaojia/comments/commentRss/175432.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/cxiaojia/services/trackbacks/175432.html</trackback:ping><description><![CDATA[<div><p><strong><span style="font-family:宋体;">一、</span>Socket</strong><strong><span style="font-family:宋体;">简单介绍</span></strong></p>  <p style="text-indent:21.0pt;">Socket<span style="font-family:宋体;">的中文翻译是套接字，它是</span>TCP/IP<span style="font-family:宋体;">网络环境下应用程序与底层通信驱动程序之间运行的开发接口，它可以将应用程序与具体的</span>TCP/IP<span style="font-family:宋体;">隔离开来，使得应用程序不需要了解</span>TCP/IP<span style="font-family:宋体;">的具体细节，就能够实现数据传输。</span></p>  <p style="text-indent:21.0pt;"><span style="font-family:宋体;">关于</span>Socket<span style="font-family:宋体;">需要了解的还有很多，我将在随后的章节里陆续写上。</span></p>  <p><strong><span style="font-family:宋体;">二、</span>Socket</strong><strong><span style="font-family:宋体;">应用程序框架</span></strong></p>  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-family:宋体;">这里先声明一下，我用的是</span>32<span style="font-family:宋体;">位</span>win7<span style="font-family:宋体;">系统，</span>vs2010<span style="font-family:宋体;">编译器。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">首先新建一个没有预编译头的</span>Win32<span style="font-family:宋体;">控制台应用程序。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">应用程序框架如下：<br /></span></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->#include&lt;iostream&gt;<br />#include&lt;winsock2.h&gt;<span style="color: #008000; ">//</span><span style="color: #008000; ">注释1</span><span style="color: #008000; "><br /></span>#pragma&nbsp;comment&nbsp;(lib,"ws2_32.lib")<span style="color: #008000; ">//</span><span style="color: #008000; ">注释1</span><span style="color: #008000; "><br /></span>#include&lt;stdlib.h&gt;<br /><span style="color: #0000FF; ">using</span>&nbsp;<span style="color: #0000FF; ">namespace</span>&nbsp;std;<br /><br /><span style="color: #0000FF; ">int</span>&nbsp;main()<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;WSADATA&nbsp;wsadata;<span style="color: #008000; ">//</span><span style="color: #008000; ">注释2</span><span style="color: #008000; "><br /></span><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>(&nbsp;WSAStartup(&nbsp;MAKEWORD(2,2),&amp;wsadata&nbsp;)!=0&nbsp;)<span style="color: #008000; ">//</span><span style="color: #008000; ">注释3</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("WSAStartup无法初始化！\n");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;0;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">使用Winsock实现网络通信<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; "><img src="http://www.cppblog.com/Images/dot.gif" alt="" /><img src="http://www.cppblog.com/Images/dot.gif" alt="" /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">最后应该做的清理工作</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>(WSACleanup()==SOCKET_ERROR)<span style="color: #008000; ">//</span><span style="color: #008000; ">注释4</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("WSACleanup出错\n");<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;system("pause");<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;0;<br />}</div><span style="font-family:宋体;"><br /></span><div><p>注释1：</p>  <p style="text-indent:21.0pt">我们在vs中一般使用Winsock2实现网络通信功能，需要引进头文件<span style="font-size:9.5pt;font-family:NSimSun;color:#A31515;">winsock2.h</span>和库文件<span style="font-size:9.5pt;font-family:NSimSun; color:#A31515;">ws2_32.lib</span>。</p>  <p>注释2：</p>  <p style="text-indent:21.0pt">WSADATA结构体中主要包含了系统所支持的Winsock版本信息。</p>  <p>注释3&#8220;</p>  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WSAStartup()函数用于初始化Windows Sockets，并返回WSADATA结构体。只有调用WSAStartup()函数后，应用程序才能调用其他Windows Sockets API函数，实现网络通信。</p>  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第一个参数是版本号，第二个参数用于接收版本信息。</p>  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果函数执行成功则会返回0。</p>  <p>注释4：</p>  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 最后应该做的一些清理工作。</p></div><span style="font-family:宋体;"><br /><br /></span><p>&nbsp;</p></div><img src ="http://www.cppblog.com/cxiaojia/aggbug/175432.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/cxiaojia/" target="_blank">C小加</a> 2012-05-20 10:17 <a href="http://www.cppblog.com/cxiaojia/archive/2012/05/20/175432.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>