#ifndef __NetWork_H__
#define __NetWork_H__
#include <queue>
#include <winsock2.h>
#pragma comment( lib, "Ws2_32.lib" )
#include <MSWSock.h>
#include <windows.h>
#include <SocketErrorString.h>
//#define TRACE_CLOSESOCKET // 追踪连接关闭
//#define TRACE_RECVSEND // 追踪收发数据
//////////////////////////////////////////////////////////////////////////
// 网络消息 消息头 所有消息必须从这个结构派生
#define NET_PACKET_SIZE 255
#pragma pack( push )
#pragma pack( 1 )
struct NetPacket
{
BYTE pakType;
BYTE pakSize;
};
#pragma pack( pop )
//////////////////////////////////////////////////////////////////////////
// 网络连接标识字符串,用来表示某个客户端连接是否是这个程序的客户端
extern char *NetWork_IdentifyString;
namespace NetWork
{
// 网络传输功能启动
bool Startup();
// 网络传输功能关闭
void Cleanup();
// 完成端口每操作缓冲区大小
const DWORD IOCP_OVERLAPPED_BUFFER_LEN = 1024;
// 完成端口完成操作类型
enum IOCP_OP
{
IOP_ACCEPT, // 接受操作完成
IOP_SEND, // 发送操作完成
IOP_RECV, // 接收操作完成
IOP_END, // 接受操作完成
IOP_MAXOP
};
// 完成建
typedef struct tagCOMPLETIONKEY {
SOCKET s;
void * o;
} COMPLETIONKEY, *LPCOMPLETIONKEY;
// 重叠端口重叠结构
typedef struct tagIOCPOVERLAPPED {
OVERLAPPED o;
SOCKET s;
SOCKET sa;
IOCP_OP OP;
char buf[IOCP_OVERLAPPED_BUFFER_LEN];
DWORD len;
} IOCPOVERLAPPED, *LPIOCPOVERLAPPED;
// 启动完成端口
bool InitIOCP();
// 结束完成端口
void TermIOCP();
// 完成端口句柄
extern HANDLE hIoCompletionPort;
// 处理接受完成
void OnIoAccept ( LPCOMPLETIONKEY pCK, LPIOCPOVERLAPPED pOL, DWORD& dwTrans );
// 处理发送完成
void OnIoSend ( LPCOMPLETIONKEY pCK, LPIOCPOVERLAPPED pOL, DWORD& dwTrans );
// 处理接收完成
void OnIoRecv ( LPCOMPLETIONKEY pCK, LPIOCPOVERLAPPED pOL, DWORD& dwTrans );
// 处理结束完成
void OnIoClose ( LPCOMPLETIONKEY pCK, LPIOCPOVERLAPPED pOL, DWORD& dwTrans );
}
// IOCP 缓冲区
class CIocpBuffer
{
public:
CIocpBuffer( DWORD dwBufferSize );
virtual ~CIocpBuffer();
// 压入数据
void Push( char *pData, DWORD dwSize, bool &bSucceed );
// 弹出数据
void Pop( char *pData, DWORD &dwSize );
void Lock();
void Lock( char *&pData, DWORD &dwSize );
void Unlock();
void Empty();
bool IsEmpty();
// 缓冲区大小
DWORD m_dwBufferSize;
// 数据缓冲
char *m_caData;
// 数据长度
DWORD m_dwLength;
// 关键区
CRITICAL_SECTION m_CRS;
};
// 常量定义
static const int IOCPSOCKET_RECV_BUFFER_LEN = 2048; // 接收缓存大小
static const int IOCPSOCKET_SEND_BUFFER_LEN = 1024; // 发送缓冲大小
// 完成端口模型
class CSocketIoCompletionPort
{
public:
// 连接状态定义
enum EnumSocketState
{
ESS_NULL, // 没有准备好
ESS_READY, // 已经准备好,可以使用状态
ESS_CLOSE, // 已经被关闭
ESS_MAX_STATE // 状态数量
};
// 处理接受完成
friend void NetWork::OnIoAccept ( NetWork::LPCOMPLETIONKEY pCK, NetWork::LPIOCPOVERLAPPED pOL, DWORD& dwTrans );
// 处理发送完成
friend void NetWork::OnIoSend ( NetWork::LPCOMPLETIONKEY pCK, NetWork::LPIOCPOVERLAPPED pOL, DWORD& dwTrans );
// 处理接收完成
friend void NetWork::OnIoRecv ( NetWork::LPCOMPLETIONKEY pCK, NetWork::LPIOCPOVERLAPPED pOL, DWORD& dwTrans );
// 处理结束完成
friend void NetWork::OnIoClose ( NetWork::LPCOMPLETIONKEY pCK, NetWork::LPIOCPOVERLAPPED pOL, DWORD& dwTrans );
CSocketIoCompletionPort();
virtual ~CSocketIoCompletionPort();
public:
void MakeReady ( VOID ) { mState = ESS_READY; }
void MakeClosed ( VOID ) { mState = ESS_CLOSE; }
bool IsNotReady ( VOID ) const { return ESS_READY != mState; }
bool IsReady ( VOID ) const { return ESS_READY == mState; }
bool IsClosed ( VOID ) const { return ESS_CLOSE == mState; }
// 创建Socket
virtual bool Create ( VOID );
// 释放Socket
virtual void Release ( VOID );
// 开始监听
virtual bool Listen ( u_short ushortPort );
// 连接服务器
virtual bool Connect ( const char *szHostName, u_short ushortPort );
// 设置一个接受进来的连接
void StartAcceptSock ( SOCKET sAccept );
// 发送数据到发送缓冲
virtual bool Send ( void *pData, DWORD size );
// 关闭socket
void CloseSocket ( VOID );
protected:
// 当有一个连接进来时调用
virtual CSocketIoCompletionPort*
OnAccept ( VOID ) { return new CSocketIoCompletionPort; }
// 当一个新连接建立完成
virtual void OnNewConnectDone ( VOID ) {}
// 当有数据接收到时调用
virtual void OnReceve ( NetPacket * pack ) { UNREFERENCED_PARAMETER(pack); }
// 当关闭时被调用
virtual void OnClose ( VOID ) {}
protected:
// 获取主机网络地址
int GetAddr ( SOCKADDR_IN& addr, const char *szHostName, u_short ushortPort );
// 设置默认Socket选项
void SetDefaultOpt ( SOCKET s );
// 发送连接标识符
bool SendIdentify ( VOID );
// 接受一个连接
void AcceptSocket ( SOCKET sAccept );
// 发出第一个接收 Io command
bool StartRecv ( HANDLE hIoCompletionPort );
// 发出一个接受 Io command
bool PostAcceptEx ( NetWork::LPIOCPOVERLAPPED lpCSSOverlapped );
// 发出一个发送 Io command
bool PostSend ( char *pData, DWORD dwSize );
// 发出一个接收 Io command
bool PostRecv ( NetWork::LPIOCPOVERLAPPED pCssOverlapped );
// 处理接收到的数据
void PushIn ( char *pData, DWORD size );
protected:
EnumSocketState mState; // 是否有效
SOCKET m_Sock; // Socket 句柄
HANDLE m_hEventCloseSocket; // 可以调用closesocket 来关闭 连接的事件
CIocpBuffer * m_pIocpRecvBuffer; // 接收缓冲
NetWork::LPCOMPLETIONKEY m_pCompletionKey; // 完成建
NetWork::LPIOCPOVERLAPPED m_pRecvOverlapped; // 重叠结构
};
typedef CSocketIoCompletionPort * ( *LPFN_NET_SERVER_NEWOBJECT ) ( VOID );
typedef void ( *LPFN_NET_CLIENT_ONRECV ) ( NetPacket * pack );
template <class T> CSocketIoCompletionPort * NetClientObject( VOID ) { return new T; }
namespace Net
{
namespace Server
{
bool Start( USHORT port, LPFN_NET_SERVER_NEWOBJECT func );
void Stop( VOID );
}
namespace Client
{
bool Connect ( LPCTSTR address, USHORT port, LPFN_NET_CLIENT_ONRECV func );
void Disconnect ( VOID );
bool Send ( NetPacket * pack );
bool IsClosed ( VOID );
}
}
#endif // __NetWork_H__
#include "stdafx.h"
#include <dbgnew.h>
#include <winsock2.h>
#include <windows.h>
#include <NetWork.h>
char *NetWork_IdentifyString = "Easideao NewWork Mask Information";
namespace NetWork
{
bool Startup()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 )
{
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
return false;
}
/* Confirm that the WinSock DLL supports 2.2.*/
/* Note that if the DLL supports versions greater */
/* than 2.2 in addition to 2.2, it will still return */
/* 2.2 in wVersion since that is the version we */
/* requested. */
if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 )
{
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
WSACleanup();
return false;
}
/* The WinSock DLL is acceptable. Proceed. */
return true;
}
void Cleanup()
{
WSACleanup();
}
// 完成端口句柄
HANDLE hIoCompletionPort = INVALID_HANDLE_VALUE;
// 工作线程
HANDLE hWorkerThreads[MAXIMUM_WAIT_OBJECTS];
DWORD dwTotalWorkThread = 0;
// IOCP 工作线程
DWORD WINAPI IOCPWorkerThread( PVOID pParam )
{
HANDLE CP = (HANDLE)pParam;
DWORD dwTrans;
LPCOMPLETIONKEY pCK;
LPIOCPOVERLAPPED pOL;
for(;;)
{
// 等待并获取完成端口信息
BOOL rc = GetQueuedCompletionStatus( CP, &dwTrans, (PULONG_PTR)&pCK, (LPOVERLAPPED *)&pOL, INFINITE );
DBG_UNREFERENCED_LOCAL_VARIABLE( rc );
// 完成了所有处理,接收到程序发出的结束线程,从此不能再继续执行任何程序
if( (!pOL) && (!pCK) && (!dwTrans) )
break;
// 关闭一个连接
if( !dwTrans )
{
switch( pOL->OP )
{
case IOP_RECV:
case IOP_SEND:
// 结束一个socket 连接
OnIoClose( pCK, pOL, dwTrans );
break;
}
continue;
}
switch( pOL->OP )
{
case IOP_ACCEPT:
OnIoAccept( pCK, pOL, dwTrans );
break;
case IOP_SEND:
OnIoSend( pCK, pOL, dwTrans );
break;
case IOP_RECV:
OnIoRecv( pCK, pOL, dwTrans );
break;
}
}
return 0;
}
bool InitIOCP()
{
// 创建一个完成端口
hIoCompletionPort = ::CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 0 );
if( INVALID_HANDLE_VALUE == hIoCompletionPort )
{
LogErr( TEXT("CreateIoCompletionPort failed.") );
return false;
}
// 获得系统信息
SYSTEM_INFO SystemInfo;
GetSystemInfo( &SystemInfo );
DWORD dwThreads = 1;
// 创建CPU*2+2个处理线程
//dwThreads = SystemInfo.dwNumberOfProcessors * 2 + 2;
for( DWORD i=0; i<dwThreads; i++ )
{
DWORD ThreadID;
hWorkerThreads = ::CreateThread( NULL, 0, IOCPWorkerThread, hIoCompletionPort, 0, &ThreadID );
dwTotalWorkThread ++;
}
Log( TEXT("Initialize Io Completion Port done.\n") );
return true;
}
void TermIOCP()
{
DWORD i;
Log( TEXT("Terminal IOCP WorkerThreads ... Starting\n") );
// 发出停止 Io 处理线程指令
for ( i=0; i<dwTotalWorkThread; ++i ) {
::PostQueuedCompletionStatus( hIoCompletionPort, 0, (ULONG_PTR)0, 0 );
}
// 等待所有线程结束
DWORD r = ::WaitForMultipleObjects( dwTotalWorkThread, hWorkerThreads, TRUE, 15000 );
switch( r )
{
case WAIT_TIMEOUT:
Log( TEXT("Not IOCP WorkerThreads dead in time!\n") );
break;
case WAIT_FAILED:
Log( TEXT("Kill IOCP WorkerThreads WaitForMultipleObjects WAIT_FAILED!\n") );
break;
default:
Log( TEXT("Terminal IOCP WorkerThreads ... done\n") );
break;
}
// 关闭所有线程
for( i=0; i<dwTotalWorkThread; i++ ) {
::CloseHandle( hWorkerThreads );
}
// 关闭完成端口
::CloseHandle( hIoCompletionPort );
}
//========================================================================
// 处理接受完成
void OnIoAccept( LPCOMPLETIONKEY pCK, LPIOCPOVERLAPPED pOL, DWORD& dwTrans )
{
UNREFERENCED_PARAMETER( dwTrans );
CSocketIoCompletionPort *pThis = (CSocketIoCompletionPort *)pCK->o;
DWORD IdLen = (DWORD)strlen( NetWork_IdentifyString );
// 接受连接
if( strncmp( pOL->buf, NetWork_IdentifyString, IdLen ) == 0 )
{
pThis->AcceptSocket( pOL->sa );
}
pThis->PostAcceptEx( pOL );
}
//========================================================================
// 处理发送完成
void OnIoSend( LPCOMPLETIONKEY pCK, LPIOCPOVERLAPPED pOL, DWORD& dwTrans )
{
CSocketIoCompletionPort *pThis = (CSocketIoCompletionPort *)pCK->o;
if( dwTrans < pOL->len )
{
pThis->MakeClosed();
SetEvent( pThis->m_hEventCloseSocket );
}
delete pOL;
}
//========================================================================
// 处理接收完成
void OnIoRecv( LPCOMPLETIONKEY pCK, LPIOCPOVERLAPPED pOL, DWORD& dwTrans )
{
CSocketIoCompletionPort *pThis = (CSocketIoCompletionPort *)pCK->o;
// 接收数据
pThis->PushIn( pOL->buf, dwTrans );
pThis->PostRecv( pOL );
}
//========================================================================
// 处理结束完成
void OnIoClose( LPCOMPLETIONKEY pCK, LPIOCPOVERLAPPED pOL, DWORD& dwTrans )
{
UNREFERENCED_PARAMETER( dwTrans );
UNREFERENCED_PARAMETER( pOL );
CSocketIoCompletionPort *pThis = (CSocketIoCompletionPort *)pCK->o;
pThis->MakeClosed();
SetEvent( pThis->m_hEventCloseSocket );
}
}
CIocpBuffer::CIocpBuffer( DWORD dwBufferSize )
{
m_caData = new char [dwBufferSize];
m_dwBufferSize = dwBufferSize;
m_dwLength = 0;
InitializeCriticalSection( &m_CRS );
}
//========================================================================
CIocpBuffer::~CIocpBuffer()
{
DeleteCriticalSection( &m_CRS );
delete [] m_caData;
}
//========================================================================
// 压入数据
void CIocpBuffer::Push( char *pData, DWORD dwSize, bool &bSucceed )
{
EnterCriticalSection( &m_CRS );
bSucceed = false;
if( dwSize > 0 && m_dwLength + dwSize <= m_dwBufferSize )
{
memcpy( &m_caData[m_dwLength], pData, dwSize );
m_dwLength += dwSize;
bSucceed = true;
}
LeaveCriticalSection( &m_CRS );
}
//========================================================================
// 弹出数据
void CIocpBuffer::Pop( char *pData, DWORD &dwSize )
{
EnterCriticalSection( &m_CRS );
dwSize = 0;
if( m_dwLength > 0 )
{
if( pData )
memcpy( pData, m_caData, m_dwLength );
dwSize = m_dwLength;
m_dwLength = 0;
}
LeaveCriticalSection( &m_CRS );
}
//========================================================================
void CIocpBuffer::Lock()
{
EnterCriticalSection( &m_CRS );
}
//========================================================================
void CIocpBuffer::Lock( char *&pData, DWORD &dwSize )
{
EnterCriticalSection( &m_CRS );
pData = m_caData;
dwSize = m_dwLength;
}
//========================================================================
void CIocpBuffer::Unlock()
{
LeaveCriticalSection( &m_CRS );
}
//========================================================================
void CIocpBuffer::Empty()
{
m_dwLength = 0;
}
//========================================================================
bool CIocpBuffer::IsEmpty()
{
return (m_dwLength == 0);
}
GUID GuidAcceptEx = WSAID_ACCEPTEX;
LPFN_ACCEPTEX lpfnAcceptEx = NULL;
//////////////////////////////////////////////////////////////////////////
//========================================================================
CSocketIoCompletionPort::CSocketIoCompletionPort()
{
m_Sock = INVALID_SOCKET;
m_hEventCloseSocket = INVALID_HANDLE_VALUE;
mState = ESS_NULL;
m_pIocpRecvBuffer = new CIocpBuffer( IOCPSOCKET_RECV_BUFFER_LEN );
m_pCompletionKey = NULL;
m_pRecvOverlapped = NULL;
}
//========================================================================
CSocketIoCompletionPort::~CSocketIoCompletionPort()
{
delete m_pIocpRecvBuffer;
}
//========================================================================
// 创建Socket
bool CSocketIoCompletionPort::Create( )
{
// 初始化完成端口
if( !NetWork::InitIOCP() )
{
LogErr( TEXT( "Initialize a IOCP Failed" ) );
return false;
}
// 创建一个Socket
m_Sock = WSASocket( AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED );
if( INVALID_SOCKET == m_Sock )
{
LogSockErr( TEXT("Create socket handle failed") );
return false;
}
SetDefaultOpt( m_Sock );
return true;
}
//========================================================================
// 释放Socket
void CSocketIoCompletionPort::Release()
{
CloseSocket();
}
//========================================================================
// 开始监听
bool CSocketIoCompletionPort::Listen( u_short ushortPort )
{
int err;
// 设置主机地址信息
SOCKADDR_IN InternetAddr;
int AddrSize = sizeof( SOCKADDR_IN );
ZeroMemory( &InternetAddr, AddrSize );
InternetAddr.sin_family = AF_INET;
InternetAddr.sin_addr.s_addr = htonl( INADDR_ANY );
InternetAddr.sin_port = htons( ushortPort );
// 绑定主机地址
err = bind( m_Sock, (PSOCKADDR)&InternetAddr, AddrSize );
if( err == SOCKET_ERROR )
{
LogSockErr( TEXT("Bind listen port failed") );
return false;
}
// 开始监听端口
err = listen( m_Sock, 5 );
if( err == SOCKET_ERROR )
{
LogSockErr( TEXT("Listen start failed") );
return false;
}
// 关联完成端口
m_pCompletionKey = new NetWork::COMPLETIONKEY;
m_pCompletionKey->s = m_Sock;
m_pCompletionKey->o = this;
CreateIoCompletionPort( (HANDLE)m_Sock, NetWork::hIoCompletionPort, (ULONG_PTR)m_pCompletionKey, 0 );
// 获得lpfnAcceptEx 函数指针
DWORD dwBytes;
err = WSAIoctl( m_Sock, SIO_GET_EXTENSION_FUNCTION_POINTER
, &GuidAcceptEx, sizeof(GuidAcceptEx)
, &lpfnAcceptEx, sizeof(lpfnAcceptEx)
, &dwBytes, NULL, NULL );
if( err == SOCKET_ERROR )
{
LogSockErr( TEXT("Get lpfnAcceptEx failed") );
return false;
}
m_pRecvOverlapped = new NetWork::IOCPOVERLAPPED;
if( !PostAcceptEx( m_pRecvOverlapped ) )
{
Log( TEXT("PostAcceptEx Failed %s(%d)\n"), TEXT(__FILE__), __LINE__ );
return false;
}
// 连接就绪
MakeReady();
return true;
}
//========================================================================
// 连接到服务器
bool CSocketIoCompletionPort::Connect( const char *szHostName, u_short ushortPort )
{
SOCKADDR_IN PeerAddr;
int AddrLen = GetAddr( PeerAddr, szHostName, ushortPort );
int err = WSAConnect( m_Sock, (SOCKADDR*)&PeerAddr, AddrLen, NULL, NULL, NULL, NULL );
if( err )
{
LogSockErr( TEXT("WSAConnect call failed") );
return false;
}
if( !SendIdentify() )
{
LogErr( TEXT("Connect send identify failed") );
return false;
}
m_hEventCloseSocket = CreateEvent( NULL, TRUE, FALSE, NULL );
// 连接就绪
MakeReady();
if( !StartRecv( NetWork::hIoCompletionPort ) )
{
LogErr( TEXT("Connect start recv net message failed") );
return false;
}
return true;
}
//========================================================================
// 设置一个接受进来的连接
void CSocketIoCompletionPort::StartAcceptSock( SOCKET sAccept )
{
m_Sock = sAccept;
SetDefaultOpt( m_Sock );
m_hEventCloseSocket = CreateEvent( NULL, TRUE, FALSE, NULL );
// 连接就绪
MakeReady();
}
//========================================================================
// 发送数据到发送缓冲
bool CSocketIoCompletionPort::Send( void *pData, DWORD size )
{
if( IsReady() )
return PostSend( (char *)pData, size );
else
return false;
}
void CSocketIoCompletionPort::CloseSocket( VOID )
{
OnClose();
#ifdef TRACE_CLOSESOCKET
const int msgLen = 4096;
TCHAR dbgMsg[msgLen];
SafeFTS( dbgMsg, msgLen, TEXT( "Close socket [%d] starting." ), m_Sock );
LogErr( dbgMsg );
#endif
::closesocket( m_Sock );
if( INVALID_HANDLE_VALUE != m_hEventCloseSocket )
{
DWORD r = WaitForSingleObject( m_hEventCloseSocket, 30000 );
if( WAIT_OBJECT_0 == r )
{
#ifdef TRACE_CLOSESOCKET
SafeFTS( dbgMsg, msgLen, TEXT( "Close socket [%d] done." ), m_Sock );
LogErr( dbgMsg );
#endif
}
else
{
#ifdef TRACE_CLOSESOCKET
SafeFTS( dbgMsg, msgLen, TEXT( "Close socket [%d] failed." ), m_Sock );
LogErr( dbgMsg );
#endif
}
CloseHandle( m_hEventCloseSocket );
}
m_Sock = INVALID_SOCKET;
delete m_pCompletionKey;
delete m_pRecvOverlapped;
}
//////////////////////////////////////////////////////////////////////////
//========================================================================
// 获取主机网络地址
int CSocketIoCompletionPort::GetAddr( SOCKADDR_IN& addr, const char *szHostName, u_short ushortPort )
{
int AddrLen = sizeof( SOCKADDR_IN );
ZeroMemory( &addr, AddrLen );
addr.sin_family = AF_INET;
addr.sin_port = htons( ushortPort );
addr.sin_addr.s_addr = inet_addr( szHostName );
if( addr.sin_addr.s_addr == INADDR_NONE )
{
HOSTENT * lphost = ::gethostbyname( szHostName );
if( lphost == NULL )
return false;
addr.sin_addr.s_addr = ((IN_ADDR *)lphost->h_addr)->s_addr;
}
return AddrLen;
}
//========================================================================
// 设置默认Socket选项
void CSocketIoCompletionPort::SetDefaultOpt( SOCKET s )
{
// 不拖延设置
BOOL bDontLinger;
int DontLingerSize = sizeof(BOOL);
getsockopt( m_Sock, SOL_SOCKET, SO_DONTLINGER, (char *)&bDontLinger, &DontLingerSize );
bDontLinger = TRUE;
setsockopt( m_Sock, SOL_SOCKET, SO_DONTLINGER, (char *)&bDontLinger, DontLingerSize );
// 设置执行closesocket时,在套接字上排队数据的逗留时间
struct linger l;
int LingerSize = sizeof(linger);
getsockopt( m_Sock, SOL_SOCKET, SO_LINGER, (char *)&l, &LingerSize );
// 保持活动
BOOL bKeepALife;
int KeepALifeSize = sizeof(BOOL);
getsockopt( m_Sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&bKeepALife, &KeepALifeSize );
// 禁止Nagle 算法 延迟发送数据
int bNoDelay = TRUE;
setsockopt( s, IPPROTO_TCP, TCP_NODELAY, (char*)&bNoDelay, sizeof(int));
}
//========================================================================
// 发送连接标识符
bool CSocketIoCompletionPort::SendIdentify()
{
// 发送连接标识
WSABUF wsaBuf;
wsaBuf.buf = NetWork_IdentifyString;
wsaBuf.len = (u_long)strlen( NetWork_IdentifyString );
DWORD dwNumberOfBytesSent;
int err = WSASend( m_Sock, &wsaBuf, 1, &dwNumberOfBytesSent, 0, 0, 0 );
if( SOCKET_ERROR == err )
{
int e = WSAGetLastError();
switch( e )
{
case WSA_IO_PENDING:
case 0:
break;
default:
LogSockErrCode( TEXT("Send identify string failed"), e );
return false;
}
}
return true;
}
//========================================================================
// 接受一个连接
void CSocketIoCompletionPort::AcceptSocket( SOCKET sAccept )
{
int rc = setsockopt( sAccept, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char *)&m_Sock, sizeof(SOCKET) );
if( rc == SOCKET_ERROR )
{
int e = WSAGetLastError();
LogErr( GetSocketErrorString( e ) );
closesocket( sAccept );
}
CSocketIoCompletionPort *pSock = OnAccept();
pSock->StartAcceptSock( sAccept );
pSock->StartRecv( NetWork::hIoCompletionPort );
pSock->OnNewConnectDone();
}
//========================================================================
// 发出第一个接收 Io command
bool CSocketIoCompletionPort::StartRecv( HANDLE hIoCompletionPort )
{
m_pCompletionKey = new NetWork::COMPLETIONKEY;
m_pCompletionKey->s = m_Sock;
m_pCompletionKey->o = this;
HANDLE hrc = CreateIoCompletionPort( (HANDLE)m_Sock, hIoCompletionPort, (ULONG_PTR)m_pCompletionKey, 0 );
if( hrc == NULL )
{
LogErr( TEXT("StartRecv Associate CompletionPort Failed\n") );
return false;
}
m_pRecvOverlapped = new NetWork::IOCPOVERLAPPED;
if( !PostRecv( m_pRecvOverlapped ) )
{
#ifdef TRACE_RECVSEND
LogErr( TEXT("CSocketService AcceptSocket Failed\n(%s:%d)") );
#endif
MakeClosed();
return false;
}
return true;
}
//========================================================================
// 发出一个接受 Io command
bool CSocketIoCompletionPort::PostAcceptEx( NetWork::LPIOCPOVERLAPPED lpCSSOverlapped )
{
SOCKET sAccept = WSASocket( AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED );
if( sAccept == INVALID_SOCKET )
{
int e = WSAGetLastError();
LogErr( GetSocketErrorString( e ) );
return false;
}
ZeroMemory( lpCSSOverlapped, sizeof(NetWork::IOCPOVERLAPPED) );
lpCSSOverlapped->OP = NetWork::IOP_ACCEPT;
lpCSSOverlapped->s = m_Sock;
lpCSSOverlapped->sa = sAccept;
lpCSSOverlapped->len = 1024;
char *buffer = lpCSSOverlapped->buf;
int bufferlen = lpCSSOverlapped->len;
DWORD dwBytes;
int err = lpfnAcceptEx( m_Sock
, sAccept
, buffer
, bufferlen - ((sizeof(SOCKADDR_IN)+16)*2)
, sizeof(SOCKADDR_IN)+16
, sizeof(SOCKADDR_IN)+16
, &dwBytes
, &lpCSSOverlapped->o );
if( err == SOCKET_ERROR )
{
int e = WSAGetLastError();
if( e != WSA_IO_PENDING )
{
LogSockErrCode( TEXT("lpfnAcceptEx call failed"), e );
return false;
}
}
return true;
}
//========================================================================
// 发出一个发送 Io command
bool CSocketIoCompletionPort::PostSend( char *pData, DWORD dwSize )
{
NetWork::LPIOCPOVERLAPPED pOverlapped = new NetWork::IOCPOVERLAPPED;
ZeroMemory( pOverlapped, sizeof(NetWork::IOCPOVERLAPPED) );
memcpy( pOverlapped->buf, pData, dwSize );
pOverlapped->len = dwSize;
pOverlapped->OP = NetWork::IOP_SEND;
pOverlapped->s = m_Sock;
pOverlapped->sa = INVALID_SOCKET;
WSABUF wsaBuf;
wsaBuf.buf = pOverlapped->buf;
wsaBuf.len = pOverlapped->len;
DWORD dwNumberOfBytesSent;
//m_SendBuffer.In( pOverlapped );
//Log( "WSASend Data[%d]\n", pOverlapped->len );
int err = WSASend( m_Sock, &wsaBuf, 1, &dwNumberOfBytesSent, 0, &pOverlapped->o, NULL );
if( err == SOCKET_ERROR )
{
int e = WSAGetLastError();
if( e != WSA_IO_PENDING )
{
#ifdef TRACE_RECVSEND
LogSockErrCode( TEXT("Post send io command failed"), e );
#endif
MakeClosed();
return false;
}
}
return true;
}
//========================================================================
// 发出一个接收 Io command
bool CSocketIoCompletionPort::PostRecv( NetWork::LPIOCPOVERLAPPED pCssOverlapped )
{
if( IsNotReady() )
return false;
ZeroMemory( pCssOverlapped, sizeof(NetWork::IOCPOVERLAPPED) );
pCssOverlapped->len = 1024;
pCssOverlapped->OP = NetWork::IOP_RECV;
pCssOverlapped->s = m_Sock;
pCssOverlapped->sa = INVALID_SOCKET;
static WSABUF wsaBuf;
wsaBuf.buf = pCssOverlapped->buf;
wsaBuf.len = pCssOverlapped->len;
DWORD dwNumberOfBytesRecvd = 0;
DWORD dwFlags = 0;
int err = WSARecv( m_Sock, &wsaBuf, 1, &dwNumberOfBytesRecvd, &dwFlags, &pCssOverlapped->o, NULL );
if( err == SOCKET_ERROR )
{
int e = WSAGetLastError();
if( e != WSA_IO_PENDING )
{
#ifdef TRACE_RECVSEND
LogSockErrCode( TEXT("PostRecv :"), e );
#endif
MakeClosed();
return false;
}
}
return true;
}
//========================================================================
// 处理接收到的数据
void CSocketIoCompletionPort::PushIn( char *pData, DWORD size )
{
if( IsNotReady() )
return;
m_pIocpRecvBuffer->Lock();
char *&pBuffer = m_pIocpRecvBuffer->m_caData;
DWORD &dwLen = m_pIocpRecvBuffer->m_dwLength;
if( dwLen + size <= m_pIocpRecvBuffer->m_dwBufferSize )
{
memcpy( pBuffer, pData, size );
dwLen += size;
DWORD p = 0;
DWORD len = 0;
while( dwLen >= sizeof(NetPacket) )
{
NetPacket * pack = (NetPacket *)&pBuffer;
len = pack->pakSize;
if( dwLen >= len )
{
OnReceve( pack );
p += len;
dwLen -= len;
}
else break;
}
if( dwLen > 0 )
{
memcpy( &pBuffer[0], &pBuffer, dwLen );
}
}
else
MakeClosed();
m_pIocpRecvBuffer->Unlock();
}
//////////////////////////////////////////////////////////////////////////
typedef CSocketIoCompletionPort * ( *LPFN_NET_SERVER_NEWOBJECT ) ( VOID );
typedef void ( *LPFN_NET_CLIENT_ONRECV ) ( NetPacket * pack );
CSocketIoCompletionPort * DummyServerNewObject( VOID )
{
return new CSocketIoCompletionPort;
}
void DummyClientOnRecv( NetPacket * pack )
{
UNREFERENCED_PARAMETER( pack );
}
LPFN_NET_SERVER_NEWOBJECT lpfn_Server_NewObject = DummyServerNewObject;
LPFN_NET_CLIENT_ONRECV lpfn_Client_OnRecv = DummyClientOnRecv;
class CInternetGameServer : public CSocketIoCompletionPort
{
public:
bool Create( u_short ushortPort )
{
if( !CSocketIoCompletionPort::Create() )
return false;
if( !Listen( ushortPort ) )
return false;
return true;
}
virtual void Release( VOID )
{
::closesocket( m_Sock );
NetWork::TermIOCP();
m_Sock = INVALID_SOCKET;
delete m_pCompletionKey;
delete m_pRecvOverlapped;
}
protected:
virtual CSocketIoCompletionPort* OnAccept ( VOID ) { return lpfn_Server_NewObject(); }
};
class CInternetGameClient : public CSocketIoCompletionPort
{
public:
virtual bool Connect( const char *szHostName, u_short ushortPort )
{
if( !CSocketIoCompletionPort::Create() )
return false;
if( !CSocketIoCompletionPort::Connect( szHostName, ushortPort ) )
return false;
return true;
}
virtual void Release()
{
CSocketIoCompletionPort::Release();
NetWork::TermIOCP();
}
virtual void OnReceve( NetPacket * pack )
{
lpfn_Client_OnRecv( pack );
}
};
namespace Net
{
CInternetGameServer * theGameServer = NULL;
CInternetGameClient * theGameClient = NULL;
namespace Server
{
bool Start( USHORT port, LPFN_NET_SERVER_NEWOBJECT func )
{
if( theGameServer )
return false;
if( !NetWork::Startup() )
{
Log( TEXT("network startup failed.\n") );
return false;
}
lpfn_Server_NewObject = func;
theGameServer = new CInternetGameServer;
if( !theGameServer->Create( port ) )
{
Log( TEXT("start server falied on port [%d]\n"), port );
return false;
}
return true;
}
void Stop( VOID )
{
if( theGameServer )
{
theGameServer->Release();
delete theGameServer;
theGameServer = NULL;
NetWork::Cleanup();
}
}
}
namespace Client
{
bool Connect( LPCTSTR address, USHORT port, LPFN_NET_CLIENT_ONRECV func )
{
if( theGameClient )
return false;
if( !NetWork::Startup() )
{
Log( TEXT("network startup failed.\n") );
return false;
}
lpfn_Client_OnRecv = func;
theGameClient = new CInternetGameClient;
char szAddress[1024];
#ifdef UNICODE
WideCharToMultiByte( CP_ACP, 0, address, -1, szAddress, 1024, NULL, NULL );
#else
strcpy_s( szAddress, 1024, m_SetupInfo.szServerAddress );
#endif
if( !theGameClient->Connect( szAddress, port ) )
{
LogErr( TEXT("Failed to connect Server!") );
return false;
}
return true;
}
void Disconnect( VOID )
{
if( theGameClient )
{
theGameClient->Release();
delete theGameClient;
theGameClient = NULL;
NetWork::Cleanup();
}
}
bool Send( NetPacket * pack )
{
if( theGameClient )
return theGameClient->Send( pack, pack->pakSize );
return false;
}
bool IsClosed( VOID )
{
if( theGameClient )
return theGameClient->IsClosed();
return false;
}
}
}