#include "StdAfx.h" #include "NetServer.h" #include "NetClient.h" #include "NetLinkMain.h" CNetServer::CNetServer(CNetLinkMain* pParent) : CSocketBase(pParent), m_lpfnAcceptEx(NULL), m_lpfnGetAcceptExSockAddrs(NULL) { } CNetServer::~CNetServer(void) { // 释放掉所有的IO上下文数据 for( int i = 0; i < m_ArrayIoContext.GetCount(); ++i) { delete m_ArrayIoContext.GetAt(i); } m_ArrayIoContext.RemoveAll(); } /***************************************************************** **【函数名称】 __newIoContext **【函数功能】 创建IoContext用于接收客户端连接 **【参数】 **【返回值】 ****************************************************************/ CNetServer::PPER_IO_CONTEXT CNetServer::__newIoContext( void ) { PPER_IO_CONTEXT pContext = new PER_IO_CONTEXT; ASSERT(pContext != NULL); m_ArrayIoContext.Add( pContext ); return pContext; } /***************************************************************** **【函数名称】 __deleteIoContext **【函数功能】 删除废弃的IoContext **【参数】 **【返回值】 ****************************************************************/ void CNetServer::__deleteIoContext( PPER_IO_CONTEXT pIoContext ) { ASSERT( pIoContext != NULL ); for( int i=0; iresetBuffer(); WSABUF *pWsaBuf = &pIoContext->m_wsaBuf; OVERLAPPED *pOl = &pIoContext->m_Overlapped; // 为以后新连入的客户端先准备好Socket( 这个是与传统accept最大的区别 ) pIoContext->m_sockAccept = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); if( INVALID_SOCKET == pIoContext->m_sockAccept ) return false; // 投递AcceptEx if(FALSE == m_lpfnAcceptEx(m_Socket, pIoContext->m_sockAccept, pWsaBuf->buf, 0, sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, &dwBytes, pOl)) { if(WSA_IO_PENDING != WSAGetLastError()) return false; } return true; } /***************************************************************** **【函数名称】 doJob **【函数功能】 开始创建客户端并投递Accept请求 **【参数】 **【返回值】 ****************************************************************/ void CNetServer::doJob( OVERLAPPED* pOverLapped, DWORD BytesTransfered ) { ASSERT(pOverLapped != NULL); SOCKADDR_IN* ClientAddr = NULL; SOCKADDR_IN* LocalAddr = NULL; int remoteLen = sizeof(SOCKADDR_IN), localLen = sizeof(SOCKADDR_IN); PER_IO_CONTEXT* pIoContext = CONTAINING_RECORD(pOverLapped, PER_IO_CONTEXT, m_Overlapped); ASSERT(pIoContext != NULL); // 1. 首先取得连入客户端的地址信息 m_lpfnGetAcceptExSockAddrs(pIoContext->m_wsaBuf.buf, 0, sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, (LPSOCKADDR*)&LocalAddr, &localLen, (LPSOCKADDR*)&ClientAddr, &remoteLen); // 2. 创建客户端 CNetClient* pClient = new CNetClient(m_pParent, PDU_LINK_TYPE_SERVER, false); ASSERT(pClient != NULL); pClient->assoSocket() = pIoContext->m_sockAccept; pClient->setFarLinkInfo(inet_ntoa(ClientAddr->sin_addr), ntohs(ClientAddr->sin_port)); // 3. 使用完毕之后,投递新的AcceptEx __postAccept( pIoContext ); // 4. 通知上层新客户端到来 m_pParent->onConnEstablished(pClient); } /***************************************************************** **【函数名称】 create **【函数功能】 创建 **【参数】 **【返回值】 ****************************************************************/ bool CNetServer::create( int a_ListenPort ) { m_Socket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); if (INVALID_SOCKET == m_Socket) return false; // 将Listen Socket绑定至完成端口中 if(!m_pParent->waitNetEnd(this)) return false; struct sockaddr_in ServerAddress; // 填充地址信息 ZeroMemory(&ServerAddress, sizeof(ServerAddress)); ServerAddress.sin_family = AF_INET; ServerAddress.sin_addr.s_addr = htonl(INADDR_ANY); ServerAddress.sin_port = htons(a_ListenPort); // 绑定地址和端口 if (SOCKET_ERROR == bind(m_Socket, (struct sockaddr *) &ServerAddress, sizeof(ServerAddress))) return false; // 开始进行监听 if (SOCKET_ERROR == listen(m_Socket, SOMAXCONN)) return false; // 使用AcceptEx函数,因为这个是属于WinSock2规范之外的微软另外提供的扩展函数 // 所以需要额外获取一下函数的指针 GUID GuidAcceptEx = WSAID_ACCEPTEX; GUID GuidGetAcceptExSockAddrs = WSAID_GETACCEPTEXSOCKADDRS; DWORD dwBytes = 0; if(SOCKET_ERROR == WSAIoctl(m_Socket, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidAcceptEx, sizeof(GuidAcceptEx), &m_lpfnAcceptEx, sizeof(m_lpfnAcceptEx), &dwBytes, NULL, NULL)) { return false; } // 获取GetAcceptExSockAddrs函数指针,也是同理 if(SOCKET_ERROR == WSAIoctl(m_Socket, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidGetAcceptExSockAddrs, sizeof(GuidGetAcceptExSockAddrs), &m_lpfnGetAcceptExSockAddrs, sizeof(m_lpfnGetAcceptExSockAddrs), &dwBytes, NULL, NULL)) { return false; } // 为AcceptEx 准备参数,然后投递AcceptEx I/O请求 for( int i = 0; i < MAX_POST_ACCEPT; ++i ) { PER_IO_CONTEXT* pAcceptIoContext = __newIoContext(); if(!__postAccept(pAcceptIoContext)) { __deleteIoContext(pAcceptIoContext); return false; } } return true; }