IOCP自学实例,看的再多不如自己一点一点的完成一次。
转载要注明出处啊:http://blog.csdn.net/wsgxiaomianao
服务器代码:
// iocp.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "iocp.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
#include <WinSock.h>
#pragma comment(lib,"Ws2_32.lib")
#include <vector>
CWinApp theApp;
using namespace std;
enum {
ACCEPT_POSTED = 0,
RECV_POSTED,
SEND_POSTER
};
#define BUF_LEN 1024
#define EXIT_CODE 0xFFFFFFFF
typedef struct _PRE_IO_DATA
{
OVERLAPPED Overlapped;
SOCKET sClient;
SOCKADDR_IN sClientAddr;
WSABUF wsaBuffer;
char buf[BUF_LEN];
DWORD NumberOfBytesRecvd;
int postType;
}_preIoData,*_pPreIoData;
HANDLE g_completionHandle = INVALID_HANDLE_VALUE;
SOCKET g_ListenSock = INVALID_SOCKET;
vector<_preIoData *>g_VecPerIoData;
/* 基于Accept的IOCP简单模型创建步骤:
(1)初始化WinSock环境
(2)创建完成端口
(3)创建完成端口的工作者线程
(4)创建监听socket并进行绑定和监听
(5)创建接收客户端连入的线程
(6)退出服务时,资源回收等
*/
void showError(TCHAR *wsBuf)
{
CString csLog;
csLog.Format(L"%s:%d",wsBuf,GetLastError());
AfxMessageBox(csLog);
}
//init sock environment
void initSock()
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2),&wsaData);
}
//create completion port
bool createCompletionPort()
{
g_completionHandle = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);
if (g_completionHandle == INVALID_HANDLE_VALUE)
{
return false;
}
return true;
}
//create worker thread
/*
in worker thread...
{
get queue completion port...in while
judge exit
judge type == recv_type
post wsaRecv
}
*/
DWORD WINAPI workerThread( LPVOID lpParameter )
{
UNREFERENCED_PARAMETER(lpParameter);
_preIoData *pPerIoData= NULL;
DWORD dwRecvLen=0;
SOCKET sClient = INVALID_SOCKET;
while(TRUE)
{
BOOL bRet = GetQueuedCompletionStatus(g_completionHandle,
&dwRecvLen,
(PULONG_PTR)&pPerIoData,//此参数收到完成端口的队列中的结构,也就是在绑定客户端socket时穿进去的lpCompletionKey
(LPOVERLAPPED*)&pPerIoData,//此处使用的是结构体内的Overlapped,不用CONTAINING_RECORD了
INFINITE);
if (!bRet)
{
showError(L"GetQueuedCompletionStatus Error");
break;
}else
{
if (dwRecvLen == EXIT_CODE)
{
cout<<"dwRecvLen == EXIT_CODE.....\n";
break;
}
switch (pPerIoData->postType)
{
case RECV_POSTED:
{
if (dwRecvLen == 0) //client has been closed the socket
{
cout<<"客户端断开 IP:"<<inet_ntoa(pPerIoData->sClientAddr.sin_addr)<<" PORT: "<<ntohs(pPerIoData->sClientAddr.sin_port)<<endl;
//
vector<_preIoData*>::iterator it;
for (it = g_VecPerIoData.begin();it < g_VecPerIoData.end();it++)
{
_preIoData *ppid = (*it);
if (ppid->sClient == pPerIoData->sClient)
{
closesocket(pPerIoData->sClient);
delete ppid;
g_VecPerIoData.erase(it);
break;
}
}
}else
{
cout<<"Client:"<<inet_ntoa(pPerIoData->sClientAddr.sin_addr)<<" data: "<<pPerIoData->buf<<endl;
DWORD dwFlag = 0;
ZeroMemory(pPerIoData->buf,sizeof(char)*BUF_LEN);
WSARecv(pPerIoData->sClient,&(pPerIoData->wsaBuffer),1,&(pPerIoData->NumberOfBytesRecvd),&dwFlag,&(pPerIoData->Overlapped),NULL);
}
}
break;
default:
break;
}//switch
}
}
cout<<"workerThread exit....\n";
return 0;
}
//create socket & bind & listen
bool createSock()
{
g_ListenSock = WSASocket(
AF_INET,
SOCK_STREAM,
IPPROTO_TCP,
NULL,
0,
WSA_FLAG_OVERLAPPED);
if (g_ListenSock == INVALID_SOCKET)
{
showError(L"WSASocket Error");
return false;
}
//将监听socket绑定到完成端口,此处不绑定貌似也不出问题
if( NULL== CreateIoCompletionPort( (HANDLE)g_ListenSock, g_completionHandle,(DWORD)NULL, 0))
{
return false;
}
SOCKADDR_IN sockSer;
ZeroMemory(&sockSer,sizeof(SOCKADDR_IN));
//sockSer.sin_addr.s_addr = inet_addr("192.168.1.194");
sockSer.sin_addr.S_un.S_addr = ADDR_ANY;
sockSer.sin_family = AF_INET;
sockSer.sin_port = htons(12345);
if (SOCKET_ERROR == bind(g_ListenSock,(struct sockaddr*)&sockSer,sizeof(SOCKADDR_IN)))
{
showError(L"bind error");
return false;
}
if (SOCKET_ERROR == listen(g_ListenSock,SOMAXCONN))
{
showError(L"listen error");
return false;
}
return true;
}
// create accept thread & in while to accept client
/*
{
//accept
//bind completion port
//new pre_io and wsaRecv
}
*/
bool acceptInWhile()
{
while(TRUE)
{
if (g_ListenSock == INVALID_SOCKET)
{
AfxMessageBox(L"g_ListenSock is INVALID_SOCKET");
break;
}
SOCKADDR_IN clientSockAddr ;//= new SOCKADDR_IN;
ZeroMemory(&clientSockAddr,sizeof(SOCKADDR_IN));
int nLen = sizeof(SOCKADDR_IN);
SOCKET clientSock = accept(g_ListenSock,(struct sockaddr*)&clientSockAddr,&nLen);
if (clientSock == INVALID_SOCKET)
{
//showError(L"accept client socket error");
break;
}
cout<<"客户端IP:"<<inet_ntoa(clientSockAddr.sin_addr)<<" PORT: "<<ntohs(clientSockAddr.sin_port)<<endl;
_preIoData *perData = new _preIoData;
g_VecPerIoData.push_back(perData);
ZeroMemory(perData,sizeof(_preIoData));
perData->sClient = clientSock;
perData->sClientAddr = clientSockAddr;
/*
将连入的客户端的SOCKET绑定到完成端口上,这里面最注意的是第三个参数,
此参数传递进去以后,当此请求返回时会在消息队列中取出,因此可以传递所需要的参数(例如客户端的IP以及端口等)
*/
if (NULL == CreateIoCompletionPort((HANDLE)clientSock,g_completionHandle,(DWORD)perData,0))
{
showError(L"bind completion port error");
return false;
}
perData->wsaBuffer.buf = perData->buf;
perData->wsaBuffer.len = BUF_LEN;
perData->NumberOfBytesRecvd = BUF_LEN;
perData->postType = RECV_POSTED;
/*
这里投递了一个WSARecv的请求,当有此类型的请求返回时,便会在完成端口的工作者线程中去处理
*/
DWORD dwFlag = 0;
if ( SOCKET_ERROR == WSARecv(clientSock,&(perData->wsaBuffer),1,&(perData->NumberOfBytesRecvd),&dwFlag,&(perData->Overlapped),NULL))
{
if (GetLastError() != WSA_IO_PENDING)
{
showError(L"SOCKET_ERROR == WSARecv");
break;
}
}
}
return true;
}
DWORD WINAPI AcceptThreadProc( LPVOID lpParameter )
{
UNREFERENCED_PARAMETER(lpParameter);
acceptInWhile();
return 0;
}
int _tmain(void)
{
int nRetCode = 0;
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
_tprintf(_T("错误: MFC 初始化失败\n"));
nRetCode = 1;
return nRetCode;
}
initSock();
if (!createCompletionPort())
{
AfxMessageBox(L"create completion port error!");
return -1;
}
SYSTEM_INFO sysInfo;
ZeroMemory(&sysInfo,sizeof(SYSTEM_INFO));
GetSystemInfo(&sysInfo);
int nProcessNum = sysInfo.dwNumberOfProcessors;
HANDLE *hWorker = new HANDLE [nProcessNum];
for (int i=0;i<nProcessNum;i++)
{
DWORD dwThreadExit;
hWorker[i] = CreateThread(NULL,0,workerThread,NULL,0,&dwThreadExit);
if (!hWorker)
{
return -1;
}
}
if (!createSock())
{
showError(L"createSock error");
}
DWORD dwAcceptExit;
HANDLE hAcceptHdl = CreateThread(NULL,0,AcceptThreadProc,NULL,0,&dwAcceptExit);
CloseHandle(hAcceptHdl);
EXIT:
cout<<"IOCP服务已经打开,按'x'键将关闭IOCP服务"<<endl;
if ('x' != getchar())
{
goto EXIT;
}
//以下是关闭服务的清理工作
for (int i=0;i<nProcessNum;i++)
{
PostQueuedCompletionStatus(g_completionHandle,EXIT_CODE,(DWORD)NULL,NULL);
}
WaitForMultipleObjects(nProcessNum,hWorker,TRUE,INFINITE);
cout<<"both of workerThreads have been closed..."<<endl;
for (int i=0;i<nProcessNum;i++)
{
CloseHandle(hWorker[i]);
}
CloseHandle(g_completionHandle);
closesocket(g_ListenSock);
WSACleanup();
delete[] hWorker;
for (int i=0;i<g_VecPerIoData.size();i++)
{
_preIoData *pPid = g_VecPerIoData.at(i);
delete pPid;
pPid = NULL;
}
g_VecPerIoData.clear();
system("PAUSE");
return nRetCode;
}
下面是客户端的测试代码:开启N个线程同时向服务器发送数据
// Client.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "Client.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// 唯一的应用程序对象
#include <iostream>
#include <string>
CWinApp theApp;
using namespace std;
#include <WinSock.h>
#pragma comment(lib,"Ws2_32.lib")
#define MAX_SOCK_CONN 100
#define SERVER_IP "192.168.1.128"
#define SERVER_PORT 12345
UINT g_Fail = 0;
typedef struct _SOCK_PARAM
{
HANDLE hThread;
DWORD dwThreadId;
SOCKET sock;
UINT uIndex;
BOOL bIsExitThread;//
//HANDLE hEventConn;
}SockParam,*pSockParam;
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
SockParam *PSockParam = (SockParam*)lpParameter;
if (!PSockParam)
{
return -1;
}
int nRetValue = -1;
CString csLog;
do
{
//create
PSockParam->sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if (PSockParam->sock == INVALID_SOCKET)
{
csLog.Format(L"create socket error:%d",GetLastError());
AfxMessageBox(csLog);
break;
}
SOCKADDR_IN sockAddr;
ZeroMemory(&sockAddr,sizeof(SOCKADDR_IN));
sockAddr.sin_addr.s_addr = inet_addr(SERVER_IP);
sockAddr.sin_family = AF_INET;
sockAddr.sin_port = htons(SERVER_PORT);
//connect
nRetValue = connect(PSockParam->sock,(sockaddr*)&sockAddr,sizeof(SOCKADDR_IN));
if (nRetValue == SOCKET_ERROR)
{
int nError = GetLastError();
PSockParam->sock = INVALID_SOCKET;
g_Fail++;
CString csLog;
csLog.Format(L"Thread index %d connect error:%d,g_Fail:%d",PSockParam->uIndex,nError,g_Fail);
OutputDebugString(csLog);
break;
}
while(TRUE)
{
Sleep(300);
if (PSockParam->bIsExitThread)
{
shutdown(PSockParam->sock,SD_SEND);
break;
}
//Send
nRetValue = send(PSockParam->sock,"hello world\n",strlen("hello world\n"),0);
if (nRetValue == SOCKET_ERROR )
{
CString csLog;
csLog.Format(L"send error:%d",GetLastError());
AfxMessageBox(csLog);
break;
}
}
} while (FALSE);
return 0;
}
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = -1;
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
_tprintf(_T("错误: MFC 初始化失败\n"));
nRetCode = 1;
return nRetCode;
}
cout<<"创建"<<MAX_SOCK_CONN<<"个网络连接一起发送data.........."<<endl;
int nRetValue = -1;
CString csLog;
WSADATA wsaData;
nRetValue = WSAStartup(MAKEWORD(2,2),&wsaData);
if (nRetValue)
{
csLog.Format(L"WSAStartup error:%d",GetLastError());
AfxMessageBox(csLog);
return nRetCode;
}
SockParam sockParam[MAX_SOCK_CONN];
ZeroMemory(&sockParam,sizeof(SockParam)*MAX_SOCK_CONN);
for (int i=0;i<MAX_SOCK_CONN;i++)
{
sockParam[i].uIndex = i;
sockParam[i].bIsExitThread = FALSE;
sockParam[i].hThread = CreateThread(NULL,0,ThreadProc,(LPVOID)&sockParam[i],/*CREATE_SUSPENDED*/0,&sockParam[i].dwThreadId);
Sleep(100);
}
Sleep(500);
cout<<"按任意键将关闭所有网络连接......"<<endl;
system("PAUSE");
for (int i=0;i<MAX_SOCK_CONN;i++)
{
Sleep(10);
sockParam[i].bIsExitThread = TRUE;
}
for (int i=0;i<MAX_SOCK_CONN;i++)
{
if (sockParam[i].sock != INVALID_SOCKET)
{
closesocket(sockParam[i].sock);
}
}
WSACleanup();
return nRetCode;
}