本文是我在學習IOCP的時候,第一次寫一個完整的例子出來,當然了,參考了CSDN上一些朋友的博客,大部分都是按照他們的思路寫的,畢竟我是初學者,參考現成的學起來比較快。當然了,真正用到項目中的IOCP肯定不止這麼簡單的,還有內存池,環形緩衝區,socket連接池等高端內容,後面我會參考一些例子,寫出一個完整的給大家看。
/************************************************************************
FileName:iocp.h
Author:eliteYang
http://www.cppfans.org
************************************************************************/
#ifndef __IOCP_H__
#define __IOCP_H__
#include
#include
#define DefaultPort 20000
#define DataBuffSize 8 * 1024
typedef struct
{
OVERLAPPED overlapped;
WSABUF databuff;
CHAR buffer[DataBuffSize];
DWORD bytesSend;
DWORD bytesRecv;
}PER_IO_OPERATEION_DATA, *LPPER_IO_OPERATION_DATA;
typedef struct
{
SOCKET socket;
}PER_HANDLE_DATA, *LPPER_HANDLE_DATA;
#endif
前面講過IOCP裏面一個很重要的東西就是IO重疊了,所以結構體裏有一個OVERLAPPED結構。
/************************************************************************
FileName:iocp.cpp
Author:eliteYang
http://www.cppfans.org
************************************************************************/
#include "iocp.h"
#include
using namespace std;
#pragma comment( lib, "Ws2_32.lib" )
DWORD WINAPI ServerWorkThread(LPVOID CompletionPortID);
void main()
{
SOCKET acceptSocket;
HANDLE completionPort;
LPPER_HANDLE_DATA pHandleData;
LPPER_IO_OPERATION_DATA pIoData;
DWORD recvBytes;
DWORD flags;
WSADATA wsaData;
DWORD ret;
if (ret = WSAStartup(0x0202, &wsaData) != 0)
{
std::cout << "WSAStartup failed. Error:" << ret << std::endl;
return;
}
completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (completionPort == NULL)
{
std::cout << "CreateIoCompletionPort failed. Error:" << GetLastError() << std::endl;
return;
}
SYSTEM_INFO mySysInfo;
GetSystemInfo(&mySysInfo);
// 創建 2 * CPU核數 + 1 個線程
DWORD threadID;
for (DWORD i = 0; i < (mySysInfo.dwNumberOfProcessors * 2 + 1); ++i)
{
HANDLE threadHandle;
threadHandle = CreateThread(NULL, 0, ServerWorkThread, completionPort, 0, &threadID);
if (threadHandle == NULL)
{
std::cout << "CreateThread failed. Error:" << GetLastError() << std::endl;
return;
}
CloseHandle(threadHandle);
}
// 啓動一個監聽socket
SOCKET listenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
if (listenSocket == INVALID_SOCKET)
{
std::cout << " WSASocket( listenSocket ) failed. Error:" << GetLastError() << std::endl;
return;
}
SOCKADDR_IN internetAddr;
internetAddr.sin_family = AF_INET;
internetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
internetAddr.sin_port = htons(DefaultPort);
// 綁定監聽端口
if (bind(listenSocket, (PSOCKADDR)&internetAddr, sizeof(internetAddr)) == SOCKET_ERROR)
{
std::cout << "Bind failed. Error:" << GetLastError() << std::endl;
return;
}
if (listen(listenSocket, 5) == SOCKET_ERROR)
{
std::cout << "listen failed. Error:" << GetLastError() << std::endl;
return;
}
// 開始死循環,處理數據
while (1)
{
acceptSocket = WSAAccept(listenSocket, NULL, NULL, NULL, 0);
if (acceptSocket == SOCKET_ERROR)
{
std::cout << "WSAAccept failed. Error:" << GetLastError() << std::endl;
return;
}
pHandleData = (LPPER_HANDLE_DATA)GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));
if (pHandleData = NULL)
{
std::cout << "GlobalAlloc( HandleData ) failed. Error:" << GetLastError() << std::endl;
return;
}
pHandleData->socket = acceptSocket;
if (CreateIoCompletionPort((HANDLE)acceptSocket, completionPort, (ULONG_PTR)pHandleData, 0) == NULL)
{
std::cout << "CreateIoCompletionPort failed. Error:" << GetLastError() << std::endl;
return;
}
pIoData = (LPPER_IO_OPERATION_DATA)GlobalAlloc(GPTR, sizeof(PER_IO_OPERATEION_DATA));
if (pIoData == NULL)
{
std::cout << "GlobalAlloc( IoData ) failed. Error:" << GetLastError() << std::endl;
return;
}
ZeroMemory(&(pIoData->overlapped), sizeof(pIoData->overlapped));
pIoData->bytesSend = 0;
pIoData->bytesRecv = 0;
pIoData->databuff.len = DataBuffSize;
pIoData->databuff.buf = pIoData->buffer;
flags = 0;
if (WSARecv(acceptSocket, &(pIoData->databuff), 1, &recvBytes, &flags, &(pIoData->overlapped), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
std::cout << "WSARecv() failed. Error:" << GetLastError() << std::endl;
return;
}
else
{
std::cout << "WSARecv() io pending" << std::endl;
return;
}
}
}
}
DWORD WINAPI ServerWorkThread(LPVOID CompletionPortID)
{
HANDLE complationPort = (HANDLE)CompletionPortID;
DWORD bytesTransferred;
LPPER_HANDLE_DATA pHandleData = NULL;
LPPER_IO_OPERATION_DATA pIoData = NULL;
DWORD sendBytes = 0;
DWORD recvBytes = 0;
DWORD flags;
while (1)
{
if (GetQueuedCompletionStatus(complationPort, &bytesTransferred, (PULONG_PTR)&pHandleData, (LPOVERLAPPED *)&pIoData, INFINITE) == 0)
{
std::cout << "GetQueuedCompletionStatus failed. Error:" << GetLastError() << std::endl;
return 0;
}
// 檢查數據是否已經傳輸完了
if (bytesTransferred == 0)
{
std::cout << " Start closing socket..." << std::endl;
if (CloseHandle((HANDLE)pHandleData->socket) == SOCKET_ERROR)
{
std::cout << "Close socket failed. Error:" << GetLastError() << std::endl;
return 0;
}
GlobalFree(pHandleData);
GlobalFree(pIoData);
continue;
}
// 檢查管道里是否有數據
if (pIoData->bytesRecv == 0)
{
pIoData->bytesRecv = bytesTransferred;
pIoData->bytesSend = 0;
}
else
{
pIoData->bytesSend += bytesTransferred;
}
// 數據沒有發完,繼續發送
if (pIoData->bytesRecv > pIoData->bytesSend)
{
ZeroMemory(&(pIoData->overlapped), sizeof(OVERLAPPED));
pIoData->databuff.buf = pIoData->buffer + pIoData->bytesSend;
pIoData->databuff.len = pIoData->bytesRecv - pIoData->bytesSend;
// 發送數據出去
if (WSASend(pHandleData->socket, &(pIoData->databuff), 1, &sendBytes, 0, &(pIoData->overlapped), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
std::cout << "WSASend() failed. Error:" << GetLastError() << std::endl;
return 0;
}
else
{
std::cout << "WSASend() failed. io pending. Error:" << GetLastError() << std::endl;
return 0;
}
}
std::cout << "Send " << pIoData->buffer << std::endl;
}
else
{
pIoData->bytesRecv = 0;
flags = 0;
ZeroMemory(&(pIoData->overlapped), sizeof(OVERLAPPED));
pIoData->databuff.len = DataBuffSize;
pIoData->databuff.buf = pIoData->buffer;
if (WSARecv(pHandleData->socket, &(pIoData->databuff), 1, &recvBytes, &flags, &(pIoData->overlapped), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
std::cout << "WSARecv() failed. Error:" << GetLastError() << std::endl;
return 0;
}
else
{
std::cout << "WSARecv() io pending" << std::endl;
return 0;
}
}
}
}
}