#include<iostream> #include<WinSock2.h> #include <mswsock.h> #include<vector> using namespace std; #pragma comment(lib,"Ws2_32.lib") #pragma comment (lib, "mswsock.lib") const int nPort=10000; const int buf_len=1024; //對於同一個客戶鏈接套接字,任意時刻只能有一個爲完成的異步I/O操做,要麼是 //WSASend(IoWrite),要麼是WSARecv(IoRead) typedef enum _IO_OPERATION{ IoRead, IoWrite }IO_OPERATION; //每個鏈接都有一個Connection對象Connection對象包含一個WSAOVERLAPPED結構 //同時因爲一個Connection只有一個WSAOVERLAPPED結構,而且因爲一個I/O異步請求 //必須有一個惟一的WSAOVERLAPPED結構,所以任意時刻對於一個鏈接只能有一個未完成的 //異步I/O操做 struct Connection{ SOCKET hSocket; char Buffer[buf_len]; int nBytes; //調用WSASend或者WSARecv是須要一個WSABUF結構的指針 WSABUF wsaBuffer; WSAOVERLAPPED overlap; IO_OPERATION op; Connection(SOCKET socket):hSocket(socket),nBytes(0) { wsaBuffer.buf=Buffer; wsaBuffer.len=buf_len; ZeroMemory(&overlap,sizeof(WSAOVERLAPPED)); //因爲程序使用事件完成通知,所以須要爲WSAOVERLAPPED結構建立一個時間內核對象 overlap.hEvent=WSACreateEvent(); } }; typedef vector<Connection*> ConnectionList; // 重置conns,把其中無效的套接字移除 void ResetConns(ConnectionList& conns){ ConnectionList::iterator it = conns.begin(); while(it != conns.end()){ if((*it)->hSocket == INVALID_SOCKET){ delete (*it); it = conns.erase(it); } else ++it; } } // 爲WSAWaitForMultipleEvents填充好須要等待的事件內核對象數組 int FillEventArray(HANDLE hEvents[], HANDLE hListenEvent, ConnectionList& conns){ // 監聽套接字的事件對象放在最前面,以後依次填入當前全部客戶鏈接套接字 // 的事件對象 hEvents[0] = hListenEvent; int nEvents = 1; ConnectionList::iterator it = conns.begin(); while(it != conns.end()){ // 使用WSAOVERLAPPED結構中的hEvent填充數組 hEvents[nEvents] = (*it)->overlap.hEvent; ++nEvents; ++it; } return (int)(conns.size() + 1); } // 異步AcceptEx請求已完成,獲取結果 bool HandleAccept(SOCKET hListenSocket, SOCKET hAcceptSocket, LPOVERLAPPED lpOverlapListen, ConnectionList& conns) { DWORD flags = 0; DWORD bytes = 0; // 獲取異步I/O的結果 if(!WSAGetOverlappedResult(hListenSocket, lpOverlapListen, &bytes, FALSE, &flags)) { cout<<"WSAGetOverlappedResult error "<< WSAGetLastError() << endl; return false; } // 超出單線程所能處理的鏈接數 if(conns.size() + 1 >= WSA_MAXIMUM_WAIT_EVENTS){ cout << "exceed connection limit" << endl; // 關閉已接受的客戶鏈接,即拒絕服務 closesocket(hAcceptSocket); return true; } // 爲新接受的客戶鏈接建立一個Connection對象 conns.push_back(new Connection(hAcceptSocket)); Connection* pConn = conns.back(); // 第一次的異步I/O請求是IoRead,由於對於回顯服務器來講,必須先接收到數據後 // 才能回顯數據 pConn->op = IoRead; flags = 0; // 對這個新的客戶鏈接發出第一個異步I/O請求 int nRet = WSARecv(pConn->hSocket, &(pConn->wsaBuffer), 1, NULL, &flags, &pConn->overlap, NULL); int lastErr = WSAGetLastError(); // 若是WSARecv失敗而且錯誤代碼不是ERROR_IO_PENDING if(nRet == SOCKET_ERROR && WSA_IO_PENDING != lastErr){ cout<<"WSARecv error "<< lastErr << endl; return false; } return true; } // 異步的WSASend或者WSARecv已完成,獲取結果 bool HandleSendRecv(Connection* pConn){ DWORD flags = 0; DWORD bytes = 0; // 獲取異步I/O的結果 if(!WSAGetOverlappedResult(pConn->hSocket, &pConn->overlap, &bytes, FALSE, &flags)) { int lastErr = WSAGetLastError(); cout<<"WSAGetOverlappedResult error "<< lastErr << endl; // 鏈接被對方意外關閉 if(lastErr == WSAECONNRESET) cout<<"Connection was reset."<<endl; return false; } if(bytes == 0){ // 對方正常關閉了鏈接 cout << "Connection closed by peer." << endl; return false; } // 若是當前已完成的異步I/O是WSARecv if(pConn->op == IoRead){ // 更新可用數據的大小 pConn->nBytes += bytes; // 爲即將調用的WSASend準備好緩衝區參數 pConn->wsaBuffer.len = pConn->nBytes; pConn->wsaBuffer.buf = pConn->Buffer; flags = 0; // 因爲WSARecv已成功接收了數據,如今能夠發出異步WSASend請求來回顯數據 pConn->op = IoWrite; int nRet = WSASend(pConn->hSocket, &(pConn->wsaBuffer), 1, NULL, flags, &pConn->overlap, NULL); int lastErr = WSAGetLastError(); if(nRet == SOCKET_ERROR && WSA_IO_PENDING != lastErr) { cout<<"WSASend error "<< lastErr << endl; return false; } } // 若是當前已完成的異步I/O是WSASend else if(pConn->op == IoWrite){ // 更新可用數據的大小 pConn->nBytes -= bytes; // 計算緩衝區空閒空間的大小 pConn->wsaBuffer.len = nBuffSize - pConn->nBytes; // 若是緩衝區還有剩餘數據沒有發送出去,則須要把它們移到緩衝區的頭部 if(pConn->nBytes > 0) { memmove(pConn->Buffer, pConn->Buffer + bytes, pConn->nBytes); } // 計算緩衝區空閒空間的偏移 pConn->wsaBuffer.buf = pConn->Buffer + pConn->nBytes; flags = 0; pConn->op = IoRead; // 發出異步WSARecv請求 int nRet = WSARecv(pConn->hSocket, &(pConn->wsaBuffer), 1, NULL, &flags, &pConn->overlap, NULL); int lastErr = WSAGetLastError(); if(nRet == SOCKET_ERROR && WSA_IO_PENDING != lastErr) { cout<<"WSARecv error "<< lastErr << endl; return false; } } return true; } //建立一個WSA_FLAG_OVERLAPPED套接字 SOCKET CreateOverlappedSocket() { SOCKET hSocket=WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,WSA_FLAG_OVERLAPPED); if(hSocket==INVALID_SOCKET) { cout<<"WSASocket 錯誤"<<WSAGetLastError()<<endl; } return hSocket; } //返回一個用於異步I/O的監聽套接字進入監聽狀態 SOCKET BindListenOverlapped() { //建立一個用於異步I/O的監聽套接字 SOCKET sd=CreateOverlappedSocket(); if(sd==INVALID_SOCKET) { return INVALID_SOCKET; } //填充本地套接字地址 sockaddr_in saListen; saListen.sin_family=AF_INET; saListen.sin_addr.s_addr=htonl(INADDR_ANY); saListen.sin_port=htons(nPort); //調用bind把本地套接字地址綁定到監聽套接字 if(bind(sd,(sockaddr*)&saListen,sizeof(sockaddr_in))==SOCKET_ERROR) { cout<<"綁定失敗"<<WSAGetLastError()<<endl; closesocket(sd); return INVALID_SOCKET; } //開始監聽 if(listen(sd,5)==SOCKET_ERROR) { cout<<"監聽失敗"<<WSAGetLastError()<<endl; closesocket(sd); return INVALID_SOCKET; } return sd; } //調用AcceptEx時須要用到的緩衝區,這個緩衝區用來保存本地和遠程地址 char bAcceptBuffer[2*(sizeof(SOCKADDR_IN)+16)]; DWORD dwAcceptBytes=0; //發出異步AcceptEx請求 SOCKET StartAccept(SOCKET hListenSocket,HANDLE hListenEvent,LPOVERLAPPED lpOverlapListen) { //建立一個異步套接字hAcceptSocket,並傳給AcceptEx。當異步的AcceptEx完成時 //即當WSAWaitForMultipleEvents成功返回其返回值表示出現信號的事件是 //監聽套接字的事件時,在此處建立的hAcceptSocket就表明成功接受的客戶鏈接 SOCKET hAcceptSocket=CreateOverlappedSocket(); if(hAcceptSocket==INVALID_SOCKET) { return INVALID_SOCKET; } //初始化監聽套接字的WSAOVERLAPPED結構 ZeroMemory(lpOverlapListen,sizeof(WSAOVERLAPPED)); lpOverlapListen->hEvent=hListenEvent; //發出異步AcceptEx請求 if(!AcceptEx(hListenSocket,hAcceptSocket,bAcceptBuffer,0,sizeof(SOCKADDR_IN)+16,sizeof(SOCKADDR_IN)+16,&dwAcceptBytes,lpOverlapListen)) { //若是AcceptEx失敗而且錯誤代碼不是ERROR_IO_PENDING int lastErr=WSAGetLastError(); if(lastErr!=ERROR_IO_PENDING) { cout<<"AcceptEx 錯誤"<<lastErr<<endl; closesocket(hAcceptSocket); return INVALID_SOCKET; } } return hAcceptSocket; } // OverlappedEventServer的主體函數 void DoWork() { // 定義事件內核對象句柄數組 HANDLE hEvents[WSA_MAXIMUM_WAIT_EVENTS]; ConnectionList conns; // 獲取一個用於異步I/O的監聽套接字 SOCKET hListenSocket = BindListenOverlapped(); if(hListenSocket == INVALID_SOCKET) goto cleanup; // 爲監聽套接字建立一個事件內核對象 HANDLE hListenEvent = WSACreateEvent(); // 用於監聽套接字的WSAOVERLAPPED結構 WSAOVERLAPPED overlapListen; // 開始監聽套接字的異步AcceptEx請求 SOCKET hAcceptSocket = StartAccept(hListenSocket, hListenEvent, &overlapListen); if(hAcceptSocket == INVALID_SOCKET) goto cleanup; // 主循環 while(true){ // 從客戶鏈接列表中去掉無效的鏈接,即那些已關閉或者發生了錯誤的鏈接 ResetConns(conns); // 用監聽套接字的事件和全部有效客戶鏈接的事件填充一個事件數組 int nEvents = FillEventArray(hEvents, hListenEvent, conns); // 等待任一(或一些)事件出現信號 int nRet = WSAWaitForMultipleEvents(nEvents, hEvents, FALSE, WSA_INFINITE, FALSE); if(nRet == WSA_WAIT_FAILED){ cout<<"WSAWaitForMultipleEvents "<< WSAGetLastError() << endl; goto cleanup; } // 獲取全部出現信號的事件中最小的索引值 nRet = nRet - WSA_WAIT_EVENT_0; // 檢查每個可能的事件,看其有沒有信號 for(int nIndex = nRet; nIndex < nEvents; ++nIndex) { // 測試索引值爲nIndex的事件是否出現信號 nRet = WSAWaitForMultipleEvents(1, &hEvents[nIndex], true, 0, FALSE); // 沒有信號則繼續主循環 if(nRet == WSA_WAIT_FAILED || nRet == WSA_WAIT_TIMEOUT) continue; // 重置出現信號的事件,以便下一次進入主循環等待時其狀態爲無信號 WSAResetEvent(hEvents[nIndex]); // nIndex爲0表明監聽套接字的事件出現信號 if(nIndex == 0){ // 監聽套接字的異步AcceptEx已經完成,新的客戶鏈接套接字是 // hAcceptSocket。調用HandleAccept來執行異步I/O完成後的工做 if(!HandleAccept(hListenSocket, hAcceptSocket, &overlapListen, conns)) goto cleanup; // 開始監聽套接字的下一個異步AcceptEx請求 hAcceptSocket = StartAccept(hListenSocket, hListenEvent, &overlapListen); if(hAcceptSocket == INVALID_SOCKET) goto cleanup; } // nIndex大於0表明客戶鏈接的套接字事件出現信號 else{ // 找到客戶鏈接的Connection對象 Connection* pConn = conns[nIndex-1]; // 調用HandleSendRecv來執行異步I/O完成後的工做 if(!HandleSendRecv(pConn)){ closesocket(pConn->hSocket); pConn->hSocket = INVALID_SOCKET; WSACloseEvent(pConn->overlap.hEvent); } } } } // 釋放資源 cleanup: ConnectionList::iterator it = conns.begin(); for(;it != conns.end();++it){ closesocket((*it)->hSocket); WSACloseEvent((*it)->overlap.hEvent); delete (*it); } if(hListenSocket != INVALID_SOCKET) closesocket(hListenSocket); WSACloseEvent(hListenEvent); } int main(int argc, char* argv[]){ WSAData wsaData; int nCode; if ((nCode = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0) { cout << "WSAStartup error " << nCode << endl; return -1; } DoWork(); WSACleanup(); return 0; }