[게임 서버] 유저의 비정상적인 종료, 연결 해제하기(PingPong 시스템)

Arthur·2024년 1월 31일
0
post-thumbnail
post-custom-banner

비정상적인 종료를 한 유저에 대한 문제점


게임 서버를 구축해서 멀티 플레이 게임을 만드는 프로젝트를 진행하고 있습니다.
구현을 하고 테스트를 하던 중에 문제점 하나를 발견했습니다.

멀티 플레이 게임에서 유저가 갑자기 인터넷이 끊기거나 의도적으로 끊으면서
발생하는 비정상적인 종료에 대한 문제

유저가 비정상적으로 종료된 것을 서버에서는 알 수가 없습니다.
서버에서는 해당 유저의 캐릭터 데이터, 세션 등을 메모리에 가지고 있게 되어 리소스 손해를 보게 됩니다.

그리고 동기화를 진행할 때 패킷을 유령으로 접속해 있는 유저에게도 전송을 하게 될 수 있습니다.

이런 유저가 한 명이면 괜찮겠지만, 수가 늘어날수록 서버 리소스에 큰 손해가 됩니다.


이 문제를 해결할 수 있는 방법 중 하나인 중에 핑퐁(Ping Pong)이라는 시스템을 찾게 되었습니다.



핑퐁(Ping Pong)이란?


Ping Pong은 탁구를 의미합니다.

클라이언트와 서버가 탁구공을 주고받듯이 계속 패킷을 주고받습니다.

여기서 최초로 패킷을 보내는 곳은 서버입니다.
Ping 패킷을 보내고, Ping 패킷을 받은 클라이언트는 Pong 패킷을 서버에 전달해야 합니다.

만약에 클라이언트에서 일정 시간 내에 Pong 패킷을 전송하지 않으면,
해당 클라이언트의 접속을 끊어버립니다.



핑퐁 코드 예시


서버 코드

// 클라이언트로부터 패킷을 전달 받은 시간
private long _pingpongTick = 0;
public void Ping()
{
    if (_pingpongTick > 0)
    {
    	// 현재 TickCount 시간에서 클라로부터 패킷을 받은 시간을 뺍니다.
        long delta = (Environment.TickCount64 - _pingpongTick);
        
        // 클라이언트로부터 패킷 전송이 지연되거나 안오면 접속을 차단합니다.
        if (delta > 30 * 1000)
        {
            Console.WriteLine("Disconnected by PingCheck");
            Disconnect();
            return;
        }
    }

	// 클라이언트로부터 정상적으로 패킷을 받으면 서버에서도 다시 클라이언트로 패킷을 전송합니다.
    S_Ping pingPacket = new S_Ping();
    Send(pingPacket);

	// 패킷 전송을 5초 후에 실행합니다.
    GameLogic.Instance.PushAfter(5000, Ping);
}

// 클라이언트로부터 패킷을 받으면 호출되는 메서드
public void HandlePong()
{
	// 클라이언트로부터 패킷을 받은 시간(밀리세컨드)을 저장합니다.
	_pingpongTick = Environment.TickCount64;
}

public override void OnConnected(EndPoint endPoint)
{
    Console.WriteLine($"OnConnected : {endPoint}");

	// 접속을 한 유저에게 5초 후에 Ping 패킷을 전송합니다.
    GameLogic.Instance.PushAfter(5000, Ping);
}

제가 진행 중인 프로젝트에서는 서버에서 Ping Pong 시스템에 대한 핵심 로직이 들어가 있습니다.
결국에 주기적으로 체크를 하는 것은 서버입니다.

여기서 서버와 클라이언트는 빈 패킷을 주고받습니다.
Ping Pong 시스템은 서버에서 여러 명의 클라이언트에게 패킷을 주기적으로 주고받아야 합니다.
그렇기 때문에 가벼운 빈 패킷을 전송하고 받도록 합니다.

(서버 코드 전체 보기 => 깃허브 링크)


클라이언트 코드

public static void S_PingHandler(PacketSession session, IMessage packet)
{
    S_Ping pingPacket = (S_Ping)packet;
    NetworkManager.Instance.PingCheck(pingPacket);

    C_Pong pongPacket = new C_Pong();
    NetworkManager.Instance.Send(pongPacket);
}

Ping Pong 시스템에서 클라이언트에서는 크게 하는 일이 없습니다.

서버로부터 Ping 패킷(S_Ping) 패킷을 받으면,
클라이언트 패킷(C_Pong)을 생성해서 서버로 전송(Send)합니다.



참고 자료


  • TCP의 연결 종료 - TCP의 단절 감지 : 유령 세션 => 링크
  • [C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part9: MMO 컨텐츠 구현 (DB연동 + 대형 구조 + 라이브 준비) - 섹션2. 대형 구조 마무리 => 링크
profile
기술에 대한 고민과 배운 것을 회고하는 게임 서버 개발자의 블로그입니다.
post-custom-banner

0개의 댓글