// iocpServer.cpp : Defines the entry point for the console application.
//
/***************************************
服务器实现功能:回显,接收客户端信息并返回给客户端
单线程
****************************************/
#include "stdafx.h"
#include <WinSock2.h>
#include <MSWSock.h>
#pragma comment(lib,"Ws2_32.lib")
#define BUFFER_SIZE 1024
void InitSocket(BYTE minorVer = 2, BYTE majorVer = 2)
{
//初始化 WS2_32.dll
WSADATA wsaData;
WORD sockVertion = MAKEWORD(minorVer, majorVer);
if (::WSAStartup(sockVertion, &wsaData) != 0)
{
exit(0);
}
}
void ClearSocket()
{
::WSACleanup();
}
typedef struct _SOCKET_OBJ //记录与套接字的相关信息
{
SOCKET s; //套接字句柄
int nOutstandingOps; //记录此套接字上的重叠I/O数量
LPFN_ACCEPTEX lpfnAcceptEx;//扩展函数AcceptEx 指针(仅对监听套接字而言);
} SOCKET_OBJ, *PSOCKET_OBJ;
PSOCKET_OBJ GetSocketObj(SOCKET s)//申请套接字对象
{
PSOCKET_OBJ pSocket = (PSOCKET_OBJ)::GlobalAlloc(GPTR, sizeof(SOCKET_OBJ));
if (pSocket != NULL)
{
pSocket->s = s;
}
return pSocket;
}
void FreeSocketObj(PSOCKET_OBJ pSocket)//释放套接字对象
{
if (pSocket->s != INVALID_SOCKET)
{
::closesocket(pSocket->s);
}
::GlobalFree(pSocket);
}
typedef struct _BUFFER_OBJ //缓冲区对象
{
OVERLAPPED ol;//重叠结构
char * buff;//send/recv/AcceptEx 所使用的缓冲区
int nLen; //buff 的长度
PSOCKET_OBJ pSocket; //此 I/O所属的套接字对象
int nOperetion; //提交的操作类型
#define OP_ACCEPT 1
#define OP_READ 2
#define OP_WRITE 3
SOCKET sAccept; //用来保存AcceptEx 接受的客户套接字(仅对监听套接字而言)
_BUFFER_OBJ *pNext;
} BUFFER_OBJ, *PBUFFER_OBJ;
HANDLE g_events[WSA_MAXIMUM_WAIT_EVENTS]; // I/O 事件句柄数组
int g_nBufferCount; //上数组中有效句柄数量
PBUFFER_OBJ g_pBufferHead, g_pBufferTail; //记录缓冲区对象组成的表的地址
PBUFFER_OBJ GetBufferObj(PSOCKET_OBJ pSocket, ULONG nLen)
{
if (g_nBufferCount > WSA_MAXIMUM_WAIT_EVENTS - 1)
return NULL;
PBUFFER_OBJ pBuffer = (PBUFFER_OBJ)::GlobalAlloc(GPTR,sizeof(BUFFER_OBJ));
if(pBuffer != NULL)
{
pBuffer->buff = (char*)::GlobalAlloc(GPTR, nLen);
pBuffer->ol.hEvent = ::WSACreateEvent(); //参数有歧义
pBuffer->pSocket = pSocket;
pBuffer->sAccept = INVALID_SOCKET;
//将新的BUFFER_OBJ 添加到列表中
if(g_pBufferHead == NULL)
{
g_pBufferHead = g_pBufferTail = pBuffer;
}
else
{
g_pBufferTail->pNext = pBuffer;
g_pBufferTail = pBuffer;
}
g_events[++ g_nBufferCount] = pBuffer->ol.hEvent;
}
return pBuffer;
}
void FreeBufferObj(PBUFFER_OBJ pBuffer)
{
//从列表中移除BUFFER_OBJ 对象
PBUFFER_OBJ pTest = g_pBufferHead;
BOOL bFind = FALSE;
if (pTest == pBuffer)
{
g_pBufferHead = g_pBufferTail = NULL;
bFind = TRUE;
}
else
{
while (pTest !=NULL && pTest->pNext != pBuffer)
{
pTest = pTest->pNext;
}
if (pTest != NULL)
{
pTest->pNext = pBuffer->pNext;
if (pTest->pNext == NULL)
{
g_pBufferTail = pTest;
}
bFind = TRUE;
}
}
//释放它占用的内存空间
if(bFind)
{
g_nBufferCount --;
::CloseHandle(pBuffer->ol.hEvent);
::GlobalFree(pBuffer->buff);
::GlobalFree(pBuffer);
}
}
PBUFFER_OBJ FindBufferObj(HANDLE hEvent)//从缓冲区查找BUFFER_OBJ对象
{
PBUFFER_OBJ pBuffer = g_pBufferHead;
while (pBuffer != NULL)
{
if (pBuffer->ol.hEvent == hEvent)
{
break;
}
pBuffer = pBuffer->pNext;
}
return pBuffer;
}
void RebuidArray()//更新事件句柄数组g_events中的内容
{
PBUFFER_OBJ pBuffer = g_pBufferHead;
int i = 1;
while(pBuffer != NULL)
{
g_events[++i] = pBuffer->ol.hEvent;
pBuffer = pBuffer->pNext;
}
}
//提交重叠I/O
BOOL PostAccept(PBUFFER_OBJ pBuffer)
{
PSOCKET_OBJ pSocket = pBuffer->pSocket;
if (pSocket->lpfnAcceptEx != NULL)
{
//设置I/O类型,增加套接字上的重叠I/O计数
pBuffer->nOperetion = OP_ACCEPT;
pSocket->nOutstandingOps ++;
//投递此重叠I/O
DWORD dwBytes;
pBuffer->sAccept =
::WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
BOOL b = pSocket->lpfnAcceptEx(pSocket->s,
pBuffer->sAccept,
pBuffer->buff,
BUFFER_SIZE - ((sizeof(sockaddr_in) + 16) *2),
sizeof(sockaddr_in) + 16,
sizeof(sockaddr_in) + 16,
&dwBytes,
&pBuffer->ol);
if (!b)
{
if (::WSAGetLastError() != WSA_IO_PENDING)
{
return FALSE;
}
}
return TRUE;
}
return FALSE;
}
BOOL PostRecv(PBUFFER_OBJ pBuffer)
{
//设置I/O类型,增加套接字上的重叠I/O计数
pBuffer->nOperetion = OP_READ;
pBuffer->pSocket->nOutstandingOps ++;
//投递此重叠I/O
DWORD dwBytes;
DWORD dwFlags = 0;
WSABUF buf;
buf.buf = pBuffer->buff;
buf.len = pBuffer->nLen;
if (::WSARecv(pBuffer->pSocket->s, &buf, 1, &dwBytes, &dwFlags, &pBuffer->ol, NULL) != NO_ERROR)
{
if (::WSAGetLastError() != WSA_IO_PENDING)
{
return FALSE;
}
}
return TRUE;
}
BOOL PostSend(PBUFFER_OBJ pBuffer)
{
//设置I/O类型, 增加套接字上的重叠I/O计数
pBuffer->nOperetion = OP_WRITE;
pBuffer->pSocket->nOutstandingOps ++;
//投递此重叠I/O
DWORD dwBytes;
DWORD dwFlags = 0;
WSABUF buf;
buf.buf = pBuffer->buff;
buf.len = pBuffer->nLen;
if (::WSASend(pBuffer->pSocket->s, &buf, 1, &dwBytes, dwFlags, &pBuffer->ol, NULL) != NO_ERROR)
{
if (::WSAGetLastError() != WSA_IO_PENDING)
{
return FALSE;
}
}
return TRUE;
}
BOOL HandleIO(PBUFFER_OBJ pBuffer)
{
PSOCKET_OBJ pSocket = pBuffer->pSocket;
//从BUFFER_OBJ 对象中提取SOCKET_OBJ 对象指针,为的是方便引用
pSocket->nOutstandingOps --;
//获取重叠操作结果
DWORD dwTrans;
DWORD dwFlags;
BOOL bRet = ::WSAGetOverlappedResult(pSocket->s, &pBuffer->ol, &dwTrans, FALSE,&dwFlags);
if (!bRet)
{
//在此套接字上有错误发生,因此关闭套接字,移除缓冲区对象
//如果没有其他抛出的I/O请求了,释放缓冲区对象,否则,等待此套接字上的其他I/O完成
if(pSocket->s != INVALID_SOCKET)
{
::closesocket(pSocket->s);
pSocket->s = INVALID_SOCKET;
}
if (pSocket->nOutstandingOps == 0)
{
FreeSocketObj(pSocket);
}
FreeBufferObj(pBuffer);
return FALSE;
}
//没有错误发生,处理已完成的I/O
switch(pBuffer->nOperetion)
{
case OP_ACCEPT: //接收到一个新的连接,并接收对方发来的第一个封包
{
//为新用户创建一个 SOCKET_OBJ 对象
PSOCKET_OBJ pClient = GetSocketObj(pBuffer->sAccept);
//为发送数据创建一个BUFFER_OBJ对象,这个对象会在套接字出错或关闭时释放
PBUFFER_OBJ pSend = GetBufferObj(pClient,BUFFER_SIZE);
if (pSend == NULL)
{
printf("Too Much Connections!\n");
FreeSocketObj(pClient);
return FALSE;
}
RebuidArray();
//数据复制到发送缓冲区
pSend->nLen = dwTrans;
memcpy(pSend->buff, pBuffer->buff,dwTrans);
//投递此发送I/O(将数据回显给客户)
if(!PostSend(pSend))
{
//如果出错,释放上面申请的两个对象
FreeSocketObj(pSocket);
FreeBufferObj(pSend);
return FALSE;
}
//继续投递接收I/O
PostAccept(pBuffer);
break;
}
case OP_READ: //接收数据完成
{
if (dwTrans > 0)
{
//创建一个缓冲区,已发送数据。这里就是用原来的缓冲区
PBUFFER_OBJ pSend = pBuffer;
pSend->nLen = dwTrans;
//投递发送I/O(将数据回显给客户)
PostSend(pSend);
}
else //套接字关闭
{
//必须关闭套接字,以便在此套接字上投递其他的I/O也返回
if(pSocket->s != INVALID_SOCKET)
{
::closesocket(pSocket->s);
pSocket->s = INVALID_SOCKET;
}
if (pSocket->nOutstandingOps == 0)
{
FreeSocketObj(pSocket);
}
FreeBufferObj(pBuffer);
return FALSE;
}
break;
}
case OP_WRITE: //发送数据完成
{
if(dwTrans > 0)
{
//继续使用这个缓冲区投递接收数据的请求
pBuffer->nLen = BUFFER_SIZE;
PostRecv(pBuffer);
}
else //套接字关闭
{
//同样要先关闭套接字
if(pSocket->s != INVALID_SOCKET)
{
::closesocket(pSocket->s);
pSocket->s = INVALID_SOCKET;
}
if (pSocket->nOutstandingOps == 0)
{
FreeSocketObj(pSocket);
}
FreeBufferObj(pBuffer);
return FALSE;
}
break;
}
}
return TRUE;
}
/*main 函数的作用
1.创建监听套接字,投递监听I/O
2.循环处理I/O事件
*/
int _tmain(int argc, _TCHAR* argv[])
{
InitSocket();
//创建监听套接字,绑定到本地端口,进入监听模式
int nPort = 4567;
SOCKET sListen =
::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
SOCKADDR_IN si;
si.sin_family = AF_INET;
si.sin_port = ::ntohs(nPort);
si.sin_addr.S_un.S_addr = INADDR_ANY;
::bind(sListen, (sockaddr*)&si, sizeof(si));
::listen(sListen, 200);
//为监听套接字创建一个SOCKET_OBJ对象
PSOCKET_OBJ pListen = GetSocketObj(sListen);
//加载扩展函数 AcceptEx;
GUID GuidAcceptEx = WSAID_ACCEPTEX;
DWORD dwBytes;
WSAIoctl(pListen->s,
SIO_GET_EXTENSION_FUNCTION_POINTER,
&GuidAcceptEx,
sizeof(GuidAcceptEx),
&pListen->lpfnAcceptEx,
sizeof(pListen->lpfnAcceptEx),
&dwBytes,
NULL,
NULL);
//创建用来重新建立g_events数组的事件对象
g_events[0] = ::WSACreateEvent();
//在此可以投递多个I/O请求
for(int i = 0; i< 5; ++i)
{
PostAccept(GetBufferObj(pListen, BUFFER_SIZE));
}
while(TRUE)
{
int nIndex =
::WSAWaitForMultipleEvents(g_nBufferCount + 1, g_events, FALSE, WSA_INFINITE, FALSE);
if (nIndex == WSA_WAIT_FAILED)
{
printf("WSAWaitForMultipleEvents() failed\n");
break;
}
nIndex = nIndex - WSA_WAIT_EVENT_0;
for (int i =0; i<= nIndex; ++i)
{
int nRet = ::WSAWaitForMultipleEvents(1, &g_events[i], TRUE, 0, FALSE);
if (nRet == WSA_WAIT_TIMEOUT)
{
continue;
}
else
{
::WSAResetEvent(g_events[i]);
//重建g_events数组
if(i == 0)
{
RebuidArray();
continue;
}
//处理这个I/O
PBUFFER_OBJ pBuffer = FindBufferObj(g_events[i]);
if (pBuffer != NULL)
{
if (!HandleIO(pBuffer))
{
RebuidArray();
}
}
}
}
}
return 0;
}