// CMyIOCPServer.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "stdio.h"
//目标,使用IOCP机制做一个聊天的服务器,这个服务器记录连接的用户,并转发客户端自己的消息,相当于一个群聊天工具
#define PACKET_LEN 2048
struct PER_IO_CONTEXT
{
SOCKET hSocket;
in_addr RemoteAddr;
int nPort;
PER_IO_CONTEXT *pNext;
};
typedef struct _PER_IO_DATA
{
OVERLAPPED ol;
int len;
char buf[PACKET_LEN];
int nOperateType;
#define OP_READ 1
#define OP_WRITE 2
}PER_IO_DATA;
DWORD WINAPI ServerThread(LPVOID lParam);
BOOL PostRecv(PER_IO_CONTEXT *pPerIoContext,PER_IO_DATA *pPerIoData);
BOOL PostSend(PER_IO_CONTEXT *pPerIoContext,PER_IO_DATA *pPerIoData);
//记录一个全局的列表,用来向全部连接的客户进行消息转发
PER_IO_CONTEXT *g_pPerIoContext;
int g_nClient;
void AddIoContext(PER_IO_CONTEXT *pPerIoContext);
void GetIoContext(PER_IO_CONTEXT *pPerIoContext);
void RemoveIoContext(PER_IO_CONTEXT *pPerIoContext);
void SendToOther(PER_IO_CONTEXT *pSelfContext,PER_IO_DATA *pPerIoData);
void ReleaseData(PER_IO_DATA *pIoData);
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2),&wsaData);
g_pPerIoContext=NULL;
g_nClient=0;
SOCKET hListen=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
HANDLE hCompletion=::CreateIoCompletionPort(INVALID_HANDLE_VALUE,0,0,0);
CreateThread(NULL,0,ServerThread,(LPVOID)hCompletion,0,0);
SOCKADDR_IN sin;
sin.sin_addr.S_un.S_addr=ADDR_ANY;
sin.sin_family=AF_INET;
sin.sin_port=htons(3456);
if(::bind(hListen,(sockaddr*)&sin,sizeof(sin))==SOCKET_ERROR)
{
printf("bind error.\n");
goto LINEERROR;
}
if(::listen(hListen,10)==SOCKET_ERROR)
{
printf("listen error.\n");
goto LINEERROR;
}
printf("服务器开始监听....... \n");
SOCKADDR_IN acceptAddr;
int acceptLen=sizeof(SOCKADDR_IN);
SOCKET hClient;
while(true)
{
hClient=::accept(hListen,(sockaddr*)&acceptAddr,&acceptLen);
PER_IO_CONTEXT *pIoContext=(PER_IO_CONTEXT*)GlobalAlloc(GPTR,sizeof(PER_IO_CONTEXT));
pIoContext->hSocket=hClient;
pIoContext->RemoteAddr=acceptAddr.sin_addr;
pIoContext->nPort=ntohs(acceptAddr.sin_port);
AddIoContext(pIoContext);
g_nClient++;
printf("一个客户到来了 IP:PORT %s:%d (%d) \n",inet_ntoa(acceptAddr.sin_addr),pIoContext->nPort,g_nClient);
::CreateIoCompletionPort((HANDLE)pIoContext->hSocket,hCompletion,(DWORD)pIoContext,0);
//投递一个请求
PER_IO_DATA *pIoData=(PER_IO_DATA*)GlobalAlloc(GPTR,sizeof(PER_IO_DATA));
pIoData->len=PACKET_LEN;
PostRecv(pIoContext,pIoData);
}
LINEERROR:
WSACleanup();
return 0;
}
DWORD WINAPI ServerThread(LPVOID lParam)
{
HANDLE hCompletionPort=(HANDLE)lParam;
DWORD dwTransferBytes=0;
PER_IO_CONTEXT *pIoContext;
PER_IO_DATA *pIoData;
while(TRUE)
{
BOOL bOK=GetQueuedCompletionStatus(hCompletionPort,&dwTransferBytes,(LPDWORD)&pIoContext,(LPOVERLAPPED*)&pIoData,WSA_INFINITE);
if(!bOK)
{
int nError=GetLastError();
printf("GetQueuedCompletionStatus Error! %d \n",nError);
g_nClient--;
printf("一个客户发生了错误 IP:PORT %s:%d (%d) \n",inet_ntoa(pIoContext->RemoteAddr),pIoContext->nPort,g_nClient);
RemoveIoContext(pIoContext);
continue;
}
else if(dwTransferBytes==0)
{
g_nClient--;
printf("一个客户断开了 IP:PORT %s:%d (%d) \n",inet_ntoa(pIoContext->RemoteAddr),pIoContext->nPort,g_nClient);
RemoveIoContext(pIoContext);
}
else
{
if(pIoData->nOperateType==OP_READ)
{
//转发消息
SendToOther(pIoContext,pIoData);
//投递一个请求
PER_IO_DATA *p2IoData=(PER_IO_DATA*)GlobalAlloc(GPTR,sizeof(PER_IO_DATA));
p2IoData->len=PACKET_LEN;
PostRecv(pIoContext,p2IoData);
}
else if(pIoData->nOperateType==OP_WRITE) //写操作完成了
{
ReleaseData(pIoData);
}
}
}
}
void AddIoContext(PER_IO_CONTEXT *pPerIoContext)
{
//维护一个链表
pPerIoContext->pNext=g_pPerIoContext;
g_pPerIoContext=pPerIoContext;
}
void GetIoContext(PER_IO_CONTEXT *pPerIoContext)
{
}
void RemoveIoContext(PER_IO_CONTEXT *pPerIoContext)
{
if(g_pPerIoContext==NULL)
return;
else if(g_pPerIoContext->hSocket==pPerIoContext->hSocket)
{
g_pPerIoContext=g_pPerIoContext->pNext;
}
else
{
PER_IO_CONTEXT *pIoContext=g_pPerIoContext;
PER_IO_CONTEXT *pNextIoContext=pIoContext->pNext;
while(pNextIoContext)
{
if(pNextIoContext->hSocket==pPerIoContext->hSocket)
{
pIoContext->pNext=pNextIoContext->pNext;
break;
}
pIoContext=pNextIoContext;
pNextIoContext=pNextIoContext->pNext;
}
}
::closesocket(pPerIoContext->hSocket);
GlobalFree((HGLOBAL)pPerIoContext);
}
void SendToOther(PER_IO_CONTEXT *pSelfContext,PER_IO_DATA *pPerIoData)
{
if(g_pPerIoContext==NULL)
return;
else
{
PER_IO_CONTEXT *pIoContext=g_pPerIoContext;
while(pIoContext)
{
if(pSelfContext->hSocket!=pIoContext->hSocket)
{
PostSend(pIoContext,pPerIoData);
//::send(pIoContext->hSocket,pPerIoData->buf,pPerIoData->len,0);
}
pIoContext=pIoContext->pNext;
}
}
}
void ReleaseData(PER_IO_DATA *pIoData)
{
::GlobalFree((HGLOBAL)pIoData);
}
BOOL PostRecv(PER_IO_CONTEXT *pPerIoContext,PER_IO_DATA *pPerIoData)
{
pPerIoData->nOperateType=OP_READ;
WSABUF buf;
buf.buf=pPerIoData->buf;
buf.len=pPerIoData->len;
DWORD dwRecvBytes=0;
DWORD dwFlags=0;
if(WSARecv(pPerIoContext->hSocket,&buf,1,&dwRecvBytes,&dwFlags,&pPerIoData->ol,NULL)!=NO_ERROR)
{
if(WSAGetLastError()==WSA_IO_PENDING)
return TRUE;
return FALSE;
}
return TRUE;
}
BOOL PostSend(PER_IO_CONTEXT *pPerIoContext,PER_IO_DATA *pPerIoData)
{
pPerIoData->nOperateType=OP_WRITE;
WSABUF buf;
buf.buf=pPerIoData->buf;
buf.len=pPerIoData->len;
DWORD dwSendBytes=0;
DWORD dwFlags=0;
if(WSASend(pPerIoContext->hSocket,&buf,1,&dwSendBytes,dwFlags,&pPerIoData->ol,NULL)!=NO_ERROR)
{
if(WSAGetLastError()==WSA_IO_PENDING)
return TRUE;
return FALSE;
}
return TRUE;
}