c++重叠I/0

来源:互联网 发布:inputselect.js 编辑:程序博客网 时间:2024/05/16 08:09
  1. #include<iostream>  
  2. #include<WinSock2.h>  
  3. #include <mswsock.h>  
  4. #include<vector>  
  5. using namespace std;  
  6. #pragma comment(lib,"Ws2_32.lib")  
  7. #pragma comment (lib, "mswsock.lib")  
  8. const int nPort=10000;  
  9. const int buf_len=1024;  
  10. //对于同一个客户连接套接字,任意时刻只能有一个为完成的异步I/O操作,要么是  
  11. //WSASend(IoWrite),要么是WSARecv(IoRead)  
  12. typedef enum _IO_OPERATION{  
  13.     IoRead,  
  14.     IoWrite  
  15. }IO_OPERATION;  
  16. //每一个连接都有一个Connection对象Connection对象包含一个WSAOVERLAPPED结构  
  17. //同时由于一个Connection只有一个WSAOVERLAPPED结构,并且由于一个I/O异步请求  
  18. //必须有一个唯一的WSAOVERLAPPED结构,因此任意时刻对于一个连接只能有一个未完成的  
  19. //异步I/O操作  
  20. struct Connection{  
  21.     SOCKET hSocket;  
  22.     char Buffer[buf_len];  
  23.     int nBytes;  
  24.     //调用WSASend或者WSARecv是需要一个WSABUF结构的指针  
  25.     WSABUF wsaBuffer;  
  26.     WSAOVERLAPPED overlap;  
  27.     IO_OPERATION op;  
  28.     Connection(SOCKET socket):hSocket(socket),nBytes(0)  
  29.     {  
  30.         wsaBuffer.buf=Buffer;  
  31.         wsaBuffer.len=buf_len;  
  32.         ZeroMemory(&overlap,sizeof(WSAOVERLAPPED));  
  33.         //由于程序使用事件完成通知,因此需要为WSAOVERLAPPED结构创建一个时间内核对象  
  34.         overlap.hEvent=WSACreateEvent();  
  35.     }  
  36. };  
  37. typedef vector<Connection*> ConnectionList;  
  38. // 重置conns,把其中无效的套接字移除  
  39. void ResetConns(ConnectionList& conns){  
  40.     ConnectionList::iterator it = conns.begin();  
  41.     while(it != conns.end()){  
  42.         if((*it)->hSocket == INVALID_SOCKET){  
  43.             delete (*it);  
  44.             it = conns.erase(it);  
  45.         }  
  46.         else  
  47.             ++it;  
  48.     }  
  49. }  
  50. // 为WSAWaitForMultipleEvents填充好需要等待的事件内核对象数组  
  51. int FillEventArray(HANDLE hEvents[], HANDLE hListenEvent,   
  52.                    ConnectionList& conns){  
  53.     // 监听套接字的事件对象放在最前面,之后依次填入当前所有客户连接套接字  
  54.     // 的事件对象  
  55.     hEvents[0] = hListenEvent;  
  56.     int nEvents = 1;  
  57.     ConnectionList::iterator it = conns.begin();  
  58.     while(it != conns.end()){  
  59.         // 使用WSAOVERLAPPED结构中的hEvent填充数组  
  60.         hEvents[nEvents] = (*it)->overlap.hEvent;  
  61.         ++nEvents;  
  62.         ++it;  
  63.     }  
  64.     return (int)(conns.size() + 1);  
  65. }  
  66. // 异步AcceptEx请求已完成,获取结果  
  67. bool HandleAccept(SOCKET hListenSocket, SOCKET hAcceptSocket, LPOVERLAPPED  
  68.                   lpOverlapListen, ConnectionList& conns) {  
  69.     DWORD flags = 0;  
  70.     DWORD bytes = 0;  
  71.     // 获取异步I/O的结果  
  72.     if(!WSAGetOverlappedResult(hListenSocket, lpOverlapListen, &bytes,   
  73.                                FALSE, &flags))  
  74.     {  
  75.         cout<<"WSAGetOverlappedResult error "<< WSAGetLastError() << endl;  
  76.         return false;  
  77.     }  
  78.     // 超出单线程所能处理的连接数  
  79.     if(conns.size() + 1 >= WSA_MAXIMUM_WAIT_EVENTS){  
  80.         cout << "exceed connection limit" << endl;  
  81.         // 关闭已接受的客户连接,即拒绝服务  
  82.         closesocket(hAcceptSocket);  
  83.         return true;  
  84.     }  
  85.     // 为新接受的客户连接创建一个Connection对象  
  86.     conns.push_back(new Connection(hAcceptSocket));  
  87.     Connection* pConn = conns.back();  
  88.     // 第一次的异步I/O请求是IoRead,因为对于回显服务器来说,必须先接收到数据后  
  89.     // 才能回显数据  
  90.     pConn->op = IoRead;  
  91.     flags = 0;  
  92.     // 对这个新的客户连接发出第一个异步I/O请求  
  93.     int nRet = WSARecv(pConn->hSocket, &(pConn->wsaBuffer), 1, NULL,   
  94.                        &flags, &pConn->overlap, NULL);  
  95.     int lastErr = WSAGetLastError();  
  96.     // 如果WSARecv失败并且错误代码不是ERROR_IO_PENDING  
  97.     if(nRet == SOCKET_ERROR && WSA_IO_PENDING != lastErr){  
  98.         cout<<"WSARecv error "<< lastErr << endl;  
  99.         return false;  
  100.     }  
  101.     return true;  
  102. }  
  103.   
  104. // 异步的WSASend或者WSARecv已完成,获取结果  
  105. bool HandleSendRecv(Connection* pConn){  
  106.     DWORD flags = 0;  
  107.     DWORD bytes = 0;  
  108.     // 获取异步I/O的结果  
  109.     if(!WSAGetOverlappedResult(pConn->hSocket, &pConn->overlap, &bytes,   
  110.                                FALSE, &flags)) {  
  111.         int lastErr = WSAGetLastError();  
  112.         cout<<"WSAGetOverlappedResult error "<< lastErr << endl;  
  113.         // 连接被对方意外关闭  
  114.         if(lastErr == WSAECONNRESET)  
  115.             cout<<"Connection was reset."<<endl;  
  116.         return false;  
  117.     }  
  118.     if(bytes == 0){  
  119.         // 对方正常关闭了连接  
  120.         cout << "Connection closed by peer." << endl;  
  121.         return false;  
  122.     }  
  123.     // 如果当前已完成的异步I/O是WSARecv  
  124.     if(pConn->op == IoRead){  
  125.         // 更新可用数据的大小  
  126.         pConn->nBytes += bytes;  
  127.         // 为即将调用的WSASend准备好缓冲区参数  
  128.         pConn->wsaBuffer.len = pConn->nBytes;  
  129.         pConn->wsaBuffer.buf = pConn->Buffer;  
  130.         flags = 0;  
  131.         // 由于WSARecv已成功接收了数据,现在可以发出异步WSASend请求来回显数据  
  132.         pConn->op = IoWrite;  
  133.         int nRet = WSASend(pConn->hSocket, &(pConn->wsaBuffer), 1, NULL,   
  134.                            flags, &pConn->overlap, NULL);  
  135.         int lastErr = WSAGetLastError();  
  136.         if(nRet == SOCKET_ERROR && WSA_IO_PENDING != lastErr) {  
  137.             cout<<"WSASend error "<< lastErr << endl;  
  138.             return false;  
  139.         }  
  140.     }  
  141.     // 如果当前已完成的异步I/O是WSASend  
  142.     else if(pConn->op == IoWrite){  
  143.         // 更新可用数据的大小  
  144.         pConn->nBytes -= bytes;  
  145.         // 计算缓冲区空闲空间的大小  
  146.         pConn->wsaBuffer.len = nBuffSize - pConn->nBytes;  
  147.         // 如果缓冲区还有剩余数据没有发送出去,则需要把它们移到缓冲区的头部  
  148.         if(pConn->nBytes > 0) {  
  149.             memmove(pConn->Buffer, pConn->Buffer + bytes, pConn->nBytes);  
  150.         }  
  151.         // 计算缓冲区空闲空间的偏移  
  152.         pConn->wsaBuffer.buf = pConn->Buffer + pConn->nBytes;  
  153.         flags = 0;  
  154.         pConn->op = IoRead;  
  155.         // 发出异步WSARecv请求  
  156.         int nRet = WSARecv(pConn->hSocket, &(pConn->wsaBuffer), 1, NULL,   
  157.                            &flags, &pConn->overlap, NULL);  
  158.         int lastErr = WSAGetLastError();  
  159.         if(nRet == SOCKET_ERROR && WSA_IO_PENDING != lastErr) {  
  160.             cout<<"WSARecv error "<< lastErr << endl;  
  161.             return false;  
  162.         }  
  163.     }  
  164.     return true;  
  165. }  
  166. //创建一个WSA_FLAG_OVERLAPPED套接字  
  167. SOCKET CreateOverlappedSocket()  
  168. {  
  169.     SOCKET hSocket=WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,WSA_FLAG_OVERLAPPED);  
  170.     if(hSocket==INVALID_SOCKET)  
  171.     {  
  172.         cout<<"WSASocket 错误"<<WSAGetLastError()<<endl;  
  173.     }  
  174.     return hSocket;  
  175. }  
  176. //返回一个用于异步I/O的监听套接字进入监听状态  
  177. SOCKET BindListenOverlapped()  
  178. {  
  179.     //创建一个用于异步I/O的监听套接字  
  180.     SOCKET sd=CreateOverlappedSocket();  
  181.     if(sd==INVALID_SOCKET)  
  182.     {  
  183.         return INVALID_SOCKET;  
  184.     }  
  185.     //填充本地套接字地址  
  186.     sockaddr_in saListen;  
  187.     saListen.sin_family=AF_INET;  
  188.     saListen.sin_addr.s_addr=htonl(INADDR_ANY);  
  189.     saListen.sin_port=htons(nPort);  
  190.     //调用bind把本地套接字地址绑定到监听套接字  
  191.     if(bind(sd,(sockaddr*)&saListen,sizeof(sockaddr_in))==SOCKET_ERROR)  
  192.     {  
  193.         cout<<"绑定失败"<<WSAGetLastError()<<endl;  
  194.         closesocket(sd);  
  195.         return INVALID_SOCKET;  
  196.     }  
  197.     //开始监听  
  198.     if(listen(sd,5)==SOCKET_ERROR)  
  199.     {  
  200.         cout<<"监听失败"<<WSAGetLastError()<<endl;  
  201.         closesocket(sd);  
  202.         return INVALID_SOCKET;  
  203.     }  
  204.     return sd;  
  205. }  
  206. //调用AcceptEx时需要用到的缓冲区,这个缓冲区用来保存本地和远程地址  
  207. char bAcceptBuffer[2*(sizeof(SOCKADDR_IN)+16)];  
  208. DWORD dwAcceptBytes=0;  
  209. //发出异步AcceptEx请求  
  210. SOCKET StartAccept(SOCKET hListenSocket,HANDLE hListenEvent,LPOVERLAPPED lpOverlapListen)  
  211. {  
  212.     //创建一个异步套接字hAcceptSocket,并传给AcceptEx。当异步的AcceptEx完成时  
  213.     //即当WSAWaitForMultipleEvents成功返回其返回值表示出现信号的事件是  
  214.     //监听套接字的事件时,在此处创建的hAcceptSocket就代表成功接受的客户连接  
  215.     SOCKET hAcceptSocket=CreateOverlappedSocket();  
  216.     if(hAcceptSocket==INVALID_SOCKET)  
  217.     {  
  218.         return INVALID_SOCKET;  
  219.     }  
  220.     //初始化监听套接字的WSAOVERLAPPED结构  
  221.     ZeroMemory(lpOverlapListen,sizeof(WSAOVERLAPPED));  
  222.     lpOverlapListen->hEvent=hListenEvent;  
  223.     //发出异步AcceptEx请求  
  224.     if(!AcceptEx(hListenSocket,hAcceptSocket,bAcceptBuffer,0,sizeof(SOCKADDR_IN)+16,sizeof(SOCKADDR_IN)+16,&dwAcceptBytes,lpOverlapListen))  
  225.     {  
  226.         //如果AcceptEx失败并且错误代码不是ERROR_IO_PENDING  
  227.         int lastErr=WSAGetLastError();  
  228.         if(lastErr!=ERROR_IO_PENDING)  
  229.         {  
  230.             cout<<"AcceptEx 错误"<<lastErr<<endl;  
  231.             closesocket(hAcceptSocket);  
  232.             return INVALID_SOCKET;  
  233.         }  
  234.     }  
  235.     return hAcceptSocket;  
  236. }  
  237. // OverlappedEventServer的主体函数  
  238. void DoWork() {  
  239.     // 定义事件内核对象句柄数组  
  240.     HANDLE hEvents[WSA_MAXIMUM_WAIT_EVENTS];  
  241.     ConnectionList conns;  
  242.     // 获取一个用于异步I/O的监听套接字  
  243.     SOCKET hListenSocket = BindListenOverlapped();  
  244.     if(hListenSocket == INVALID_SOCKET)  
  245.         goto cleanup;  
  246.     // 为监听套接字创建一个事件内核对象  
  247.     HANDLE hListenEvent = WSACreateEvent();  
  248.     // 用于监听套接字的WSAOVERLAPPED结构  
  249.     WSAOVERLAPPED overlapListen;  
  250.     // 开始监听套接字的异步AcceptEx请求  
  251.     SOCKET hAcceptSocket = StartAccept(hListenSocket, hListenEvent,   
  252.         &overlapListen);  
  253.     if(hAcceptSocket == INVALID_SOCKET)  
  254.         goto cleanup;  
  255.     // 主循环  
  256.     while(true){  
  257.         // 从客户连接列表中去掉无效的连接,即那些已关闭或者发生了错误的连接  
  258.         ResetConns(conns);  
  259.         // 用监听套接字的事件和所有有效客户连接的事件填充一个事件数组  
  260.         int nEvents = FillEventArray(hEvents, hListenEvent, conns);  
  261.         // 等待任一(或一些)事件出现信号  
  262.         int nRet = WSAWaitForMultipleEvents(nEvents, hEvents, FALSE,   
  263.                                             WSA_INFINITE, FALSE);  
  264.         if(nRet == WSA_WAIT_FAILED){  
  265.             cout<<"WSAWaitForMultipleEvents "<< WSAGetLastError() << endl;  
  266.             goto cleanup;  
  267.         }  
  268.         // 获取所有出现信号的事件中最小的索引值  
  269.         nRet = nRet - WSA_WAIT_EVENT_0;  
  270.         // 检查每一个可能的事件,看其有没有信号  
  271.         for(int nIndex = nRet; nIndex < nEvents; ++nIndex) {  
  272.             // 测试索引值为nIndex的事件是否出现信号  
  273.             nRet = WSAWaitForMultipleEvents(1, &hEvents[nIndex], true, 0,  
  274.                                             FALSE);  
  275.             // 没有信号则继续主循环  
  276.             if(nRet == WSA_WAIT_FAILED || nRet == WSA_WAIT_TIMEOUT)  
  277.                 continue;  
  278.             // 重置出现信号的事件,以便下一次进入主循环等待时其状态为无信号  
  279.             WSAResetEvent(hEvents[nIndex]);  
  280.             // nIndex为0代表监听套接字的事件出现信号  
  281.             if(nIndex == 0){  
  282.                 // 监听套接字的异步AcceptEx已经完成,新的客户连接套接字是  
  283.                 // hAcceptSocket。调用HandleAccept来执行异步I/O完成后的工作  
  284.                 if(!HandleAccept(hListenSocket, hAcceptSocket,   
  285.                                  &overlapListen, conns))  
  286.                     goto cleanup;  
  287.                 // 开始监听套接字的下一个异步AcceptEx请求  
  288.                 hAcceptSocket = StartAccept(hListenSocket, hListenEvent,  
  289.                                             &overlapListen);  
  290.                 if(hAcceptSocket == INVALID_SOCKET)  
  291.                     goto cleanup;  
  292.             }  
  293.             // nIndex大于0代表客户连接的套接字事件出现信号  
  294.             else{  
  295.                 // 找到客户连接的Connection对象  
  296.                 Connection* pConn = conns[nIndex-1];  
  297.                 // 调用HandleSendRecv来执行异步I/O完成后的工作  
  298.                 if(!HandleSendRecv(pConn)){  
  299.                     closesocket(pConn->hSocket);  
  300.                     pConn->hSocket = INVALID_SOCKET;  
  301.                     WSACloseEvent(pConn->overlap.hEvent);  
  302.                 }  
  303.             }  
  304.         }  
  305.     }  
  306.     // 释放资源  
  307. cleanup:  
  308.     ConnectionList::iterator it = conns.begin();  
  309.     for(;it != conns.end();++it){  
  310.         closesocket((*it)->hSocket);  
  311.         WSACloseEvent((*it)->overlap.hEvent);  
  312.         delete (*it);  
  313.     }  
  314.     if(hListenSocket != INVALID_SOCKET)  
  315.         closesocket(hListenSocket);  
  316.     WSACloseEvent(hListenEvent);  
  317. }  
  318. int main(int argc, char* argv[]){  
  319.     WSAData wsaData;  
  320.     int nCode;  
  321.     if ((nCode = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0) {  
  322.         cout << "WSAStartup error " << nCode << endl;  
  323.         return -1;  
  324.     }  
  325.     DoWork();  
  326.     WSACleanup();  
  327.     return 0;  
  328. }  
0 0