IOCP框架之Windows Sockets网络编程
//
// IPv4 Socket address, Internet style
//
typedef struct sockaddr_in {
#if(_WIN32_WINNT < 0x0600)
short sin_family;
#else //(_WIN32_WINNT < 0x0600)
ADDRESS_FAMILY sin_family;
#endif //(_WIN32_WINNT < 0x0600)
USHORT sin_port;
IN_ADDR sin_addr;
CHAR sin_zero[8];
} SOCKADDR_IN, *PSOCKADDR_IN;
//
// Structure used to store most addresses.
//
typedef struct sockaddr {
#if (_WIN32_WINNT < 0x0600)
u_short sa_family;
#else
ADDRESS_FAMILY sa_family; // Address family.
#endif //(_WIN32_WINNT < 0x0600)
CHAR sa_data[14]; // Up to 14 bytes of direct address.
} SOCKADDR, *PSOCKADDR, FAR *LPSOCKADDR;
/*
* 启动服务--
* 1. 启动接受客户端连接线程-- 接受客户端连接到服务器的请求
* 2. 启动服务线程-- 处理每个客户端的数据操作
*/
void CServerView::OnServerStart()
{
int reVal; //返回值
CServerAddrDlg servAddrDlg; //服务器地址对话框
if (IDOK != servAddrDlg.DoModal())
{
return;
}
// 1. 初始化套接字动态库
WSADATA wsaData;
if ((reVal = WSAStartup(0x0202, &wsaData)) != 0)
{
AfxMessageBox(_T("初始化套接字动态库错误!"));
return ;
}
// 2. 创建套接字
if ((m_sListen = WSASocket(AF_INET,
SOCK_STREAM,
0,
NULL,
0,
WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
{
AfxMessageBox(_T("创建套接字错误!"));
WSACleanup();
return ;
}
// 3. 绑定套接字
SOCKADDR_IN servAddr;//服务器地址
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl(servAddrDlg.m_dwServIP);
servAddr.sin_port = htons(servAddrDlg.m_shServPort);
if (bind(m_sListen, (SOCKADDR*)&servAddr, sizeof(servAddr))
== SOCKET_ERROR)
{
AfxMessageBox(_T("绑定套接字错误!"));
closesocket(m_sListen);
WSACleanup();
return ;
}
// 4. 设置监听套接字(Listen Socket)进入监听状态
if(listen(m_sListen, SOMAXCONN) == SOCKET_ERROR)
{
AfxMessageBox(_T("监听套接字错误!"));
closesocket(m_sListen);
WSACleanup();
return ;
}
m_bRunning = TRUE; //服务器运行Flag
// 5. 创建接受客户端连接事件对象
m_hEvent = WSACreateEvent();
if ( m_hEvent == WSA_INVALID_EVENT )
{
closesocket(m_sListen);
WSACleanup();
return ;
}
// 6. 为监听套接字注册FD_ACCEPT网络事件
int nRet = WSAEventSelect(m_sListen,
m_hEvent,
FD_ACCEPT);
if ( nRet == SOCKET_ERROR )
{
AfxMessageBox(_T("注册网络事件错误!"));
closesocket(m_sListen);
WSACleanup();
return ;
}
// 7. 创建完成端口
if ((m_hCompPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)) == NULL)
{
AfxMessageBox(_T("创建完成端口失败!"));
WSACloseEvent(m_hEvent);
closesocket(m_sListen);
WSACleanup();
return ;
}
// 8. 创建接受客户端请求线程-- Socket IO模型:WSAEventSelect模型
DWORD dwThreadID;
m_hThread[0] = CreateThread(NULL,
0,
AcceptThread, // 接受客户端连接线程
this,
0,
&dwThreadID);
if (NULL == m_hThread[0])
{
AfxMessageBox(_T("创建接受客户端线程失败!"));
WSACloseEvent(m_hEvent);
closesocket(m_sListen);
WSACleanup();
return;
}
m_nThreadNum = 1;
// 获取CPU数量,根据CPU数量创建服务线程
SYSTEM_INFO SystemInfo;
GetSystemInfo(&SystemInfo);
// 9. 创建服务线程-- Socket IO模型:IOCP模型
for(int i = 0; i < SystemInfo.dwNumberOfProcessors * 2; i++)
{
if ((m_hThread[m_nThreadNum++] = CreateThread(NULL,
0,
ServiceThread, // 服务器服务线程
this,
0,
&dwThreadID)) == NULL)
{
AfxMessageBox(_T("创建服务线程失败!"));
WSACloseEvent(m_hEvent);
closesocket(m_sListen);
WSACleanup();
return ;
}
}
//设置定时器,每分钟发送WM_TIMER消息
SetTimer(1, 1000 * 60 * 1,NULL);
}
/*
* 接收客户端连接请求-- Socket IO模型:WSAEventSelect模型
*/
DWORD WINAPI CServerView::AcceptThread( void *pParam )
{
CServerView *pServView = (CServerView*)pParam; //主窗口指针
HANDLE hComPort = pServView->m_hCompPort; //完成端口
SOCKET sListen = pServView->m_sListen; //监听套接字
SOCKET sAccept = INVALID_SOCKET; //接受套接字
while(pServView->m_bRunning)
{
DWORD dwRet;
dwRet = WSAWaitForMultipleEvents(1, //等待网络事件
&pServView->m_hEvent,
FALSE,
100,
FALSE);
if(!pServView->m_bRunning) //服务器停止服务
break;
if (dwRet == WSA_WAIT_TIMEOUT) //函数调用超时
continue; // 进入下一轮等待
WSANETWORKEVENTS events; //查看发生的网络事件
int nRet = WSAEnumNetworkEvents(pServView->m_sListen,
pServView->m_hEvent,//事件对象被重置
&events);
if (nRet == SOCKET_ERROR)
{
AfxMessageBox(_T("WSAEnumNetworkEvents函数错误"));
break;
}
if ( events.lNetworkEvents & FD_ACCEPT) //发生FD_ACCEPT网络事件
{
if ( events.iErrorCode[FD_ACCEPT_BIT] == 0 && pServView->m_bRunning)
{
// 1. 接受客户端请求
SOCKADDR_IN servAddr;
int serAddrLen = sizeof(servAddr);
if ((sAccept = WSAAccept(sListen,
(SOCKADDR*)&servAddr,
&serAddrLen,
NULL,
0)) == SOCKET_ERROR)
{
AfxMessageBox(_T("WSAAccept函数错误"));
break;
}
// 2. 创建客户端节点
CClientContext *pClient = new CClientContext(sAccept,pServView);
if (CreateIoCompletionPort((HANDLE)sAccept, //套接字与完成端口关联起来
hComPort,
(DWORD) pClient,//完成键
0) == NULL)
{
return -1;
}
// 3. 加入管理客户端链表
CClientManager *pClientMgr = CClientManager::GetClientManager();
pClientMgr->AddClient(pClient);
// 4. 发起第一个针对该连接客户端的异步操作-- 等待接收客户端发送的数据
if(!pClient->AsyncRecvHead()) //接收数据-- 发起异步操作
{
pClientMgr->DeleteClient(pClient);
}
} // end if ( events.iErrorCode[FD_ACCEPT_BIT] == 0 && pServView->m_bRunning)
} // end if ( events.lNetworkEvents & FD_ACCEPT)
} // end while()
//释放资源
CClientManager *pClientMgr = CClientManager::GetClientManager();
pClientMgr->DeleteAllClient();
pClientMgr->ReleaseManager();
// exit
return 0;
}
/*
* 服务线程-- Socket IO模型:IOCP模型
*/
DWORD WINAPI CServerView::ServiceThread( void *pParam )
{
CServerView *pServerView = (CServerView*)pParam;//主窗口指针
HANDLE hComPort = pServerView->m_hCompPort;//完成端口
DWORD dwIoSize; //传输字节数
CClientContext *pClient; //客户端指针
LPOVERLAPPED lpOverlapped; //重叠结构指针
bool bExit = FALSE; //服务线程退出
while (!bExit)
{
dwIoSize = -1;
lpOverlapped = NULL;
pClient = NULL;
// 根据IO完成通知返回结果,进行不同的情况的数据处理
// 1. 等待I/O操作结果
BOOL bIORet = GetQueuedCompletionStatus(hComPort,
&dwIoSize,
(LPDWORD) &pClient,
&lpOverlapped,
INFINITE);
// 1.1 失败的操作完成
if (FALSE == bIORet && NULL != pClient)
{
//客户端断开
if (CClientContext::DOING == pClient->m_eState
|| CClientContext::LOGIN == pClient->m_eState)
{
pClient->m_eState = CClientContext::DISCON;
pClient->SaveDisConnectState();
}
CClientManager *pClientMgr = CClientManager::GetClientManager();
pClientMgr->DeleteClient(pClient);
}
// 1.2 成功的操作完成
if(bIORet && lpOverlapped && pClient)
{
CClientManager *pClientMgr = CClientManager::GetClientManager();
// 进行当前客户端完成的IO数据处理-- 客户端管理器负责Client类内函数的派遣
pClientMgr->ProcessIO(pClient, lpOverlapped, dwIoSize);
}
// 1.3 服务器退出
if(pClient == NULL&& lpOverlapped ==NULL && !pServerView->m_bRunning)
{
bExit = TRUE;
}
}
// exit code = 0
return 0;
}