您的位置:首页 > 理论基础 > 计算机网络

基于Overlapped IO的TCP服务器模型

2009-01-17 22:38 483 查看
用了一个工作线程,可管理64个TCP连接。

// OverlappedIO.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
using std::map;
#define DATA_BUFSIZE 4096
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;
bool			bHasHandled;
}_CLIENT;
typedef map<WSAEVENT, _CLIENT> MAP_CLIENT;
MAP_CLIENT g_mapClient;
#define _ENTER_CS_CLT EnterCriticalSection(&g_CSListClients)
#define _LEAVE_CS_CLT LeaveCriticalSection(&g_CSListClients)
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;
WSAStartup(MAKEWORD(2,2),&wsaData);
//创建一个TCP套接字,带上WSA_FLAG_OVERLAPPED标志
SOCKET ListenSocket = WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
//创建用于AcceptEx的缓冲区和事件对象
unsigned char ucBuf[256] = {0};
WSAOVERLAPPED AcceptOverlapped;
WSAEVENT hWsaEventAccept = WSACreateEvent();
//bind地址开始监听
SOCKADDR_IN ServerAddr;
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_addr.S_un.S_addr = INADDR_ANY;
ServerAddr.sin_port = htons(1234);
bind(ListenSocket,(LPSOCKADDR)&ServerAddr,sizeof(ServerAddr));
listen(ListenSocket,5);
//起一个工作线程,用于处理客户端连接
ResetEvent(g_hEventConn);
g_hThreadServer = (HANDLE)_beginthreadex(NULL, 0, ServerProc, NULL, 0, NULL);
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: