LOGIN • JOININ

SIMPLE CLIENT

1... 헤더파일 인크루드
클라이언트 Socket도 역시 사용을 위해서는 ‘CGNetSocketTemplates.h’를 Include해야 합니다.


2... 클라이언트 Socket 클래스 정의하기
클라이언트 소켓은 소켓의 기본 클래스인 'CGNet::Socket::CTCP<>'에 추가적으로  'CGNet::IO::Connector::NTCP' 를 다중 상속해 주면 됩니다.
‘CGNet::IO::Connector::NTCP’클래스는 '접속 시도' 기능 즉 'Connect(...)'함수를 추가해 줍니다
class CSocketClient : public CGNet::Socket::CTCP<>,
	public CGNet::IO::Connector::NTCP	// 1) Connector를 추가 상속.
{
	virtual void OnConnect() override
	{
		printf("Connected\n");
	}

	// 2) 접속 실패 시 호출되는 함수
	virtual void OnFailConnect(uint32_t  /*_Reason*/) override
	{
		printf("Connected\n");
	}

	virtual void OnDisconnect(uint32_t) override
	{
		printf("Disconnected\n");
	}

	virtual int OnMessage(CGMSG& /*_MSG*/) override
	{
		printf("Message Received\n");

		return	0;
	}
};

1) 기본 소켓 클래스인 ‘CGNet::Socket::CTCP<>’에 접속시도 기능을 추가해주는  ‘CGNet::IO::Connector::NTCP’를 다중 상속받습니다.
2) 접속이 되었을 때, 접속이 종료되었을 때, 메시지를 수신 받았을 때 호출되는 함수인 OnConnect, OnDisconnect, OnMessage()함수를 재정의했습니다.
또 클라이언트 소켓은 접속을 시도했다 실패하는 경우가 있을 수 있으므로 이때 호출되는 함수인 ‘OnFailConnect()’함수의 재정의도 추가했습니다.


3... 접속 시도하기
	// 2) Socket을 생성한다.
	auto	pSocket	 = NEW();

	// 3) '127.0.0.1' 호스트에 20000번 포트로 접속을 시도한다.
	//     주의) Connect()함수는 Blocking Mode로 동작하지 않는다.
	pSocket->Connect("127.0.0.1", 20000);
   
3) 먼저 클라이언트 소켓 객체를 생성합니다.
4) Connect()함수를 사용해 접속을 시도합니다.
위 예제에는 ‘127.0.0.1’ 에 20000번 포트로 접속을 시도합니다.
Ex1) 문자열로 주소를 쓰는 다양한 방법으로 가능합니다.
  pSocket->Connect("localhost", 20000);
  pSocket->Connect("127.0.0.1", 20000);
  pSocket->Connect("localhost:2000");
  pSocket->Connect("128.0.0.1:2000");

Ex2) SOCKADDR_IN 구조체를 직접 작성하는 방법도 가능합니다.
  SOCKADDR_IN sockaddr
  sockaddr.sin_family = AF_INET;
  sockaddr.sin_port = htons(20000);
  sockaddr.sin_addr.S_un.S_addr= inet_addr("127.0.0.1");
  pSocket->Connect(sockaddr);

일단 접속되고 난 이후의 처리는 서버나 클라이언트와 처리는  동일하게 OnConnect(), OnDisconnect(), OnMessage()등의 함수들이 호출됩니다.


4... 클라이언트로 셋팅
서버는 일반적으로 다중 쓰레드로 동작하지만 클라이언트는 단일 쓰레드로 게임의 메인 처리 루틴에서 동기식으로 처리해야 하는 경우가 많습니다.
이렇게 동작시키기 위해 내부적인 쓰레드를 만들지 않고 동기식으로 처리될 수 있도록 할 수 있습니다.
int _tmain(int /*argc*/, _TCHAR* /*argv*/[])
{
	// 1) 내부적 Thread를 만들지 않도록 한다.
	//    (이것을 하지 않으면 서버와 같이 Multi Thread로 동작한다.)
	CGExecutor::Default::InitInstance(CGEXECUTOR_NOTHREAD);

	// 2) Socket을 생성한다.
	auto	pSocket	 = NEW();

	// 3) '127.0.0.1' 호스트에 20000번 포트로 접속을 시도한다.
	pSocket->Connect("127.0.0.1", 20000);

	// 4) 클라이언트 메인 루프
	for(;;)
	{
		...

		// 5) Socket I/O Execution
		//    CGEXECUTOR_NOTHREAD 옵션으로 초기화를 하였으므로 반드시 
		//    따로 RunExecutor()함수를 호출해 주어야 한다. 이때 Socket I/O가 처리된다.
		CGExecutor::Default::RunExecutor();

		...
	}

	return 0;
}

1) 소켓 처리를 위한 쓰레드를 따로 생성하지 않기 위해서는 반드시 첫 번째 소켓이 만들어지기 전에 'CGExecutor::Default::InitInstance(CGEXECUTOR_NOTHREAD)'를 호출해 주어야 합니다.
5) 일정 시간마다 'CGExecutor::Default::RunExecutor()'함수를 호출해 'Default Executor'에 의해 처리되어야 할 각종 처리를 수행합니다.

[TutorialTCPClientWithReconnect.cpp]
#include "stdafx.h"
#include "CGNetSocketTemplates.h"

const uint32_t	MESSAGE_SEND_A				 = 0x00000020;
const uint32_t	MESSAGE_SEND_B				 = 0x00000021;


class CSocketClient : 
	public CGNet::Socket::CTCP<>,
	public CGNet::IO::Connector::NTCPReconnect	// (@) 이것만 재접속용으로!
{
	// 1) 접속 시 호출되는 Hook함수
	virtual void OnConnect() override
	{
		printf("Connected\n");
	}

	// 2) 접속 실패 시 호출되는 Hook함수  (@) 클라이언트에서는 접속에 실패할 수도 있으니 이 훅함수를 하나 더 추가했다!!
	virtual void OnFailConnect(uint32_t  /*_Reason*/) override
	{
		printf("Fail to Connect!\n");
	}

	// 3) 접속 종료 시 호출되는 Hook함수
	virtual void OnDisconnect(uint32_t) override
	{
		printf("Disconnected\n");
	}

	// 4) 메시지가 전송되어 왔을 때 호출 되는 함수.
	virtual int OnMessage(CGMSG& /*_MSG*/) override
	{
		printf("Message Received\n");

		return	0;
	}
};

int _tmain(int /*argc*/, _TCHAR* /*argv*/[])
{
	// 1) 내부적 Thread를 만들지 않도록 한다.
	//    (이것을 하지 않으면 서버와 같이 Multi Thread로 동작한다.)
	CGExecutor::Default::InitInstance(CGEXECUTOR_NOTHREAD);

	// 2) Socket을 생성한다.
	auto	pSocket	 = NEW<CSocketClient>();

	// 3) 접속전 재접속 기능을 활성화시켜야 한다.
	pSocket->EnableReconnect();

	// 4) '127.0.0.1' 호스트에 20000번 포트로 접속을 시도한다.
	pSocket->Connect("127.0.0.1", 20000);

	// Trace) 
	printf("  [설명] A키-MESSAGE_SEND_A 전송\n");
	printf("  [설명] B키-MESSAGE_SEND_B 전송\n");
	printf("  [설명] C키-강제접속 종료\n");
	printf("  [설명] ESC-종료\n");

	// 5) 클라이언트 메인 루프
	for(;;)
	{
		// - Key를 눌렀을 경우 key값을 읽어 처리한다.
		if(_kbhit())
		{
			// - Key를 읽는다.
			int	ch	 = _getch();

			// - ESC키를 누르면 접속을 종료한다.
			if(ch==27)
			{
				break;
			}
			// - A키 누르면 Message B를 전송한다.
			else if(ch=='a' || ch=='A')
			{
				CCGBuffer(MEM_POOL_ALLOC(32))<<DWORD(8)<<DWORD(message_send_a)>>SEND(pSocket);
			}
			// - B키 누르면 Message B를 전송한다.
			else if(ch=='b' || ch=='B')
			{
				CCGBuffer(MEM_POOL_ALLOC(32))<<DWORD(8)<<DWORD(message_send_b)>>SEND(pSocket);
			}
			// - C키 누르면 강제접속 종료를 한다.
			else if(ch=='b' || ch=='B')
			{
				pSocket->CloseSocket();	// Reconnection에 의해 다시 접속이 시도 될 것이다.
			}
		}

		// 6) Socket I/O Execution
		//    CGEXECUTOR_NOTHREAD 옵션으로 초기화를 하였으므로 반드시 
		//    RunExecutor()함수를 호출해 주어야 한다. 이때 Socket I/O가 처리된다.
		CGExecutor::Default::RunExecutor();

		// - CPU를 너무 많이 먹으므로...
		Sleep(1);
	}

	// 7) 접속을 종료하기 전 반드시 재접속을 Disable시켜야 한다.
	//   (그렇지 않고 끊으면 다시 재접속을 시도하게 된다.)
	pSocket->DisableReconnect();

	// 8) Socket의 접속을 끊는다.
	pSocket->CloseSocket();

	// 9) CloseSocket이후 남은 I/O를 처리하기 위해 호출해 준다.
	CGExecutor::Default::RunExecutor();

	return 0;
}