Restructure repository to include all source folders

Move git root from Client/ to src/ to track all source code:
- Client: Game client source (moved to Client/Client/)
- Server: Game server source
- GameTools: Development tools
- CryptoSource: Encryption utilities
- database: Database scripts
- Script: Game scripts
- rylCoder_16.02.2008_src: Legacy coder tools
- GMFont, Game: Additional resources

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-29 20:17:20 +09:00
parent 5d3cd64a25
commit dd97ddec92
11602 changed files with 1446576 additions and 0 deletions

View File

@@ -0,0 +1,152 @@
#include "Global.h"
#include <Iphlpapi.h>
#pragma comment(lib,"Iphlpapi.lib")
#include <vector>
#include <Nave/NFStringUtil.h>
std::vector<Nave::StringA> g_AdapterInfo;
void UpdateAdapterInfo()
{
PIP_ADAPTER_INFO pAdapterInfo;
PIP_ADAPTER_INFO pAdapter = NULL;
DWORD dwRetVal = 0;
pAdapterInfo = (IP_ADAPTER_INFO *) malloc( sizeof(IP_ADAPTER_INFO) );
ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
// Make an initial call to GetAdaptersInfo to get
// the necessary size into the ulOutBufLen variable
if (GetAdaptersInfo( pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW)
{
free(pAdapterInfo);
pAdapterInfo = (IP_ADAPTER_INFO *) malloc (ulOutBufLen);
}
if ((dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen)) == NO_ERROR)
{
pAdapter = pAdapterInfo;
while (pAdapter)
{
if(strcmp(pAdapter->IpAddressList.IpAddress.String, "0.0.0.0") != 0)
g_AdapterInfo.push_back(pAdapter->IpAddressList.IpAddress.String);
/*
printf("\tAdapter Name: \t%s\n", pAdapter->AdapterName);
printf("\tAdapter Desc: \t%s\n", pAdapter->Description);
printf("\tAdapter Addr: \t%ld\n", pAdapter->Address);
printf("\tIP Address: \t%s\n", pAdapter->IpAddressList.IpAddress.String);
printf("\tIP Mask: \t%s\n", pAdapter->IpAddressList.IpMask.String);
printf("\tGateway: \t%s\n", pAdapter->GatewayList.IpAddress.String);
printf("\t***\n");
if (pAdapter->DhcpEnabled)
{
printf("\tDHCP Enabled: Yes\n");
printf("\t\tDHCP Server: \t%s\n", pAdapter->DhcpServer.IpAddress.String);
printf("\tLease Obtained: %ld\n", pAdapter->LeaseObtained);
}
else
printf("\tDHCP Enabled: No\n");
if (pAdapter->HaveWins)
{
printf("\tHave Wins: Yes\n");
printf("\t\tPrimary Wins Server: \t%s\n", pAdapter->PrimaryWinsServer.IpAddress.String);
printf("\t\tSecondary Wins Server: \t%s\n", pAdapter->SecondaryWinsServer.IpAddress.String);
}
else
printf("\tHave Wins: No\n");
*/
pAdapter = pAdapter->Next;
}
}
/* else
{
printf("Call to GetAdaptersInfo failed.\n");
}
*/
free(pAdapterInfo);
}
BOOL GetLocalIP(WCHAR* pLocalIP_Out, BOOL bClosedIP)
{
if(g_AdapterInfo.empty())
UpdateAdapterInfo();
// 고정아이피가 있으면 고정아이피 리턴
// 고정아이피가 없고 가상아이피만 존재하면 가상아이피 리턴
int isize = g_AdapterInfo.size();
for(int i = 0; i < isize; ++i)
{
wcscpy(pLocalIP_Out, Nave::ToUnicode(g_AdapterInfo[i]).c_str());
if(bClosedIP) // 내부아이피를 얻어와라.
{
if(wcsncmp(pLocalIP_Out, L"0", 1) == 0)
continue;
else if(wcsncmp(pLocalIP_Out, L"192", 3) == 0)
return TRUE;
else if(wcsncmp(pLocalIP_Out, L"127", 3) == 0)
return TRUE;
}
else // 외부 아이피를 얻어와라
{
if(wcsncmp(pLocalIP_Out, L"0", 1) == 0)
continue;
else if(wcsncmp(pLocalIP_Out, L"192", 3) == 0)
continue;
else if(wcsncmp(pLocalIP_Out, L"127", 3) == 0)
continue;
return TRUE;
}
}
if(!bClosedIP)
return GetLocalIP(pLocalIP_Out, TRUE);
return FALSE;
}
BOOL GetLocalIP(CHAR* pLocalIP_Out, BOOL bClosedIP)
{
if(g_AdapterInfo.empty())
UpdateAdapterInfo();
// 고정아이피가 있으면 고정아이피 리턴
// 고정아이피가 없고 가상아이피만 존재하면 가상아이피 리턴
int isize = g_AdapterInfo.size();
for(int i = 0; i < isize; ++i)
{
strcpy(pLocalIP_Out, g_AdapterInfo[i].c_str());
if(bClosedIP) // 내부아이피를 얻어와라.
{
if(strncmp(pLocalIP_Out, "0", 1) == 0)
continue;
else if(strncmp(pLocalIP_Out, "192", 3) == 0)
return TRUE;
else if(strncmp(pLocalIP_Out, "127", 3) == 0)
return TRUE;
}
else // 외부 아이피를 얻어와라
{
if(strncmp(pLocalIP_Out, "0", 1) == 0)
continue;
else if(strncmp(pLocalIP_Out, "192", 3) == 0)
continue;
else if(strncmp(pLocalIP_Out, "127", 3) == 0)
continue;
return TRUE;
}
}
if(!bClosedIP)
return GetLocalIP(pLocalIP_Out, TRUE);
return FALSE;
}

View File

@@ -0,0 +1,49 @@
/**
* @file Global.h
* @brief 공용 함수 (로그메시지 및 버퍼 관리함수)
* @remarks
* @author 강동명(edith2580@gmail.com)
* @date 2009-05-09
*/
#pragma once
#define WIN32_LEAN_AND_MEAN // 거의 사용되지 않는 내용은 Windows 헤더에서 제외합니다.
// Windows 헤더 파일입니다.
#include <windows.h>
#include <time.h> // timer
#include <assert.h> // assert
#include <process.h> // Thread
#include <stdio.h> // standard I/O
#include <stdlib.h>
#include <wchar.h>
// sock
#include<winsock2.h> // win32 socket
#pragma comment(lib,"ws2_32.lib")
#include<Mswsock.h> // extension socket library
#pragma comment(lib,"mswsock.lib")
#include "mmsystem.h" // 멀티미디어 타이머를 쓰기
#pragma comment(lib,"winmm.lib")
#include <Nave/Nave.h>
#include <Nave/NFSync.h>
#include <Nave/NFLog.h>
#include <Nave/NFStringUtil.h>
#include <NaveNet/NFPacket.h>
#include "NFDefine.h"
#include "NFConnection.h"
#include "NFConnectionManager.h"
#include "NFUpdateManager.h"
#include "UIFramework.h"
/**
* @brief 로컬 아이피를 가져옵니다.
* @param LocalIP 로컬아이피 저장변수.
* @param bClosedIP 내부아이피를 얻어올때
* @return 성공여부
*/
BOOL GetLocalIP(CHAR* pLocalIP_Out, BOOL bClosedIP = FALSE);
BOOL GetLocalIP(WCHAR* pLocalIP_Out, BOOL bClosedIP = FALSE);

View File

@@ -0,0 +1,835 @@
#include "Global.h"
#include "NFConnection.h"
#include <Nave/NFLog.h>
namespace NaveServer {
NFConnection::NFConnection() : m_nMaxBuf(0), m_nMaxPacketSize(0), m_sckListener(NULL),
m_Socket(NULL), m_eConnectFlag(CONNECT_NONE), m_bIsConnect(FALSE),
m_bForce(FALSE), m_hIOCP(NULL), m_pPacketPool(NULL), m_uRecvTickCnt(0)
{
m_RecvPacket.Init();
m_RecvIO.NewIOBuf(0); // 0으로 하면 기본적으로 1024*64만큼 잡힌다.
}
NFConnection::~NFConnection()
{
m_nMaxBuf = m_nMaxPacketSize = 0;
m_sckListener = NULL;
Disconnect();
m_eConnectFlag = CONNECT_NONE;
m_bIsConnect = FALSE;
m_bForce = FALSE;
m_hIOCP = NULL;
m_pPacketPool = NULL;
m_uRecvTickCnt = 0;
m_RecvIO.DeleteIOBuf();
}
BOOL NFConnection::Create( DWORD dwIndex,
HANDLE hIOCP,
SOCKET listener,
NFPacketPool* pPacketPool,
INT nMaxBuf)
{
// 객체의 멤버 변수 설정
m_dwIndex = dwIndex;
m_hIOCP = hIOCP;
m_sckListener = listener;
m_pPacketPool = pPacketPool;
m_nMaxBuf = m_nMaxPacketSize = nMaxBuf;
// 객체 OPEN
if(!Open())
{
// 실패하면 어떻게 해야 하는가? 이 컨넥션 클래스는 죽은 클래스가 된다.
Close_Open(FALSE);
return FALSE;
}
return TRUE;
}
BOOL NFConnection::Open()
{
// 절대 값 있어야 함.
assert(m_nMaxBuf);
assert(m_nMaxPacketSize);
// 패킷 Pool이 제대로 설정 되었는지 검사
if( !m_pPacketPool ) return FALSE;
// Listener Socket 상태 검사
if( m_sckListener == NULL ) return FALSE;
m_RecvIO.InitIOBuf(); // 패킷버퍼를 초기화한다.
// create socket for send/recv
m_Socket = WSASocket( AF_INET, SOCK_STREAM, IPPROTO_IP,NULL,0,WSA_FLAG_OVERLAPPED );
// 현재 소켓이 제대로 생성 되었는지 검사
if(m_Socket == NULL) return FALSE;
// Accpet할 오버랩구조체와 패킷버퍼를 준비한다.
LPOVERLAPPEDPLUS newolp = PrepareAcptPacket();
if(NULL == newolp)
{
closesocket(m_Socket);
m_Socket = NULL;
return FALSE;
}
/////////////////////////////////////////////////////////////////////
// Socket과 Listener와 연결
// Overlapped에 들어가는 변수가 나중에 IOCP 이벤트 발생 처리에 쓰인다
BOOL bRet = AcceptEx(newolp->sckListen, // Listen Socket
newolp->sckClient, // Socket
&(newolp->wbuf.buf[0]), // 버퍼 포인터
m_nMaxBuf, // 버퍼 사이즈
sizeof(sockaddr_in) + 16, // 소켓 정보 - IP, Address, Name.. etc
sizeof(sockaddr_in) + 16, // 소켓 정보 - IP, Address, Name.. etc
&newolp->dwBytes, // 처리 바이트 크기
&newolp->overlapped); // *중요*
// 에러 처리
if(!bRet && WSAGetLastError() != WSA_IO_PENDING)
{
ShowMessage(ACCEPT_FAILED);
closesocket(m_Socket);
m_Socket = NULL;
ReleaseAcptPacket(newolp);
return FALSE;
}
/////////////////////////////////
// 소켓의 성능 최적화를 위한 세팅
INT zero = 0;
INT err = 0;
// Send Buffer에 대한 세팅
if( (err = setsockopt( m_Socket, SOL_SOCKET, SO_SNDBUF, (CHAR *)&zero, sizeof(zero))) == SOCKET_ERROR)
{
ShowMessage(ACCEPT_FAILED);
closesocket(m_Socket);
m_Socket = NULL;
ReleaseAcptPacket(newolp);
return FALSE;
}
// Receive Buffer에 대한 세팅
if((err = setsockopt( m_Socket, SOL_SOCKET, SO_RCVBUF, (CHAR *)&zero, sizeof(zero) )) == SOCKET_ERROR)
{
ShowMessage(ACCEPT_FAILED);
closesocket(m_Socket);
m_Socket = NULL;
ReleaseAcptPacket(newolp);
return FALSE;
}
// 변수 초기화
InterlockedExchange((LONG*)&m_bIsConnect,0);
Clear();
m_uRecvTickCnt = 0 ;
return TRUE;
}
BOOL NFConnection::Close_Open( BOOL bForce )
{
// 소켓과 리스너 상태 확인
// Disconnect 함수안에서 OnDisconnect(), Clear() 이 호출된다.
Disconnect(bForce);
// 이 패킷 다시 초기화
if(!Open())
{
// 실패하면 어떻게 해야 하는가? 이 컨넥션 클래스는 죽은 클래스가 된다.
// 뭔가 죽은 상태를 표시해주고 체크하자. 그리고 나중에 타이머에서 다시 초기화 해본다.
ShowMessage(DEAD_CONNECTION);
Disconnect();
return FALSE;
}
return TRUE; // 정상 처리
}
VOID NFConnection::ShowMessage(INT MsgIndex)
{
switch(MsgIndex)
{
case ACCEPT_FAILED:
LOG_ERROR((L"[%04d] AcceptEx(): SOCKET_ERROR, %d.", GetIndex(), WSAGetLastError()));
break;
case CLOSE_SOCKET:
LOG_INFO((L"[%04d] 접속 해제.", GetIndex()));
break;
case DEAD_CONNECTION:
LOG_ERROR((L"[%04d] Dead Connection.", GetIndex()));
break;
case CONNECT_SUCCESS:
LOG_INFO((L"[%04d] 접속 성공.", GetIndex()));
break;
case CONNECT_FAILED:
LOG_ERROR((L"[%04d] 접속 실패 (Check Sum 오류).", GetIndex()));
break;
case DISPATCH_FAILED:
LOG_ERROR((L"[%04d] Dispatch return FALSE.", GetIndex()));
break;
case DOIOSWITCH_FAILED:
LOG_ERROR((L"[%04d] DoIo(..) - Switch Default.", GetIndex()));
break;
case ALLOCACPT_FAILED:
LOG_ERROR((L"[%04d] PrepareAcptPacket() acpt packet alloc failed.", GetIndex()));
break;
case PREPAREACPT_FAILED:
LOG_ERROR((L"[%04d] PrepareAcptPacket().newolp == NULL.", GetIndex()));
break;
case PREPARERECVSIZE_FAILED:
LOG_ERROR((L"[%04d] PrepareRecvPacket() srclen > m_nMaxPacketSize.", GetIndex()));
break;
case PREPARESENDSIZE_FAILED:
LOG_ERROR((L"[%04d] PrepareSendPacket() srclen > m_nMaxPacketSize.", GetIndex()));
break;
case ALLOCRECV_FAILED:
LOG_ERROR((L"[%04d] PrepareRecvPacket() recv packet alloc failed.", GetIndex()));
break;
case ALLOCSEND_FAILED:
LOG_ERROR((L"[%04d] PrepareSendPacket() send packet alloc failed.", GetIndex()));
break;
case PREPARERECV_FAILED:
LOG_ERROR((L"[%04d] PrepareRecvPacket().newolp == NULL.", GetIndex()));
break;
case PREPARESEND_FAILED:
LOG_ERROR((L"[%04d] PrepareSendPacket().newolp == NULL.", GetIndex()));
break;
case RELEASEACPT_FAILED:
LOG_ERROR((L"[%04d] ReleaseAcptPacket() free acpt packet failed.", GetIndex()));
break;
case RELEASERECV_FAILED:
LOG_ERROR((L"[%04d] ReleaseRecvPacket() free recv packet failed.", GetIndex()));
break;
case RELEASESEND_FAILED:
LOG_ERROR((L"[%04d] ReleaseSendPacket() free send packet failed.", GetIndex()));
break;
case BINDIOCP_FAILED:
LOG_ERROR((L"[%04d] BindIOCP().lpOverlapPlus == NULL.", GetIndex()));
break;
case RECVPOST_FAILED:
LOG_ERROR((L"[%04d] RecvPost() m_Socket == NULL or IsConnect() == FALSE.", GetIndex()));
break;
case RECVPOSTPENDING_FAILED:
LOG_ERROR((L"[%04d] RecvPost().WSARecv() == SOCKET_ERROR, %d.", GetIndex(), WSAGetLastError()));
break;
// case ALLOCPROC_FAILED:
// LOG_ERROR((L"[%04d] SendPost().AllocProcBuffer == NULL or SOCKET_ERROR, %d.", GetIndex(), WSAGetLastError()));
// break;
case SENDPOSTPENDING_FAILED:
LOG_ERROR((L"[%04d] SendPost().WSASend() == SOCKET_ERROR, %d.", GetIndex(), WSAGetLastError()));
break;
case SENDPOST_FAILED:
LOG_ERROR((L"[%04d] SendPost() == SOCKET_ERROR, %d.", GetIndex(), WSAGetLastError()));
break;
}
}
BOOL NFConnection::DoIo( LPOVERLAPPEDPLUS lpOverlapPlus )
{
// 할당 패킷의 상태 확인
switch(lpOverlapPlus->nConnState)
{
// ACCEPT관련 처리
case ClientIoAccept:
BindIOCP(lpOverlapPlus); // 현재 소켓과 IOCP 바인딩 처리
InterlockedIncrement((LONG*)&m_bIsConnect); // 접속 상태 변수 ON !!!
// 검사된 연결자 아난 경우 연결 해제
if(lpOverlapPlus->dwBytes && strncmp(lpOverlapPlus->wbuf.buf,CONNECT_CHECK_DATA,CONNECT_CHECK_SIZE) == 0)
{
ShowMessage(CONNECT_SUCCESS);
SetConnectFlag(CONNECT_TRUE);
}
else
{
ShowMessage(CONNECT_FAILED);
LOG_ERROR((L"[%04d] Check Sum : %d, %s.", GetIndex(), lpOverlapPlus->dwBytes, lpOverlapPlus->wbuf.buf));
SetConnectFlag(CONNECT_FALSE);
ReleaseAcptPacket( lpOverlapPlus );
break;
}
// Accept 할당 패킷 해제
if(!RecvPost())
{
SetClose_Open(lpOverlapPlus);
break;
}
// TICK 카운트 설정
InterlockedExchange((LONG*)&m_uRecvTickCnt,timeGetTime());
ReleaseAcptPacket( lpOverlapPlus );
break;
// RECEIVE 관련 처리
case ClientIoRead:
// TICK 카운트 설정
InterlockedExchange((LONG*)&m_uRecvTickCnt,timeGetTime());
// 처리 데이타가 없다면
if(lpOverlapPlus->dwBytes == 0)
{
SetClose_Open(lpOverlapPlus, FALSE); // 에러, 객체 다시 초기화
}
else if((INT)lpOverlapPlus->dwBytes == SOCKET_ERROR) // 에러라면
{
SetClose_Open(lpOverlapPlus, TRUE); // 에러, 객체 다시 초기화
}
else// 정상이라면
{
// 메세지 저장
if(!DispatchPacket(lpOverlapPlus))
{
ShowMessage(DISPATCH_FAILED);
SetClose_Open( lpOverlapPlus, TRUE );
}
else
{
// Receive 할당 패킷 해제
ReleaseRecvPacket( lpOverlapPlus );
}
}
break;
case ClientIoWrite:
ReleaseSendPacket( lpOverlapPlus );
break;
default:
ShowMessage(DOIOSWITCH_FAILED);
assert(0);
break;
}
return TRUE;
}
LPOVERLAPPEDPLUS NFConnection::PrepareAcptPacket()
{
LPOVERLAPPEDPLUS newolp = NULL;
// get accept overlapped structure and packet buffer.
if(m_pPacketPool->AllocAcptPacket(newolp) == FALSE)
{
ShowMessage(ALLOCACPT_FAILED);
return NULL;
}
// 치명적인 에러
if(!newolp)
{
ShowMessage(PREPAREACPT_FAILED);
return NULL;
}
// clear buffer
memset(&newolp->overlapped , NULL, sizeof(OVERLAPPED));
memset(&newolp->wbuf.buf[0], NULL, sizeof(m_nMaxPacketSize));
// init olp structure
newolp->sckListen = m_sckListener;
newolp->sckClient = m_Socket;
newolp->nConnState = ClientIoAccept;
newolp->pClientConn = (PVOID)this;
newolp->wbuf.len = CONNECT_CHECK_SIZE; // newolp->wbuf.len = MAXPACKETSIZE;
// ** WARNING **
// When you change your packet certfying correct connection,
// you must change the size of definition 'CONNECT_CHECK_SIZE'.
return newolp;
}
LPOVERLAPPEDPLUS NFConnection::PrepareRecvPacket(UINT srclen)
{
// 치명적인 에러
if(srclen > (UINT)m_nMaxPacketSize)
{
ShowMessage(PREPARERECVSIZE_FAILED);
return NULL;
}
LPOVERLAPPEDPLUS newolp = NULL;
// get recv overlapped structure and packet buffer.
if( FALSE == m_pPacketPool->AllocRecvPacket(newolp) )
{
ShowMessage(ALLOCRECV_FAILED);
return NULL;
}
// 치명적인 에러
if(!newolp)
{
ShowMessage(PREPARERECV_FAILED);
return NULL;
}
// clear buffer
memset(&newolp->overlapped , NULL, sizeof(OVERLAPPED));
memset(&newolp->wbuf.buf[0], NULL, sizeof(m_nMaxPacketSize));
// init olp structure
newolp->sckListen = m_sckListener;
newolp->sckClient = m_Socket;
newolp->nConnState = ClientIoRead;
newolp->pClientConn = (PVOID) this;
if(srclen == 0) newolp->wbuf.len = m_nMaxPacketSize;
else newolp->wbuf.len = srclen;
return newolp;
}
LPOVERLAPPEDPLUS NFConnection::PrepareSendPacket(CHAR *psrcbuf, UINT srclen)
{
// 치명적인 에러
if(srclen < 0 || srclen > (UINT)m_nMaxPacketSize)
{
ShowMessage(PREPARESENDSIZE_FAILED);
return NULL;
}
LPOVERLAPPEDPLUS newolp = NULL;
// get recv overlapped structure and packet buffer.
if( FALSE == m_pPacketPool->AllocSendPacket(newolp) )
{
ShowMessage(ALLOCSEND_FAILED);
return NULL;
}
// 치명적인 에러
if(!newolp)
{
ShowMessage(PREPARESEND_FAILED);
return NULL;
}
// clear buffer
memset(&newolp->overlapped , NULL, sizeof(OVERLAPPED));
memset(&newolp->wbuf.buf[0], NULL, sizeof(m_nMaxPacketSize));
// init olp structure
newolp->sckListen = m_sckListener;
newolp->sckClient = m_Socket;
newolp->nConnState = ClientIoWrite;
newolp->pClientConn = (PVOID) this;
newolp->wbuf.len = srclen;
memcpy(newolp->wbuf.buf,psrcbuf,srclen);
return newolp;
}
BOOL NFConnection::ReleaseAcptPacket(LPOVERLAPPEDPLUS olp)
{
if(NULL == olp) return FALSE;
if(NULL == olp->wbuf.buf) return FALSE;
if(!m_pPacketPool->FreeAcptPacket(olp))
{
ShowMessage(RELEASEACPT_FAILED);
return FALSE;
}
return TRUE;
}
BOOL NFConnection::ReleaseRecvPacket(LPOVERLAPPEDPLUS olp)
{
if(olp == NULL) return FALSE;
if(olp->wbuf.buf == NULL) return FALSE;
if(!m_pPacketPool->FreeRecvPacket(olp))
{
ShowMessage(RELEASERECV_FAILED);
return FALSE;
}
return TRUE;
}
BOOL NFConnection::ReleaseSendPacket(LPOVERLAPPEDPLUS olp)
{
if(olp == NULL) return FALSE;
if(olp->wbuf.buf == NULL) return FALSE;
if(!m_pPacketPool->FreeSendPacket(olp))
{
ShowMessage(RELEASESEND_FAILED);
return FALSE;
}
return TRUE;
}
BOOL NFConnection::BindIOCP(LPOVERLAPPEDPLUS lpOverlapPlus)
{
// 치명적인 에러
if(!lpOverlapPlus)
{
ShowMessage(BINDIOCP_FAILED);
return FALSE;
}
INT locallen, remotelen;
sockaddr_in * plocal = 0,
* premote = 0;
GetAcceptExSockaddrs(
&(lpOverlapPlus->wbuf.buf[0]),
m_nMaxBuf,
sizeof(sockaddr_in) + 16,
sizeof(sockaddr_in) + 16,
(sockaddr **)&plocal, // 서버단
&locallen,
(sockaddr **)&premote, // 로컬단
&remotelen
);
memcpy(&m_Local, plocal, sizeof(sockaddr_in));
memcpy(&m_Peer, premote, sizeof(sockaddr_in));
if(CreateIoCompletionPort((HANDLE)lpOverlapPlus->sckClient,m_hIOCP,0,0) == 0) return FALSE;
return TRUE;
}
void NFConnection::SetClose_Open(LPOVERLAPPEDPLUS lpOverlapPlus, BOOL bForce)
{
if(m_eConnectFlag != CONNECT_NONE)
{
LOG_ERROR((L"NFConnection::SetClose_Open() m_eConnectFlag == %d", (int)m_eConnectFlag));
}
// 할당 패킷 상태 검사후 패킷 할당 해제
if(NULL != lpOverlapPlus)
{
if(NULL != lpOverlapPlus->wbuf.buf && NULL != m_pPacketPool)
{
// 마지막으로 완료 되었던 오버랩구조체와 버퍼를 릴리즈한다.
switch( lpOverlapPlus->nConnState)
{
case ClientIoAccept:
ReleaseAcptPacket( lpOverlapPlus );
break;
case ClientIoRead:
ReleaseRecvPacket( lpOverlapPlus );
break;
case ClientIoWrite:
ReleaseSendPacket( lpOverlapPlus );
break;
default:
break;
}
}
}
m_bForce = bForce;
InterlockedExchange((LONG*)&m_eConnectFlag,CLOSEOPEN_TRUE);
NFUpdateManager::GetInstance().Add(this, NULL);
}
void NFConnection::SetConnectFlag(CONNECT_EVENT eState)
{
if(m_eConnectFlag != CONNECT_NONE)
{
LOG_ERROR((L"NFConnection::SetConnectFlag() m_eConnectFlag == %s", (int)m_eConnectFlag));
}
InterlockedExchange((LONG*)&m_eConnectFlag,eState);
NFUpdateManager::GetInstance().Add(this, NULL);
}
BOOL NFConnection::DispatchPacket(LPOVERLAPPEDPLUS lpOverlapPlus)
{
// Read같은경우 RecvPost 할때 마다 1번 발생하는데 현재
// Read할때 RecvPost를 1번만 하기 대문에 스레드가 아무리 많아도.DispatchPacket은 1번만 일어난다.
CHAR* psrcbuf = &( lpOverlapPlus->wbuf.buf[0] );
INT srclen = lpOverlapPlus->dwBytes;
// Packet정보 자체가 [H 2byte][P size] 형식이다.
m_RecvIO.Append(psrcbuf, srclen);
m_RecvIO.Lock();
// IOCP는 스레드 패킷처리에 의한 성능향상이 주 능력이다
// 그런데 아래와 같이 UpdateManaget에 패킷을 넣은후 주 스레드에서 Update를 처리하면
// 데드락같은 문제는 생기지 않지만 다중처리에 의한 성능향상이 생기지 않는다.
// 패킷은 스레드 상태에서 바로 처리하게 수정하고 UpdateManager은 커넥트 플래스를
// 업데이트 하는걸로 제한해보자.
#ifdef ENABLE_UPDATEQUE
if(m_RecvIO.GetPacket(&m_RecvPacket) == 1)
{
m_RecvPacket.DecryptPacket();
if(m_RecvPacket.IsAliveChecksum())
NFUpdateManager::GetInstance().Add(this, &m_RecvPacket);
m_RecvPacket.Init();
}
else
m_RecvIO.UnLock();
#else
// 아래와 같이 스레드에서 패킷을 처리하게 되면
// Dispatch에 크리티컬 세션을 집어넣어 데드락에 주의해야한다.
if(m_RecvIO.GetPacket(&m_RecvPacket) == 1)
{
m_RecvPacket.DecryptPacket();
if(m_RecvPacket.IsAliveChecksum())
{
DispatchPacket(m_RecvPacket);
}
m_RecvPacket.Init();
}
else
m_RecvIO.UnLock();
#endif
// 그리고 새로운 recieve buffer를 준비하여 Post한다.
return RecvPost();
}
VOID NFConnection::UpdatePacket(NaveNet::NFPacket& Packet)
{
if(Packet.GetCommand() != 0)
{
DispatchPacket(Packet);
return;
}
// 커넥션의 상태를 업데이트 하기위한 부분.
if(m_eConnectFlag == CONNECT_NONE)
return;
if(m_eConnectFlag == CONNECT_TRUE)
{
InterlockedExchange((LONG*)&m_eConnectFlag,CONNECT_NONE);
OnConnect(TRUE);
}
else if(m_eConnectFlag == CONNECT_FALSE)
{
InterlockedExchange((LONG*)&m_eConnectFlag,CONNECT_NONE);
OnConnect(FALSE);
Close_Open();
}
else if(m_eConnectFlag == DISCONNECT_TURE)
{
InterlockedExchange((LONG*)&m_eConnectFlag,CONNECT_NONE);
Disconnect(FALSE);
}
else if(m_eConnectFlag == CLOSEOPEN_TRUE)
{
InterlockedExchange((LONG*)&m_eConnectFlag,CONNECT_NONE);
Close_Open(m_bForce);
}
}
BOOL NFConnection::SendPost( CHAR* pPackte, INT Len )
{
if(!m_Socket) return FALSE;
if(!IsConnect()) return FALSE;
if(m_Socket == NULL || IsConnect() == FALSE)
{
ShowMessage(SENDPOST_FAILED);
return FALSE;
}
// prepare recieve buffer
LPOVERLAPPEDPLUS newolp = PrepareSendPacket(pPackte,Len);
// 제대로 할당 받았는지 조사
if(newolp == NULL) return FALSE;
INT ret = WSASend( newolp->sckClient,
&newolp->wbuf,
1,
&newolp->dwBytes, // 만약 호출했을때 바로 받았다면 여기로 받은 크기가 넘어오지만 iocp에서는 의미가 없다.
newolp->dwFlags,
&newolp->overlapped, // Overlapped 구조체
NULL );
// 에러 처리
if(ret == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING)
{
ShowMessage(SENDPOSTPENDING_FAILED);
ReleaseSendPacket(newolp);
return FALSE;
}
return TRUE;
}
BOOL NFConnection::SendPost( NaveNet::NFPacket& Packet ) // 실제 Send 처리
{
// 보내기 직전 체크섬을 생성한다.
Packet.EncryptPacket();
INT Len = Packet.m_Header.Size;
CHAR* pPacket = (CHAR*)&Packet;
return SendPost(pPacket, Len);
}
BOOL NFConnection::RecvPost(UINT buflen)
{
if(m_Socket == NULL || IsConnect() == FALSE)
{
ShowMessage(RECVPOST_FAILED);
return FALSE;
}
// prepare recieve buffer
LPOVERLAPPEDPLUS newolp = PrepareRecvPacket(buflen);
// 제대로 할당 받았는지 조사
if(newolp == NULL) return FALSE;
INT ret = WSARecv( newolp->sckClient,
&newolp->wbuf,
1,
&newolp->dwBytes, // 만약 호출했을때 바로 받았다면 여기로 받은 크기가 넘어오지만 iocp에서는 의미가 없다.
&newolp->dwFlags,
&newolp->overlapped, // Overlapped 구조체
NULL );
// 에러 처리
if(ret == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING)
{
ShowMessage(RECVPOSTPENDING_FAILED);
ReleaseRecvPacket(newolp);
return FALSE;
}
return TRUE;
}
BOOL NFConnection::GetClientIP(INT *iIP)
{
if(iIP == NULL) return FALSE;
if(!IsConnect())
{
iIP[0] = 0;
return FALSE;
}
iIP[0] = m_Peer.sin_addr.S_un.S_un_b.s_b1;
iIP[1] = m_Peer.sin_addr.S_un.S_un_b.s_b2;
iIP[2] = m_Peer.sin_addr.S_un.S_un_b.s_b3;
iIP[3] = m_Peer.sin_addr.S_un.S_un_b.s_b4;
return TRUE;
}
BOOL NFConnection::GetClientIP(CHAR *szIP)
{
if(szIP == NULL) return FALSE;
if(!IsConnect())
{
szIP[0] = NULL;
return FALSE;
}
sprintf(szIP,"%d.%d.%d.%d",(INT)m_Peer.sin_addr.S_un.S_un_b.s_b1,
(INT)m_Peer.sin_addr.S_un.S_un_b.s_b2,
(INT)m_Peer.sin_addr.S_un.S_un_b.s_b3,
(INT)m_Peer.sin_addr.S_un.S_un_b.s_b4);
return TRUE;
}
BOOL NFConnection::GetClientIP(WCHAR *szIP)
{
if(szIP == NULL) return FALSE;
if(!IsConnect())
{
szIP[0] = NULL;
return FALSE;
}
swprintf(szIP, L"%d.%d.%d.%d",(INT)m_Peer.sin_addr.S_un.S_un_b.s_b1,
(INT)m_Peer.sin_addr.S_un.S_un_b.s_b2,
(INT)m_Peer.sin_addr.S_un.S_un_b.s_b3,
(INT)m_Peer.sin_addr.S_un.S_un_b.s_b4);
return TRUE;
}
BOOL NFConnection::GetClientIP(sockaddr_in& addr)
{
if(!IsConnect())
{
memcpy(&addr,0,sizeof(sockaddr_in));
return FALSE;
}
CopyMemory((CHAR*)&addr,(CHAR*)&m_Peer,sizeof(sockaddr_in));
return TRUE;
}
VOID NFConnection::Disconnect(BOOL bForce)
{
// 연결 되어있는지 확인
if(!IsConnect())
return;
// 변수 초기화
InterlockedExchange((LONG*)&m_eConnectFlag,CONNECT_NONE);
InterlockedExchange((LONG*)&m_bIsConnect,FALSE);
m_uRecvTickCnt = 0;
m_bForce = FALSE;
// 소켓과 리스너 상태 확인
if(m_Socket)
{
struct linger li = {0, 0}; // Default: SO_DONTLINGER
shutdown(m_Socket, SD_BOTH );
// 소켓에 처리 되지 않은 데이타가 소켓 버퍼에 남아있다면,
if(bForce) li.l_onoff = 1; // SO_LINGER, timeout = 0
// 잔여 데이타가 있으면 제거
setsockopt(m_Socket, SOL_SOCKET, SO_LINGER, (CHAR *)&li, sizeof(li));
closesocket(m_Socket); // 소켓 닫기
m_Socket = NULL; // 초기화
ShowMessage(CLOSE_SOCKET);
}
OnDisconnect();
Clear();
}
BOOL NFConnection::IsConnect()
{
BOOL bRet = m_bIsConnect;
return bRet;
}
LONG NFConnection::GetRecvTickCnt()
{
LONG lRecvTickCount = m_uRecvTickCnt;
return lRecvTickCount;
}
}

View File

@@ -0,0 +1,317 @@
/**
* @file NFConnection.h
* @brief 하나의 Connection을 관리하는 객체이다.
* @remarks
* @author 강동명(edith2580@gmail.com)
* @date 2009-05-09
*/
#pragma once
#include "NFMemPool.h"
#include "NFPacketPool.h"
#include <NaveNet/NFIOBuffer.h>
#include <Nave/NFSync.h>
namespace NaveServer {
/**
* @class NFConnection
* @brief 하나의 소켓객체를 나타낸다 1개의 클라이언트를 나타낸다.
* @remarks
*
* @par
* @author Edith
* @date 2009-08-28
*/
class NFConnection
{
public:
NFConnection();
virtual ~NFConnection();
public:
// 이 객체 생성
/**
* @brief 멤버 변수 세팅 및 객체 초기화
* @param dwIndex 객체 고유 ID
* @param hIOCP IOCP Handle
* @param listener Listen Socket
* @param pPacketPool 패킷 Pool 포인터
* @param nMaxBuf 최대 버퍼크기
* @return 성공여부
*/
virtual BOOL Create( DWORD dwIndex,
HANDLE hIOCP,
SOCKET listener,
NFPacketPool* pPacketPool,
INT nMaxBuf);
/**
* @brief 패킷을 업데이트처리 합니다.
* @param Packet 업데이트 할 패킷정보
*/
virtual VOID UpdatePacket(NaveNet::NFPacket& Packet);
/**
* @brief 연결 강제 종료
* @param bForce TRUE면 소켓에 남은 데이터 정리
*/
VOID Disconnect( BOOL bForce = FALSE );
/**
* @brief 내부 변수등을 정리합니다.
*/
virtual VOID Clear() { return; };
/**
* @brief 객체의 클라이언트 연결 해제 및 초기화
* @param bForce TRUE면 소켓에 남은 데이터 정리
* @return 성공여부
*/
virtual BOOL Close_Open( BOOL bForce = FALSE );
/**
* @brief IOCP 처리 핸들링
* @param lpOverlapPlus 처리할 패킷
* @return
*/
virtual BOOL DoIo( LPOVERLAPPEDPLUS lpOverlapPlus );
/**
* @brief 패킷 전송
* @param pPackte 보낼 패킷 포인터
* @param Len 보낼 패킷 크기
* @return 성공여부
*/
virtual BOOL SendPost( CHAR* pPackte, INT Len );
/**
* @brief 패킷 전송
* @param Packet 보낼 패킷 구조체
* @return 성공여부
*/
virtual BOOL SendPost( NaveNet::NFPacket& Packet);
/**
* @brief 연결자의 IP얻기
* @param iIP 정수형 4개 배열
* @return
*/
BOOL GetClientIP( INT* iIP );
/**
* @brief 연결자의 IP얻기
* @param szIP 문자형
* @return
*/
BOOL GetClientIP( CHAR* szIP );
/**
* @brief 연결자의 IP얻기
* @param szIP 유니코드 문자형
* @return
*/
BOOL GetClientIP( WCHAR* szIP );
/**
* @brief 연결자의 IP얻기
* @param addr sockaddr_in 형식
* @return
*/
BOOL GetClientIP( sockaddr_in& addr );
/**
* @brief 연결개체의 고유 인덱스를 설정합니다.
* @param iIndex 연결개체의 고유 인덱스
*/
inline VOID SetIndex(INT iIndex) { m_dwIndex = iIndex; };
/**
* @brief 연결개체의 고유 인덱스 얻기
* @return 인덱스값
*/
inline DWORD GetIndex() { return m_dwIndex; }
/**
* @brief 수신 Tick 얻기
* @return 수신 Tick 카운트
*/
LONG GetRecvTickCnt();
/**
* @brief 연결 객체의 활성화 상태 얻기
* @return 활성화 상태
*/
BOOL IsConnect();
/**
* @brief 연결 객체의 상태를 설정합니다.
* @param eState 연결 객체의 상태 정보
*/
void SetConnectFlag(CONNECT_EVENT eState);
/**
* @brief 전달된 Overlapped객체를 이용해 연결 객체를 종료시킨후 초기화 시킵니다.
* @param lpOverlapPlus Overlapped 객체
* @param bForce TRUE면 소켓에 남은 데이터 정리
*/
void SetClose_Open(LPOVERLAPPEDPLUS lpOverlapPlus, BOOL bForce=FALSE );
//------------------------ 멤 버 함 수 -------------------------//
protected:
/**
* @brief 객체 초기화, 리슨 소켓 결합
* @return 성공여부
*/
virtual BOOL Open();
/**
* @brief Socket과 IOCP 바인딩 작업
* @param lpOverlapPlus 바인딩 시킬 Overlapped 구조체
* @return
*/
BOOL BindIOCP( LPOVERLAPPEDPLUS lpOverlapPlus );
/**
* @brief Accept패킷 할당
* @return 할당된 Overlapped 구조체
*/
LPOVERLAPPEDPLUS PrepareAcptPacket();
/**
* @brief IOCP를 이용한 Recv Overlapped 셋팅
* @param buflen 받을 크기
* @return 할당된 Overlapped 구조체
*/
LPOVERLAPPEDPLUS PrepareRecvPacket(UINT buflen);
/**
* @brief IOCP를 이용한 Send Overlapped 셋팅
* @param *psrcbuf 보낼 데이터 포인터
* @param srclen 보낼 크기
* @return 할당된 Overlapped 구조체
*/
LPOVERLAPPEDPLUS PrepareSendPacket(CHAR *psrcbuf, UINT srclen);
/**
* @brief Accept 패킷 해제
* @param lpOverlapPlus 해제할 Overlapped구조체
* @return 성공여부
*/
BOOL ReleaseAcptPacket(LPOVERLAPPEDPLUS lpOverlapPlus);
/**
* @brief Receive 패킷 해제
* @param lpOverlapPlus 해제할 Overlapped구조체
* @return 성공여부
*/
BOOL ReleaseRecvPacket(LPOVERLAPPEDPLUS lpOverlapPlus);
/**
* @brief Send 패킷 해제
* @param lpOverlapPlus 해제할 Overlapped구조체
* @return 성공여부
*/
BOOL ReleaseSendPacket(LPOVERLAPPEDPLUS lpOverlapPlus);
/**
* @brief 실제 Receive 처리
* @param buflen 보낼 버퍼 사이즈
* @return 성공여부
*/
BOOL RecvPost(UINT buflen=0);
/**
* @brief 로그에 메시지를 출력합니다.
* @param MsgIndex 메시지 아이디
*/
virtual VOID ShowMessage(INT MsgIndex);
/**
* @brief 연결자에서 Connect 이벤트 발생
* @param bConnect Connect 성공여부
*/
virtual VOID OnConnect(BOOL bConnect) { };
/// Disconnect 이벤트 발생
virtual VOID OnDisconnect() { };
// 내부 처리 전송 함수
/**
* @brief 메세지 저장 및 Receive 신호 발생, Socket정리
* @param lpOverlapPlus Overlapped 구조체
* @return Recv 처리 성공 여부
*/
virtual BOOL DispatchPacket( LPOVERLAPPEDPLUS lpOverlapPlus );
/**
* @brief 실제 패킷을 처리하는 부분이다
* @param Packet 처리할 패킷 구조체
*/
virtual VOID DispatchPacket( NaveNet::NFPacket& Packet )
{
// 상속받아서 패킷을 처리할때는 아래와 같이 세션 변수를 등록해서 사용한다.
//Nave::Sync::CSSync Live(&m_Sync);
};
protected:
/// 최대 버퍼 사이즈
INT m_nMaxBuf;
/// 최대 패킷 사이즈
INT m_nMaxPacketSize;
/// 클라이언트 연결 소켓
SOCKET m_Socket;
/// Listener 소켓
SOCKET m_sckListener;
/// 서버 정보를 가지고 있는 객체
sockaddr_in m_Local;
/// 클라이언트 정보를 가지고 있는 객체
sockaddr_in m_Peer;
/// 이 클라이언트 연결 객체의 번호
INT m_dwIndex;
/// 보낸 바이트 검사 변수
INT m_nBytesSent;
/// IOCP 핸들
HANDLE m_hIOCP;
//이 클래스는 IOCP에서 패킷을 주고 받을때 Socket에 직접 연결되는 버퍼를 관리한다.
/// 패킷풀 포인터
NFPacketPool* m_pPacketPool;
/// 이 객체가 클라이언트와 연결 검사
BOOL m_bIsConnect;
/// 수신 Tick Count
LONG m_uRecvTickCnt;
/// 커넥션 상태 변수
CONNECT_EVENT m_eConnectFlag;
/// 소켓 버퍼 초기화 상태 변수
BOOL m_bForce;
/// Sync 객체
Nave::NFSync m_Lock;
// checksum 처리 /////////////////////////////////////////////
// CPacketIOBuffer은 Recv받은 패킷 정보(이땐 패킷풀의 버퍼에 있다)를 저장하는 IOBuffer 이다.
// IOBuffer을 사용하는 이유는 Recv 받은 패킷이 완전한 패킷이 아닐때 처리하기 위한 하나의
// 방편이다.
/// Recv의 IOBuffer
NaveNet::NFPacketIOBuffer m_RecvIO;
// CPacket는 하나의 패킷을 관리하는 클래스로 IOBuffer 에서 패킷을 꺼내 저장할때 사용된다.
// Packet에는 패킷 버퍼가 배열로 정의되어 있다. (memcpy를 사용하기 때문에 오버로드가 발생한다.)
/// Recv를 하기위해 사용하는 임시 패킷 변수
NaveNet::NFPacket m_RecvPacket;
};
}

View File

@@ -0,0 +1,101 @@
#include "Global.h"
#include "NFConnectionManager.h"
#include <Nave/NFLog.h>
namespace NaveServer {
NFConnectionManager::NFConnectionManager(VOID) : m_iCount(0), m_iMaxCount(0)
{
m_vecConn.clear();
}
NFConnectionManager::~NFConnectionManager(VOID)
{
m_vecConn.clear();
}
VOID NFConnectionManager::Init(INT iMaxCount)
{
Nave::NFSyncLock Sync(&m_Lock);
m_iCount = 0;
m_iMaxCount = iMaxCount;
m_vecConn.reserve(iMaxCount);
for(INT i = 0; i < m_iMaxCount; ++i)
{
m_vecConn.push_back(NULL);
}
}
VOID NFConnectionManager::SendPostAll( CHAR* pPackte, INT Len ) // 실제 Send 처리
{
for(INT i = 0; i < m_iMaxCount; ++i)
{
if(!m_vecConn[i])
continue;
m_vecConn[i]->SendPost(pPackte, Len);
}
}
VOID NFConnectionManager::SendPostAll( NaveNet::NFPacket& Packet)
{
Packet.EncryptPacket();
INT Len = Packet.m_Header.Size;
CHAR* pPacket = (CHAR*)&Packet;
SendPostAll(pPacket, Len);
}
VOID NFConnectionManager::Draw()
{
Nave::NFSyncLock Sync(&m_Lock);
INT Count = 0;
for(INT i = 0; i < m_iMaxCount; ++i)
{
if(m_vecConn[i])
Count++;
}
LOG_ERROR((L"Leave 되지 않은 Count : %d", Count));
}
// Connect, Disconnect 를 관리하는 함수.
VOID NFConnectionManager::Join(NFConnection* pUser)
{
Nave::NFSyncLock Sync(&m_Lock);
if(!pUser)
return;
INT iIndex = pUser->GetIndex();
if(m_vecConn[iIndex] == NULL)
{
m_vecConn[iIndex] = pUser;
++m_iCount;
}
else
{
LOG_ERROR((L"[%04d] 중복 Join호출", iIndex));
}
}
VOID NFConnectionManager::Levae(INT iIndex)
{
Nave::NFSyncLock Sync(&m_Lock);
if(m_vecConn[iIndex])
{
--m_iCount;
m_vecConn[iIndex] = NULL;
}
else
{
LOG_ERROR((L"[%04d] 중복 Leave호출 Socket : %d", iIndex, WSAGetLastError()));
}
}
}

View File

@@ -0,0 +1,90 @@
/**
* @file NFConnectionManager.h
* @brief Connection을 관리하는 관리객체
* @remarks
* @author 강동명(edith2580@gmail.com)
* @date 2009-05-09
*/
#pragma once
#include <Nave/NFSync.h>
#include <vector>
namespace NaveServer {
/**
* @class NFConnectionManager
* @brief 특정한 커넥션 리스트를 관리하기 위한 메니져 객체 동시에 여러개가 생길수 있다.
* @remarks
*
* @par
* @author Edith
* @date 2009-08-28
*/
class NFConnectionManager
{
public:
NFConnectionManager(VOID);
~NFConnectionManager(VOID);
public:
/**
* @brief 메니져 초기화
* @param iMaxCount 관리 가능한 최대 커넥션 개수
*/
VOID Init(INT iMaxCount);
/**
* @brief 유저를 등록시킨다.
* @param pUser 유저의 연결객체 포인터
*/
VOID Join(NFConnection* pUser);
/**
* @brief 해당 인덱스의 객체를 삭제한다.
* @param iIndex 삭제할 인덱스
*/
VOID Levae(INT iIndex);
/**
* @brief 현재 등록된 연결객체 개수 얻기
* @return 커넥션 개수
*/
INT GetCount() { return m_iCount; }
/**
* @brief 관리하는 연결객체 최대 개수
* @return 최대 연결객체 개수
*/
INT GetMaxCount() { return m_iMaxCount; }
/**
* @brief 등록된 모든 연결객체에 패킷을 전달한다.
* @param pPackte 보낼 패킷 버퍼
* @param Len 보낼 패킷 사이즈
*/
virtual VOID SendPostAll( CHAR* pPackte, INT Len );
/**
* @brief 등록된 모든 연결객체에 패킷을 전달한다.
* @param Packet 보낼 패킷 구조체
*/
virtual VOID SendPostAll( NaveNet::NFPacket& Packet);
VOID Draw();
private:
/// 연결객체 관리 변수
std::vector<NFConnection*> m_vecConn;
typedef std::vector<NFConnection*>::iterator iterConn;
/// 현재 등록된 연결객체의 개수
INT m_iCount;
/// 관리가 가능한 최대 연결객체의 개수
INT m_iMaxCount;
/// Sync 객체
Nave::NFSync m_Lock;
};
}

View File

@@ -0,0 +1,50 @@
#include <stdio.h>
#include "NFDBComponent.h"
namespace NaveServer {
NFDBComponent::NFDBComponent(void)
{
}
NFDBComponent::~NFDBComponent()
{
}
bool NFDBComponent::Connect(LPCWSTR szDBServerName, LPCWSTR szDBName,
LPCWSTR szDBAccount, LPCWSTR szDBPass,
NFOleDB::ConnType connType)
{
swprintf(m_LastMessage, L"DB¿¡ ·Î±×ÀÎÇÕ´Ï´Ù : %s, DB : %s, Account : %s, Password : %s", szDBServerName, szDBName, szDBAccount, szDBPass);
if (!ConnectSQLServer(szDBServerName, szDBName, szDBAccount, szDBPass, connType))
{
swprintf(m_LastMessage, L"DB Connect failed : %s", GetErrorString());
return false;
}
return true;
}
bool NFDBComponent::Select(const WCHAR *Query_In, void** lpLoginTable_Out,
int Size_In, int StartNum_In, int RowNum_In, int *GetRowNum_Out)
{
if (StartNum_In == 0 && !ExecuteQuery(Query_In))
{
swprintf(m_LastMessage, L"Execute DB Query failed : %s", GetErrorString());
}
else if (!GetData((void **)lpLoginTable_Out, Size_In, RowNum_In, GetRowNum_Out))
{
swprintf(m_LastMessage, L"Get DBdata failed : %s", GetErrorString());
}
else
{
return true;
}
return false;
}
}

View File

@@ -0,0 +1,51 @@
/**
* @file NFDBComponent.h
* @brief OLE DB를 이용한 DB 컴퍼넌트
* @remarks
* @author 강동명(edith2580@gmail.com)
* @date 2009-05-09
*/
#pragma once
#include "NFOLEDB.h"
// 현재 소지하고 있는 OLEDB는 nchar 와 nvarchar즉 2byte 유니코드 문자형을 제대로 지원하지 못하고 있다.
namespace NaveServer {
/**
* @class
* @brief
* @remarks
*
* @par
* @author Edith
* @date 2009-05-09
*/
class NFDBComponent : public NFOleDB
{
public:
enum
{
MAX_QUERY_LENGTH = 4096,
QUERY_BUFFER_LEN = 1024
};
NFDBComponent();
virtual ~NFDBComponent();
bool Connect(LPCWSTR szDBServerName, LPCWSTR szDBName,
LPCWSTR szDBAccount, LPCWSTR szDBPass,
NFOleDB::ConnType connType = NFOleDB::ConnType_MSSQL);
bool Select(const WCHAR *Query_In, void** lpLoginTable_Out, int Size_In,
int StartNum_In, int RowNum_In, int *GetRowNum_Out);
WCHAR* GetQueryBuffer() { return m_Query; }
const int GetQueryBufferLen() { return MAX_QUERY_LENGTH; }
private:
WCHAR m_Query[MAX_QUERY_LENGTH];
};
}

View File

@@ -0,0 +1,100 @@
/**
* @file NFDefine.h
* @brief Packet에 관련된 기본 Define 및 스트럭쳐 선언
* @remarks
* @author 강동명(edith2580@gmail.com)
* @date 2009-05-09
*/
#pragma once
namespace NaveServer {
// 패킷 처리를 UpdateQue를 이용해 처리할것인지에 대한 Define
// 이걸 활성화 시키면 패킷을 처리할때 Receive스레드가 아닌
// Process스레드에서 패킷을 업데이트 한다.
// UPDATEQUE 방식은 Work스레드에서 Packet이 완성되면 PacketQue를 이용해
// 패킷을 처리한다. (MMO시에 필요)
#define ENABLE_UPDATEQUE
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// [1] User Define
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define DEF_MAXUSER 50 // Maximum connections for Test
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// [2] Server Declarations
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define IOCP_SHUTDOWN ((DWORD) -1L) // Closing Server Message
#define DEF_SERVER_PORT 25000 // PORT Number ( Client와 동일 해야함 )
enum ESHOW_MSGTYPE
{
ACCEPT_FAILED,
CLOSE_SOCKET,
DEAD_CONNECTION,
CONNECT_SUCCESS,
CONNECT_FAILED,
DISPATCH_FAILED,
DOIOSWITCH_FAILED,
// accept
ALLOCACPT_FAILED,
PREPAREACPT_FAILED,
// send, recv
PREPARERECVSIZE_FAILED,
PREPARESENDSIZE_FAILED,
ALLOCRECV_FAILED,
ALLOCSEND_FAILED,
PREPARERECV_FAILED,
PREPARESEND_FAILED,
RELEASEACPT_FAILED,
RELEASERECV_FAILED,
RELEASESEND_FAILED,
BINDIOCP_FAILED,
RECVPOST_FAILED,
RECVPOSTPENDING_FAILED,
// ALLOCPROC_FAILED,
SENDPOST_FAILED,
SENDPOSTPENDING_FAILED,
};
enum CONNECT_EVENT
{
CONNECT_NONE,
CONNECT_TRUE,
CONNECT_FALSE,
DISCONNECT_TURE,
CLOSEOPEN_TRUE,
};
/// 열거형 정의 : 클라이언트 작동 상태를 정의 한다
typedef enum CONN_STATUS
{
ClientIoUnknown, /// Raw status
ClientIoAccept, /// accept status
ClientIoRead, /// read status
ClientIoWrite, /// write status
}*pCONN_STATUS;
/// 확장 오버랩 구조체 : IOCP처리시 사용
typedef struct OVERLAPPEDPLUS {
OVERLAPPED overlapped; /// OVERLAPPED struct
SOCKET sckListen; /// listen socket handle
SOCKET sckClient; /// send/recv socket handle
CONN_STATUS nConnState; /// operation flag
WSABUF wbuf; /// WSA buffer
DWORD dwRemain; ///
DWORD dwBytes; /// Processing Data Size
DWORD dwFlags; ///
PVOID pClientConn; /// Processing Client
}*LPOVERLAPPEDPLUS;
}

View File

@@ -0,0 +1,243 @@
/**
* @file NFMemPool.h
* @brief Packet Memory 관리객체
* @remarks
* @author 강동명(edith2580@gmail.com)
* @date 2009-05-09
*/
#pragma once
#include <Nave/NFSync.h>
#include <Nave/NFLog.h>
namespace NaveServer {
#pragma pack(push, struct_packing_before)
#pragma pack(1)
/**
* @class NFMemPool
* @brief IOCP에서 사용할 패킷 버퍼를 관리할 메모리Pool 클래스
* @remarks
*
* @par
* @author Edith
* @date 2009-08-28
*/
template <class TYPE>
class NFMemPool
{
public:
/// 할당 받을 갯수와 버퍼 사이즈 입력
NFMemPool(INT nTotalCnt, INT nAllocBufferSize=0);
/// 할당 해제 처리
~NFMemPool();
/**
* @brief 로그파일의 이름을 결정합니다.
* @param strLogTitle 로그의 이름
*/
VOID SetLogTitle(WCHAR* strLogTitle)
{
wcscpy(m_strLogTitle, strLogTitle);
}
/// 현재 할당 받은 수
INT GetCount();
/// 비어 있는 패킷 수
INT GetEmpty();
/// 버퍼에 따른 인덱스 얻기
INT GetIndex( TYPE *ptr );
/// 할당된 패킷중 빈 패킷 반환
TYPE* Alloc();
/// 할당 받은 메모리를 해제
BOOL Free( TYPE *ptr );
private:
/// INDEX Structure
struct INDEX
{
/// 다음 Index 포인터를 가진다
INDEX* pNext;
/// 현재 인덱스 번호
INT nIndex;
/// Structure constructure
INDEX(){
pNext = NULL;
nIndex = 0;
}
};
private:
/// 현재 할당 받은 수
INT m_nCount;
/// 만들어진 할당 패킷 수
INT m_nTotalCnt;
/// 한 패킷당 할당 받은 버퍼 사이즈
INT m_nAllocBufferSize;
/// 한 패킷당 전체 사이즈
INT m_nPerPacketSize;
/// 로그파일 이름
WCHAR m_strLogTitle[32];
/// 전체 할당 포인터
PVOID m_pPakets;
/// 현재 비어있는 처리 인덱스 포인터
INDEX* m_pFreeRoom;
Nave::NFSync m_Sync;
};
#pragma pack( pop, struct_packing_before )
template <class TYPE>
NFMemPool<TYPE>::NFMemPool( INT nTotalCnt, INT nAllocBufferSize )
{
assert( nTotalCnt > 0 );
m_nCount = 0;
m_pFreeRoom = NULL;
m_pPakets = NULL;
m_nTotalCnt = nTotalCnt; // MAXUSER * 2
m_nAllocBufferSize = nAllocBufferSize; // 1024 + 64
m_nPerPacketSize = sizeof(INDEX) + sizeof(TYPE) + m_nAllocBufferSize;
m_pPakets = VirtualAlloc( NULL,
m_nTotalCnt * m_nPerPacketSize,
MEM_RESERVE | MEM_COMMIT, // reserve and commit
PAGE_READWRITE );
//////////////////////////////////////////////////////////
// < 할당 데이타 블럭도 > //
// //
// ----------------------------------------------- //
// | INDEX | TYPE | m_nExBlockSize | //
// ----------------------------------------------- //
//////////////////////////////////////////////////////////
assert( m_pPakets );
INDEX* pIndex = (INDEX*) m_pPakets;
assert( pIndex );
//////////////////////////////////////////
// init linear linked list for buffer pool
// 할당 데이타 구조 맨 마지막 단위의 포인터 얻기
pIndex = (INDEX*) ((DWORD)pIndex + (m_nTotalCnt - 1) * m_nPerPacketSize);
// 할당 데이타 구조의 마지막 부터 Linked List를 구성하여 올라오기
for( INT i = m_nTotalCnt-1; i >= 0; i-- )
{
pIndex->pNext = m_pFreeRoom; // 맨 마지막의 pNext = NULL이다
pIndex->nIndex = i; // INDEX 작업
m_pFreeRoom = pIndex; // 다음 연결을 위한 세팅
#ifdef _DEBUG
// 각 블럭 마다 ExBlockSize의 부분에 데이타 세팅 작업
if( m_nAllocBufferSize )
memset((PVOID)( (DWORD)pIndex+sizeof(INDEX)+sizeof(TYPE)),
(i%10+'0'),
m_nAllocBufferSize );
#endif
// 다음 리스트로 C8List 포인트 이동
pIndex = (INDEX*)((DWORD)pIndex - m_nPerPacketSize);
}
}
template <class TYPE>
NFMemPool<TYPE>::~NFMemPool()
{
if(NULL != m_pPakets) VirtualFree( m_pPakets, 0, MEM_RELEASE );
}
template <class TYPE>
TYPE* NFMemPool<TYPE>::Alloc()
{
// 변수 초기화
INDEX* pIndex = NULL;
TYPE* pType = NULL;
Nave::NFSyncLock CL(&m_Sync);
pIndex = m_pFreeRoom;
if( pIndex != NULL )
{
// 빈공간 이동 인덱스 포인터를 다음 인덱스로 이동
m_pFreeRoom = m_pFreeRoom->pNext;
m_nCount++; // 카운트 증가
// 지정한 인덱스 포인터에서 템플릿 타입 위티 포인터 반환
pType = (TYPE*)(pIndex+1);
///////////////////////////////////////////
// 치명적인 에러 발생
// assert( m_nCount > 0 ); // make sure we don't overflow
if(m_nCount > m_nTotalCnt)
{
LOG_ERROR((L"[%s] MemPool Alloc Overflow Count(%d) >= %d", m_strLogTitle, m_nCount, m_nTotalCnt));
return NULL;
}
}
else
LOG_ERROR((L"[%s] MemPool Alloc m_pFreeRoom = NULL, Count(%d)", m_strLogTitle, m_nCount));
return pType; // 템플릿 타입 반환
}
template <class TYPE>
BOOL NFMemPool<TYPE>::Free( TYPE *ptr )
{
Nave::NFSyncLock CL(&m_Sync);
BOOL bRet = FALSE;
INDEX* pIndex = ((INDEX*)(ptr))-1; // 사용 패킷의 인덱스 포인터 얻기
// 사용중인 패킷이 있다면
if( m_nCount > 0 )
{
// 빈공간 인덱스 포인터를 앞으로 당기는 루틴
pIndex->pNext = m_pFreeRoom;
m_pFreeRoom = pIndex;
m_nCount--; // 사용 패킷수 1 감소
bRet = TRUE; // 정상 처리 알림
}
else
LOG_ERROR((L"[%s] MemPool Free Faild Count(%d) <= 0", m_strLogTitle, m_nCount));
return bRet; // 정상 처리 유무 반환
}
template <class TYPE>
INT NFMemPool<TYPE>::GetCount()
{
Nave::NFSyncLock CL(&m_Sync);
INT nRet = m_nCount; // 변수 설정
return nRet; // 반환
}
template <class TYPE>
INT NFMemPool<TYPE>::GetEmpty()
{
Nave::NFSyncLock CL(&m_Sync);
INT nRet = m_nTotalCnt - m_nCount; // 설정
return nRet; // 반환
}
template <class TYPE>
INT NFMemPool<TYPE>::GetIndex( TYPE *ptr )
{
Nave::NFSyncLock CL(&m_Sync);
INT nRet = 0; // 변수 초기화
INDEX* pIndex = ((INDEX*)(ptr))-1; // 인데스 포인터 얻기
nRet = pIndex->nIndex; // 변수 설정
return nRet; // 반환
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,216 @@
/**
* @file NFOLEDB.h
* @brief OLD DB 객체
* @remarks
* @author 강동명(edith2580@gmail.com)
* @date 2009-05-09
*/
#pragma once
#include <oledb.h>
#include <oledberr.h>
//#define _CHECK_OLEDB_PERFORMANCE // DB 퍼포먼스 체크
#ifdef _CHECK_OLEDB_PERFORMANCE
#define DB_PERFORMANCE_CHECK(x) x
#else
#define DB_PERFORMANCE_CHECK(x) (void*)0;
#endif
/*
SQL Server Data Type ADO Data Type SQLOLEDB data type
bigint adBigInt DBTYPE_I8
binary adBinary DBTYPE_BYTES
bit adBoolean DBTYPE_BOOL
char adChar DBTYPE_STR
datetime adDBTimeStamp DBTYPE_DBTIMESTAMP
decimal adNumeric DBTYPE_NUMERIC
float adDouble DBTYPE_R8
image adVarbinary DBTYPE_BYTES
int adInteger DBTYPE_I4
money adCurrency DBTYPE_CY
nchar adWChar DBTYPE_WSTR
ntext adWChar DBTYPE_WSTR
numeric adNumeric DBTYPE_NUMERIC
nvarchar adWChar DBTYPE_WSTR
real adSingle DBTYPE_R4
smalldatetime adTimeStamp DBTYPE_DBTIMESTAMP
smallint adSmallInt DBTYPE_I2
smallmoney adCurrency DBTYPE_CY
sql_variant adVariant DBTYPE_VARIANT, DBTYPE_SQLVARIANT*
sysname adWChar DBTYPE_WSTR
text adChar DBTYPE_STR
timestamp adBinary DBTYPE_BYTES
tinyint adVarbinary DBTYPE_UI1
uniqueidentifier adGUID DBTYPE_GUID
varbinary adVarBinary DBTYPE_BYTES
varchar adChar DBTYPE_STR
분류 데이터 타입 범위 저장소크기
정수
Bit O 또는 1 bit
Int -2,147,483,648 ~ 2,147,483,647 4 바이트
Smallint -32,768 ~ 32,767 2 바이트
Tinyint 0 ~ 255 1 바이트
Bigint -2^63 ~ 2^63-1 8 바이트
부동소수점
Float[n] -1.79E+308 ~ 1.79E+308
n = 1~24 4 바이트
Float[n] -1.79E+308 ~ 1.79E+308
n = 25~53 8 바이트
Real -3.40E + 38 ~ 3.40E + 38 4 바이트
문자데이터
char[n] n = 1~8000 n 바이트
Varchar[n] n = 1~8000 입력한 데이터의 길이
Text 최대 2,147,483,647자의가변길이
유니코드
문자데이터 Nchar n = 1~4000 n*2 바이트
nvarchar n = 1~4000 입력한 데이터의 길이*2 바이트
Ntext 최대 1,073,741,823자의 가변길이
이진데이터
binary n = 1~8000 n+4 바이트
varbinary n = 1~8000 입력한 데이터의 길이+4 바이트
Image 최대 2,147,483,647자의 가변길이
날짜와시간
datetime 1753/1/1~9999/12/31 8 바이트
smalldatetime 1900/1/1~2079/6/6 4 바이트
화폐
money -922,337,203,685,477.5808~
+922,337,203,685,477.5807 8 바이트
smallmoney -214,748.3648~214,748.3647 4 바이트
*/
namespace NaveServer {
/**
* @class NFOleDB
* @brief NFOleDB를 이용해 Database에 접근하기 위한 클래스
* @remarks
*
* @todo 현재 nchar와 nvarchar 즉 2byte 유니코드 문자형을 제대로 지원하지 못한다.
* 이부분을 테스트 해야한다. 2byte 문자열을 쿼리에 날릴때 문자앞에 유니코드
* 라고 지정하는게 있는데 이거랑 연관관계가 있는지 확인해야함
* @par
* @author Edith
* @date 2009-05-09
*/
class NFOleDB
{
public:
const static unsigned long MaxRowNum = 2000; /// 최대 열 숫자
const static unsigned long MaxErrorLen = 512; /// 최대 에러 길이
const static unsigned long MaxQueryTextLen = 8192; /// 최대 쿼리 문자열 길이
public:
/// DBConnect 타입을 말한다.
enum ConnType
{
ConnType_ODBC = 0,
ConnType_MSSQL = 1,
ConnType_ORACLE
};
// 쿼리 실행후 로우셋
enum Rowset
{
Rowset_Get = 0,
Rowset_Update = 1
};
/// 쿼리를 날릴때 파라메터로 쿼리를 날릴경우 사용한다.
typedef struct PARAM_INFO
{
const static unsigned short MaxColNum = 30; /// 최대 컬럼 이름 길이
unsigned long ColNum; /// 컬럼 숫자
DBPARAMIO eParamIO[MaxColNum]; /// 컬럼 파라미터 타입
unsigned long ColSize[MaxColNum]; /// 컬럼 사이즈
unsigned short ColType[MaxColNum]; /// 컬럼 타입
}*LPPARAM_INFO;
/// 바이너리의 정보를 전달할때 사용한다.
typedef struct SET_BINARY
{
unsigned long Size;
}*LPSET_BINARY;
/// 컬럼의 정보를 설정한다. (현재 지원하지 않는다.)
typedef struct COL_INFO
{
const static unsigned short MaxColNameLen = 100; /// 최대 컬럼 이름 길이
char ColName[MaxColNameLen]; /// 컬럼 이름
unsigned long ColSize; /// 컬럼 사이즈
}*LPCOL_INFO;
public:
NFOleDB(void);
virtual ~NFOleDB(void);
inline LPCWSTR GetErrorString(void) const { return m_ErrorString; }
inline LPCWSTR GetLastMessage(void) const { return m_LastMessage; }
inline HRESULT GetLastError(void) const { return m_dwLastError; }
bool ConnectDataSourcePrompt(HWND hWnd_In);
bool ConnectSQLServer(LPCWSTR ServerName_In, LPCWSTR DataBaseName_In, LPCWSTR UserID_In, LPCWSTR UserPass_In, ConnType ConnType_In);
bool DisconnectDataSource(void);
bool ExecuteQuery(LPCWSTR Query_In, Rowset Rowset_In = Rowset_Get);
bool ExecuteQueryWithParams(LPCWSTR Query_In, char *Data_In, const PARAM_INFO& ColInfo_In);
bool ExecuteQueryGetData(LPCWSTR Query_In, void *Buffer_Out);
bool GetData(void *Buffer_Out);
bool GetData(void **Buffer_Out, int RowSize_In, int Row_In, int *pGetRow_Out);
bool SetBinaryData(int ColNum_In, LPSET_BINARY lpSetBinary);
COL_INFO& GetColInfo(void) { return m_ColInfo; }
public:
IDBInitialize* m_pIDBInit;
IDBCreateSession* m_pIDBCreateSession;
IDBCreateCommand* m_pIDBCreateCommand;
IRowset* m_pIRowset;
IRowsetChange* m_pIRowsetChange;
HRESULT m_dwLastError;
WCHAR m_ErrorString[MaxErrorLen];
WCHAR m_LastMessage[MaxErrorLen];
private:
/// 컬럼 결과값을 지정한다.
typedef struct RESULT_COLS
{
unsigned long ColNum;
DBCOLUMNINFO* lpDBColumnInfo;
WCHAR* lpStringsBuffer;
}*LPRESULT_COLS;
WCHAR m_QueryText[MaxQueryTextLen]; /// 쿼리 문자열
COL_INFO m_ColInfo; /// 컬럼의 정보
private:
bool HandleError(int ErrorLine_In, HRESULT hResult_In, WCHAR *Buffer_In);
bool CreateSession(void);
bool DBCreateCommand(void);
bool AllocResultCols(IUnknown* lpIUnknown_In, RESULT_COLS &Rsult_Cols);
bool ReleaseResultCols(IUnknown* lpIUnknown_In, RESULT_COLS &Rsult_Cols);
bool SetConnectionProperties(LPCWSTR ServerName_In, LPCWSTR DataBaseName_In, LPCWSTR UserID_In, LPCWSTR UserPass_In);
DBBINDING* AllocBindGetData(int ColsNum_In, DBCOLUMNINFO* pDBColumnInfo_In);
DBBINDING* AllocBindParamInputData(const PARAM_INFO &ColInfo_In);
};
}

View File

@@ -0,0 +1,328 @@
#include "Global.h"
#include "NFMemPool.h"
#include "NFPacketPool.h"
#include <Nave/NFLog.h>
namespace NaveServer {
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : 생성자 //
// [2]PARAMETER : //
// [3]RETURN : //
// [4]DATE : 2000년 9월 4일 //
//////////////////////////////////////////////////////////////////
NFPacketPool::NFPacketPool()
{
m_hLogFile = NULL; // 파일 핸들 초기화
m_pAcptPool = NULL;
m_pRecvPool = NULL;
m_pProcPool = NULL; // 변수 초기화
m_nMaxPacketSize = 0;
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : 소멸자 - 메모리 할당 해제 //
// [2]PARAMETER : //
// [3]RETURN : //
// [4]DATE : 2000년 9월 4일 //
//////////////////////////////////////////////////////////////////
NFPacketPool::~NFPacketPool()
{
Release();
}
VOID NFPacketPool::Release()
{
LogClose(); // 로그 파일 핸들 닫기
m_nMaxPacketSize = 0;
if(m_pAcptPool)
delete m_pAcptPool;
if(m_pRecvPool)
delete m_pRecvPool;
if(m_pProcPool)
delete m_pProcPool;
m_pAcptPool = NULL;
m_pRecvPool = NULL;
m_pProcPool = NULL; // 변수 초기화
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : 초기화 함수 //
// [2]PARAMETER : lpszFileName - 파일 이름, 없으면 NULL //
// [3]RETURN : TRUE - 정상처리 FALSE - 실패 //
// [4]DATE : 2000년 9월 4일 //
//////////////////////////////////////////////////////////////////
BOOL NFPacketPool::Create(INT nMaxUser,CHAR* lpszFileName,INT nMaxPacketSize,INT nAcptBuffCnt,INT nRecvBuffCnt,INT nSendBuffCnt)
{
// 포인터(이름)가 존재 하면..
if(lpszFileName) LogOpen( lpszFileName ); // 파일 오픈
m_nMaxPacketSize = nMaxPacketSize;
// 각 패킷열 메모리 할당
// Winsock 자체에서 쓰는 메모리 땜에 64를 더한다
// winsock에서 사용하는 OVERLAPPED 때문일수도..
m_pAcptPool = new NFMemPool <OVERLAPPEDPLUS> (nMaxUser*nAcptBuffCnt, m_nMaxPacketSize+64);
m_pRecvPool = new NFMemPool <OVERLAPPEDPLUS> (nMaxUser*nRecvBuffCnt, m_nMaxPacketSize+64);
m_pProcPool = new NFMemPool <OVERLAPPEDPLUS> (nMaxUser*nSendBuffCnt, m_nMaxPacketSize+64);
// 파일 쓰기
LogWrite("acpt=%08X, recv=%08X, proc=%08X", m_pAcptPool, m_pRecvPool, m_pProcPool);
// 하나라도 메모리 할당이 안되었다면
if( !m_pAcptPool || !m_pRecvPool || !m_pProcPool)
return FALSE; // FALSE
m_pAcptPool->SetLogTitle(L"ACPT");
m_pRecvPool->SetLogTitle(L"RECV");
m_pProcPool->SetLogTitle(L"SEND");
return TRUE; // TRUE
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : 파일 핸들 열기 //
// [2]PARAMETER : lpszFileName - 파일 이름 //
// [3]RETURN : TRUE - 정상처리 FALSE - 실패 //
// [4]DATE : 2000년 9월 4일 //
//////////////////////////////////////////////////////////////////
BOOL NFPacketPool::LogOpen( CHAR *lpszFileName )
{
// 이름을 정하지 않았다면
if( m_hLogFile || lpszFileName == NULL ) return FALSE; // FALSE 리턴
m_hLogFile = fopen( lpszFileName, "ab" ); // 처리 파일 오픈
// 에러 검사
if( !m_hLogFile ) return FALSE;
return TRUE; // TRUE 리턴 - 정상 처리
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : 파일에 연결하여 쓰기 //
// [2]PARAMETER : lpszFileName - 파일 이름 정하기 //
// [3]RETURN : TRUE - 정상처리 FALSE - 실패 //
// [4]DATE : 2000년 9월 4일 //
//////////////////////////////////////////////////////////////////
BOOL __cdecl NFPacketPool::LogWrite( CHAR *lpszFmt, ... )
{
if(NULL == m_hLogFile) return FALSE;
va_list argptr;
CHAR szOutStr[1024];
va_start(argptr, lpszFmt);
vsprintf(szOutStr, lpszFmt, argptr);
va_end(argptr);
INT nBytesWritten = fprintf( m_hLogFile, "%s\r\n", szOutStr );// LOG 내용
fflush( m_hLogFile );
return TRUE;
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : 오픈된 파일 닫기 //
// [2]PARAMETER : void //
// [3]RETURN : void //
// [4]DATE : 2000년 9월 4일 //
//////////////////////////////////////////////////////////////////
void NFPacketPool::LogClose()
{
// 파일 핸들이 존재 한다면,
if(m_hLogFile) fclose( m_hLogFile ); // 파일 핸들 닫기
m_hLogFile = NULL; // 파일 핸들 초기화
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : Accept Packet 할당 //
// [2]PARAMETER : newolp - 참조 변수 //
// [3]RETURN : TRUE - 정상처리 //
// [4]DATE : 2000년 9월 4일 //
//////////////////////////////////////////////////////////////////
BOOL NFPacketPool::AllocAcptPacket( LPOVERLAPPEDPLUS &newolp )
{
LPOVERLAPPEDPLUS olp = NULL; // 구조체 객체 생성
BOOL bRet = FALSE;
if(!m_pAcptPool)
return bRet; // 반환
olp = m_pAcptPool->Alloc(); // Accept Pool에설 할당 받음
LogWrite( "AlocAcptPacket(%08X)", (DWORD)olp ); // logging
// 할당이 제대로 되었다면
if(olp)
{
// 할당 메모리 처리 부분
newolp = olp; // 변수 세팅
newolp->wbuf.buf = (CHAR*)(olp+1); // 버퍼 세팅
bRet = TRUE; // 반환값 - TRUE로 바꿈
}
return bRet; // 반환
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : Receive Packet 할당 //
// [2]PARAMETER : newolp - 참조 변수 //
// [3]RETURN : TRUE - 정상처리 //
// [4]DATE : 2000년 9월 4일 //
//////////////////////////////////////////////////////////////////
BOOL NFPacketPool::AllocRecvPacket( LPOVERLAPPEDPLUS &newolp )
{
LPOVERLAPPEDPLUS olp = NULL; // 구조체 객체 생성
BOOL bRet = FALSE;
if(!m_pRecvPool)
return bRet; // 반환
olp = m_pRecvPool->Alloc(); // Receive Pool에설 할당 받음
LogWrite( "AlocRecvPacket(%08X)", (DWORD)olp ); // logging
// 할당이 제대로 되었다면
if(olp)
{
// 할당 메모리 처리 부분
newolp = olp; // 변수 세팅
newolp->wbuf.buf = (CHAR*)(olp+1); // 버퍼 세팅
bRet = TRUE; // 반환값 - TRUE로 바꿈
}
else
LOG_ERROR((L"NFPacketPool::AllocRecvPacket Pool Alloc Faild.."));
return bRet; // 반환
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : Process Packet 할당 //
// [2]PARAMETER : newolp - 참조 변수 //
// [3]RETURN : TRUE - 정상처리 //
// [4]DATE : 2000년 10월 7일 //
//////////////////////////////////////////////////////////////////
BOOL NFPacketPool::AllocSendPacket(LPOVERLAPPEDPLUS &newolp)
{
LPOVERLAPPEDPLUS olp = NULL; // 구조체 객체 생성
BOOL bRet = FALSE;
if(!m_pProcPool)
return bRet; // 반환
olp = m_pProcPool->Alloc(); // Accept Pool에설 할당 받음
LogWrite( "AlocAcptPacket(%08X)", (DWORD)olp ); // logging
// 할당이 제대로 되었다면
if(olp)
{
// 할당 메모리 처리 부분
newolp = olp; // 변수 세팅
newolp->wbuf.buf = (CHAR*)(olp+1); // 버퍼 세팅
bRet = TRUE; // 반환값 - TRUE로 바꿈
}
return bRet; // 반환
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : Process Buffer 할당 //
// [2]PARAMETER : newolp - 참조 변수 //
// [3]RETURN : TRUE - 정상처리 //
// [4]DATE : 2000년 10월 7일 //
//////////////////////////////////////////////////////////////////
BOOL NFPacketPool::AllocProcBuffer(PCHAR &newbuf)
{
LPOVERLAPPEDPLUS olp = NULL; // 구조체 객체 생성
BOOL bRet = FALSE;
if(!m_pProcPool)
return bRet; // 반환
olp = m_pProcPool->Alloc(); // Accept Pool에설 할당 받음
LogWrite( "AllocProcBuffer(%08X)", (DWORD)olp ); // logging
// 할당이 제대로 되었다면
if(olp)
{
// 할당 메모리 처리 부분
newbuf = (CHAR*)(olp+1); // 버퍼 세팅
bRet = TRUE; // 반환값 - TRUE로 바꿈
}
return bRet;
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : Accept 할당 패킷 해제 //
// [2]PARAMETER : olp - 해제할 패킷 //
// [3]RETURN : TRUE - 정상 처리 //
// [4]DATE : 2000년 9월 4일 //
//////////////////////////////////////////////////////////////////
BOOL NFPacketPool::FreeAcptPacket( LPOVERLAPPEDPLUS olp )
{
LogWrite( "FreeAcptPacket(%08X)", (DWORD)olp ); // Logging
if(!m_pAcptPool)
return FALSE; // 반환
return(m_pAcptPool->Free(olp)); // 패킷 해제 및 결과 반환
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : Receive 할당 패킷 해제 //
// [2]PARAMETER : olp - 해제할 패킷 //
// [3]RETURN : TRUE - 정상 처리 //
// [4]DATE : 2000년 9월 4일 //
//////////////////////////////////////////////////////////////////
BOOL NFPacketPool::FreeRecvPacket( LPOVERLAPPEDPLUS olp )
{
LogWrite( "FreeRecvPacket(%08X)", (DWORD)olp ); // Logging
if(!m_pRecvPool)
return FALSE; // 반환
return( m_pRecvPool->Free(olp) ); // 패킷 해제 및 결과 반환
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : Process 할당 패킷 해제 //
// [2]PARAMETER : olp - 해제할 패킷 //
// [3]RETURN : TRUE - 정상 처리 //
// [4]DATE : 2000년 9월 4일 //
//////////////////////////////////////////////////////////////////
BOOL NFPacketPool::FreeSendPacket(LPOVERLAPPEDPLUS olp)
{
LogWrite( "FreeProcPacket(%08X)", (DWORD)olp ); // Logging
if(!m_pProcPool)
return FALSE; // 반환
return(m_pProcPool->Free(olp)); // 패킷 해제 및 결과 반환
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : Process 할당 버퍼 해제 //
// [2]PARAMETER : olp - 해제할 패킷 //
// [3]RETURN : TRUE - 정상 처리 //
// [4]DATE : 2000년 9월 4일 //
//////////////////////////////////////////////////////////////////
BOOL NFPacketPool::FreeProcBuffer(PCHAR buffer)
{
LPOVERLAPPEDPLUS olp;
olp = ((LPOVERLAPPEDPLUS)buffer) - 1;
LogWrite( "FreeProcBuffer(%08X)", (DWORD)olp ); // Logging
if(!m_pProcPool)
return FALSE; // 반환
return(m_pProcPool->Free(olp)); // 패킷 해제 및 결과 반환
}
}

View File

@@ -0,0 +1,58 @@
/**
* @file NFPacketPool.h
* @brief Packet Pool 관리객체, 이곳에서 Buffer를 할당하거나 해제한다.
* @remarks
* @author 강동명(edith2580@gmail.com)
* @date 2009-05-09
*/
#pragma once
namespace NaveServer {
//////////////////////////////////////////////////////////////////////////////////////////////
// NFPacketPool Class
//////////////////////////////////////////////////////////////////////////////////////////////
class NFPacketPool
{
//---------------------- Member Functions ----------------------//
public:
NFPacketPool(); // Contructor
virtual ~NFPacketPool(); // Destructor
BOOL Create(INT nMaxUse = DEF_MAXUSER, // 최대 유저
CHAR* lpszFileName = NULL, // 로그 파일 이름
INT nMaxPacketSize = DEF_MAXPACKETSIZE,
INT nAcptBuffCnt = 1,
INT nRecvBuffCnt = 8,
INT nSendBuffCnt = 32); // Create Packet Pool
VOID Release();
// Operations
BOOL AllocAcptPacket(LPOVERLAPPEDPLUS &newolp); // Accept 패킷 생성
BOOL AllocRecvPacket(LPOVERLAPPEDPLUS &newolp); // Receive 패킷 생성
BOOL AllocSendPacket(LPOVERLAPPEDPLUS &newolp); // Send Buffer 생성
BOOL AllocProcBuffer(PCHAR &newbuf); // Send Buffer 생성
BOOL FreeAcptPacket(LPOVERLAPPEDPLUS olp); // Accept 패킷 해제
BOOL FreeRecvPacket(LPOVERLAPPEDPLUS olp); // Receive 패킷 해제
BOOL FreeSendPacket(LPOVERLAPPEDPLUS olp); // Send Buffer 해제
BOOL FreeProcBuffer(PCHAR buffer); // Send Buffer 해제
private:
BOOL LogOpen(CHAR *lpszFileName); // 로그 파일 열기
BOOL __cdecl LogWrite(CHAR *lpszFmt, ...); // 로그 파일 만들기
VOID LogClose(); // 로그 파일 닫기
//---------------------- Member Variables ----------------------//
private:
FILE* m_hLogFile; // 로그 파일을 만들기 위한 파일 핸들 객체
NFMemPool <OVERLAPPEDPLUS>* m_pAcptPool; // Accept Packet
NFMemPool <OVERLAPPEDPLUS>* m_pRecvPool; // Receive Packet
NFMemPool <OVERLAPPEDPLUS>* m_pProcPool; // Process Packet
INT m_nMaxPacketSize; // 패킷 사이즈 정한것
};
}

View File

@@ -0,0 +1,446 @@
///////////////////////////////////////////////////////////////////////////////
// Filename: IOCPServerCtrl.cpp
// Coder : 강동명 (Edithe@chollian.net)
// Comp. : Navezine
// Compiler: Visual C++ .net
// Title : IOCP Base 컨트롤러 클래스
///////////////////////////////////////////////////////////////////////////////
#include "Global.h"
#include "NFServerCtrl.h"
#include <Nave/NFLog.h>
#include <Nave/NFSync.h>
namespace NaveServer {
// INT iMaxRecvPacket = 0;
// INT iRecvPacket = 0; // 이 객체가 클라이언트와 연결 검사
// DWORD uRecvTickCnt = 0; // 수신 Tick Count
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
NFServerCtrl::NFServerCtrl()
{
m_bServerRun = FALSE; // 서버 구동상태
m_bPause = FALSE;
m_nMaxThreadNum = (GetNumberOfProcess() * 2); // CPU수 * 2개로 Thread 수 결정
m_iPort = 0;
m_iMaxConn = 50;
m_pWorkThread = NULL; // 메인 스레드 핸들
m_hProcThread = 0;
m_hPacketThread = 0;
m_hIOCP = NULL; // IOCP 핸들
m_pUpdateManager = new NFUpdateManager();
}
NFServerCtrl::~NFServerCtrl()
{
m_bServerRun = FALSE;
_DELETE(m_pUpdateManager);
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : IOCP 핸들 생성 //
// [2]PARAMETER : void //
// [3]RETURN : HANDLE = 생성된 IOCP //
// [4]DATE : 2003년 10월 24일 //
//////////////////////////////////////////////////////////////////
HANDLE NFServerCtrl::CreateIOCP()
{
return CreateIoCompletionPort(INVALID_HANDLE_VALUE,0,0,0);
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : Listen 소켓 생성 //
// [2]PARAMETER : void //
// [3]RETURN : SOCKET = 생성된 LISTEN SOCKET //
// [4]DATE : 2003년 10월 24일 //
//////////////////////////////////////////////////////////////////
SOCKET NFServerCtrl::CreateListenSocket(INT nServerPort, CHAR cBindQue)
{
SOCKET Socket = NULL; // a Socket Variable for using Listener
// 소켓 상태 설정 구조체 선언
SOCKADDR_IN addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons( (short)nServerPort );
// [1] Create Listen Socket
Socket = WSASocket( AF_INET, SOCK_STREAM, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED );
if(Socket == NULL)
return Socket;
// [2] bind listen socket
if(bind(Socket,(SOCKADDR *)&addr,sizeof(addr)) != 0)
return Socket;
// [3] listening for an concurrent incoming connections limited in 5
listen(Socket, cBindQue);
LISTENER stLin;
stLin.Init();
stLin.s = Socket;
stLin.nPort = nServerPort;
stLin.cBindQue = cBindQue;
m_vecListener.push_back(stLin);
return Socket;
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : Listen Socket 을 iocp 소켓에 연결 //
// [2]PARAMETER : SOCKET sckListener //
// [3]RETURN : HANDLE = 연결된 IOCP //
// [4]DATE : 2003년 10월 24일 //
//////////////////////////////////////////////////////////////////
HANDLE NFServerCtrl::ConnectIOCPSocket(SOCKET sckListener)
{
return CreateIoCompletionPort((HANDLE)sckListener,m_hIOCP,0,0);
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : SOCKET과련 라이브러리 활성화 //
// [2]PARAMETER : void //
// [3]RETURN : void //
// [4]DATE : 2003년 10월 24일 //
//////////////////////////////////////////////////////////////////
BOOL NFServerCtrl::InitSocket()
{
WSADATA wsaData; // Initialzing Variables
return (SOCKET_ERROR != WSAStartup(0x202,&wsaData)); // Start Up
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : 시스템의 CPU 수를 구함 //
// [2]PARAMETER : void //
// [3]RETURN : INT = 현재 시스템의 CPU 수 반환 //
// [4]DATE : 2003년 10월 24일 //
//////////////////////////////////////////////////////////////////
INT NFServerCtrl::GetNumberOfProcess()
{
SYSTEM_INFO si; // a System Info Structure Object
GetSystemInfo( &si ); // Get the System Information
return (INT)si.dwNumberOfProcessors; // return the number of processors
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : 서버 정지 //
// [2]PARAMETER : void //
// [3]RETURN : BOOL - 의미 없음 //
// [4]DATE : 2003년 10월 24일 //
//////////////////////////////////////////////////////////////////
BOOL NFServerCtrl::Stop()
{
//////////////////////////////////////////////////////////////////////
// Server Closing Process //
//////////////////////////////////////////////////////////////////////
INT nCnt;
// [01] Queue Suicide Packets into each IOCP Main Thread
for( nCnt = 0; nCnt < m_nMaxThreadNum; nCnt++ )
{
if(PostQueuedCompletionStatus(m_hIOCP,0,IOCP_SHUTDOWN,0) ==NULL)
return FALSE;
}
// [02] Wait for thread terminations
nCnt = WaitForMultipleObjects( m_nMaxThreadNum, m_pWorkThread, TRUE, 15000 );
switch ( nCnt )
{
case WAIT_TIMEOUT:
LOG_ERROR((L"Not All WorkThreads Died in Time."));
break;
case WAIT_FAILED:
LOG_ERROR((L"WAIT_FAILED, WaitForMultipleObjects()."));
break;
default:
break;
}
// [03] Close Thread Handles
if( m_pWorkThread )
{
for( nCnt = 0; nCnt < m_nMaxThreadNum; nCnt++ )
CloseHandle( m_pWorkThread[nCnt] );
_DELETE_ARRAY(m_pWorkThread);
}
// [04] Process Thread Stop and Close
m_bServerRun = FALSE;
CloseHandle(m_hProcThread);
CloseHandle(m_hPacketThread);
if(ReleaseSession() == TRUE)
{
LOG_IMPORTANT((L"shutdown session.."));
}
// [05] Close Listener Socket
int iListener = m_vecListener.size();
for(int i = 0; i < iListener; ++i)
{
m_vecListener[i].Release();
}
// [6] Close IOCP Handle
if(m_hIOCP) CloseHandle(m_hIOCP);
// [7] 소켓 라이브러리 종료
WSACleanup();
// [8] Show the Result of Close Processing
LOG_IMPORTANT((L"shutdown sequence finished.."));
return TRUE;
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : 서버 구동 //
// [2]PARAMETER : void //
// [3]RETURN : BOOL - 의미 없음 //
// [4]DATE : 2003년 10월 24일 //
//////////////////////////////////////////////////////////////////
BOOL NFServerCtrl::Start(INT nPort, INT nMaxConn, INT nMaxThreadNum)
{
m_iPort = nPort;
m_iMaxConn = nMaxConn;
// 현재 사용되는 스레드는 Work용 스레드 CPU*2개
// 받은 패킷이나 이벤트등을 처리하는 Update용 스레드 1개를 사용한다.
// 만약 몬스터나 기타 다른 게임의 상태를 업데이트 하기위해서는
// 직접 작업용 스레드를 생성해 물리면 된다.
/*
생각하는것
IOCP용 스레드 CPU개수만큼 (IOCP를 통해 Accept, Recv, Send를 하기 위한 스레드)
EventUpdate용 스레드 1개 (패킷을 업데이트 하기 위한 큐)
게임의 상태(몬스터및기타상태)용 스레드 ?개
*/
// 0이아니면 그 개수 사용
if(nMaxThreadNum != 0)
m_nMaxThreadNum = nMaxThreadNum;
else
m_nMaxThreadNum = (GetNumberOfProcess() * 2); // CPU수 * 2개로 Thread 수 결정
// [00] 변수 및 객체 선언
INT nCnt = 0; // 루프 변수
UINT nDummy; // 쓰레기 값 처리
SOCKET skListener;
// [01] initialize socket library
if(!InitSocket()) goto Error_Procss;
// [02] Create IOCP
if((m_hIOCP = CreateIOCP()) == NULL) goto Error_Procss;
// [03] Create Listener Socket
if((skListener = CreateListenSocket(m_iPort)) == INVALID_SOCKET) goto Error_Procss;
// [04] Connect listener socket to IOCP
if(ConnectIOCPSocket(skListener) == NULL) goto Error_Procss;
// [05] 화면에 서버 정보 표시
ShowServerInfo();
// [06] Create Session..
if(CreateSession(skListener) == FALSE) goto Error_Procss;
// [07] Update Session..
if((m_pWorkThread = new HANDLE[m_nMaxThreadNum]) == NULL) goto Error_Procss; // Create thread Control Handle
for(nCnt = 0; nCnt < m_nMaxThreadNum; nCnt++) // Run Thread
{
if((m_pWorkThread[nCnt] = (HANDLE)_beginthreadex(0,0,Thread_MainEx,m_hIOCP,0,&nDummy)) == NULL) goto Error_Procss;
}
//--------------> Server Initializing has been done <---------------//
// [8] Process Thread 생성하기
m_bServerRun = TRUE;
if((m_hProcThread = (HANDLE)_beginthreadex(0,0,Process_MainEx,this,0,&nDummy)) == NULL) goto Error_Procss;
// [9] Process Thread 생성하기
if((m_hPacketThread = (HANDLE)_beginthreadex(0,0,Packet_MainEx,this,0,&nDummy)) == NULL) goto Error_Procss;
return TRUE;
Error_Procss:
Stop();
m_bServerRun = FALSE;
return FALSE;
}
VOID NFServerCtrl::ShowServerInfo()
{
WCHAR szDate[32],
szTime[32];
_tzset();
_wstrdate( szDate );
_wstrtime( szTime );
LOG_IMPORTANT((L"------------------------------------------------"));
LOG_IMPORTANT((L" %s initialized at %s, %s", L"Server", szDate, szTime) );
LOG_IMPORTANT((L"------------------------------------------------"));
//////////////////////////////////////////////////////////////////////
// Server start //
//////////////////////////////////////////////////////////////////////
LOG_IMPORTANT((L"------------------------------------------------"));
LOG_IMPORTANT((L"| SERVER START |"));
LOG_IMPORTANT((L"------------------------------------------------"));
WCHAR Ip[32];
GetLocalIP(Ip);
LOG_IMPORTANT((L"IP(%s) Port(%d)", Ip, m_iPort ));
}
VOID NFServerCtrl::Pause(BOOL bPause)
{
m_bPause = bPause;
}
VOID NFServerCtrl::Update()
{
/* if(timeGetTime() - uRecvTickCnt > 1000)
{
InterlockedExchange((LONG*)&iMaxRecvPacket,iRecvPacket);
InterlockedExchange((LONG*)&iRecvPacket,0);
InterlockedExchange((LONG*)&uRecvTickCnt,timeGetTime());
}
*/
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : IOCP처리를 담당하는 메인 스레드 //
// [2]PARAMETER : lpVoid - IOCP Handle //
// [3]RETURN : BOOL - 의미 없음 //
// [4]DATE : 2003년 10월 24일 //
//////////////////////////////////////////////////////////////////
unsigned NFServerCtrl::Thread_MainEx(LPVOID lpVoid)
{
DWORD dwIoSize; // 완료처리 사이즈 얻음
ULONG lCompletionKey; // 무한 루프 탈출 용이 됨
BOOL bSuccess; // 블럭킹 처리 에러 확인
HANDLE hIOCP = (HANDLE)lpVoid; // IOCP 핸들 얻음
LPOVERLAPPED lpOverlapped; // 중첩 확장 포인터
////////////
// 무한 루프
while( TRUE )
{
// IOCP 처리를 기다리는 BLOCKING MODE
bSuccess = GetQueuedCompletionStatus(hIOCP, // IOCP Handle
&dwIoSize, // 처리 사이즈
&lCompletionKey, // 완료 키
(LPOVERLAPPED*) &lpOverlapped, // 중첩 확장
INFINITE); // Waiting Time
LPOVERLAPPEDPLUS lpOverlapPlus = (LPOVERLAPPEDPLUS)lpOverlapped;
if(bSuccess)
{
// 종료 신호가 들어왔다면, 루프 탈출
if( lCompletionKey == IOCP_SHUTDOWN ) break;
if( NULL != lpOverlapPlus )
{
///////////////////////////////////////////////
// 처리가 정상적으로 이루어진다면 이쪽으로...
lpOverlapPlus->dwBytes = dwIoSize; // 처리 데이타 Size
// 처리 변수 Cast 변환
// 속도를 위해 메인프레임에선 try~catch를 뺀다.
// 알수 없는 오류를 검사하기 위해선 Exception::EnableUnhandledExceptioinFilter(true)를 사용한다
/*
// Recv 카운트 처리
if(lpOverlapPlus->nConnState == ClientIoRead)
{
InterlockedExchange((LONG*)&iRecvPacket,iRecvPacket+1);
}
*/
NFConnection* lpClientConn = (NFConnection*) lpOverlapPlus->pClientConn;
lpClientConn->DoIo(lpOverlapPlus); // IOCP 처리 핸들링
}
}
else
{
if(!lpOverlapPlus)
{
LOG_ERROR((L"Critical Error on GetQueuedCompletionStatus()."));
}
else
{
// 처리 변수 Cast 변환
NFConnection* lpClientConn = (NFConnection*) lpOverlapPlus->pClientConn;
// 강제로 Clear해주면 안된다. (데이타가 제대로 초기화 되지 않을수도 있다)
// lpClientConn->Clear();
// LOG_ERROR(("[%04d] IOCP OverlapPlus Error, Close_Open()호출. SOCKET_ERROR, %d", lpClientConn->GetIndex(), WSAGetLastError()));
lpClientConn->SetClose_Open(lpOverlapPlus, TRUE); // 연결 해제
}
}
}
return 0;
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : IOCP처리를 담당하는 메인 스레드 //
// [2]PARAMETER : lpVoid - IOCP Handle //
// [3]RETURN : BOOL - 의미 없음 //
// [4]DATE : 2003년 10월 24일 //
//////////////////////////////////////////////////////////////////
unsigned NFServerCtrl::Process_MainEx(LPVOID lpVoid)
{
NFServerCtrl* pCtrl = (NFServerCtrl*)lpVoid; // IOCP 핸들 얻음
assert(pCtrl);
while( pCtrl->IsRun())
{
if(pCtrl->IsPause())
{
Sleep(1);
continue;
}
pCtrl->Update();
Sleep(1);
}
return 0;
}
//////////////////////////////////////////////////////////////////
// [1]DESCRIPTION : UpateQue처리를 담당하는 메인 스레드 //
// [2]PARAMETER : lpVoid - IOCP Handle //
// [3]RETURN : BOOL - 의미 없음 //
// [4]DATE : 2003년 10월 24일 //
//////////////////////////////////////////////////////////////////
unsigned NFServerCtrl::Packet_MainEx(LPVOID lpVoid)
{
NFServerCtrl* pCtrl = (NFServerCtrl*)lpVoid; // IOCP 핸들 얻음
NFUpdateManager* pUpdateManager = NFUpdateManager::GetInstancePtr();
assert(pCtrl);
assert(pUpdateManager);
while( pCtrl->IsRun())
{
pUpdateManager->Update();
Sleep(1);
}
return 0;
}
}

View File

@@ -0,0 +1,99 @@
/**
* @file NFServerCtrl.h
* @brief IOCP Base 컨트롤러 클래스
* @remarks
* @author 강동명(edith2580@gmail.com)
* @date 2009-05-09
*/
#pragma once
#include "NFConnection.h"
#include <vector>
namespace NaveServer {
// extern INT iMaxRecvPacket;
typedef struct LISTENER
{
SOCKET s; // Listen Socket
INT nPort; // 포트
CHAR cBindQue; // 바인드 큐
VOID Init()
{
s = INVALID_SOCKET; // Listen Socket
nPort = 0; // 포트
cBindQue = 0;
}
VOID Release()
{
// 클로즈 소켓 전에 큐된 데이타를 보낼지 말지 결정하는 옵션
struct linger li = {1, 0}; // Default: SO_DONTLINGER
shutdown(s, SD_BOTH);
setsockopt(s, SOL_SOCKET, SO_LINGER, (CHAR *)&li, sizeof(li));
closesocket(s );
Init();
}
}*LPLISTENER;
class NFServerCtrl
{
public:
NFServerCtrl();
virtual ~NFServerCtrl();
public:
SOCKET CreateListenSocket(INT nServerPort, CHAR cBindQue = 32); // Listen Socket 생성
HANDLE ConnectIOCPSocket(SOCKET sckListener); // Listen Socket 을 iocp 소켓에 연결
inline HANDLE GetIOCP() { return m_hIOCP; };
inline BOOL IsRun() { return m_bServerRun; };
inline BOOL IsPause() { return m_bPause; };
static unsigned __stdcall WINAPI Thread_MainEx(LPVOID lpVoid); // IOCP 구동 메인 Thread
static unsigned __stdcall WINAPI Process_MainEx(LPVOID lpVoid); // Process 처리 Thread
static unsigned __stdcall WINAPI Packet_MainEx(LPVOID lpVoid); // Packet 처리 Thread
virtual VOID Update(); // Process 처리 함수
virtual VOID ShowServerInfo(); // 시작시 화면에 서버 정보 표시
virtual BOOL CreateSession(SOCKET sckListener) { return TRUE; } // Client Session 을 생성한다.
virtual BOOL ReleaseSession() { return TRUE; } // Client Session 을 삭제한다.
protected:
HANDLE CreateIOCP(); // IOCP 핸들 생성
BOOL InitSocket(); // 소켓 라이브러리 활성
INT GetNumberOfProcess(); // CPU수를 구한다 => 스레드 구동시 이용
INT GetNumberOfThread() { return m_nMaxThreadNum; }; // 생성된 스레드의 개수를 구한다.
BOOL Start(INT nPort, INT nMaxConn, INT nMaxThreadNum = 0); // 서버 구동
VOID Pause(BOOL bPause);
BOOL Stop(); // 서버 정지
protected:
INT m_nMaxThreadNum; // 최대 스레드 수 결정
HANDLE* m_pWorkThread; // IOCP Work 스레드 핸들
HANDLE m_hIOCP; // IOCP 핸들
std::vector<LISTENER> m_vecListener; // 리스너 리스트 여러개의 리스너를 생성할 수도 있다.
HANDLE m_hProcThread; // 프로세싱을 위한 스레드
HANDLE m_hPacketThread; // PacketUpdate을 위한 스레드
INT m_iPort;
INT m_iMaxConn;
BOOL m_bPause;
BOOL m_bServerRun; // 서버 시작 변수 실행되면 TRUE변경.
NFUpdateManager* m_pUpdateManager;
};
}

View File

@@ -0,0 +1,113 @@
#include "Global.h"
#include "NFUpdateManager.h"
namespace NaveServer {
// INT iMaxProcPacket = 0;
// INT iProcPacket = 0; // 이 객체가 클라이언트와 연결 검사
// DWORD uProcTickCnt = 0; // 수신 Tick Count
NFUpdateManager::NFUpdateManager(VOID) : m_Head(0), m_Tail(0)
{
}
NFUpdateManager::~NFUpdateManager(VOID)
{
}
VOID NFUpdateManager::Draw()
{
Nave::NFSyncLock Sync(&m_Lock);
INT Tail = m_Tail;
INT S = Tail-m_Head;
if(Tail < m_Head)
S = (MaxQueCount-m_Head)+Tail;
LOG_IMPORTANT((L"UpdateManager PacketCount : %d", S));
}
VOID NFUpdateManager::Update()
{
Nave::NFSyncLock Sync(&m_Lock);
INT i;
INT Tail = m_Tail;
/*
if(timeGetTime() - uProcTickCnt > 1000)
{
InterlockedExchange((LONG*)&iMaxProcPacket,iProcPacket);
InterlockedExchange((LONG*)&iProcPacket,0);
InterlockedExchange((LONG*)&uProcTickCnt,timeGetTime());
}
*/
// 없다.
if(m_Head == Tail)
return;
INT iCount = 0;
// Tail이 앞에 있다.
if(Tail < m_Head)
{
for(i = m_Head; i < MaxQueCount; ++i)
{
if(m_Conn[i] == NULL)
continue;
m_Conn[i]->UpdatePacket(m_Packet[i]);
m_Conn[i] = NULL;
iCount++;
}
for(i = 0; i < Tail; ++i)
{
if(m_Conn[i] == NULL)
continue;
m_Conn[i]->UpdatePacket(m_Packet[i]);
m_Conn[i] = NULL;
iCount++;
}
}
else // Tail이 뒤에있다.
{
for(i = m_Head; i < Tail; ++i)
{
if(m_Conn[i] == NULL)
continue;
m_Conn[i]->UpdatePacket(m_Packet[i]);
m_Conn[i] = NULL;
iCount++;
}
}
// InterlockedExchange((LONG*)&iProcPacket,iProcPacket+iCount);
m_Head = Tail;
}
VOID NFUpdateManager::Add(NFConnection* pUser, NaveNet::NFPacket* pPacket)
{
Nave::NFSyncLock Sync(&m_Lock);
if(!pUser)
return;
INT Tail = m_Tail;
m_Conn[Tail] = pUser;
if(pPacket)
memcpy(&m_Packet[Tail], pPacket, sizeof(NaveNet::NFPacket));
else
m_Packet[Tail].Init();
if(Tail+1 >= MaxQueCount)
m_Tail = 0;
else
m_Tail = Tail+1;
// if(m_Tail == m_Head)
// NAVE_ERROR(("UpdateManager Overflow. (H:%d, T:%d)", m_Head, m_Tail));
}
}

View File

@@ -0,0 +1,44 @@
/**
* @file NFUpdateManager.h
* @brief 한개의 완성된 패킷을 받았을때 해당패킷을 업데이트 하는 원형큐
* @remarks
* @author 강동명(edith2580@gmail.com)
* @date 2009-05-09
*/
#pragma once
#include <Nave/NFSingleton.h>
#include <Nave/NFSync.h>
#include <NaveNet/NFPacket.h>
#include "NFConnection.h"
namespace NaveServer {
class NFUpdateManager : public Nave::NFSingleton<NFUpdateManager>
{
public:
NFUpdateManager(VOID);
~NFUpdateManager(VOID);
public:
VOID Add(NFConnection* pUser, NaveNet::NFPacket* pPacket);
VOID Update();
VOID Draw();
private:
enum {
MaxQueCount = 1024,
};
// 원형큐 대용.
INT m_Head;
INT m_Tail;
NFConnection* m_Conn[MaxQueCount];
NaveNet::NFPacket m_Packet[MaxQueCount];
/// Sync 객체
Nave::NFSync m_Lock;
};
}

View File

@@ -0,0 +1,22 @@
========================================================================
정적 라이브러리 : Server 프로젝트 개요
========================================================================
응용 프로그램 마법사에서 이 Server 라이브러리를 만들었습니다.
프로젝트에 대해 소스 파일은 만들어지지 않았습니다.
Server.vcproj
응용 프로그램 마법사를 사용하여 생성한 VC++ 프로젝트의 기본 프로젝트 파일입니다.
파일을 생성한 Visual C++ 버전에 대한 정보와
응용 프로그램 마법사를 사용하여 선택한 플랫폼, 구성 및 프로젝트 기능에 대한
정보가 들어 있습니다.
/////////////////////////////////////////////////////////////////////////////
기타 참고:
응용 프로그램 마법사에서 사용하는 "TODO:" 주석은 사용자가 추가하거나 사용자 지정해야 하는
소스 코드 부분을 나타냅니다.
/////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,10 @@
#pragma once
//-----------------------------------------------------------------------------
// 스트럭쳐 1바이트 정렬.
#ifdef _WIN32
#pragma pack( push ,1 )
#else
#pragma pack(1)
#endif

View File

@@ -0,0 +1,97 @@
#include "Global.h"
#include "UICmdEdit.h"
#include "UICmdMsgView.h"
namespace NaveServer {
HWND UICmdEdit::s_hEditWnd = NULL; // 에디트,
CHAR UICmdEdit::s_EditStr[MAX_EDITSTRING];
WNDPROC UICmdEdit::s_OldEditProc;
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
LRESULT UICmdEdit::OnEditDefault( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
return CallWindowProc( s_OldEditProc, hWnd, uMsg, wParam, lParam );
}
LRESULT UICmdEdit::OnEditKeyDown( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch( wParam )
{
case VK_TAB:
// ListView에 포커스 넘김
SetFocus(UICmdMsgView::s_hListWnd);
return FALSE;
}
return OnEditDefault(hWnd, uMsg, wParam, lParam);
}
LRESULT UICmdEdit::OnEditChar( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch( wParam )
{
case 13: // EditBox에서 return을 했을때
SendMessage( hWnd, WM_GETTEXT, MAX_EDITSTRING, (LPARAM)(LPSTR )s_EditStr);
if(strlen(s_EditStr) == 0)
break;
// 입력한것에 대한 처리 셀을 선택
SendMessage( hWnd, EM_SETSEL, 0, (LPARAM )-1 );
// 부모에게 Return을 통지
SendMessage( GetParent(hWnd), WM_EDIT_RETURN, strlen(s_EditStr), (LPARAM)(LPSTR )s_EditStr );
return FALSE;
}
return OnEditDefault(hWnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK UICmdEdit::EditProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch( uMsg )
{
case WM_KEYDOWN:
return OnEditKeyDown( hWnd, uMsg, wParam, lParam );
case WM_CHAR :
return OnEditChar( hWnd, uMsg, wParam, lParam );
default :
return OnEditDefault( hWnd, uMsg, wParam, lParam );
}
}
HWND UICmdEdit::InitEditProc( HWND hParent, HFONT hFont )
{
// 메인 윈도우로.. 처리
ZeroMemory(s_EditStr, sizeof(s_EditStr));
RECT rect;
GetClientRect( hParent, &rect );
s_hEditWnd = CreateWindowW( L"EDIT", L"",
WS_CHILD | WS_CLIPSIBLINGS | WS_TABSTOP | WS_VISIBLE | WS_BORDER | ES_WANTRETURN | ES_AUTOHSCROLL,
0, rect.bottom-EDIT_HEIGHT, rect.right, EDIT_HEIGHT,
hParent, (HMENU )ID_EDIT,
(HINSTANCE )GetWindowLong( hParent, GWL_HINSTANCE ), NULL );
ShowWindow( s_hEditWnd, SW_SHOW );
UpdateWindow( s_hEditWnd );
// 메인 폰트로 폰트를 셋팅한다.
::SendMessage(s_hEditWnd, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(FALSE,0));
// 여기서 에디트 박스에 폰트를 셋팅하게 한다.
s_OldEditProc = (WNDPROC )GetWindowLong( s_hEditWnd, GWL_WNDPROC );
SetWindowLong( s_hEditWnd, GWL_WNDPROC, (LONG )EditProc );
return s_hEditWnd;
}
VOID UICmdEdit::ResizeEdit( HWND hParent )
{
RECT r;
GetClientRect( hParent, &r );
MoveWindow( s_hEditWnd, 0, r.bottom-EDIT_HEIGHT, r.right, EDIT_HEIGHT, TRUE );
}
}

View File

@@ -0,0 +1,37 @@
/**
* @file UICmdEdit.h
* @brief Server UIFramework에서 사용하는 Edit컨트롤
* @remarks
* @author 강동명(edith2580@gmail.com)
* @date 2009-05-09
*/
#pragma once
namespace NaveServer {
class UICmdEdit
{
public:
UICmdEdit();
~UICmdEdit();
public:
#define MAX_EDITSTRING 1024
#define WM_EDIT_RETURN (WM_USER+101)
#define EDIT_HEIGHT 16
#define ID_EDIT 200
static HWND s_hEditWnd; // 에디트,
static CHAR s_EditStr[MAX_EDITSTRING];
static WNDPROC s_OldEditProc;
static LRESULT OnEditDefault( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
static LRESULT OnEditKeyDown( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
static LRESULT OnEditChar( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
static LRESULT CALLBACK EditProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
static HWND InitEditProc( HWND hParent, HFONT hFont );
static VOID ResizeEdit( HWND hParent );
};
}

View File

@@ -0,0 +1,111 @@
#include "Global.h"
#include "UICmdMsgView.h"
#include "UICmdEdit.h"
//#include "Richedit.h"
namespace NaveServer {
HWND UICmdMsgView::s_hInfoWnd = NULL; // 리스트
HWND UICmdMsgView::s_hListWnd = NULL; // 리스트
/*
Len = (int)SendMessage( s_hListWnd, LB_GETCOUNT, 0, 0L );
while( Len > MAX_MSGLIST )
Len = (int)SendMessage( s_hListWnd, LB_DELETESTRING, 0, 0L );
// 전체 카운드
int Top = (int)SendMessage( s_hListWnd, LB_GETTOPINDEX, 0, 0L );
int ItemHeight = (int)SendMessage( s_hListWnd, LB_GETITEMHEIGHT, 0, 0L );
RECT rect;
GetClientRect(s_hListWnd, &rect);
int PageCnt = abs(rect.bottom-rect.top)/ItemHeight;
int Total = abs(Len-Top);
if(Top+PageCnt+1 == Len)
SendMessage( s_hListWnd, LB_SETTOPINDEX, Top+1, 0L );
*/
// HINSTANCE g_hInstRichEdit;
LRESULT UICmdMsgView::OnVkeyToItem( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
if( (HWND )lParam == s_hListWnd )
{
if(LOWORD( wParam ) == VK_TAB )
SetFocus( GetParent(s_hListWnd) );
}
return DefWindowProc( hWnd, uMsg, wParam, lParam );
}
VOID UICmdMsgView::ClrarMsgView( void )
{
SendMessage(s_hListWnd, LB_RESETCONTENT, 0, 0);
}
VOID UICmdMsgView::UpdateInfo(WCHAR* strInfo)
{
SendMessage(s_hInfoWnd, WM_SETTEXT, MAX_EDITSTRING, (LPARAM)strInfo);
}
HWND UICmdMsgView::InitMsgView( HWND hParent, HFONT hFont )
{
RECT rect;
GetClientRect( hParent, &rect );
// g_hInstRichEdit = LoadLibraryA("RICHED20.DLL");
/*
s_hInfoWnd = CreateWindow( _T("RICHEDIT20A"), _T("Info"),
WS_CHILD | WS_CLIPSIBLINGS | WS_TABSTOP | WS_VISIBLE | WS_BORDER | WS_VSCROLL | ES_READONLY | ES_MULTILINE | ES_AUTOVSCROLL,
0, 0, rect.right, INFO_HEIGHT,
hParent, (HMENU )ID_INFO,
(HINSTANCE )GetWindowLong( hParent, GWL_HINSTANCE ), NULL );
*/
s_hInfoWnd = CreateWindowW( L"EDIT", L"Infomation",
WS_CHILD | WS_CLIPSIBLINGS | WS_TABSTOP | WS_VISIBLE | WS_BORDER | WS_VSCROLL | ES_READONLY | ES_MULTILINE | ES_AUTOVSCROLL,
0, 0, rect.right, INFO_HEIGHT,
hParent, (HMENU )ID_INFO,
(HINSTANCE )GetWindowLong( hParent, GWL_HINSTANCE ), NULL );
ShowWindow( s_hInfoWnd, SW_SHOW );
UpdateWindow( s_hInfoWnd );
// rich edit 2.0
// rich edit 에서 Hyperlink로 표시
// ::SendMessage(s_hInfoWnd, EM_AUTOURLDETECT, TRUE, 0 );
// 메인 폰트로 폰트를 셋팅한다.
::SendMessage(s_hInfoWnd, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(FALSE,0));
s_hListWnd = CreateWindowW( L"LISTBOX", L"Message List",
WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | WS_BORDER | WS_VSCROLL
| LBS_DISABLENOSCROLL | LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | LBS_USETABSTOPS,
0, INFO_HEIGHT, rect.right, rect.bottom - EDIT_HEIGHT - INFO_HEIGHT,
hParent, (HMENU )ID_MSGVIEW,
(HINSTANCE )GetWindowLong( hParent, GWL_HINSTANCE ),
NULL );
ShowWindow( s_hListWnd, SW_SHOW );
UpdateWindow( s_hListWnd );
// 메인 폰트로 폰트를 셋팅한다.
::SendMessage(s_hListWnd, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(FALSE,0));
// FreeLibrary(g_hInstRichEdit);
return s_hListWnd;
}
VOID UICmdMsgView::ResizeLog( HWND hParent )
{
RECT r;
GetClientRect( hParent, &r );
MoveWindow( s_hInfoWnd, 0, 0, r.right, INFO_HEIGHT, TRUE );
MoveWindow( s_hListWnd, 0, INFO_HEIGHT, r.right, r.bottom - EDIT_HEIGHT - INFO_HEIGHT, TRUE );
}
}

View File

@@ -0,0 +1,38 @@
/**
* @file UICmdMsgView.h
* @brief Server UIFramework에서 사용하는 List, Info컨트롤
* @remarks
* @author 강동명(edith2580@gmail.com)
* @date 2009-05-09
*/
#pragma once
namespace NaveServer {
class UICmdMsgView
{
public:
UICmdMsgView();
~UICmdMsgView();
public:
#define MAX_MSGLIST 1024
#define MAX_MSGSTRING 1024
#define INFO_HEIGHT 200
#define ID_INFO 201
#define ID_MSGVIEW 202
static HWND s_hInfoWnd; // 리스트
static HWND s_hListWnd; // 리스트
static HWND InitMsgView( HWND hParent, HFONT hFont );
static VOID ClrarMsgView( void );
static VOID UpdateInfo(WCHAR* strInfo);
static VOID ResizeLog( HWND hParent );
// ListView 입력.
static LRESULT OnVkeyToItem( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
};
}

View File

@@ -0,0 +1,247 @@
#include "Global.h"
#include "UIFramework.h"
namespace NaveServer {
//////////////////////////////////////
UIConsol::UIConsol()
{
m_bCommandExit = FALSE;
}
UIConsol::~UIConsol()
{
}
BOOL UIConsol::Init()
{
Nave::NFLog::SetLogHandel(UICmdMsgView::s_hListWnd);
// Nave::NFLog::EnableLogFile(true);
// 오브젝트 초기화
InitObject();
// 커멘드 초기화.
InitializeCommand();
m_bCommandExit = FALSE;
return TRUE;
}
VOID UIConsol::StartCommand()
{
wprintf(L"\n");
WCHAR Command[DEF_BUFSIZE];
while(!m_bCommandExit)
{
_getws(Command);
DoCommand(Command);
}
ReleaseObject();
}
VOID UIConsol::EndCommand()
{
m_bCommandExit = TRUE;
}
static UIWindow* g_pMain = NULL;
///////////////////////////////////////////////////////////////////////////////
// Descrip : 말그대로 윈도우용. Proc..
// Date : 2001-04-11오전 10:41:02
///////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if(g_pMain)
return g_pMain->MsgProc(hWnd, uMsg, wParam, lParam);
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
UIWindow::UIWindow()
{
m_bCommandExit = FALSE;
m_hFont = NULL;
g_pMain = this;
}
UIWindow::~UIWindow()
{
g_pMain = NULL;
if(m_hFont)
DeleteObject(m_hFont);
m_hFont = NULL;
}
VOID UIWindow::OnEditReturn(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
WCHAR buff[MAX_EDITSTRING];
ZeroMemory(buff, sizeof(buff));
CopyMemory( buff, (WCHAR*)lParam, sizeof(buff) );
// 여기서 입력된 문자열을 가지고 분석해서 입력한 값에 따라 처리한다.
DoCommand(buff);
}
VOID UIWindow::ResizeWindows( VOID )
{
UICmdEdit::ResizeEdit(m_hMainWnd);
UICmdMsgView::ResizeLog(m_hMainWnd);
}
///////////////////////////////////////////////////////////////////////////////
// Descrip : 말그대로 사용할 MsgProc , WindowProc에서 호출된다.
// Date : 2001-04-11오전 10:41:02
///////////////////////////////////////////////////////////////////////////////
LRESULT UIWindow::MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch(uMsg)
{
case WM_SETFOCUS:
SetFocus( UICmdEdit::s_hEditWnd );
break;
case WM_ERASEBKGND:
return FALSE;
case WM_SIZE:
ResizeWindows();
return FALSE;
case WM_COMMAND:
OnCommand(hWnd, LOWORD(wParam), HIWORD(wParam), lParam);
return FALSE;
// ListView 키보드 입력.
case WM_VKEYTOITEM :
return UICmdMsgView::OnVkeyToItem(hWnd, uMsg, wParam, lParam);
// EditBox 엔터 입력.
case WM_EDIT_RETURN:
OnEditReturn(hWnd, uMsg, wParam, lParam);
return FALSE;
case WM_CLOSE:
case WM_DESTROY:
case WM_QUIT:
EndCommand();
return FALSE;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
BOOL UIWindow::Init(HINSTANCE hInstance, int nCmdShow, int iWidth, int iHeight, WCHAR* AppName, WCHAR* Icon)
{
// 윈도우의 생성
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wcex.lpfnWndProc = (WNDPROC)WindowProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 16;
wcex.hInstance = (HINSTANCE)hInstance;
wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)Icon);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = NULL;
wcex.lpszMenuName = NULL;
wcex.lpszClassName = AppName;
wcex.hIconSm = LoadIcon(wcex.hInstance, Icon);
// 레지등록.
RegisterClassExW(&wcex);
if(iHeight < INFO_HEIGHT+150)
{
iHeight = INFO_HEIGHT+150;
}
// 윈도우 생성
m_hMainWnd = CreateWindowExW(WS_EX_WINDOWEDGE, AppName, AppName, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, iWidth, iHeight,
NULL, NULL, hInstance, NULL);
if(!m_hMainWnd)
{
MessageBoxW(NULL, L"윈도우 생성 에러", L"Error", MB_OK);
return FALSE;
}
m_hFont = ::CreateFontW(12, 0, 0, 0, 0, 0, 0, 0, HANGEUL_CHARSET, 3, 2, 1, VARIABLE_PITCH|FF_ROMAN, L"굴림체");
UICmdEdit::InitEditProc(m_hMainWnd, m_hFont);
UICmdMsgView::InitMsgView(m_hMainWnd, m_hFont);
Nave::NFLog::SetLogHandel(UICmdMsgView::s_hListWnd);
// Nave::NFLog::EnableLogFile(true);
ShowWindow(nCmdShow); // Show The UIWindow
SetForegroundWindow(m_hMainWnd); // Slightly Higher Priority
// EditBox에 포커스 주기
SetFocus(UICmdEdit::s_hEditWnd); // Sets Keyboard Focus To The UIWindow
// 아이콘 로드 (작은 것 따로 하나)
HICON hIcon = LoadIcon( hInstance, (LPCTSTR)Icon );
SendMessage( m_hMainWnd, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)hIcon );
SendMessage( m_hMainWnd, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hIcon );
// 오브젝트 초기화
InitObject();
// 커멘드 초기화.
InitializeCommand();
m_bCommandExit = FALSE;
return TRUE;
}
VOID UIWindow::ShowWindow(int nCmdShow)
{
::ShowWindow(m_hMainWnd, nCmdShow); // Show The UIWindow
}
VOID UIWindow::StartCommand()
{
MSG msg;
while( !m_bCommandExit )
{
if( GetMessage( &msg, NULL, 0U, 0U ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
LOG_IMPORTANT((L"Programe Quitting"));
ReleaseObject();
InvalidateRect(m_hMainWnd, NULL, FALSE);
// 종료 메시지 표현 해 주기 위해서
for(int i = 0; i < 10; i++)
{
// 마지막 메시지를 화면에 출력하기 위해서.
if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
Nave::NFLog::CloseLog();
Sleep(1000);
}
VOID UIWindow::EndCommand()
{
m_bCommandExit = TRUE;
InvalidateRect(m_hMainWnd, NULL, FALSE);
}
}

View File

@@ -0,0 +1,220 @@
/**
* @file UIFramework.h
* @brief Server의 화면인터페이스를 구현하는 UIFramework 객체
* @remarks
* @author 강동명(edith2580@gmail.com)
* @date 2009-05-09
*/
#pragma once
#include "UICmdEdit.h"
#include "UICmdMsgView.h"
#include <vector>
namespace NaveServer {
/// 새로운 커맨드를 추가합니다.
/// ADD_COMMAND(_T("help"), CCmdHelp, L"화면에 커멘드리스트를 보여줍니다.");
#define ADD_COMMAND(cmd, object, msg) AddCommand(cmd, new object, msg)
/**
* @class UICommand
* @brief 어플리케이션에서 커맨드를 작성할때 사용할 커멘드 객체
* @remarks
*
* @par
* @author Edith
* @date 2009-05-09
*/
class UICommand
{
public:
virtual BOOL DoProcess(WCHAR* lpParam) = 0;
};
/**
* @class UICommandFactory
* @brief 커맨드를 관리하는 Factory 객체 Server의 Server의 UI객체가 상속받는다.
* @remarks
*
* @par
* @author Edith
* @date 2009-05-09
*/
class UICommandFactory
{
public:
BOOL AddCommand(const WCHAR* szCommand, UICommand* lpCommand, const WCHAR* szMessage)
{
if(0 == szCommand, 0 == lpCommand)
return FALSE;
m_CMDVector.push_back(COMMAND(szCommand, lpCommand, szMessage));
return TRUE;
}
VOID ReleaseCommand()
{
int iSize = m_CMDVector.size();
for(int i = 0; i < iSize; ++i)
{
delete m_CMDVector[i].m_lpCMD;
}
m_CMDVector.clear();
}
virtual VOID InitializeCommand() {};
virtual VOID StartCommand() {}; // UIConsol형식 Command 입력
virtual VOID EndCommand() {};
VOID ShowCommand()
{
LOG_IMPORTANT( (L"------------------- Commands -------------------") );
int iSize = m_CMDVector.size();
for(int i = 0; i < iSize; ++i)
{
LOG_IMPORTANT((L"%s : %s", m_CMDVector[i].m_szCMD, m_CMDVector[i].m_szMSG));
}
LOG_IMPORTANT( (L"----------------- Commands End -----------------") );
}
VOID DoCommand(WCHAR* Command)
{
WCHAR Buff[DEF_BUFSIZE];
ZeroMemory(Buff, DEF_BUFSIZE);
wcscpy(Buff, Command);
WCHAR* cmd;
cmd = wcstok(Command, L" ");
if(cmd)
{
Nave::uint32 uiHash = Nave::Hash(cmd);
WCHAR* NextCmd = &Buff[wcslen(cmd)+1];
int iSize = m_CMDVector.size();
for(int i = 0; i < iSize; ++i)
{
if(m_CMDVector[i].m_uiHash != uiHash)
continue;
m_CMDVector[i].m_lpCMD->DoProcess(NextCmd);
return;
}
}
}
public:
UICommandFactory()
{
}
~UICommandFactory()
{
ReleaseCommand();
}
private:
struct COMMAND
{
Nave::uint32 m_uiHash;
WCHAR m_szCMD[32];
WCHAR m_szMSG[128];
UICommand* m_lpCMD;
COMMAND(const WCHAR* szCommand, UICommand* lpCMD, const WCHAR* szMessage)
{
m_uiHash = Nave::Hash(szCommand);
wcscpy(m_szCMD, szCommand);
if(szMessage)
wcscpy(m_szMSG, szMessage);
else
wcscpy(m_szMSG, L"No Help Message");
m_lpCMD = lpCMD;
}
};
typedef std::vector<COMMAND> CMDVector;
CMDVector m_CMDVector;
};
/**
* @class UIConsol
* @brief 콘솔 UI
* @remarks
*
* @par
* @author Edith
* @date 2009-05-09
*/
class UIConsol : public UICommandFactory
{
BOOL m_bCommandExit;
public:
// 사용자 오브젝트를 초기화 합니다. (윈도우설정이 종료됀 후에 호출)
virtual VOID InitObject() {};
// 사용자 오브젝트를 삭제합니다. (메인루프가 끊난후(EndProcess호출후) 호출)
virtual VOID ReleaseObject() {};
BOOL Init();
virtual VOID InitializeCommand() {};
virtual VOID StartCommand(); // UIConsol형식 Command 입력
virtual VOID EndCommand();
public:
UIConsol(VOID);
~UIConsol(VOID);
};
/**
* @class UIWindow
* @brief 윈도우 UI
* @remarks
*
* @par
* @author Edith
* @date 2009-05-09
*/
class UIWindow : public UICommandFactory
{
public:
HWND GetWnd() { return m_hMainWnd; }
BOOL IsExit() { return m_bCommandExit; }
// 사용자 오브젝트를 초기화 합니다. (윈도우설정이 종료됀 후에 호출)
virtual VOID InitObject() {};
// 사용자 오브젝트를 삭제합니다. (메인루프가 끊난후(EndProcess호출후) 호출)
virtual VOID ReleaseObject() {};
virtual VOID ShowWindow(int nCmdShow);
// Windows MsgProc
virtual LRESULT MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
BOOL Init(HINSTANCE hInstance, int nCmdShow, int iWidth, int iHeight, WCHAR* AppName, WCHAR* Icon);
virtual VOID InitializeCommand() {};
virtual VOID StartCommand(); // UIConsol형식 Command 입력
virtual VOID EndCommand();
public:
UIWindow(VOID);
~UIWindow(VOID);
private:
HWND m_hMainWnd;
BOOL m_bCommandExit;
HFONT m_hFont;
VOID ResizeWindows( VOID );
// WM_EDIT_RETURN
virtual VOID OnEditReturn(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
// WM_COMMAND
virtual VOID OnCommand(HWND hWnd, INT nID, INT iEvent, LPARAM lParam) { return; }
};
}