IOCP简单示例

来源:互联网 发布:网络动作游戏排行榜 编辑:程序博客网 时间:2024/06/08 04:11
// TestIOCP.cpp : 定义控制台应用程序的入口点。
//
 
#include "stdafx.h"
#include <iostream>
#include <winsock2.h>
#include <winsock.h>
using namespace std;


#pragma  comment(lib, "ws2_32.lib")
#pragma  comment(lib, "kernel32.lib")


HANDLE g_hIOCP;


enum IO_OPERATION
{
IO_READ,
IO_WRITE
};


struct IO_DATA
{
OVERLAPPED Overlapped;
WSABUF Wsabuf;
int nBytes;
IO_OPERATION opCode;
SOCKET client;
};


char buffer[1024];




// 这是一个很简单的IOCP通讯。 里面有很多不和逻辑的地方。 仅供参考
DWORD WINAPI WorkerThread(LPVOID WorkThreadContext)
{
IO_DATA* lpIOContext = NULL;
DWORD nBytes = 0;
DWORD dwFlags = 0;
int nRet = 0;


DWORD dwIoSize = 0;


void* lpCompletionKey = NULL;
LPOVERLAPPED lpOverlapped = NULL;


while (true)
{
// 此处是一个阻塞函数, 当完成端口处理完client发来的数据并通知这个线程,并把数据存储到lpOverlapped里, 继续往下执行
GetQueuedCompletionStatus(g_hIOCP, &dwIoSize, (LPDWORD)&lpCompletionKey, (LPOVERLAPPED*)&lpOverlapped, INFINITE);


lpIOContext = (IO_DATA*)lpOverlapped;
if ( dwIoSize == 0 )
{
cout << "Client disonnect" << endl;
closesocket(lpIOContext->client);
delete lpIOContext;
continue;
}
else
{
cout<<"Data from " << (lpIOContext->opCode == IO_READ?"Clent " : " Server")<< "; " << buffer<<endl;
}




if ( lpIOContext->opCode == IO_READ)
{
memset(buffer, NULL, sizeof(buffer));
memcpy(buffer, "data from server ", sizeof(buffer));


ZeroMemory(&lpIOContext->Overlapped, sizeof(lpIOContext->Overlapped));
lpIOContext->Wsabuf.buf = buffer;
lpIOContext->Wsabuf.len = strlen(buffer) + 1;
lpIOContext->opCode = IO_WRITE;
lpIOContext->nBytes = strlen(buffer) + 1;
dwFlags = 0;
nBytes = strlen(buffer) + 1;


nRet = WSASend(
lpIOContext->client,
&lpIOContext->Wsabuf, 1, &nBytes, dwFlags,
&(lpIOContext->Overlapped), NULL);


if (nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()))
{
cout << "WSASend failed::Reason Code::" << WSAGetLastError() << endl;
closesocket(lpIOContext->client);
delete lpIOContext;
continue;
}



}else if ( lpIOContext->opCode == IO_WRITE )
{
// 当recv一次之后要重新recv 
lpIOContext->opCode = IO_READ;
nBytes = 1024;
dwFlags = 0;
lpIOContext->Wsabuf.buf = buffer;
lpIOContext->Wsabuf.len = nBytes;
lpIOContext->nBytes = nBytes;
ZeroMemory(&lpIOContext->Overlapped, sizeof(lpIOContext->Overlapped));


// 此处是异步接收消息的
nRet = WSARecv(
lpIOContext->client,
&lpIOContext->Wsabuf, 1, &nBytes,
&dwFlags, &lpIOContext->Overlapped, NULL);


if ( nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()))
{
cout << "WSARecv Failed::Reason Code1::" << WSAGetLastError() << endl;
closesocket(lpIOContext->client);
delete lpIOContext;
continue;
}
}


}


return 0;
}


void main()
{
/*
为了在应用程序当中调用任何一个WinSock API函数, 首先第一件事情就是必须通过WSAStartup函数完成对Winsock服务的初始化
此步作用就是完成对Winsock服务的初始化
*/
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);


// 创建套接字
SOCKET m_socket = WSASocket( AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);


sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(9876);
server.sin_addr.S_un.S_addr = htons(INADDR_ANY);


bind(m_socket, (sockaddr*)&server, sizeof(server));


listen(m_socket, 8);


SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
int g_ThreadCount = sysInfo.dwNumberOfProcessors * 2;


g_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, g_ThreadCount);


for (int i = 0; i < g_ThreadCount; ++i )
{
HANDLE hThread;
DWORD dwThreadId;
hThread = CreateThread(NULL, 0, WorkerThread, 0, 0, &dwThreadId);

CloseHandle(hThread);
}


while (true)
{
// 监听服务端的socket 如果监听到将监听到的socket返回出来
SOCKET client = accept(m_socket, NULL, NULL);
cout << "Client connected." << endl;


// 将监听到的客户端的socket与完成端口关联起来。
if ( CreateIoCompletionPort((HANDLE)client, g_hIOCP, 0, 0) == NULL)
{
cout << "Binding Client Socket to IO Completion Port Failed::Reason Code :: " << GetLastError() << endl;
closesocket(client);
}
else
{
IO_DATA* data = new IO_DATA;
memset(buffer, NULL, sizeof(buffer));
memset(&data->Overlapped, 0, sizeof(data->Overlapped));
data->opCode = IO_READ;
data->nBytes = 0;
data->Wsabuf.buf = buffer;
data->Wsabuf.len = sizeof(buffer);
data->client = client;
DWORD nBytes = 1024, dwFlags = 0;


// 此处是异步接收消息的,  让关联的客户端发来消息时为给Wsabuf内复制,并通知完成端口。 完成端口会把处理的结果发送到线程。
int nRet = WSARecv(client, &data->Wsabuf, 1, &nBytes, &dwFlags, &data->Overlapped, NULL);
if ( nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()))
{
cout << "WSARecv Failed::Reason Code::" << WSAGetLastError() << endl;
closesocket(client);
delete data;
}
}


}


closesocket(m_socket);
WSACleanup();

}