LOGIN • JOININ

AUTO RECONNECTION

[Tutorials/[Network] 1. Socket classes/Client/TutorialSocketClient02_TCP_Reconnect]



접속이 종료될 경우 자동으로 재접속 해야 하는 경우가 있습니다.
이럴 경우 간단하게 
 Reconnect용 Connector 클래스의 상속만으로 간단히 구현 가능합니다.



1... 재접속 기능을 가진 클라이언트 소켓 정의하기

클라이언트용 접속요청 기능을 가지기 위해 'CGNet::IO::Connector::NTCP'를 상속받습니다.

이것을 재접속  기능을 가진인 'CGNet::IO::Connector::NTCPReconnect'로 바꾸어 상속해 주면 됩니다.

class CSocket : public CGNet::Socket::CTCP<>,
	public CGNet::IO::Connector::NTCPReconnect	// (@) 이것만 재접속 용으로!
{
	...
};



2... 접속 시도하기

재접속 기능을 사용하여 위해서 는 접속 전에 활성화 시켜주어야 합니다.

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

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

	// 4) 재접속 시도 간격을 3초로 설정한다. 
	//    (설정하지 않으면 기본값은 0이며 접속을 실패하자 마자 바로 재접속을 시도한다.)
	pSocket->SetReconnectInterval(TICK::seconds(3));

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

3) 재접속 전에 자동재접속 기능을 활성화 시킵니다. 

4) 재접속 간격을 설정한다.  설정하지 않으면 기본값은 0입니다.

5) 그리고 Connect()함수를 호출하여 접속을 시도합니다.



3... 접속 종료하기

재접속 기능을 사용하게 되면 접속이 종료되었을 때 다시 재접속을 시도하게 됩니다.

따라서 접속을 끊으려면 먼저 재접속 기능을 끄고 나서 접속을 종료시켜줄 필요가 있습니다.

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

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

8) 접속을 종료시키기 전에 먼저 자동재접속 기능을 끕니다.

9) 그리고 나서 접속을 종료합니다.



4... 조건부 재접속 설정

모든 접속 종료일 때 재접속을 시도하고 싶다면 앞에 까지의 설정만으로 됩니다.
하지만 

특정한 이유로 접속이 종료되었을 때는 재접속을 하지 않도록 하고 싶다면?

특정한 이유로 접속이 종료되었을 때만 재접속이 되도록 하고 싶다면?
OnDisconnect()함수에 넘어오는 접속 종료 이유 _Reason값을 참조해 재접속 기능을 켜거나 끄도록 처리해 주면 됩니다.
 _Reason값의 의미는 다음과 같습니다.

const uint32_t	SOCKET_REASON_NONE			 = 0x00000000;	// 일상적인 처리 과정일 경우
const uint32_t	SOCKET_REASON_FAIL			 = 0x00010000;	// 오류로 인한 접속 종료인 경우
const uint32_t	SOCKET_REASON_ACTIVE		 = 0x00020000;	// 접속 종료를 요청한 측일 경우(설정되어 있지 않으면 Passive)
const uint32_t	SOCKET_REASON_ABORTIVE		 = 0x00040000;	// 강제 접속종료일 경우(설정되어 있지 않으면 Graceful)
const uint32_t	SOCKET_REASON_EXCEPTION		 = 0x00080000;	// 예외로 인해 것일 경우
const uint32_t	SOCKET_REASON_MASK			 = 0xffff0000;	// Mask
const uint32_t	SOCKET_REASON_MASK_USER		 = 0x0000ffff;	// User 정의

const uint32_t	SOCKET_REASON_SUCCESS		 = 0;
const uint32_t	SOCKET_REASON_GRACEFUL		 = 0;
const uint32_t	SOCKET_REASON_FAIL_TIMEOUT	 = (SOCKET_REASON_FAIL | SOCKET_REASON_ACTIVE);
const uint32_t	SOCKET_REASON_FAIL_ABORTIVE	 = (SOCKET_REASON_FAIL | SOCKET_REASON_ABORTIVE | SOCKET_REASON_ACTIVE);
const uint32_t	SOCKET_REASON_FAIL_EXCEPTION = (SOCKET_REASON_FAIL | SOCKET_REASON_EXCEPTION);

예를 들어 상대에 의한  강제접속 종료했을 경우에는 재접속을 하지 않는다고 한다면 OnDisconnect()를 아래와 같이 설정하면 것입니다.

	
	// 3) 접속 종료 시 호출되는 Hook함수
	virtual void OnDisconnect(uint32_t _Reason) override
	{
		// @) 상대가 강제로 접속 종료한 경우 재접속을 수행하지 않는다... 
		//    * 상대가 강제로 접속 종료한 경우란.(true값 OR(|)연산해 쓴다)
		//		SOCKET_REASON_FAIL		 == true
		//		SOCKET_REASON_ACTIVE	 == false
		//		SOCKET_REASON_ABORTIVE	 == false
		//		SOCKET_REASON_EXCEPTION	 == false
		if(_Reason & SOCKET_REASON_MASK == SOCKET_REASON_FAIL)
		{
			// 재접속 기능을 끈다.
			DisableReconnect();
		}

		printf("Disconnected\n");
	}


[TutorialSimpleClient.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 _Reason) override
	{
		// @) 상대가 강제로 접속 종료한 경우 재접속을 수행하지 않는다... 
		//    * 상대가 강제로 접속 종료한 경우란.(true값 OR(|)연산해 쓴다)
		//		SOCKET_REASON_FAIL		 == true
		//		SOCKET_REASON_ACTIVE	 == false
		//		SOCKET_REASON_ABORTIVE	 == false
		//		SOCKET_REASON_EXCEPTION	 == false
		if(_Reason & SOCKET_REASON_MASK == SOCKET_REASON_FAIL)
		{
			// 재접속 기능을 끈다.
			DisableReconnect();
		}

		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) 재접속 시도 간격을 3초로 설정한다. 
	//    (설정하지 않으면 기본값은 0이며 접속을 실패하자 마자 바로 재접속을 시도한다.)
	pSocket->SetReconnectInterval(TICK::seconds(3));

	// 5) '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");

	// 6) 클라이언트 메인 루프
	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에 의해 다시 접속이 시도 될 것이다.
			}
		}

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

		// 8) CPU를 너무 많이 먹으므로..(일반적으로는 Update등의 처리를 하는 루틴이 들어간다.)
		Sleep(1);
	}

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

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

	return 0;
}