﻿<?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++博客-小不点,努力学习中......-随笔分类-网络编程</title><link>http://www.cppblog.com/amyvmiwei/category/6011.html</link><description>        战胜自己就是战胜一切!
</description><language>zh-cn</language><lastBuildDate>Wed, 16 Mar 2011 04:37:48 GMT</lastBuildDate><pubDate>Wed, 16 Mar 2011 04:37:48 GMT</pubDate><ttl>60</ttl><item><title>[转]Flex Socket 与 C++ 通讯 --- 安全沙箱问题解决</title><link>http://www.cppblog.com/amyvmiwei/archive/2011/03/16/141942.html</link><dc:creator>小不点</dc:creator><author>小不点</author><pubDate>Wed, 16 Mar 2011 02:22:00 GMT</pubDate><guid>http://www.cppblog.com/amyvmiwei/archive/2011/03/16/141942.html</guid><wfw:comment>http://www.cppblog.com/amyvmiwei/comments/141942.html</wfw:comment><comments>http://www.cppblog.com/amyvmiwei/archive/2011/03/16/141942.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/amyvmiwei/comments/commentRss/141942.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/amyvmiwei/services/trackbacks/141942.html</trackback:ping><description><![CDATA[<div class=tit>Flex Socket 与 C++ 通讯 --- 安全沙箱问题解决</div>
<div id=blog_text class=cnt>
<p>最近一个项目的客户端要改成Flex，使用Socket与C++通讯时遇到了安全沙箱问题，这是我的解决方法；</p>
<p>1):策略文件与主套接字在同一端口，只需调用 Socket.connect() 或 XMLSocket.connect() 方法；</p>
<p>2):策略文件与主套接字在不同端口，需使用特殊的&#8220;xmlsocket&#8221;语法调用 Security.loadPolicyFile() 方法，如下所示：</p>
<p>Security.loadPolicyFile("xmlsocket://server.com:2525");</p>
<p>先调用 Security.loadPolicyFile() 方法，然后再调用 Socket.connect() 或 XMLSocket.connect() 方法。</p>
<p>测试代码：使用同一端口</p>
<p>view plaincopy to clipboardprint?<br>#include &lt;winsock2.h&gt;&nbsp;&nbsp; <br>#include &lt;windows.h&gt;&nbsp;&nbsp; <br>#include &lt;iostream&gt;&nbsp;&nbsp; <br>using namespace std;&nbsp;&nbsp; <br>#pragma comment(lib,"ws2_32.lib")&nbsp;&nbsp; <br><br>void main()&nbsp;&nbsp; <br>{&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; WORD wVersionRequested;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; WSADATA wsaData;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; int err;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; short port=1800;//端口号&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; wVersionRequested = MAKEWORD( 1, 1 );&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; err = WSAStartup( wVersionRequested, &amp;wsaData );//初始化套接字&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; if ( err != 0 )&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 )&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WSACleanup( );&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);//创建套接字&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; SOCKET sockConn;//用来和客户端通信的套接字&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; SOCKADDR_IN addrSrv;//用来和客户端通信的套接字地址&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; addrSrv.sin_family=AF_INET;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; addrSrv.sin_port=htons(port);&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; bind(sockSrv,(SOCKADDR*)&amp;addrSrv,sizeof(SOCKADDR));//绑定端口&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; listen(sockSrv,5);//侦听&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; printf("Server %d is listening......\n",port);&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; SOCKADDR_IN addrClient;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; int len=sizeof(SOCKADDR);&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; char buf[4096];//接收的数据&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; char rbuf[100]=&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "&lt;cross-domain-policy&gt; "&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "&lt;allow-access-from domain=\"*\" to-ports=\"*\"/&gt;"&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "&lt;/cross-domain-policy&gt; ";//套接字策略文件&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; while(1)&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //接受连接&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sockConn=accept(sockSrv,(SOCKADDR*)&amp;addrClient,&amp;len);&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("Accept connection from %s\n",inet_ntoa(addrClient.sin_addr));&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>recv:&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //接收数据&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int bytes;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if((bytes=recv(sockConn,buf,sizeof(buf),0))==SOCKET_ERROR)&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("接收数据失败!\n");&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(-1);&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; <br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf[bytes]='\0';&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("Message from %s: %s\n",inet_ntoa(addrClient.sin_addr),buf);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (0 == strcmp(buf,"&lt;policy-file-request/&gt;"))&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //发送数据&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(send(sockConn,rbuf,strlen(rbuf)+1,0)==SOCKET_ERROR)&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("发送数据失败！");&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(-1);&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("Message to %s: %s\n",inet_ntoa(addrClient.sin_addr),rbuf);&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Echo&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(send(sockConn,buf,strlen(buf)+1,0)==SOCKET_ERROR)&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("发送数据失败！");&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(-1);&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("Message to %s: %s\n",inet_ntoa(addrClient.sin_addr),buf);&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto recv;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //清理套接字占用的资源&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; closesocket(sockConn);&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; <br>} <br>#include &lt;winsock2.h&gt;<br>#include &lt;windows.h&gt;<br>#include &lt;iostream&gt;<br>using namespace std;<br>#pragma comment(lib,"ws2_32.lib")</p>
<p>void main()<br>{ <br>WORD wVersionRequested;<br>WSADATA wsaData;<br>int err;<br>short port=1800;//端口号<br><br>wVersionRequested = MAKEWORD( 1, 1 );<br>err = WSAStartup( wVersionRequested, &amp;wsaData );//初始化套接字<br>if ( err != 0 )<br>{<br>&nbsp;&nbsp; return;<br>}<br><br>if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 )<br>{<br>&nbsp;&nbsp; WSACleanup( );<br>&nbsp;&nbsp; return;<br>}<br><br>SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);//创建套接字<br>SOCKET sockConn;//用来和客户端通信的套接字<br>SOCKADDR_IN addrSrv;//用来和客户端通信的套接字地址<br>addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);<br>addrSrv.sin_family=AF_INET;<br>addrSrv.sin_port=htons(port);<br><br>bind(sockSrv,(SOCKADDR*)&amp;addrSrv,sizeof(SOCKADDR));//绑定端口<br>listen(sockSrv,5);//侦听<br><br>printf("Server %d is listening......\n",port);<br><br>SOCKADDR_IN addrClient;<br><br>int len=sizeof(SOCKADDR);<br>char buf[4096];//接收的数据<br>char rbuf[100]=<br>&nbsp;&nbsp; "&lt;cross-domain-policy&gt; " <br>&nbsp;&nbsp; "&lt;allow-access-from domain=\"*\" to-ports=\"*\"/&gt;" <br>&nbsp;&nbsp; "&lt;/cross-domain-policy&gt; ";//套接字策略文件<br><br>while(1)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //接受连接<br>&nbsp;&nbsp; sockConn=accept(sockSrv,(SOCKADDR*)&amp;addrClient,&amp;len);<br>&nbsp;&nbsp; printf("Accept connection from %s\n",inet_ntoa(addrClient.sin_addr));<br>&nbsp;&nbsp;<br>recv:<br>&nbsp;&nbsp; //接收数据<br>&nbsp;&nbsp; int bytes;<br>&nbsp;&nbsp; if((bytes=recv(sockConn,buf,sizeof(buf),0))==SOCKET_ERROR)<br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; printf("接收数据失败!\n");<br>&nbsp;&nbsp;&nbsp; exit(-1);<br>&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp; buf[bytes]='\0';<br>&nbsp;&nbsp; printf("Message from %s: %s\n",inet_ntoa(addrClient.sin_addr),buf);&nbsp;&nbsp;</p>
<p>&nbsp;&nbsp; if (0 == strcmp(buf,"&lt;policy-file-request/&gt;"))<br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; //发送数据<br>&nbsp;&nbsp;&nbsp; if(send(sockConn,rbuf,strlen(rbuf)+1,0)==SOCKET_ERROR)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp; printf("发送数据失败！");<br>&nbsp;&nbsp;&nbsp;&nbsp; exit(-1);<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; printf("Message to %s: %s\n",inet_ntoa(addrClient.sin_addr),rbuf);<br>&nbsp;&nbsp; }<br>&nbsp;&nbsp; else<br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; //Echo<br>&nbsp;&nbsp;&nbsp; if(send(sockConn,buf,strlen(buf)+1,0)==SOCKET_ERROR)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp; printf("发送数据失败！");<br>&nbsp;&nbsp;&nbsp;&nbsp; exit(-1);<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; printf("Message to %s: %s\n",inet_ntoa(addrClient.sin_addr),buf);<br>&nbsp;&nbsp;&nbsp; goto recv;<br>&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //清理套接字占用的资源<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; closesocket(sockConn);<br>}<br>}</p>
<p>&nbsp;</p>
<p>无论是哪种情况，服务器均必须等待客户端的第一次传输之后再决定是发送策略文件还是建立主连接。当 Flash Player 请求策略文件时，它始终会在建立连接后传输以下字符串：</p>
<p>&lt;policy-file-request/&gt;<br>服务器收到此字符串后，即会传输该策略文件。程序对于策略文件请求和主连接并不会使用同一连接，因此应在传输策略文件后关闭连接。如果不关闭连接，Flash Player 将关闭策略文件连接，之后重新连接以建立主连接。</p>
<p>附网络资料：</p>
<p>1,首先检测目标服务器的843端口是否提供安全策略<br>2,如果1没有检测到策略，则检测actionscript是否使用了Security.loadPolicyFile(xmlsocket://) 手段提供安全策略，如果还没检测到，则使用第3步检测<br>3,检测目标服务器目标端口是否提供安全策略</p>
<p>&nbsp;</p>
<p>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/xuxiangwin/archive/2009/07/07/4324218.aspx">http://blog.csdn.net/xuxiangwin/archive/2009/07/07/4324218.aspx</a></p>
</div>
<img src ="http://www.cppblog.com/amyvmiwei/aggbug/141942.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/amyvmiwei/" target="_blank">小不点</a> 2011-03-16 10:22 <a href="http://www.cppblog.com/amyvmiwei/archive/2011/03/16/141942.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转】完成端口的一个简单封装类 </title><link>http://www.cppblog.com/amyvmiwei/archive/2008/01/18/41439.html</link><dc:creator>小不点</dc:creator><author>小不点</author><pubDate>Fri, 18 Jan 2008 13:29:00 GMT</pubDate><guid>http://www.cppblog.com/amyvmiwei/archive/2008/01/18/41439.html</guid><wfw:comment>http://www.cppblog.com/amyvmiwei/comments/41439.html</wfw:comment><comments>http://www.cppblog.com/amyvmiwei/archive/2008/01/18/41439.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/amyvmiwei/comments/commentRss/41439.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/amyvmiwei/services/trackbacks/41439.html</trackback:ping><description><![CDATA[<p align=left><span>/////////////////////////////////////////////////////////////////////////////////////</span></p>
<p align=left><span>//&nbsp;&nbsp; Iocp </span><span>头文件</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>#pragma</span><span> <span>once</span></span></p>
<p align=left>&nbsp;</p>
<p align=left><span>#include</span><span> &lt;winsock2.h&gt;</span></p>
<p align=left><span>#pragma</span><span> <span>comment</span>( <span>lib</span>, "ws2_32.lib" )</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>const</span><span> <span>int</span> OP_READ = 0;</span></p>
<p align=left><span>const</span><span> <span>int</span> OP_WRITE = 1;</span></p>
<p align=left><span>const</span><span> <span>int</span> OP_ACCEPT = 2;</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>/*</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; OVERLAPPEDPLUS </span><span>结构体设计思路</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; OVERLAPPED </span><span>是一个固定的用于处理网络消息事件返回值的结构体变量</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>在完成端口和重叠</span><span>I/O</span><span>模型里用于返回消息事件的结果</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>因为在处理网络消息的时候，发送的是一个返回值的结构体指针，只要结构体</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>的前面部分满足系统的要求，在系统操作成功的时候也就会把这个结构体指针</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>发回给用户，我们只要在系统定义的结构体后面扩展一些自己的东西，就可以</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>很轻松的确定该消息是谁发过来的。</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>不过好像完成端口在设计的时候也满足了这样的需求，所以在这里我只是放入</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>一些与系统连接有关的数据，用户需要存放的数据这里就不在存放</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>这里存储与系统相关的数据有：</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; socket </span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; OpCode </span><span>本次消息的操作类型（在完成端口的操作里面，是以消息通知系统，</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>读数据</span><span>/</span><span>写数据，都是要发这样的消息结构体过去的，所以如果系统要同时</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>进行读写操作的话，就需要有一个变量来区分操作了）</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; WSABUF&nbsp;&nbsp; wbuf;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; </span><span>读写缓冲区结构体变量</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp;&nbsp; dwBytes, dwFlags;&nbsp;//&nbsp;&nbsp; </span><span>一些在读写时用到的标志性变量</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; char buf[4096];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; </span><span>自己的缓冲区</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>上面的</span><span>4</span><span>个变量存放的是一些与消息相关的数据，都是一些操作上用到的，</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>这些东西都是固定的，具体作用需要参考一下完成端口相关函数的参数接口</span></p>
<p align=left><span>*/</span></p>
<p align=left><span>struct</span><span> OVERLAPPEDPLUS</span></p>
<p align=left><span>{</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; OVERLAPPED&nbsp;&nbsp;&nbsp; ol;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; SOCKET&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>int</span> OpCode;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; WSABUF&nbsp;&nbsp; wbuf;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp;&nbsp; dwBytes, dwFlags;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>char</span> buf[4096];</span></p>
<p align=left><span>};</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>class</span><span> CIOCP</span></p>
<p align=left><span>{</span></p>
<p align=left><span>protected</span><span>:</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; HANDLE g_hwThread;&nbsp;&nbsp;&nbsp;&nbsp; <span>//&nbsp;&nbsp; </span></span><span>工作线程句柄</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; DWORD&nbsp;m_wthreadID;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; HANDLE g_haThread;&nbsp;&nbsp;&nbsp;&nbsp; <span>//&nbsp;&nbsp; </span></span><span>连接线程句柄</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; DWORD&nbsp;m_athreadID;</span></p>
<p align=left><span>public</span><span>:</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>bool</span> m_workThread;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>bool</span> m_acceptThread;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; HANDLE m_hIocp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//&nbsp;&nbsp; </span></span><span>完成端口的句柄</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; SOCKET m_sSocket;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></p>
<p align=left><span>public</span><span>:</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; CIOCP(<span>void</span>);</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; ~CIOCP(<span>void</span>);</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>virtual</span> <span>void</span> OnRead(<span>void</span> * p, <span>char</span> *buf, <span>int</span> len){};</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>virtual</span> <span>void</span> OnAccept(SOCKET socket);</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>virtual</span> <span>void</span> OnClose(<span>void</span> * p){};</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>bool</span> SetIoCompletionPort(SOCKET socket, <span>void</span> *p, <span>char</span> *buf = NULL, <span>int</span> len = 0);</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//&nbsp;&nbsp; </span></span><span>把一个</span><span>socket</span><span>与一个自定义的结构体关联到完成端口（相当于把</span><span>socket</span><span>与一个结构体变量进行绑定），</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//&nbsp;&nbsp; </span></span><span>这样当发送上面</span><span>3</span><span>种网络事件的时候，该结构体变量会再传回给程序</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//&nbsp;&nbsp; </span></span><span>这样就可以区分当前网络事件是那个</span><span>socket</span><span>发出的</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>bool</span> Init(<span>void</span>);</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>bool</span> Listen(<span>int</span> port);</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>static</span> DWORD <span>__stdcall</span> WorkThread(LPVOID Param);</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>static</span> DWORD <span>__stdcall</span> AcceptThread(LPVOID Param);</span></p>
<p align=left><span>};</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>class</span><span> CIOCPClient: <span>public</span> CIOCP</span></p>
<p align=left><span>{</span></p>
<p align=left><span>protected</span><span>:</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; SOCKET m_socket;</span></p>
<p align=left><span>public</span><span>:</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>bool</span> Connect(<span>char</span> *ip, <span>int</span> port);</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>void</span> Send(<span>char</span> *buf, <span>int</span> len);</span></p>
<p align=left><span>};</span></p>
<p align=left>&nbsp;</p>
<p align=left>&nbsp;</p>
<p align=left>&nbsp;</p>
<p align=left>&nbsp;</p>
<p align=left>&nbsp;</p>
<p align=left>&nbsp;</p>
<p align=left>&nbsp;</p>
<p align=left>&nbsp;</p>
<p align=left>&nbsp;</p>
<p align=left>&nbsp;</p>
<p align=left>&nbsp;</p>
<p align=left><span>//////////////////////////////////////////////////////////////////////////////////////////</span></p>
<p align=left><span>//&nbsp;&nbsp; Iocp </span><span>实现文件</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>#include</span><span> "StdAfx.h"</span></p>
<p align=left><span>#include</span><span> "iocp.h"</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>static</span><span> <span>bool</span> bInit = <span>false</span>;</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>DWORD <span>__stdcall</span> CIOCP::WorkThread(LPVOID Param)</span></p>
<p align=left><span>{</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; CIOCP * pthis = (CIOCP *)Param;</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>void</span> * re;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; OVERLAPPED * pOverlap;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; DWORD berByte;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>while</span>(pthis-&gt;m_workThread)</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>int</span> ret;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = GetQueuedCompletionStatus(pthis-&gt;m_hIocp, &amp;berByte, (LPDWORD)&amp;re, (LPOVERLAPPED *)&amp;pOverlap, INFINITE);</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>if</span> (ret == ERROR_SUCCESS)</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>if</span> (berByte == 0)</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//&nbsp;&nbsp; </span></span><span>客户端断开连接</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pthis-&gt;OnClose(re);</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OVERLAPPEDPLUS *olp = (OVERLAPPEDPLUS *)pOverlap;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; closesocket(olp-&gt;s);</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>delete</span> olp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//&nbsp;&nbsp; </span></span><span>释放</span><span> </span><span>与</span><span>socket</span><span>绑定的结构体变量</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>continue</span>;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>if</span> (re == NULL) <span>return</span> 0;</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OVERLAPPEDPLUS *olp = (OVERLAPPEDPLUS *)pOverlap;</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>switch</span>(olp-&gt;OpCode)</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>case</span> OP_READ:</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pthis-&gt;OnRead(re, olp-&gt;wbuf.buf, berByte);&nbsp;&nbsp;&nbsp;&nbsp; <span>//&nbsp;&nbsp; </span></span><span>调用</span><span> OnRead() </span><span>通知应用程序，服务器收到来自客户端的网络数据</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WSARecv(olp-&gt;s, &amp;olp-&gt;wbuf, 1, &amp;olp-&gt;dwBytes, &amp;olp-&gt;dwFlags, &amp;olp-&gt;ol, NULL);&nbsp;<span>//&nbsp;&nbsp; </span></span><span>继续调用一个接收的</span><span> I/O </span><span>异步请求</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>break</span>;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>default</span>:</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>break</span>;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>return</span> 0;</span></p>
<p align=left><span>}</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>DWORD <span>__stdcall</span> CIOCP::AcceptThread(LPVOID Param)</span></p>
<p align=left><span>{</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; CIOCP * pthis = (CIOCP *)Param;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>while</span>(pthis-&gt;m_acceptThread)</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SOCKET client;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>if</span> ((client= accept(pthis-&gt;m_sSocket, NULL, NULL)) == INVALID_SOCKET)</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//&nbsp;&nbsp; </span></span><span>错误处理</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pthis-&gt;OnAccept(client);&nbsp;&nbsp;&nbsp; <span>//&nbsp;&nbsp; </span></span><span>调用</span><span> OnAccept()</span><span>通知应用程序有新客户端连接</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>return</span> 1;</span></p>
<p align=left><span>}</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>CIOCP::CIOCP(<span>void</span>)</span></p>
<p align=left><span>{</span></p>
<p align=left><span>}</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>CIOCP::~CIOCP(<span>void</span>)</span></p>
<p align=left><span>{</span></p>
<p align=left><span>}</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>bool</span><span> CIOCP::Init(<span>void</span>)</span></p>
<p align=left><span>{</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>if</span> (bInit)</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>return</span> <span>true</span>;</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; WSADATA wsd;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>if</span> (WSAStartup(MAKEWORD(2,2), &amp;wsd) != 0) </span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>return</span> <span>false</span>; </span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; bInit = <span>true</span>;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>return</span> <span>true</span>;</span></p>
<p align=left><span>}</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>bool</span><span> CIOCP::Listen(<span>int</span> port)</span></p>
<p align=left><span>{</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>if</span> (!bInit)</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>if</span> (!Init())</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>return</span> <span>false</span>;</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; m_sSocket = socket(AF_INET, SOCK_STREAM, 0); </span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>if</span> (m_sSocket == INVALID_SOCKET) </span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>return</span> <span>false</span>; </span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>//SOCKADDR_IN addr; </span></span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; sockaddr_in addr;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; addr.sin_family = AF_INET; </span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; addr.sin_port = htons(port); </span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>//addr.sin_addr.S_un.S_addr = inet_addr(ip); </span></span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>if</span> (bind(m_sSocket, (<span>struct</span> sockaddr *)&amp;addr, <span>sizeof</span>(addr)) == SOCKET_ERROR) </span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>return</span> <span>false</span>; </span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>if</span> (listen(m_sSocket, 10) == SOCKET_ERROR) </span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>return</span> <span>false</span>; </span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>if</span> ((m_hIocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 0)) == NULL)&nbsp;&nbsp;&nbsp;&nbsp; <span>//&nbsp;&nbsp; </span></span><span>创建完成端口的句柄</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>return</span> <span>false</span>;</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>this</span>-&gt;m_acceptThread = <span>true</span>;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; g_haThread = CreateThread(NULL, 0, AcceptThread, (LPVOID)<span>this</span>, 0, &amp;m_athreadID);&nbsp;&nbsp;&nbsp; <span>//&nbsp;&nbsp; </span></span><span>创建连接线程，用来接收客户端的连接</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>this</span>-&gt;m_workThread = <span>true</span>;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; g_hwThread = CreateThread(NULL, 0, WorkThread, (LPVOID)<span>this</span>, 0, &amp;m_wthreadID); <span>//&nbsp;&nbsp; </span></span><span>创建工作线程，用来处理完成端口消息的</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>return</span> <span>true</span>;</span></p>
<p align=left><span>}</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>bool</span><span> CIOCP::SetIoCompletionPort(SOCKET socket, <span>void</span> *p, <span>char</span> *buf, <span>int</span> len)</span></p>
<p align=left><span>{</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>if</span> (CreateIoCompletionPort((HANDLE)socket, m_hIocp, (ULONG_PTR)p, 0) == NULL)</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>return</span> <span>false</span>;</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; OVERLAPPEDPLUS *olp = <span>new</span> OVERLAPPEDPLUS;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; memset(olp, 0, <span>sizeof</span>(OVERLAPPEDPLUS));</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; olp-&gt;s = socket;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>if</span> (buf)</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//&nbsp;&nbsp; </span></span><span>这里可以使用用户自定义的缓冲区地址，如果用户不想设置，也可以采用默认分配的缓冲区</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; olp-&gt;wbuf.buf = buf;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; olp-&gt;wbuf.len = len;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>else</span></span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; olp-&gt;wbuf.buf = olp-&gt;buf;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; olp-&gt;wbuf.len = 4096;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; olp-&gt;OpCode = OP_READ;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>int</span> ret = WSARecv(olp-&gt;s, &amp;olp-&gt;wbuf, 1, &amp;olp-&gt;dwBytes, &amp;olp-&gt;dwFlags, &amp;olp-&gt;ol, NULL);</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>if</span> (ret == SOCKET_ERROR)</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>if</span> (WSAGetLastError() != ERROR_IO_PENDING)</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>return</span> <span>false</span>;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>return</span> <span>true</span>;</span></p>
<p align=left><span>}</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>void</span><span> CIOCP::OnAccept(SOCKET socket)</span></p>
<p align=left><span>{</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>this</span>-&gt;SetIoCompletionPort(socket, NULL);</span></p>
<p align=left><span>}</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>//===================================================================================</span></p>
<p align=left><span>bool</span><span> CIOCPClient::Connect(<span>char</span> *ip, <span>int</span> port)</span></p>
<p align=left><span>{</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>//&nbsp;&nbsp; </span></span><span>连接服务器</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>if</span> (!bInit)</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>if</span> (!Init())</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>return</span> <span>false</span>;</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>//&nbsp;&nbsp; </span></span><span>初始化连接</span><span>socket</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>if</span> (m_socket == SOCKET_ERROR)</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p align=left><span>//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("cocket Create fail");</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>return</span> <span>false</span>;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>// </span></span><span>填写服务器地址信息</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>// </span></span><span>端口为</span><span>1982</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>// IP</span></span><span>地址为</span><span>INADDR_ANY</span><span>，注意使用</span><span>htonl</span><span>将</span><span>IP</span><span>地址转换为网络格式</span><span>ServerAddr.sin_family = AF_INET;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; sockaddr_in ClientAddr;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; ClientAddr.sin_family = AF_INET;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; ClientAddr.sin_port = htons(port);&nbsp;&nbsp;&nbsp; </span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; ClientAddr.sin_addr.s_addr = inet_addr(ip);</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>// </span></span><span>绑定监听端口</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; bind(m_socket, (SOCKADDR *)&amp;ClientAddr, <span>sizeof</span>(ClientAddr));</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>if</span> (connect(m_socket, (SOCKADDR *)&amp;ClientAddr, <span>sizeof</span>(ClientAddr)) == SOCKET_ERROR)</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>return</span> <span>false</span>;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>if</span> ((m_hIocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 0)) == NULL)&nbsp;&nbsp;&nbsp;&nbsp; <span>//&nbsp;&nbsp; </span></span><span>创建完成端口的句柄</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>return</span> <span>false</span>;</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>this</span>-&gt;m_workThread = <span>true</span>;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; g_hwThread = CreateThread(NULL, 0, WorkThread, (LPVOID)<span>this</span>, 0, &amp;m_wthreadID); <span>//&nbsp;&nbsp; </span></span><span>创建工作线程，用来处理完成端口消息的</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>this</span>-&gt;SetIoCompletionPort(m_socket, &amp;m_socket);&nbsp;&nbsp;&nbsp; <span>//&nbsp;&nbsp; </span></span><span>设置完成端口监听的</span><span>socket</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>return</span> <span>true</span>;</span></p>
<p align=left><span>}</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>void</span><span> CIOCPClient::Send(<span>char</span> *buf, <span>int</span> len)</span></p>
<p align=left><span>{</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; send(m_socket, buf, len, 0);</span></p>
<p align=left><span>}</span></p>
<p align=left>&nbsp;</p>
<p align=left>&nbsp;</p>
<p align=left><span>///////////////////////////////////////////////////////////////////////////////////</span></p>
<p align=left><span>// IOCPclient </span><span>应用代码</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>#include</span><span> "stdafx.h"</span></p>
<p align=left><span>#include</span><span> "IOCP.h"</span></p>
<p align=left><span>#include</span><span> "TClientSocket.h"</span></p>
<p align=left><span>class</span><span> Iocp :<span>public</span> CIOCPClient </span></p>
<p align=left><span>{</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<span>void</span> OnRead(<span>void</span> * p, <span>char</span> *buf, <span>int</span> len)</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;{</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;printf(buf);</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;Sleep(1000);</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<span>this</span>-&gt;Send(buf, len);</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;}</span></p>
<p align=left><span>};</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>int</span><span> _tmain(<span>int</span> argc, _TCHAR* argv[])</span></p>
<p align=left><span>{</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; Iocp iocp;</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; iocp.Init();</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; iocp.Connect("127.0.0.1", 4311);</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; iocp.Send("test\0", 5);</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; gets(<span>new</span> <span>char</span>[1000]);</span></p>
<p align=left><span>&nbsp;&nbsp;&nbsp;&nbsp; <span>return</span> 0;</span></p>
<p align=left><span>}</span></p>
<img src ="http://www.cppblog.com/amyvmiwei/aggbug/41439.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/amyvmiwei/" target="_blank">小不点</a> 2008-01-18 21:29 <a href="http://www.cppblog.com/amyvmiwei/archive/2008/01/18/41439.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转】完成端口与高性能服务器程序开发 </title><link>http://www.cppblog.com/amyvmiwei/archive/2008/01/18/41438.html</link><dc:creator>小不点</dc:creator><author>小不点</author><pubDate>Fri, 18 Jan 2008 13:27:00 GMT</pubDate><guid>http://www.cppblog.com/amyvmiwei/archive/2008/01/18/41438.html</guid><wfw:comment>http://www.cppblog.com/amyvmiwei/comments/41438.html</wfw:comment><comments>http://www.cppblog.com/amyvmiwei/archive/2008/01/18/41438.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/amyvmiwei/comments/commentRss/41438.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/amyvmiwei/services/trackbacks/41438.html</trackback:ping><description><![CDATA[<div class=postcontent style="FONT-FAMILY: Arial"><font size=2>早在两年前我就已经能很熟练的运用完成端口这种技术了,只是一直没有机会将它用在什么项目中,这段时间见到这种技术被过分炒作,过分的神秘化,就想写一篇解释它如何工作的文章.想告诉大家它没有传说中的那么高深难懂!有什么错误的地方还请高人指正.转载请注明出处及作者,谢谢!<br><br>以一个文件传输服务端为例,在我的机器上它只起两个线程就可以为很多个个客户端同时提供文件下载服务,程序的性能会随机器内CPU个数的增加而线性增长,我尽可能做到使它清晰易懂,虽然程序很小却用到了NT 5的一些新特性,重叠IO,完成端口以及线程池,基于这种模型的服务端程序应该是NT系统上性能最好的了.<br><br>首先.做为完成端口的基础,我们应该理解重叠IO,这需要你已经理解了内核对象及操作系统的一些概念概念,什么是信号/非信号态,什么是等待函数,什么是成功等待的副作用,什么是线程挂起等,如果这些概令还没有理解,你应该先看一下Windows 核心编程中的相关内容.如果已经理解这些,那么重叠IO对你来说并不难.<br><br>你可以这样认为重叠IO,现在你已经进入一个服务器/客户机环境,请不要混淆概念,这里的服务器是指操作系统,而客户机是指你的程序(它进行IO操作),是当你进行IO操作(send,recv,writefile,readfile....)时你发送一个IO请求给服务器(操作系统),由服务器来完成你需要的操作,然后你什么事都没有了,当服务器完成IO请求时它会通知你,当然在这期间你可以做任何事,一个常用的技巧是在发送重叠IO请求后,程序在一个循环中一边调用PeekMessage,TranslateMessage和DispatchMessage更新界面,同时调用GetOverlappedResult等待服务器完成IO操作,更高效一点的做法是使用IO完成例程来处理服务器(操作系统)返回的结果,但并不是每个支持重叠IO操作的函数都支持完成例程如TransmitFile函数.<br><br>例1.一次重叠写操作过程(GetOverlappedResult方法):<br>1.填写一个OVERLAPPED结构<br>2.进行一次写操作,并指定重叠操作参数(上面的OVERLAPPED结构变量的指针)<br>3.做其它事(如更新界面)<br>4.GetOverlappedResult取操作结果<br>5.如果IO请求没有完成,并且没有出错则回到期３<br>6.处理IO操作结果<br><br>例2.一次重叠写操作过程(完成例程方法):<br>1.填写一个OVERLAPPED结构<br>2.进行一次写操作,并指定重叠操作参数(上面的OVERLAPPED结构变量的指针),并指定完成例程<br>3.做其它事(如更新界面)<br>4.当完成例程被调用说明IO操作已经完成或出错,现在可以对操作结果进行处理了<br><br><br>如果你已经理解上面的概念,就已经很接近IO完成端口了,当然这只是很常规的重叠操作它已经非常高效,但如果再结合多线程对一个File或是Socket进行重叠IO操作就会非常复杂,通常程序员很难把握这种复杂度.完成端口可以说就是为了充分发挥多线程和重叠IO操作相结合的性能而设计的.很多人都说它复杂,其实如果你自己实现一个多线程的对一个File或是Socket进行重叠IO操作的程序(注意是多个线程对一个HANDLE或SOCKET进行重叠IO操作,而不是启一个线程对一个HANDLE进行重叠IO操作)就会发现完成端口实际上简化了多线程里使用重叠IO的复杂度,并且性能更高,性能高在哪?下面进行说明.<br><br>我们可能写过这样的服务端程序:<br><br>例3.主程序:<br>1.监听一个端口<br>2.等待连接<br>3.当有连接来时<br>4.启一个线程对这个客户端进行处理<br>5.回到2<br><br>服务线程:<br>1.读客户端请求<br>2.如果客户端不再有请求,执行6<br>3.处理请求<br>4.返回操作结果<br>5.回到1<br>6.退出线程<br><br>这是一种最简单的网络服务器模型,我们把它优化一下<br><br>例4.主程序:<br>1.开一个线程池,里面有机器能承受的最大线程数个线程,线程都处于挂起(suspend)状态<br>1.监听一个端口<br>2.等待连接<br>3.当有连接来时<br>4.从线程池里Resume一个线程对这个客户端进行处理<br>5.回到2<br><br>服务线程与例3模型里的相同,只是当线程处理完客户端所有请求后,不是退出而是回到线程池,再次挂起让出CPU时间,并等待为下一个客户机服务.当然在此期间线程会因为IO操作(服务线程的第1,5操作,也许还有其它阻塞操作)挂起自己,但不会回到线程池,也就是说它一次只能为一个客户端服务.<br><br>这可能是你能想到的最高效的服务端模型了吧!它与第一个服务端模型相比少了很多个用户态到内核态的CONTEXT Switch,反映也更加快速,也许你可能觉得这很微不足道,这说明你缺少对大规模高性能服务器程序(比如网游服务端)的认识,如果你的服务端程序要对几千万个客户端进行服务呢?这也是微软Windows NT开发组在NT 5以上的系统中添加线程池的原因.<br><br>思考一下什么样的模型可以让一个线程为多个客户端服务呢!那就要跳出每来一个连接启线程为其服务的固定思维模式,我们把线程服务的最小单元分割为单独的读或写操作(注意是读或写不是读和写),而不是一个客户端从连接到断开期间的所有读写操作.每个线程都使用重叠IO进行读写操作,投递了读写请求后线程回到线程池,等待为其它客户机服务,当操作完成或出错时再回来处理操作结果,然后再回到线程池.<br><br>看看这样的服务器模型:<br>例5.主程序:<br>1.开一个线程池,里面有机器内CPU个数两倍的线程,线程都处于挂起(suspend)状态,它们在都等处理一次重叠IO操作的完成结果<br>1.监听一个端口<br>2.等待连接<br>3.当有连接来时<br>4.投递一个重叠读操作读取命令<br>5.回到2<br><br>服务线程:<br>1.如果读完成,则处理读取的内容(如HTTP GET命令),否则执行3<br>2.投递一个重叠写操作(如返回HTTP GET命令需要的网页)<br>3.如果是一个写操作完成,可以再投递一个重叠读操作,读取客户机的下一个请求,或者是关闭连接(如HTTP协议里每发完一个网页就断开)<br>4.取得下一个重叠IO操作结果,如果IO操作没有完成或没有IO操作则回到线程池<br><br>假设这是一个WEB服务器程序,可以看到工作者线程是以读或写为最小的工作单元运行的,在主程序里面进行了一次重叠读操作<br><br>当读操作完成时一个线程池中的一个工作者线程被激活取得了操作结果,处理GET或POST命令,然后发送一个网页内容,发送也是一个重叠操作,然后处理对其它客户机的IO操作结果,如果没有其它的东西需要处理时回到线程池等待.可以看到使用这种模型发送和接收可以是也可以不是一个线程.<br><br>当发送操作完成时,线程池中的一个工作者线程池激活,它关闭连接(HTTP协议),然后处理其它的IO操作结果,如果没有其它的东西需要处理时回到线程池等待.<br><br>看看在这样的模型中一个线程怎么为多个客户端服务,同样是模拟一个WEB服务器例子:<br><br>假如现在系统中有两个线程,ThreadA,ThreadB它们在都等处理一次重叠IO操作的完成结果<br><br>当一个客户机ClientA连接来时主程序投递一个重叠读操作,然后等待下一个客户机连接,当读操作完成时ThreadA被激活,它收到一个HTTP GET命令,然后ThreadA使用重叠写操作发送一个网页给ClientA,然后立即回到线程池等待处理下一个IO操作结果,这时发送操作还没有完成,又有一个客户机ClientB连接来,主程序再投递一个重叠读操作,当读操作完成时ThreadA(当然也可能是ThreadB)再次被激活,它重复同样步骤,收到一个GET命令,使用重叠写操作发送一个网页给ClientB,这次它没有来得及回到线程池时,又有一个连接ClientC连入,主程序再投递一个重叠读操作,读操作完成时ThreadB被激活(因为ThreadA还没有回到线程池)它收到一个HTTP GET命令,然后ThreadB使用重叠写操作发送一个网页给ClientC,然后ThreadB回到线程池,这时ThreadA也回到了线程池.<br><br>可以想象现在有三个挂起的发送操作分别是ThreadA发送给ClientA和ClientB的网页,以及ThreadB发送给ClientC的网页,它们由操作系统内核来处理.ThreadA和ThreadB现在已经回到线程池,可以继续为其它任何客户端服务.<br><br>当对ClientA的重叠写操作已经完成,ThreadA(也可以是ThreadB)又被激活它关闭与ClientA连接,但还没有回到线程池,与此同时发送给ClientB的重叠写操作也完成,ThreadB被激活(因为ThreadA还没有回到线程池)它关闭与ClientB的连接,然后回到线程池,这时ClientC的写操作也完成,ThreadB再次被激活(因为ThreadA还是没有回到线程池),它再关闭与ClientC的连接,这时ThreadA回到线程池,ThreadB也回到线程池.这时对三个客户端的服务全部完成.可以看到在整个服务过程中,"建立连接","读数据","写数据"和"关闭连接"等操作是逻辑上连续而实际上分开的.<br><br>到现在为止两个线程处理了三次读操作和三次写操作,在这些读写操作过程中所出现的状态机(state machine)是比较复杂的,我们模拟的是经过我简化过的,实际上的状态要比这个还要复杂很多,然而这样的服务端模型在客户端请求越多时与前两个模型相比的性能越高.而使用完成端口我们可以很容易实现这样的服务器模型.<br><br>微软的IIS WEB服务器就是使用这样的服务端模型,很多人说什么阿帕奇服务器比IIS的性能好什么什么的我表示怀疑,除非阿帕奇服务器可以将线程分割成,为更小的单元服务,我觉得不太可能!这种完成端口模型已经将单个读或写操作作为最小的服务单元,我觉得在相同机器配置的情况下IIS的性能要远远高于其它WEB服务器,这也是从实现机理上来分析的,如果出现性能上的差别可能是在不同的操作系统上,也许Linux的内核比Windows的要好,有人真的研究过吗?还是大家一起在炒作啊.<br><br>对于状态机概念,在很多方面都用到,TCPIP中有,编译原理中有,OpengGL中有等等,我的离散数学不好(我是会计专业不学这个),不过还是搞懂了些,我想如果你多花些时间看,还是可以搞懂的.最后是一个简单的文件传输服务器程序代码,只用了两个线程(我的机器里只有一块CPU)就可以服务多个客户端.我调试时用它同时为6个nc客户端提供文件下载服务都没有问题,当然更多也不会有问题,只是略为使用了一下NT 5的线程池和完成端口技术就可以有这样高的性能,更不用说IIS的性能咯!<br><br>希望大家不要陷在这个程序的框架中,Ctrl+C,Ctrl+V没有什么意义,要理解它的实质.程序使用Visual C++ 6.0 SP5+2003 Platform SDK编译通过,在Windows XP Professional下调试运行通过.程序运行的最低要求是Windows 2000操作系统.<br><br>/********************************************************************<br>&nbsp; created: &nbsp; 2005/12/24<br>&nbsp; created: &nbsp; 24:12:2005 &nbsp; 20:25<br>&nbsp; modified: &nbsp; 2005/12/24<br>&nbsp; filename: &nbsp; d:\vcwork\iocomp\iocomp.cpp<br>&nbsp; file path: &nbsp; d:\vcwork\iocomp<br>&nbsp; file base: &nbsp; iocomp<br>&nbsp; file ext: &nbsp; cpp<br>&nbsp; author: &nbsp; &nbsp; kruglinski(kruglinski_at_gmail_dot_com)<br>&nbsp; <br>&nbsp; purpose: &nbsp; 利用完成端口技术实现的高性能文件下载服务程序<br>*********************************************************************/<br><br>#define _WIN32_WINNT &nbsp; 0x0500<br><br>#include &lt;cstdlib&gt;<br>#include &lt;clocale&gt;<br>#include &lt;ctime&gt;<br>#include &lt;iostream&gt;//一使用输入输出流程序顿时增大70K<br>#include &lt;vector&gt;<br>#include &lt;algorithm&gt;<br>#include &lt;winsock2.h&gt;<br>#include &lt;mswsock.h&gt;<br><br>using namespace std;<br><br>#pragma comment(lib,"ws2_32.lib")<br>#pragma comment(lib,"mswsock.lib")<br><br>const int MAX_BUFFER_SIZE=1024;<br>const int PRE_SEND_SIZE=1024;<br>const int QUIT_TIME_OUT=3000;<br>const int PRE_DOT_TIMER=QUIT_TIME_OUT/80;<br><br>typedef enum{IoTransFile,IoSend,IoRecv,IoQuit} IO_TYPE;<br><br>typedef struct<br>{<br>&nbsp; SOCKET hSocket;<br>&nbsp; SOCKADDR_IN ClientAddr;<br>}PRE_SOCKET_DATA,*PPRE_SOCKET_DATA;<br><br>typedef struct<br>{<br>&nbsp; OVERLAPPED &nbsp; oa;<br>&nbsp; WSABUF &nbsp; &nbsp; DataBuf;<br>&nbsp; char &nbsp; &nbsp; Buffer[MAX_BUFFER_SIZE];<br>&nbsp; IO_TYPE &nbsp; &nbsp; IoType;<br>}PRE_IO_DATA,*PPRE_IO_DATA;<br><br>typedef vector&lt;PPRE_SOCKET_DATA&gt; &nbsp; SocketDataVector;<br>typedef vector&lt;PPRE_IO_DATA&gt; &nbsp; &nbsp; IoDataVector;<br><br>SocketDataVector &nbsp; gSockDataVec;<br>IoDataVector &nbsp; &nbsp; gIoDataVec;<br><br>CRITICAL_SECTION &nbsp; csProtection;<br><br>char* TimeNow(void)<br>{<br>&nbsp; time_t t=time(NULL);<br>&nbsp; tm *localtm=localtime(&amp;t);<br>&nbsp; static char timemsg[512]={0};<br>&nbsp; <br>&nbsp; strftime(timemsg,512,"%Z: %B %d %X,%Y",localtm);<br>&nbsp; return timemsg;<br>}<br><br>BOOL TransFile(PPRE_IO_DATA pIoData,PPRE_SOCKET_DATA pSocketData,DWORD dwNameLen)<br>{<br>&nbsp; //这一句是为nc做的,你可以修改它<br>&nbsp; pIoData-&gt;Buffer[dwNameLen-1]='\0';<br>&nbsp; <br>&nbsp; HANDLE hFile=CreateFile(pIoData-&gt;Buffer,GENERIC_READ,0,NULL,OPEN_EXISTING,0,NULL);<br>&nbsp; BOOL bRet=FALSE;<br><br>&nbsp; if(hFile!=INVALID_HANDLE_VALUE)<br>&nbsp; {<br>&nbsp; &nbsp; cout&lt;&lt;"Transmit File "&lt;&lt;pIoData-&gt;Buffer&lt;&lt;" to client"&lt;&lt;endl;<br>&nbsp; &nbsp; pIoData-&gt;IoType=IoTransFile;<br>&nbsp; &nbsp; memset(&amp;pIoData-&gt;oa,0,sizeof(OVERLAPPED));<br>&nbsp; &nbsp; *reinterpret_cast&lt;HANDLE*&gt;(pIoData-&gt;Buffer)=hFile;<br>&nbsp; &nbsp; TransmitFile(pSocketData-&gt;hSocket,hFile,GetFileSize(hFile,NULL),PRE_SEND_SIZE,reinterpret_cast&lt;LPOVERLAPPED&gt;(pIoData),NULL,TF_USE_SYSTEM_THREAD);<br>&nbsp; &nbsp; bRet=WSAGetLastError()==WSA_IO_PENDING;<br>&nbsp; }<br>&nbsp; else<br>&nbsp; &nbsp; cout&lt;&lt;"Transmit File "&lt;&lt;"Error:"&lt;&lt;GetLastError()&lt;&lt;endl;<br><br>&nbsp; return bRet;<br>}<br><br>DWORD WINAPI ThreadProc(LPVOID IocpHandle)<br>{<br>&nbsp; DWORD dwRecv=0;<br>&nbsp; DWORD dwFlags=0;<br>&nbsp; <br>&nbsp; HANDLE hIocp=reinterpret_cast&lt;HANDLE&gt;(IocpHandle);<br>&nbsp; DWORD dwTransCount=0;<br>&nbsp; PPRE_IO_DATA pPreIoData=NULL;<br>&nbsp; PPRE_SOCKET_DATA pPreHandleData=NULL;<br><br>&nbsp; while(TRUE)<br>&nbsp; {<br>&nbsp; &nbsp; if(GetQueuedCompletionStatus(hIocp,&amp;dwTransCount,<br>&nbsp; &nbsp; &nbsp; &nbsp; reinterpret_cast&lt;LPDWORD&gt;(&amp;pPreHandleData),<br>&nbsp; &nbsp; &nbsp; &nbsp; reinterpret_cast&lt;LPOVERLAPPED*&gt;(&amp;pPreIoData),INFINITE))<br>&nbsp; &nbsp; {<br>&nbsp; &nbsp; &nbsp; &nbsp; if(0==dwTransCount&amp;&amp;IoQuit!=pPreIoData-&gt;IoType)<br>&nbsp; &nbsp; &nbsp; &nbsp; {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cout&lt;&lt;"Client:"<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;&lt;inet_ntoa(pPreHandleData-&gt;ClientAddr.sin_addr)<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;&lt;":"&lt;&lt;ntohs(pPreHandleData-&gt;ClientAddr.sin_port)<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;&lt;" is closed"&lt;&lt;endl;<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; closesocket(pPreHandleData-&gt;hSocket);<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; EnterCriticalSection(&amp;csProtection);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; IoDataVector::iterator itrIoDelete=find(gIoDataVec.begin(),gIoDataVec.end(),pPreIoData);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; gIoDataVec.erase(itrIoDelete);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SocketDataVector::iterator itrSockDelete=find(gSockDataVec.begin(),gSockDataVec.end(),pPreHandleData);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; gSockDataVec.erase(itrSockDelete);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; LeaveCriticalSection(&amp;csProtection);<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; delete *itrIoDelete;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; delete *itrSockDelete;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue;<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br>&nbsp; &nbsp; &nbsp; &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; switch(pPreIoData-&gt;IoType){<br>&nbsp; &nbsp; &nbsp; &nbsp; case IoTransFile:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cout&lt;&lt;"Client:"<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;&lt;inet_ntoa(pPreHandleData-&gt;ClientAddr.sin_addr)<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;&lt;":"&lt;&lt;ntohs(pPreHandleData-&gt;ClientAddr.sin_port)<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;&lt;" Transmit finished"&lt;&lt;endl;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CloseHandle(*reinterpret_cast&lt;HANDLE*&gt;(pPreIoData-&gt;Buffer));<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; goto LRERECV;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; case IoSend:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cout&lt;&lt;"Client:"<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;&lt;inet_ntoa(pPreHandleData-&gt;ClientAddr.sin_addr)<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;&lt;":"&lt;&lt;ntohs(pPreHandleData-&gt;ClientAddr.sin_port)<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;&lt;" Send finished"&lt;&lt;endl;<br><br>LRERECV:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pPreIoData-&gt;IoType=IoRecv;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pPreIoData-&gt;DataBuf.len=MAX_BUFFER_SIZE;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; memset(&amp;pPreIoData-&gt;oa,0,sizeof(OVERLAPPED));<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; WSARecv(pPreHandleData-&gt;hSocket,&amp;pPreIoData-&gt;DataBuf,1,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &amp;dwRecv,&amp;dwFlags,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; reinterpret_cast&lt;LPWSAOVERLAPPED&gt;(pPreIoData),NULL);<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;<br><br>&nbsp; &nbsp; &nbsp; &nbsp; case IoRecv:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cout&lt;&lt;"Client:"<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;&lt;inet_ntoa(pPreHandleData-&gt;ClientAddr.sin_addr)<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;&lt;":"&lt;&lt;ntohs(pPreHandleData-&gt;ClientAddr.sin_port)<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;&lt;" recv finished"&lt;&lt;endl;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pPreIoData-&gt;IoType=IoSend;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(!TransFile(pPreIoData,pPreHandleData,dwTransCount))<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; memset(&amp;pPreIoData-&gt;oa,0,sizeof(OVERLAPPED));<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; strcpy(pPreIoData-&gt;DataBuf.buf,"File transmit error!\r\n");<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pPreIoData-&gt;DataBuf.len=strlen(pPreIoData-&gt;DataBuf.buf);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; WSASend(pPreHandleData-&gt;hSocket,&amp;pPreIoData-&gt;DataBuf,1,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &amp;dwRecv,dwFlags,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; reinterpret_cast&lt;LPWSAOVERLAPPED&gt;(pPreIoData),NULL);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; case IoQuit:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; goto LQUIT;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; default:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ;<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br>&nbsp; &nbsp; } &nbsp; <br>&nbsp; }<br>&nbsp; <br>LQUIT:<br>&nbsp; return 0;<br>}<br><br>HANDLE hIocp=NULL;<br>SOCKET hListen=NULL;<br><br>BOOL WINAPI ShutdownHandler(DWORD dwCtrlType)<br>{<br>&nbsp; PRE_SOCKET_DATA PreSockData={0};<br>&nbsp; PRE_IO_DATA PreIoData={0};<br><br>&nbsp; PreIoData.IoType=IoQuit;<br><br>&nbsp; if(hIocp)<br>&nbsp; {<br>&nbsp; &nbsp; PostQueuedCompletionStatus(hIocp,1,<br>&nbsp; &nbsp; &nbsp; &nbsp; reinterpret_cast&lt;ULONG_PTR&gt;(&amp;PreSockData),<br>&nbsp; &nbsp; &nbsp; &nbsp; reinterpret_cast&lt;LPOVERLAPPED&gt;(&amp;PreIoData));<br><br>&nbsp; &nbsp; cout&lt;&lt;"Shutdown at "&lt;&lt;TimeNow()&lt;&lt;endl&lt;&lt;"wait for a moment please"&lt;&lt;endl;<br>&nbsp; &nbsp; <br>&nbsp; &nbsp; //让出CPU时间,让线程退出<br>&nbsp; &nbsp; for(int t=0;t&lt;80;t+=1)<br>&nbsp; &nbsp; {<br>&nbsp; &nbsp; &nbsp; &nbsp; Sleep(PRE_DOT_TIMER);<br>&nbsp; &nbsp; &nbsp; &nbsp; cout&lt;&lt;".";<br>&nbsp; &nbsp; }<br>&nbsp; &nbsp; <br>&nbsp; &nbsp; CloseHandle(hIocp);<br>&nbsp; }<br>&nbsp; <br>&nbsp; int i=0;<br><br>&nbsp; for(;i&lt;gSockDataVec.size();i++)<br>&nbsp; {<br>&nbsp; &nbsp; PPRE_SOCKET_DATA pSockData=gSockDataVec[i];<br>&nbsp; &nbsp; closesocket(pSockData-&gt;hSocket);<br>&nbsp; &nbsp; delete pSockData;<br>&nbsp; }<br><br>&nbsp; for(i=0;i&lt;gIoDataVec.size();i++)<br>&nbsp; {<br>&nbsp; &nbsp; PPRE_IO_DATA pIoData=gIoDataVec[i];<br>&nbsp; &nbsp; delete pIoData;<br>&nbsp; }<br><br>&nbsp; DeleteCriticalSection(&amp;csProtection);<br>&nbsp; if(hListen)<br>&nbsp; &nbsp; closesocket(hListen);<br><br>&nbsp; WSACleanup();<br>&nbsp; exit(0);<br>&nbsp; return TRUE;<br>}<br><br>LONG WINAPI MyExceptionFilter(struct _EXCEPTION_POINTERS *ExceptionInfo)<br>{<br>&nbsp; ShutdownHandler(0);<br>&nbsp; return EXCEPTION_EXECUTE_HANDLER;<br>}<br><br>u_short DefPort=8182;<br><br>int main(int argc,char **argv)<br>{<br>&nbsp; if(argc==2)<br>&nbsp; &nbsp; DefPort=atoi(argv[1]);<br><br>&nbsp; InitializeCriticalSection(&amp;csProtection);<br>&nbsp; SetUnhandledExceptionFilter(MyExceptionFilter);<br>&nbsp; SetConsoleCtrlHandler(ShutdownHandler,TRUE);<br><br>&nbsp; hIocp=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);<br><br>&nbsp; WSADATA data={0};<br>&nbsp; WSAStartup(0x0202,&amp;data);<br><br>&nbsp; hListen=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);<br>&nbsp; if(INVALID_SOCKET==hListen)<br>&nbsp; {<br>&nbsp; &nbsp; ShutdownHandler(0);<br>&nbsp; }<br>&nbsp; <br>&nbsp; SOCKADDR_IN addr={0};<br>&nbsp; addr.sin_family=AF_INET;<br>&nbsp; addr.sin_port=htons(DefPort);<br>&nbsp; <br>&nbsp; if(bind(hListen,reinterpret_cast&lt;PSOCKADDR&gt;(&amp;addr),<br>&nbsp; &nbsp; sizeof(addr))==SOCKET_ERROR)<br>&nbsp; {<br>&nbsp; &nbsp; ShutdownHandler(0);<br>&nbsp; }<br>&nbsp; <br>&nbsp; if(listen(hListen,256)==SOCKET_ERROR)<br>&nbsp; &nbsp; ShutdownHandler(0);<br><br>&nbsp; SYSTEM_INFO si={0};<br>&nbsp; GetSystemInfo(&amp;si);<br>&nbsp; si.dwNumberOfProcessors&lt;&lt;=1;<br><br>&nbsp; for(int i=0;i&lt;si.dwNumberOfProcessors;i++)<br>&nbsp; {<br>&nbsp; &nbsp; <br>&nbsp; &nbsp; QueueUserWorkItem(ThreadProc,hIocp,WT_EXECUTELONGFUNCTION);<br>&nbsp; }<br>&nbsp; <br>&nbsp; cout&lt;&lt;"Startup at "&lt;&lt;TimeNow()&lt;&lt;endl<br>&nbsp; &nbsp; &lt;&lt;"work on port "&lt;&lt;DefPort&lt;&lt;endl<br>&nbsp; &nbsp; &lt;&lt;"press CTRL+C to shutdown"&lt;&lt;endl&lt;&lt;endl&lt;&lt;endl;<br><br>&nbsp; while(TRUE)<br>&nbsp; {<br>&nbsp; &nbsp; int namelen=sizeof(addr);<br>&nbsp; &nbsp; memset(&amp;addr,0,sizeof(addr));<br>&nbsp; &nbsp; SOCKET hAccept=accept(hListen,reinterpret_cast&lt;PSOCKADDR&gt;(&amp;addr),&amp;namelen);<br><br>&nbsp; &nbsp; if(hAccept!=INVALID_SOCKET)<br>&nbsp; &nbsp; {<br>&nbsp; &nbsp; &nbsp; &nbsp; cout&lt;&lt;"accept a client:"&lt;&lt;inet_ntoa(addr.sin_addr)&lt;&lt;":"&lt;&lt;ntohs(addr.sin_port)&lt;&lt;endl;<br><br>&nbsp; &nbsp; &nbsp; &nbsp; PPRE_SOCKET_DATA pPreHandleData=new PRE_SOCKET_DATA;<br>&nbsp; &nbsp; &nbsp; &nbsp; pPreHandleData-&gt;hSocket=hAccept;<br>&nbsp; &nbsp; &nbsp; &nbsp; memcpy(&amp;pPreHandleData-&gt;ClientAddr,&amp;addr,sizeof(addr));<br>&nbsp; &nbsp; &nbsp; &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; CreateIoCompletionPort(reinterpret_cast&lt;HANDLE&gt;(hAccept),<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; hIocp,reinterpret_cast&lt;DWORD&gt;(pPreHandleData),0);<br>&nbsp; &nbsp; &nbsp; &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; PPRE_IO_DATA pPreIoData=new(nothrow) PRE_IO_DATA; <br><br>&nbsp; &nbsp; &nbsp; &nbsp; if(pPreIoData)<br>&nbsp; &nbsp; &nbsp; &nbsp; {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; EnterCriticalSection(&amp;csProtection);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; gSockDataVec.push_back(pPreHandleData);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; gIoDataVec.push_back(pPreIoData);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; LeaveCriticalSection(&amp;csProtection);<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; memset(pPreIoData,0,sizeof(PRE_IO_DATA));<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pPreIoData-&gt;IoType=IoRecv;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pPreIoData-&gt;DataBuf.len=MAX_BUFFER_SIZE;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pPreIoData-&gt;DataBuf.buf=pPreIoData-&gt;Buffer;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; DWORD dwRecv=0;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; DWORD dwFlags=0;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; WSARecv(hAccept,&amp;pPreIoData-&gt;DataBuf,1,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &amp;dwRecv,&amp;dwFlags,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; reinterpret_cast&lt;WSAOVERLAPPED*&gt;(pPreIoData),NULL);<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br>&nbsp; &nbsp; &nbsp; &nbsp; else<br>&nbsp; &nbsp; &nbsp; &nbsp; {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; delete pPreHandleData;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; closesocket(hAccept);<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br>&nbsp; &nbsp; }<br>&nbsp; }<br>&nbsp; <br>&nbsp; return 0;<br>}<br><br>参考资料:<br>《MSDN 2001》<br>《Windows 网络编程》<br>《Windows 核心编程》<br>《TCP/IP详解》</font><br></div>
<img src ="http://www.cppblog.com/amyvmiwei/aggbug/41438.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/amyvmiwei/" target="_blank">小不点</a> 2008-01-18 21:27 <a href="http://www.cppblog.com/amyvmiwei/archive/2008/01/18/41438.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>socket编程—技术实现 </title><link>http://www.cppblog.com/amyvmiwei/archive/2008/01/17/41375.html</link><dc:creator>小不点</dc:creator><author>小不点</author><pubDate>Thu, 17 Jan 2008 14:22:00 GMT</pubDate><guid>http://www.cppblog.com/amyvmiwei/archive/2008/01/17/41375.html</guid><wfw:comment>http://www.cppblog.com/amyvmiwei/comments/41375.html</wfw:comment><comments>http://www.cppblog.com/amyvmiwei/archive/2008/01/17/41375.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/amyvmiwei/comments/commentRss/41375.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/amyvmiwei/services/trackbacks/41375.html</trackback:ping><description><![CDATA[<font size=2>什么是socket？socket就是...，我在这里就不抄书了，有兴趣的同仁去查查书吧。<br>不过还要说一句，socket就是不同进程之间的一种通信方式。就象打电话是朋友之间的一种通信方式是一样。个人理解：所谓&#8220;通信&#8221;，就是相互之间发送数据。有人理解socket是不同计算机之间的一种通信方<br>式，这是不确切的。两个进程，不管是运行在同一台计算机上，还是运行在不同计算机上，都可通过<br>socket技术进行通信。<br><br>socket套接字的使用需要有网卡的支持，所以socket一般都被用来在不同机器之间通信，而如果在同一台计算机上的两个进程进行通信，通常采用效率更高的共享内存技术来实现。<br><br>两个进程之间进行通讯，就需要两个进程同时都在运行了（废话），在具体实现中，两个进程我们通常要区别对待，一个进程专门等待另一个进程给自己发消息，收到消息后进行处理，在把处理结果发送回去。我们把专门处理消息、提供服务的进程称为服务器端，把发送消息、请求处理的进程称为客户端。总体过程就是客户端发送一个消息给服务器端，服务器端进程收到消息进行处理，把处理结果发送给客户端。恩，就是这样。<br><br>还有一个问题，如果我现在有一个进程要跟另一台计算机上的某个进程进行socket通信，那在我这个进程中如何指定另一个进程呢？这里还需要说一下另一个概念——端口，如果把操作系统比作一座房子的话，那端口就是房子的窗口，是系统外界同系统内部进行通信的通道。在socket实现中，我们不进行另一个进程的指定，而是指定发送消息或接收消息的端口号。比如说现在进程A要给进程B发消息，我们会把消息发送到进程B所运行的计算机的端口N上，而进程B此时正在监视端口N，这样进程B就能收到进程A发送来的数据，同样进程B也把消息发送到该端口上，进程A也能从该端口收到进程B发送来的数据，当然，这需要客户端和服务器端关于端口号进行一个约定，即共同操作同一个端口。如果客户端把消息发送到端口N1上，而服务器端监视的是端口N2，那通信一定不能成功。端口号最大为65535，不能比这个再大了，但在我们自己的程序中尽量不要用小于1024的端口号，小于1024的端口好很多都被系统使用了，比如23被telnet所使用。<br><br>socket的实现是很简单的，只要按照一定的步骤，就可马上建立一个这样的通信通道。<br><br>下面较详细的介绍几个核心的函数：<br><br>SOCKET socket(int af, int type, int protocol);<br>无论是客户端还是服务器端，下面这个函数是一定要用到的，也是最先用到的。<br>这个函数是要告诉系统，给我准备好一个socket通道，我要和其它进程通信了。函数的返回值很重要，我们要记下来，它表示系统为我们准备好的这个socket通道，在以后的每个socket相关函数中都会用到，如果这个值等于SOCKET_ERROR，表示函数执行失败了。函数的参数我们分别给：PF_INET、SOCK_STREAM和IPPROTO_TCP。<br><br>int bind(SOCKET s, const sockaddr *addr, int namelen);<br>这个函数只有服务器端程序使用，作用是与某个socket通道绑定。可以用返回值判断该函数执行结果怎么样，如果等于SOCKET_ERROR，那就是失败了。第一个参数s，就是socket()函数的返回值；在结构addr中，我们要给定一个端口号；namelen等于结构sockaddr的大小。<br><br>int listen(SOCKET s, int backlog);<br>这个函数只有服务器端程序使用，作用是监听该端口。返回值与bind函数意义一样。<br><br>int accept(SOCKET s, sockaddr *addr, int *addrlen);<br>这个函数只有服务器端程序使用，作用是响应客户端的连接。返回值与bind函数意义一样。<br><br>int connect(SOCKET s, const sockaddr *name, int namelen);<br>这个函数只有客户端程序使用，作用是把客户端和某个计算机的某个端口建立连接。返回值与bind函数意义一样。第一个参数s，就是socket()函数的返回值；在结构name中，我们要给定一个端口号和目的机器名；namelen等于结构sockaddr的大小。<br><br>int send(SOCKET s, char *buf, int len, int flags);<br>int recv(SOCKET s, char *buf, int len, int flags);<br>这两个函数就是发送数据和接收数据，客户端和服务器端程序都能用，哪个发送哪个接收不用说了吧？呵呵。<br>从函数的返回值可以检查函数执行是否成功。参数中buf是指向发送或接收的数据的指针，len是数据长度。flags我们给个0就可以（其实是我不知道具体含义）。<br><br>最后就是关闭socket了，这个很容易忘掉，但这个函数很重要，一定要用。<br>int closesocket(SOCKET s);<br><br><br>好了，关键函数就这么几个，下图是这几个函数的执行顺序：<br><br>client端 service端<br><br>&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br>&nbsp; v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;v<br>socket()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;socket()<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; |<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; v<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;bind()<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;|<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;v<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;listen()<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; |<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; v<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;accept() 挂起，直到有客户端来连接<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;|<br>&nbsp; v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;三段握手过程 &nbsp; |<br>connect()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;-------------&gt; |<br>&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br>&nbsp; v &nbsp; 发送消息 &nbsp; v<br>&nbsp; +---&gt; send() ---------------&gt; recv() &lt;-------+<br>&nbsp; | &nbsp; | &nbsp; &nbsp; . |<br>&nbsp; | &nbsp; | &nbsp; &nbsp; . 处理消息 |<br>&nbsp; | &nbsp; v &nbsp; 响应消息 &nbsp; . |<br>&nbsp; +---- recv() &lt;--------------- send() --------+<br>&nbsp; | &nbsp; &nbsp; |<br>&nbsp; v &nbsp; &nbsp; |<br>close() ---------------&gt; recv()<br>&nbsp; &nbsp; |<br>&nbsp; &nbsp; v<br>&nbsp; closesocket()<br><br>上图我觉得能很好的说明客户端和服务器端的运行轨迹。<br><br>使用以上几个函数在 linux 系统上就可成功建立一个socket通信连路，但如果在windows系统上，还要用到另一个函数：<br>int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);<br>在windows系统上，首先要执行这个函数，所以要把这个函数放在socket()函数的前面。<br><br>我对上面的函数进行了一些封装，为节省篇幅，我去掉所有注释和非重要的函数，在这里可以看到各个函数的具体用法：<br><br>在 VC60 环境下要运行下面的函数，要包含头文件 errno.h 和 winsock2.h，还有，在连接的时候要连接上ws2_32.dll文件。<br><br>这是头文件内容：<br>class Socket {<br>public:<br><br>bool setup();<br><br>void close();<br><br>bool connect(string host, int port);<br><br>bool listen();<br><br>int accept();<br><br>int recv(char *buf, int len);<br><br>int recv(int new_fd, char *buf, int len);<br><br>int send(const char *msg, int len);<br><br>int send(int new_fd, const char *msg, int len);<br><br>private:<br>&nbsp; int _fd;<br>};<br><br>这是实现文件内容：<br>bool Socket::setup() {<br><br>WSADATA wsd;<br>_fd = WSAStartup(MAKEWORD(2,2), &amp;wsd); <br>if(_fd) {<br>return false;<br>}<br><br>_fd = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);<br>if (_fd == -1) {<br>return false;<br>}<br>return true;<br>}<br><br>bool Socket::listen() {<br>struct sockaddr_in my_addr;<br><br>my_addr.sin_family = AF_INET; <br>my_addr.sin_port = htons(52309);<br>my_addr.sin_addr.s_addr = INADDR_ANY;<br><br>if(::bind(_fd, (struct sockaddr *)&amp;my_addr, sizeof(struct sockaddr)) == SOCKET_ERROR) {<br>return false;<br>}<br><br>if(::listen(_fd, BACKLOG) == SOCKET_ERROR) {<br>return false;<br>}<br><br>return true;<br>}<br><br>int Socket::accept()<br>{<br>int new_fd;<br>struct sockaddr_in their_addr;<br>int sin_size = sizeof(their_addr);<br><br>printf("accepting... \n");<br><br>new_fd = ::accept(_fd, <br>&nbsp; (struct sockaddr *)&amp;their_addr,<br>&nbsp; &amp;sin_size);<br>return new_fd == SOCKET_ERROR ? -1:new_fd;<br>}<br><br>bool Socket::connect(string host, int port) {<br>struct hostent *_h = gethostbyname(host.c_str());<br>if (_h == 0) {<br>return false;<br>}<br><br>struct in_addr *_addr = (struct in_addr *)_h-&gt;h_addr;<br>struct sockaddr_in sin;<br>sin.sin_family = AF_INET;<br>sin.sin_addr = *_addr;<br>sin.sin_port = htons(port);<br><br>if (::connect(_fd, (sockaddr *)&amp;sin, sizeof(sin)) == SOCKET_ERROR) {<br>return false;<br>}<br><br>return true;<br>}<br><br>int Socket::recv(int new_fd, char *buf, int len)<br>{<br>int nb = ::recv(new_fd, buf, len, 0);<br>if (nb == -1) {<br>printf("Error! recv.\n");<br>}<br>return nb;<br>}<br><br>int Socket::recv(char *buf, int len) {<br>return recv(_fd, buf, len);<br>}<br><br>int Socket::send(const char *msg, int len) {<br>return send(_fd, msg, len);<br>}<br><br>int Socket::send(int new_fd, const char *msg, int len)<br>{<br>int nb = ::send(new_fd, msg, len, 0);<br>if (nb == -1) {<br>printf("Error! send.\n");<br>}<br><br>return nb;<br>}<br><br>void Socket::close() {<br><br>int trytimes = 0;<br>while(::closesocket(_fd) &amp;&amp; trytimes &lt; CLOSE_TRY_TIMES)<br>trytimes++;<br><br>if(trytimes == 10) {<br>printf("Cannot close socket!\n");<br>}<br>}<br><br>好，socket类是封装好了，下面就是组织了，服务器端和客户端是不一样的，下面分别给出代码，到这里已经就很简单了。<br><br>客户端：<br>int main(int argc, char **argv)<br>{<br>printf("socket of client is run ...\n");<br>Socket s;<br>if (!s.connect("dezhi", 52309))<br>return 0;<br><br>char *msg = "ok, send a message.";<br>for (int i=0; i&lt;10; i++) {<br>s.send(msg, 20);<br>printf("message = %s\n", msg);<br>}<br>s.send("q", 1);<br>s.close();<br><br>return 0;<br>}<br><br>服务器：<br>int main(int argc, char **argv) {<br>printf("socket of service is run ...\n");<br><br>Socket s;<br>s.listen();<br>int new_fd = s.accept();<br><br>char buf[8];<br>buf[7] = '\0';<br>while (1) {<br>if (s.recv(new_fd, buf, 5) != -1) {<br>&nbsp; printf("%s\n", buf);<br>&nbsp; if (buf[0] == 'q')<br>&nbsp; break;<br>}<br>}<br>s.close();<br>}<br><br>下面为运行结果：<br>客户端：<br>socket of client is run ...<br>Socket: WSAStartup success execute.<br>Socket: socket success execute.<br>Socket: Establish the connection to "127.0.0.1:52309"<br>message = ok, send a message.<br>message = ok, send a message.<br>message = ok, send a message.<br>message = ok, send a message.<br>message = ok, send a message.<br>message = ok, send a message.<br>message = ok, send a message.<br>message = ok, send a message.<br>message = ok, send a message.<br>message = ok, send a message.<br>Socket: Close connection to "127.0.0.1:52309"<br>Press any key to continue<br><br>服务器端<br>socket of service is run ...<br>Socket: WSAStartup success execute.<br>Socket: socket success execute.<br>bind ok!<br>listen ok!<br>accepting...<br>ok, send a message.<br>ok, send a message.<br>ok, send a message.<br>ok, send a message.<br>ok, send a message.<br>ok, send a message.<br>ok, send a message.<br>ok, send a message.<br>ok, send a message.<br>ok, send a message.<br>qk, send a message.<br>Press any key to continue<br><br>就到这里吧。socket的相关内容可远不止这些，我在这里只是给大家来个抛砖引玉，想深究？路还很漫长。关于详细的实现代码，去我的《源码》上找吧，不放在这里，是为了让篇幅小些。</font> <br>
<img src ="http://www.cppblog.com/amyvmiwei/aggbug/41375.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/amyvmiwei/" target="_blank">小不点</a> 2008-01-17 22:22 <a href="http://www.cppblog.com/amyvmiwei/archive/2008/01/17/41375.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Winsock工作模型 ( 转 )</title><link>http://www.cppblog.com/amyvmiwei/archive/2008/01/17/41365.html</link><dc:creator>小不点</dc:creator><author>小不点</author><pubDate>Thu, 17 Jan 2008 13:22:00 GMT</pubDate><guid>http://www.cppblog.com/amyvmiwei/archive/2008/01/17/41365.html</guid><wfw:comment>http://www.cppblog.com/amyvmiwei/comments/41365.html</wfw:comment><comments>http://www.cppblog.com/amyvmiwei/archive/2008/01/17/41365.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/amyvmiwei/comments/commentRss/41365.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/amyvmiwei/services/trackbacks/41365.html</trackback:ping><description><![CDATA[<p>首先得弄清楚同步、异步、阻塞、非阻塞的概念。<br>同步和异步是针对通讯的工作模式，阻塞和非阻塞是指socket的I/O操作。<br>实际上对于socket，只存在阻塞和非阻塞，同步与异步是在程序实现上有所不同。<br>以阻塞的方式执行recv函数，在没有收到数据前，此函数是不会返回的，所以这很容易执行函数的线程处于等待I/O上的数据状态，然后被挂起。非阻塞就不一样，执行recv时候不管有没有数据都立即返回，有数据时返回数据，没数据时返回错误。非阻塞可以带来程序的高效，也带来了写程序中必须注意的地方，非阻塞情况下，发送与接收数据时候，要用户自己管理自己的缓冲区，并且要记录发送与接受的位置，因为很可能发送与接受数据的任务不能一次完成，需要多次调用send和recv才可以完成。<br>本来同步异步是用来表示通讯模式的，通信的同步，主要是指客户端在发送请求后，必须得在服务端有回应后才发送下一个请求。所以这个时候的所有请求将会在服务端得到同步。通信的异步，指客户端在发送请求后，不必等待服务端的回应就可以发送下一个请求，这样对于所有的请求动作来说将会在服务端得到异步，这条请求的链路就象是一个请求队列，所有的动作在这里不会得到同步的。但是个人感觉，在说到socket的同步异步时候，同步跟阻塞概念差不多，都是有了结果才返回，异步则是告诉系统我要recv数据，然后马上返回，等待数据来了后，系统跟程序说数据到了，然后程序再recv数据。引用在网上看到的比较好的描述&#8220;阻塞 block 是指，你拨通某人的电话，但是此人不在，于是你拿着电话等他回来，其间不能再用电话。同步大概和阻塞差不多。非阻塞 nonblock 是指，你拨通某人的电话，但是此人不在，于是你挂断电话，待会儿再打。至于到时候他回来没有，只有打了电话才知道。即所谓的&#8220;轮询 / poll&#8221;。异步是指，你拨通某人的电话，但是此人不在，于是你叫接电话的人告诉那人(leave a message)，回来后给你打电话（call back）。&#8221;</p>
<p>显然，异步要高效一些。在Winsock中实现异步的方法有很多，Winsock工作模型有下面六种<br>&nbsp;&nbsp;&nbsp; 一：select模型<br>&nbsp;&nbsp;&nbsp; 二：WSAAsyncSelect模型<br>&nbsp;&nbsp;&nbsp; 三：WSAEventSelect模型<br>&nbsp;&nbsp;&nbsp; 四：Overlapped I/O 事件通知模型<br>&nbsp;&nbsp;&nbsp; 五：Overlapped I/O 完成例程模型<br>&nbsp;&nbsp;&nbsp; 六：IOCP模型<br>从一到六越来越高级，越来越高效，实现越来越复杂。曾在网上看到一些比喻用来很好的说明这些模型，在这里引用一下。</p>
<p>&nbsp;&nbsp;&nbsp; 老陈有一个在外地工作的女儿，不能经常回来，老陈和她通过信件联系。他们的信会被邮递员投递到他们的信箱里。<br>一：select模型</p>
<p>老陈非常想看到女儿的信。以至于他每隔10分钟就下楼检查信箱，看是否有女儿的信~~~~~<br>在这种情况下，&#8220;下楼检查信箱&#8221;然后回到楼上耽误了老陈太多的时间，以至于老陈无法做其他工作。</p>
<p>二：WSAAsyncSelect模型</p>
<p>后来，老陈使用了微软公司的新式信箱。这种信箱非常先进，一旦信箱里有新的信件，盖茨就会给老陈打电话：喂，大爷，你有新的信件了！从此，老陈再也不必频繁上下楼检查信箱了，牙也不疼了，你瞅准了，蓝天......不是，微软~~~~~~~~</p>
<p>三：WSAEventSelect模型</p>
<p>后来，微软的信箱非常畅销，购买微软信箱的人以百万计数......以至于盖茨每天24小时给客户打电话，累得腰酸背痛，喝蚁力神都不好使~~~~~~<br>微软改进了他们的信箱：在客户的家中添加一个附加装置，这个装置会监视客户的信箱，每当新的信件来临，此装置会发出&#8220;新信件到达&#8221;声，提醒老陈去收信。盖茨终于可以睡觉了。</p>
<p>四：Overlapped I/O 事件通知模型</p>
<p>后来，微软通过调查发现，老陈不喜欢上下楼收发信件，因为上下楼其实很浪费时间。于是微软再次改进他们的信箱。新式的信箱采用了更为先进的技术，只要用户告诉微软自己的家在几楼几号，新式信箱会把信件直接传送到用户的家中，然后告诉用户，你的信件已经放到你的家中了！老陈很高兴，因为他不必再亲自收发信件了！</p>
<p>五：Overlapped I/O 完成例程模型</p>
<p>老陈接收到新的信件后，一般的程序是：打开信封----掏出信纸----阅读信件----回复信件......为了进一步减轻用户负担，微软又开发了一种新的技术：用户只要告诉微软对信件的操作步骤，微软信箱将按照这些步骤去处理信件，不再需要用户亲自拆信/阅读/回复了！老陈终于过上了小资生活！</p>
<p>六：IOCP模型</p>
<p>微软信箱似乎很完美，老陈也很满意。但是在一些大公司情况却完全不同！这些大公司有数以万计的信箱，每秒钟都有数以百计的信件需要处理，以至于微软信箱经常因超负荷运转而崩溃！需要重新启动！微软不得不使出杀手锏......<br>微软给每个大公司派了一名名叫&#8220;Completion Port&#8221;的超级机器人，让这个机器人去处理那些信件！</p>
<p><br>其实，上面每种模型都有优点，要根据程序需求而适当选择合适的模型，前面三种模型效率已经比较高，实现起来难道不大，很多一般的网络程序都采用前三种模型，只有对网络要求特别高的一些服务器才会考虑用后面的那些模型。MFC中的CAsyncSocket类就是用的WSAAsyncSelect模型，电驴中也是用的这种，不过在寻找对应socket的时候进行了优化，查找更快，在GridCast中采用的是WSAEventSelect模型，等待。</p>
<p><br>BTW：上面所说均在Windows平台下，只用WinSock才有这么多模型，在linux下，好像就只有第一种select模式，我对linux下的socket不是很了解，应该也有很多提高效率的地方。</p>
<img src ="http://www.cppblog.com/amyvmiwei/aggbug/41365.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/amyvmiwei/" target="_blank">小不点</a> 2008-01-17 21:22 <a href="http://www.cppblog.com/amyvmiwei/archive/2008/01/17/41365.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>