Server
 * recv_n에 10초간 타임아웃을 주고 대기함
        char    buff[1024];
        size_t  recvLen = 0;
        ACE_Time_Value  timeout(10);
        int ret = peer.recv_n( buff, sizeof(buff)-1, &timeout, &recvLen );
        ACE_DEBUG( (LM_DEBUG,
                ACE_TEXT("ret[%d] recvLen[%d] : [%.*C]\n"),
                ret, recvLen, recvLen, buff) );
Client
 * 접속하자 마자 첫 메세지("Hello ")를 보내고(send_n)
 * 5초후 (sleep 5초) 나머지 데이터("World!")를 보내고 접속을 끊는다.
        peer.send_n( "Hello ", 6 );

        ACE_OS::sleep( 5 );
        peer.send_n( "World!" , 6 );
        peer.close();
 * 결과 :
 *  recv_n는 첫 메세지를 받고 timeout(10초)동안 블럭이 걸려있었으며
 *  5초후 나머지 데이터를 받고 0을 반환후에 종료하였습니다.
 *  받은 데이터 사이즈는 recv_n 4번째 인자로 받습니다.
$ ./recv
ret[0] recvLen[12] : [Hello World!]
서버 소스 Recv.C
/*
 * 10초간 recv를 걸어놓고 받는것을 테스트함.
 *
 * 상대방에서는(send.C) 접속하자 마자 첫 메세지를 보내고
 * 5초간 sleep를 준다음 나머지 데이터를 보내고 접속을 끊는다.
 *
 * 결과 :
 *  recv_n는 첫 메세지를 받고 timeout(10초)동안 블럭이 걸려있었으며
 *  5초후 나머지 데이터를 받고 0을 반환후에 종료하였습니다.
 *  받은 데이터 사이즈는 recv_n 4번째 인자로 받습니다.
 */
#include <ace/SOCK_Stream.h>
#include <ace/INET_Addr.h>
#include <ace/SOCK_Acceptor.h>
#include <ace/Time_Value.h>
#include <ace/Log_Msg.h>

int ACE_TMAIN( int, ACE_TCHAR * [] )
{
        ACE_INET_Addr   myaddr(9090);

        ACE_SOCK_Acceptor       acceptor;

        if( acceptor.open( myaddr ) == -1 )
        {
                ACE_ERROR_RETURN( (LM_ERROR, ACE_TEXT("%p\n"), ACE_TEXT("acceptor.open()")), 1 );
        }

        ACE_OS::sleep(5);
        ACE_SOCK_Stream peer;
        if( acceptor.accept( peer ) == -1 )
        {
                ACE_ERROR_RETURN( (LM_ERROR, ACE_TEXT("%p\n"), ACE_TEXT("acceptor.accept()")), 1 );
        }

        char    buff[1024];
        size_t  recvLen = 0;
        ACE_Time_Value  timeout(10);
        int ret = peer.recv_n( buff, sizeof(buff)-1, &timeout, &recvLen );
        if( ret == -1 )
        {
                ACE_ERROR_RETURN( (LM_ERROR, ACE_TEXT("%p\n"), ACE_TEXT("recv_n()")), 1 );
        }

        ACE_DEBUG( (LM_DEBUG, ACE_TEXT("ret[%d] recvLen[%d] : [%.*C]\n"), ret, recvLen, recvLen, buff) );
        peer.close();

        return 0;
}
클라이언트 소스
send.C
/*
 * 서버에 접속하자 마자 첫 메세지("Hello ")를 보내고
 * 5초간 sleep를 준다음 나머지 데이터( "World!\n")를 보내고 접속을 끊는다.
 */

#include <ace/SOCK_Stream.h>
#include <ace/INET_Addr.h>
#include <ace/SOCK_Connector.h>
#include <ace/Time_Value.h>
#include <ace/Log_Msg.h>
#include <ace/OS.h>

int ACE_TMAIN( int, ACE_TCHAR * [] )
{
        ACE_INET_Addr   svrAddr(9090, ACE_TEXT("127.0.0.1"));

        ACE_SOCK_Connector      conn;

        ACE_SOCK_Stream peer;
        if( conn.connect( peer, svrAddr ) == -1 )
        {
                ACE_ERROR_RETURN( (LM_ERROR, ACE_TEXT("%p\n"), ACE_TEXT("connect()")), 1 );
        }

        char    buff[1024];
        ACE_OS::strcpy( buff, "Hello " );
        if( peer.send_n( buff, ACE_OS::strlen(buff) ) == -1 )
        {
                ACE_ERROR_RETURN( (LM_ERROR, ACE_TEXT("%p\n"), ACE_TEXT("send_n() 2")), 1 );
        }

        ACE_OS::sleep( 5 );
        ACE_OS::strcpy( buff, "World!" );
        if( peer.send_n( buff, ACE_OS::strlen(buff) ) == -1 )
        {
                ACE_ERROR_RETURN( (LM_ERROR, ACE_TEXT("%p\n"), ACE_TEXT("send_n() 1")), 1 );
        }

        peer.close();

        return 0;
}

결과
$ ./recv
ret[0] recvLen[12] : [Hello World!]
ACE_OutputCDR를 사용하는데 Big endian 저장이되지 않는 현상을 발견했습니다. 아무리 옵션을 바꿔봐도 되지않아 찾아보니... ACE 컴파일을 다시 해야하더군요.
$ACE_ROOT/ace/config.h 파일에 아래 내용을 추가한후 재컴파일하여 빌드하세요.
#define	ACE_ENABLE_SWAP_ON_WRITE	1	// enable swap byte_order
테스트 코드

#include "ace/cdr_stream.h" #include "ace/log_msg.h" void OutputCDR_Test( ACE_CDR::UShort id, int endian ) { ACE_OutputCDR cdr( sizeof( id ), endian ); cdr << id; ACE_HEX_DUMP( ( LM_DEBUG, cdr.begin()->rd_ptr(), cdr.begin()->length() ) ); } int ACE_TMAIN (int argc, ACE_TCHAR *argv[]) { ACE_DEBUG( ( LM_DEBUG, ACE_TEXT("\nBYTE_ORDER_LITTLE_ENDIAN\n") ) ); OutputCDR_Test( 0x1234, ACE_CDR::BYTE_ORDER_LITTLE_ENDIAN ); ACE_DEBUG( ( LM_DEBUG, ACE_TEXT("\nBYTE_ORDER_BIG_ENDIAN\n") ) ); OutputCDR_Test( 0x1234, ACE_CDR::BYTE_ORDER_BIG_ENDIAN ); return 0; }
결과값
BYTE_ORDER_LITTLE_ENDIAN
HEXDUMP 2 bytes
34 12                                              4.

BYTE_ORDER_BIG_ENDIAN
HEXDUMP 2 bytes
12 34                                              .4
ACE 기반 채팅 서버(Proactor)와 MFC기반 채팅 클라이언트입니다.

이 채팅 프로그램은  

TCP/IP 소켓 프로그래/

한빛미디어

책을 참고 하였음을 알려드립니다

클라이언트 부분은 버그 몇개(심각한 버그 포함)를 수정하여 책을 보고 작성및 소스를 참고 하였으며

서버 부분은 책과는 다르게 프로토콜을 제외한 모든 부분을 새로 작성하였으며
ACE Reactor 기반을 거처 Proactor 기반으로 변경하여 만들었습니다.

실행방법은 압축을 풀면 bin디렉토리에 ACE_ChatServer.exe를 실행시키고
ChatClient.exe 를 실행시키면됩니다.

사용포트는 9090 포트를 이용합니다.


플로우 차트와 설계 문서는 아래를 참고 하세요.

ACE 기반 채팅서버 - 서버 접속

ACE기반 채팅서버 - 대화방생성

ACE 기반 채팅서버 - 대화방 입장

채팅서버 - 대화

채팅서버 - 클라이언트 접속 종료

대기방



대화방



서버



소스및 실행파일

클라이언트 접속 종료 플로우 차트


클라이언트 접속 종료 시퀸스 다이어그램

대화 플로우 차트


대화 시퀸스 다이어그램

대화방 입장 플로우 차트


대화방 입장 시퀸스 다이어그램





소스및 실행파일
** 주의 아직 완성이 되지 않아 클라이언트 만 종료하실일이 있으면 모두 껐다 켜서 테스트하세요


대화방 생성 플로우 차트



시퀸스 다이어그램



클래스 다이어그램




서버 입장및 대기실 입장 플로우 차트




시퀸스 다이어그램



소스및 실행파일
** 실행시 주의 사항
 아직 접속 종료를 구현하지 않아 클라이언만 종료 하여 접속하면 에러가 발생합니다.
 다시 할려면 서버, 클라이언트 모두 종료후에 다시 접속하세요.



◆ 설명 ◆
이 프로그램은 ACE기반으로 만들어져 있습니다.

이프로그램의 용도는 제가 작성한 자동화 프로그램중
  클라이언트 담당 (파일 압축, 서버에 파일전송)
  그리고 서버 담당 (압축해제, 파일 분배)한 프로그램입니다.

이 프로그램은 보여주기 위한 코드이며 상당부분이 제거 되어 있습니다.

◆실행 방법 ◆
예제를 간단히 하기 위해 파일을 c:\OneTouch에 압축을 풀어 실행하세요.

1. 서버 실행
 서버 프로그램을 서비스에 설치하거나 디버그 모드로 실행합니다.
 서비스 모드 설치 후 윈도우 서비스 모드에서 실행합니다.

 디버그 모드로 실행
  c:\OneTouch\bin\OneTouchServer.exe -d

 서비스 모드로 실행
 c:\OneTouch\bin\OneTouchServer.exe -i

 서버 부분에서는 서버로 파일을 동기화 하는 부분이 빠져있습니다.
 빠진 이유는 전 회사에서 처리하는 소스가 포함되어 있기 때문에 빠져있습니다.

2. 클라이언트 실행
 클라이언트를 더블클릭하여 실행후  Start 버튼 클릭.
  c:\OneTouch\bin\OneTouchClient.exe

 클라이언트 부분에서는  DB에 접속하여 게임에서 쓰는 데이터 를 파일로 만드는 작업,
 유료 아이템정보 암호화 등이 빠져있습니다.

3. 실행결과
 로컬디렉토리(c:\OneTouch\Local\2010_05_05\Patch)를 압축하여
 리모트디렉토리(c:\OneTouch\Remote\2010_05_05\Patch)에 압축을 풀어
 이를 분산 복사 합니다.
 
  설정 파일 config.ini, config_s.ini를 참고 해주세요.
ACE_wrappers/examples/NT_Service 에 있는 서비스 등록을 참고(배껴서? ㅡ.ㅡ)하여 서버 프로그램을
서비스화 하였다.

그런데 서비스 등록후 시작하지 않는 문제가 발생했습니다.

이상한건 특정 사용자로 로그온하여 실행하면 된다는것입니다.


알고 보니 파일에 권한에 SYSTEM 없었습니다.^^
그래서 SYSTEM 권한을 추가하였습니다.

그런데. 그런데로 안되었습니다 ㅡ.ㅡ
또 알고 보니 실행파일이 있는 곳에 로그 파일을 만드는데.. 로그 파일을 못만들어서 생기는 문제가 있더군요.
해당 디렉토리를 작업 디렉토리로 설정하는것을 까먹었더군요^^


        // 작업 디렉토리 설정.
        char    workdir[128];
        GetModuleFileName( 0, workdir, sizeof workdir  );

        char *    pDir = ::strrchr( workdir, '\\' );
        if( 0  != pDir )
            *pDir = 0;

        ::SetCurrentDirectory( workdir );
마지막으로 해당 디렉토리에 SYSTEM 권한을 주어 해결하였습니다.


+ Recent posts