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

WIN网络编程-EventSelectServer模型的服务器设计

2009-12-21 21:16 525 查看
//EventSelectServer模型的服务器设计

/////////////////////////////////////////////////////
//EventSelectServer.h文件

//主线程负责监听客户端的连接请求,接受到新连接之后,将新套节字安排给

//工作线程I/O,每个工作线程最多处理64个套节字,如果再有新的套节字,就

//再创建新的工作线程。

//服务器程序每接受到一个新的连接,便为新连接申请了一个SOCKET_OBJ结构,初

//始化该结构的成员。当连接关闭或出错时,释放内存空间。

//当客户数量增加时,服务器就要创建额外的线程去处理I/O。每创建一个线

//程,便为线程申请一个THREAD_OBJ结构,初始化该结构的成员。当客户数量减少

//,处理I/O线程关闭时,再释放内存空间。

//

//HANDLE WINAPI CreateThread(
// __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,
// __in SIZE_T dwStackSize,
// __in LPTHREAD_START_ROUTINE lpStartAddress,
// __in_opt LPVOID lpParameter,
// __in DWORD dwCreationFlags,
// __out_opt LPDWORDlpThreadId
//);

//

//

DWORD WINAPI ServerThread(LPVOIDlpParam);

// 套节字对象
typedef struct _SOCKET_OBJ
{
SOCKETs; //套节字句柄
HANDLEevent; //与此套节字相关联的事件对象句柄
sockaddr_inaddrRemote; //客户端地址信息

_SOCKET_OBJ*pNext; //指向下一个SOCKET_OBJ对象,为的是连成一个表
} SOCKET_OBJ, *PSOCKET_OBJ;

// 线程对象
typedef struct _THREAD_OBJ
{
HANDLEevents[WSA_MAXIMUM_WAIT_EVENTS]; //记录当前线程要等待的事件对象的句柄
intnSocketCount; //记录当前线程处理的套节字的数量 <= WSA_MAXIMUM_WAIT_EVENTS

PSOCKET_OBJpSockHeader; //当前线程处理的套节字对象列表,pSockHeader指向表头
PSOCKET_OBJpSockTail; //pSockTail指向表尾

CRITICAL_SECTIONcs; //关键代码段变量,为的是同步对本结构的访问
_THREAD_OBJ*pNext; //指向下一个THREAD_OBJ对象,为的是连成一个表

} THREAD_OBJ, *PTHREAD_OBJ;

// 线程列表
PTHREAD_OBJ g_pThreadList; //指向线程对象列表表头
CRITICAL_SECTIONg_cs; //同步对此全局变量的访问

// 状态信息
LONG g_nTatolConnections; //总共连接数量
LONG g_nCurrentConnections; //当前连接数量

// 申请一个套节字对象,初始化它的成员
PSOCKET_OBJ GetSocketObj(SOCKET s)
{
PSOCKET_OBJ pSocket =(PSOCKET_OBJ)::GlobalAlloc(GPTR, sizeof(SOCKET_OBJ));
if(pSocket != NULL)
{
pSocket->s =s;
pSocket->event =::WSACreateEvent();
}
return pSocket;
}

// 释放一个套节字对象
void FreeSocketObj(PSOCKET_OBJ pSocket)
{
::CloseHandle(pSocket->event);
if(pSocket->s !=INVALID_SOCKET)
{
::closesocket(pSocket->s);
}
::GlobalFree(pSocket);
}

// 申请一个线程对象,初始化它的成员,并将它添加到线程对象列表中
PTHREAD_OBJ GetThreadObj()
{
PTHREAD_OBJ pThread =(PTHREAD_OBJ)::GlobalAlloc(GPTR, sizeof(THREAD_OBJ));
if(pThread != NULL)
{
::InitializeCriticalSection(&pThread->cs);
//创建一个事件对象,用于指示该线程的句柄数组需要重组
pThread->events[0]= ::WSACreateEvent();

// 将新申请的线程对象添加到列表中
::EnterCriticalSection(&g_cs);
pThread->pNext =g_pThreadList;
g_pThreadList = pThread;
::LeaveCriticalSection(&g_cs);
}
return pThread;
}

// 释放一个线程对象,并将它从线程对象列表中移除
void FreeThreadObj(PTHREAD_OBJ pThread)
{
// 在线程对象列表中查找pThread所指的对象,如果找到就从中移除
::EnterCriticalSection(&g_cs);
PTHREAD_OBJ p = g_pThreadList;
if(p ==pThread) // 是第一个?
{
g_pThreadList =p->pNext;
}
else
{
while(p != NULL&& p->pNext !=pThread)
{
p =p->pNext;
}
if(p != NULL)
{
//此时,p是pThread的前一个,即“p->pNext == pThread”
p->pNext= pThread->pNext;
}
}
::LeaveCriticalSection(&g_cs);

// 释放资源
::CloseHandle(pThread->events[0]);
::DeleteCriticalSection(&pThread->cs);
::GlobalFree(pThread);
}

// 重新建立线程对象的events数组
void RebuildArray(PTHREAD_OBJ pThread)
{
::EnterCriticalSection(&pThread->cs);
PSOCKET_OBJ pSocket =pThread->pSockHeader;
int n = 1; //从第1个开始写,第0个用于指示需要重建了
while(pSocket != NULL)
{
pThread->events[n++]= pSocket->event;
pSocket =pSocket->pNext;
}
::LeaveCriticalSection(&pThread->cs);
}

/////////////////////////////////////////////////////////////////////

// 向一个线程的套节字列表中插入一个套节字
BOOL InsertSocketObj(PTHREAD_OBJ pThread, PSOCKET_OBJpSocket)
{
BOOL bRet = FALSE;
::EnterCriticalSection(&pThread->cs);
if(pThread->nSocketCount< WSA_MAXIMUM_WAIT_EVENTS - 1)
{
if(pThread->pSockHeader== NULL)
{
pThread->pSockHeader= pThread->pSockTail = pSocket;
}
else
{
pThread->pSockTail->pNext= pSocket;
pThread->pSockTail= pSocket;
}
pThread->nSocketCount++;
bRet = TRUE;
}
::LeaveCriticalSection(&pThread->cs);

// 插入成功,说明成功处理了客户的连接请求
if(bRet)
{
::InterlockedIncrement(&g_nTatolConnections);
::InterlockedIncrement(&g_nCurrentConnections);
}
return bRet;
}

// 将一个套节字对象安排给空闲的线程处理
void AssignToFreeThread(PSOCKET_OBJ pSocket)
{
pSocket->pNext = NULL;

::EnterCriticalSection(&g_cs);
PTHREAD_OBJ pThread = g_pThreadList;
// 试图插入到现存线程
while(pThread != NULL)
{
if(InsertSocketObj(pThread,pSocket))
break;
pThread =pThread->pNext;
}

// 没有空闲线程,为这个套节字创建新的线程
if(pThread == NULL)
{
pThread = GetThreadObj();
InsertSocketObj(pThread,pSocket);
::CreateThread(NULL, 0,ServerThread, pThread, 0, NULL);
}
::LeaveCriticalSection(&g_cs);

// 指示线程重建句柄数组
::WSASetEvent(pThread->events[0]);
}

// 从给定线程的套节字对象列表中移除一个套节字对象
void RemoveSocketObj(PTHREAD_OBJ pThread, PSOCKET_OBJpSocket)
{
::EnterCriticalSection(&pThread->cs);

// 在套节字对象列表中查找指定的套节字对象,找到后将之移除
PSOCKET_OBJ pTest =pThread->pSockHeader;
if(pTest == pSocket)
{
if(pThread->pSockHeader== pThread->pSockTail)
pThread->pSockTail= pThread->pSockHeader =pTest->pNext;
else
pThread->pSockHeader= pTest->pNext;
}
else
{
while(pTest != NULL&& pTest->pNext !=pSocket)
pTest =pTest->pNext;
if(pTest != NULL)
{
if(pThread->pSockTail== pSocket)
pThread->pSockTail= pTest;
pTest->pNext= pSocket->pNext;
}
}
pThread->nSocketCount --;

::LeaveCriticalSection(&pThread->cs);

// 指示线程重建句柄数组
::WSASetEvent(pThread->events[0]);

// 说明一个连接中断
::InterlockedDecrement(&g_nCurrentConnections);
}

BOOL HandleIO(PTHREAD_OBJ pThread, PSOCKET_OBJ pSocket)
{
// 获取具体发生的网络事件
WSANETWORKEVENTS event;
::WSAEnumNetworkEvents(pSocket->s,pSocket->event, &event);
do
{
if(event.lNetworkEvents&FD_READ) //套节字可读
{
if(event.iErrorCode[FD_READ_BIT]== 0)
{
charszText[256];
intnRecv = ::recv(pSocket->s, szText, strlen(szText),0);
if(nRecv>0)
{
szText[nRecv]= '/0';
printf("接收到数据:%s/n", szText);
}
}
else
break;
}
else if(event.lNetworkEvents& FD_CLOSE) // 套节字关闭
{
break;
}
else if(event.lNetworkEvents& FD_WRITE) // 套节字可写
{
if(event.iErrorCode[FD_WRITE_BIT]== 0)
{
}
else
break;
}
return TRUE;
}
while(FALSE);

// 套节字关闭,或者有错误发生,程序都会转到这里来执行
RemoveSocketObj(pThread, pSocket);
FreeSocketObj(pSocket);
return FALSE;
}

PSOCKET_OBJ FindSocketObj(PTHREAD_OBJ pThread, int nIndex) //nIndex从1开始
{
// 在套节字列表中查找
PSOCKET_OBJ pSocket =pThread->pSockHeader;
while(--nIndex)
{
if(pSocket == NULL)
returnNULL;
pSocket =pSocket->pNext;
}
return pSocket;
}

DWORD WINAPI ServerThread(LPVOID lpParam)
{
// 取得本线程对象的指针
PTHREAD_OBJ pThread = (PTHREAD_OBJ)lpParam;
while(TRUE)
{
// 等待网络事件
int nIndex =::WSAWaitForMultipleEvents(
pThread->nSocketCount+ 1, pThread->events, FALSE, WSA_INFINITE,FALSE);
nIndex = nIndex -WSA_WAIT_EVENT_0;
// 查看受信的事件对象
for(int i=nIndex;i<pThread->nSocketCount + 1;i++)
{
nIndex =::WSAWaitForMultipleEvents(1,&pThread->events[i], TRUE, 1000,FALSE);
if(nIndex ==WSA_WAIT_FAILED || nIndex == WSA_WAIT_TIMEOUT)
{
continue;
}
else
{
if(i==0) //events[0]受信,重建数组
{
RebuildArray(pThread);
//如果没有客户I/O要处理了,则本线程退出
if(pThread->nSocketCount== 0)
{
FreeThreadObj(pThread);
return0;
}
::WSAResetEvent(pThread->events[0]);
}
else //处理网络事件
{
//查找对应的套节字对象指针,调用HandleIO处理网络事件
PSOCKET_OBJpSocket = (PSOCKET_OBJ)FindSocketObj(pThread, i);
if(pSocket!= NULL)
{
if(!HandleIO(pThread,pSocket))
RebuildArray(pThread);
}
else
printf("Unable to find socket object /n ");
}
}
}
}
return 0;
}

///////////////////////////////////////////
// EventSelectServer.cpp文件

#include "../common/initsock.h"

#include<stdio.h>
#include <windows.h>

#include "EventSelectServer.h"

// 初始化Winsock库
CInitSock theSock;

int main()
{
USHORT nPort = 4567; //此服务器监听的端口号

// 创建监听套节字
SOCKET sListen = ::socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(nPort);
sin.sin_addr.S_un.S_addr = INADDR_ANY;
if(::bind(sListen,(sockaddr*)&sin, sizeof(sin)) ==SOCKET_ERROR)
{
printf(" Failed bind()/n");
return -1;
}
::listen(sListen, 200);

// 创建事件对象,并关联到监听的套节字
WSAEVENT event = ::WSACreateEvent();
::WSAEventSelect(sListen, event,FD_ACCEPT|FD_CLOSE);

::InitializeCriticalSection(&g_cs);

// 处理客户连接请求,打印状态信息
while(TRUE)
{
int nRet =::WaitForSingleObject(event, 5*1000);
if(nRet == WAIT_FAILED)
{
printf("Failed WaitForSingleObject() /n");
break;
}
else if(nRet ==WSA_WAIT_TIMEOUT) // 定时显式状态信息
{
printf("/n");
printf(" TatolConnections: %d /n", g_nTatolConnections);
printf("CurrentConnections: %d /n", g_nCurrentConnections);
continue;
}
else //有新的连接未决
{
::ResetEvent(event);
//循环处理所有未决的连接请求
while(TRUE)
{
sockaddr_insi;
intnLen = sizeof(si);
SOCKETsNew = ::accept(sListen, (sockaddr*)&si,&nLen);
if(sNew== SOCKET_ERROR)
break;
PSOCKET_OBJpSocket = GetSocketObj(sNew);
pSocket->addrRemote= si;
::WSAEventSelect(pSocket->s,pSocket->event, FD_READ|FD_CLOSE|FD_WRITE);
AssignToFreeThread(pSocket);
}
}
}
::DeleteCriticalSection(&g_cs);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: