// OverlappedIO.cpp IO重叠之事件通知example cpp
#include "stdafx.h"
using std::map;
#define DATA_BUFSIZE 4096 //数据缓冲区的大小
#define _ENTER_CS_CLT EnterCriticalSection(&g_CSListClients) //加锁
#define _LEAVE_CS_CLT LeaveCriticalSection(&g_CSListClients) //解锁
DWORD g_udwIndexEvent = 0; //事件索引
CRITICAL_SECTION g_CSListClients; // 临界区
HANDLE g_hThreadServer; //服务线程
HANDLE g_hEventConn; //事件
typedef struct MapClientNode
{
SOCKET sock; // 句柄
WSAEVENT tEvent; //事件
DWORD udwRecvBytes; //接收到的内容的大小
WSABUF tBuf; // 接收的缓冲区
WSAOVERLAPPED tOverLp; // 重叠IO结构
bool bHasHandled; // 是否己处理
}_CLIENT;
typedef map<WSAEVENT, _CLIENT> MAP_CLIENT;
MAP_CLIENT g_mapClient;
int WriteToArray(MAP_CLIENT::iterator begin, MAP_CLIENT::iterator end, WSAEVENT *aEvents)
{
int c = 0;
for (; begin != end; ++begin)
{
if (false == begin->second.bHasHandled) //若未处理,则将事件加入事件数组
{
aEvents[c++] = begin->second.tEvent;
}
}
return c;
}
//工作线程
unsigned int __stdcall ServerProc(void *pvParam)
{
DWORD udwFlags = 0;
MAP_CLIENT::iterator iter;
WSAEVENT aEvents[WSA_MAXIMUM_WAIT_EVENTS];
for (;;)
{
WaitForSingleObject(g_hEventConn, INFINITE);
_ENTER_CS_CLT;
//这个是主要部分,对每个需要投递WSARecv的连接投递一次
for (iter = g_mapClient.begin(); iter != g_mapClient.end();)
{
if (iter->second.bHasHandled && (WSARecv(iter->second.sock, &iter->second.tBuf, 1, &iter->second.udwRecvBytes,
&udwFlags, &iter->second.tOverLp, NULL) == SOCKET_ERROR))
{
iter->second.bHasHandled = false;
switch (WSAGetLastError())
{
case WSAENOBUFS:
++iter;
break;
case WSA_IO_PENDING:
++iter;
break;
default:
closesocket(iter->second.sock);
WSACloseEvent(iter->second.tEvent);
g_mapClient.erase(iter++);
break;
}
}
else
{
++iter;
}
}
//把完成投递但是没被处理的事件写到一个缓冲区中
int c = WriteToArray(g_mapClient.begin(), g_mapClient.end(), aEvents);
//等待其中一个的完成,如果超时就返回再重复
DWORD dwIndex = WSAWaitForMultipleEvents(c, aEvents, false, 100, false);
if (WSA_WAIT_TIMEOUT == dwIndex)
{
_LEAVE_CS_CLT;
continue;
}
dwIndex -= WSA_WAIT_EVENT_0;
WSAEVENT tEvent = aEvents[dwIndex];
WSAResetEvent(tEvent);
//解析事件,读取报文
_CLIENT *ptClient = &g_mapClient[tEvent];
WSAGetOverlappedResult(ptClient->sock, &ptClient->tOverLp, &ptClient->udwRecvBytes, false, &udwFlags);
ptClient->bHasHandled = true;
if (0 == ptClient->udwRecvBytes)
{
closesocket(ptClient->sock);
WSACloseEvent(tEvent);
delete[] ptClient->tBuf.buf;
g_mapClient.erase(tEvent);
printf("connection closed/n");
if (g_mapClient.empty())
{
printf("no connection/n");
ResetEvent(g_hEventConn);
}
}
else
{
ptClient->tBuf.buf[ptClient->udwRecvBytes] = 0;
printf("%s/n", ptClient->tBuf.buf);
ZeroMemory(&ptClient->tOverLp, sizeof(WSAOVERLAPPED));
ptClient->tOverLp.hEvent = ptClient->tEvent;
}
_LEAVE_CS_CLT;
}
return 0;
}
int main(int argc, char* argv[])
{
InitializeCriticalSection(&g_CSListClients);
g_hEventConn = CreateEvent(NULL, true, false, NULL);
//初始化工作
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2,2),&wsaData)!= 0)
{
OutputDebugStringA("Error : call function WSAStartup() failed!");
return -1;
}
//创建一个TCP套接字,带上WSA_FLAG_OVERLAPPED标志
SOCKET ListenSocket = WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
if(ListenSocket == INVALID_SOCKET)
{
OutputDebugStringA("Error : call function WSASocket() failed !");
return -2;
}
//bind地址
SOCKADDR_IN ServerAddr;
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_addr.S_un.S_addr = INADDR_ANY;
ServerAddr.sin_port = htons(1234);
if(SOCKET_ERROR == bind(ListenSocket,(LPSOCKADDR)&ServerAddr,sizeof(ServerAddr)))
{
OutputDebugStringA("Error : call function bind() failed !");
return -3;
}
//开始监听
if(SOCKET_ERROR == listen(ListenSocket,5))
{
OutputDebugStringA("Error : call function lister() failed");
return -4;
}
//起一个工作线程,用于处理客户端连接
ResetEvent(g_hEventConn);
g_hThreadServer = (HANDLE)_beginthreadex(NULL, 0, ServerProc, NULL, 0, NULL);
//创建Overlapped 结构变量
WSAOVERLAPPED AcceptOverlapped;
//创建用于AcceptEx的事件对象
WSAEVENT hWsaEventAccept = WSACreateEvent();
//创建用于AcceptEx的缓冲区
unsigned char ucBuf[256] = {0};
for (;;)
{
DWORD udwRecv = 0;
ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED));
AcceptOverlapped.hEvent = hWsaEventAccept;
//创建一个新的套接字用于AcceptEx,因为AcceptEx本身不创建套接字
SOCKET AcceptSocket = WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
//传递一个overlapped结构,这样就就可以用事件对象来通知
AcceptEx(ListenSocket, AcceptSocket, ucBuf, 0, sizeof(SOCKADDR_IN) + 16,
sizeof(SOCKADDR_IN) + 16, &udwRecv, &AcceptOverlapped);
//等待连接事件的发生,待事件被通知时记得要重置
DWORD udwWaitRet = WSAWaitForMultipleEvents(1, &hWsaEventAccept, false, WSA_INFINITE, false);
WSAResetEvent(hWsaEventAccept);
int dwLocal, dwRemote;
sockaddr_in *ptLocalAddr;
sockaddr_in *ptRemoteAddr;
//解析缓冲区读取远程地址
GetAcceptExSockaddrs(ucBuf, 0, sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16,
(sockaddr**)&ptLocalAddr, &dwLocal, (sockaddr**)&ptRemoteAddr, &dwRemote);
printf("new connection %s-%u/n", inet_ntoa(ptLocalAddr->sin_addr), ntohs(ptRemoteAddr->sin_port));
_CLIENT tClient;
//填写这个新连接的数据结构字段,包括套接字,缓冲区和事件对象
tClient.tEvent = WSACreateEvent();
tClient.sock = AcceptSocket;
tClient.tBuf.buf = new char[DATA_BUFSIZE];
tClient.tBuf.len = DATA_BUFSIZE;
ZeroMemory(&tClient.tOverLp.hEvent, sizeof(WSAOVERLAPPED));
tClient.tOverLp.hEvent = tClient.tEvent;
tClient.bHasHandled = true;
_ENTER_CS_CLT;
//如果已经有64个连接,那么就报错
if (g_mapClient.size() == 64)
{
closesocket(AcceptSocket);
printf("too much connection/n");
continue;
}
//插入到工作线程的连接表中
g_mapClient.insert(MAP_CLIENT::value_type(tClient.tEvent, tClient));
if (1 == g_mapClient.size())
{
SetEvent(g_hEventConn);
}
_LEAVE_CS_CLT;
}
CloseHandle(g_hEventConn);
CloseHandle(g_hThreadServer);
DeleteCriticalSection(&g_CSListClients);
return 0;
}