#6. 프라우드넷

게임 서버 프로그래밍/게임 서버 프로그래밍 교과서

2020. 2. 17. 16:04

6.프라우드넷

프라우드넷


1. 개발 환경과 기본 모듈
  • NetServer Class

    게임 서버의 메인 모듈. 클라이언트의 연결을 받으며,클라이언트와 메시지를

    주고받음

  • NetClient Class

    게임 클라이언트에서 네트워크 모듈. 서버로 연결을 맺은 후 메시지 주고받기

    를 수행. 다른 클라이언트와 P2P 통신 가능

2. 클라이언트 - 서버 통신
  1. 클라이언트
  • 클라이언트가 서버에 접속하는 법

    • Create()
    • Connect()
  • 접속 성공여부 확인

    • OnJoinServerComplete 이벤트
  • 클라이언트가 연결 해제하는 법

    • DisConnect()
    • OnLeaveServer 이벤트
  1. 서버
  • 서버가 클라이언트를 연결시키는 법

    • Create()
    • Start()
  • 클라이언트 접속 확인

    • OnClientJoin 이벤트
  • 클라이언트가 연결을 해제했을 때

    • OnClientLeave 이벤트

이벤트에 체인 걸 함수들은 직접 생성해야함!

  1. 서버 구동 의사 코드
// Server Create
NetServer s = new NetServer();
// TCP Listen port
param.tcpPorts.Add(44444);
param.udpPorts.Add(44444);
// Server와 Client가 모두 맞추어야하는 버젼값 GUID 이용
param.protocolVersion = Guid.From({...});
s.Start(param);
  1. 클라이언트 서버 접속 의사 코드
NetClient c = new NetClient();
//param은 실제코드에서 NetConnectionParam , EndPoint/Version 설정
param.serverAddr = "192.168...";
param.serverPort = 44444;
//Server와 동일한 값을 넣어줌
param.protocolVersion = Guid.From({...});
s.Connect(param);
  1. 이벤트 처리 코드
c.JoinServerCompleteHandler = (result) =>
{
    if(result.m_errorType == ErrorType.ok)
    {
        // 성공처리
    }else
    {
        print(result.ToString());
    }
}

s.ClientJoinHandler = (Info) =>
{

    // Info에는 새 클라이언트 정보가 있다.
    ...
};

 

  1. 메인 루프와 콜백함수
  • 서버나 다른 클라이언트의 메시지 처리는 메인루프 어딘가에서 한다

  • NetClient.FrameMove()

    FrameMove()를 호출했던 때 이후부터 지금까지 누적된 이벤트나 수신된 메시지에 대한

    이벤트 콜벡이 일어남

2.메시지 주고받기
  • 주고 받는 방법

    • 바이너리 데이터 주고받기
    • 다른 컴퓨터에 있는 함수를 원격으로 호출하기
  1. 바이너리 데이터 주고받기

    • SendUserMessage()

      상대방에게 메시지가 전송됨

      • 필요한 매개변수

        • 누구한테? (HostID or HostID array)

          메시지를 보내고자 하는 대상

        • 어떻게?(reliable, unreliable, ...)

          reliable(상대방에게 반드시 전송됨을 보장)

          unreliable(상대방에게 전송되지 못하더라도 즉시 전송)

          이외에도 암호화,압축등등 .. 옵션있음

        • 무엇을?

          메시지(바이너리 데이터)

    • 메시지 보내는 루틴

      var data = new byte[100];
      c.SendUserMessage(HostID.Server,RmiContext.ReliableSend,data);
      var sendTo = new HostID[2];
      s.SendUserMessage(SendTo,RmiContext.UnreliableSend, data);
      
    • 메시지 받는 루틴

      c.ReceiveUserMessageHandler = (sender, rmiContext, payload)=>
      {
      	// 수신된 이벤트의 처리
      }
      

      메시지를 수신하면 OnReceiveUserMessage() 이벤트 함수 호출

  2. 원격 메서드 호출

    • 프라우드넷의 RMI (Remote Method Invocation)

      상대방 컴퓨터 안에 있는 프로그램의 특정 함수를 멀리서 실행!

      수동으로 만들었어야 하는 코드를 자동으로 만들어준다. 송신하는 쪽에서는

      실제 함수 대신 송신을 담당하는 코드가 실행

    • proxy

      함수 호출을 대신 해 준다는 뜻.

    • stub

      함수를 호출해 주는 기반이라는 뜻

    • 원격 메서드 호출의 장점

      • 송신을 처리하는 코드와 수신을 처리하는 코드를 손으로 일일이 구현할 필요x
      • 송수신 메시지의 형태가 변경될 때 코드 수정의 실수가 없음
    • 사용법

      PIDL 확장자 파일에서 작성해야 함

      //CalcC2S는 네임스페이스 이름이 됨
      //1000은 메시지 ID(RMI 함수들은 서로다른 ID를 가져야 함)
      //RMI는 자동으로 1000 1001 1002로 증가..
      global CalcC2S 1000
      {
      	RequestAdd([in] int a, [in] int b);
      }
      global CalcS2C 2000
      {
          ResponseAdd([in] int sum);
      }
      
    • 직렬화

      함수 매개변수를 메시지 블록으로 만드는 과정

    • 역직렬화

      메시지 블록에서 함수 매개변수를 추출하는 것

    • PIDL 컴파일러로 생성된 클래스들 부착법

      클라이언트는 서버의 Stub을 가져야하고, 서버는 클라이언트의 Proxy를 가져야 함

      CalcC2S.Proxy CalcC2SProxy;
      c.AttachProxy(&CalcC2SProxy);
      CalcS2C.StubFunctional CalcS2CStub;
      c.AttachStub(CalcS2CStub);
      
    • 클라이언트에서 서버로 RMI 호출

      //3,4 같은것은 사용자가 설정한 parameter에 대한 값임
      CalcC2SProxy.RequestAdd(HostID.Server,RmiContext.ReliableSend,3,4);
      
    • 서버에서 RMI 호출받기

      //클라에서 보낸 데이터를 받아서 처리하는 과정
      CalcC2SStub.RequestAdd = (remote , rmiContext, a,b)=>
      {
      	int sum = a+b;
      	//서버에서 클라이언트로 두값의 합을 보냄
      	CalcS2CProxy.ResponseAdd(remote,RmiContext.ReliableSend,sum);
      };
      

      서버에서 클라이언트로 원격 함수를 호출하려면 CalcS2C의 Proxy는 서버에 붙이고 CalcS2C의 Stub은 클라이언트에붙인다!

    • 전체 과정 요약

      • PIDL 파일에 RMI 함수들 정의
      • 컴파일
      • 생성된 Proxy,Stub을 NetClient, NetServer에 부착
      • 생성된 Proxy의 함수를 호출하면 메시지 전송됨
      • 생성된 Stub에 함수를 부착하면 그 함수들이 호출됨
  • 와이파이 셀룰러 연결 핸드오버 기능

    모바일 게임을 개발할 때는 무선 네트워크가 끊어질 수 있다는 점을 고려!

    • 연결 유지 기능

      게임의 와이파이 지역을 벗어나거나 반대로 와이파이 지역 안으로 들어가더라도

      연결이 끊어지는 현상 없이 게임을 플레이할 수 있다

      param.autoConnectionRecovery = true;
      
      ...
          
      c.Connect(param);
      
    • 와이파이 지역 바깥에서 안으로 들어 올 때

      • 네트워크 통신이 일시적으로 멈춤
      • 네트워크 연결이 완전히 회복되면 그동안 주고받지 못한 메시지를 한꺼번에 받음
      • OnServerOffline() : 네트워크가 일시정지되었을 때 처리하는 함수
      c.ServerOfflineHandler = (args) =>{};
      
      //....
          
      c.ServerOnLineHandler = (args) =>{};
      
      

       

  • 클라이언트끼리 P2P통신

    서버를 거치지 않고 서로 통신하는 기능

    • 프라우드넷의 P2P 통신

      • P2P 연결을 생성하는 데 시간이 걸리지 않는다

      • P2P 연결을 맺거나 끊는 것은 클라이언트가 임의로 할 수 없다

        서버가 승인해야 함

    • P2P Group

      메신저에서의 채팅 창과 비슷한 역할

      • 특징

        • P2P 그룹에는 클라이언트를 0개 이상 넣을 수 있다
        • 클라이언트 하나가 여러 P2P 그룹에 들어가도 된다
        • 서버도 P2P 그룹에 들어가는 것이 허락된다
      • 서버

      G =s.CreateP2PGroup({C1,C2});
      
      • 클라이언트
      c.P2PMemberJoinHandler =(memberHostID, groupHostID, memberCount, customField)=>
      {
      	G = groupHostID;
      	Peers.Add(memberHostID);
      };
      
      • P2P Message Send
      //첫 번째 매개변수로 G를 넣으면 G안의 클라이언트들에게 멀티캐스트가 된다
      c.SendUserMessage(G,RmiContext.ReliableSend,data);
      
      • P2P Group에 멤버 추가/삭제/파괴

        • JoinP2PGroup(추가)
        • NetServer.LeaveP2PGroup() (제거)
        • DestroyP2PGroup() (파괴)
      • P2P 홀펀칭 상태 변화 감지

        인터넷 공유기 뒤에 있는 클라이언트끼리도 서로 P2P 통신을 할 수 있는 기법

      • P2P 릴레이

        홀펀칭이 되어 있지 않을 때 P2P 네트워킹이 서버를 통해서 간접적으로 이루어지는 것

        P2P보다 비효율적 (전송 시간이 더 걸린다)

      • 홀펀칭/릴레이 판단

        OnChangeP2PRelayState()

      c.ChangeP2PRelayStateHandler =(remoteHostID,reason)=>
      {
      	// reason == ok면 홀펀칭 되었다는 뜻
      };
      
      • P2P 에서의 RMI

        클라이언트가 송신자이자 수신자

      MyGamep2p.Proxy P2PProxy;
      MyGameP2P.Stub P2PStub;
      P2PStub.Player_Move = (sendFrom, rmiContext, position)=>
      {
      	// P2P로 RMI를 받으면 그것을 처리하는 함수를 정의한다.
      };
      
      C.AttachProxy(P2PProxy);
      C.AttachStub(P2PStub);
      
      P2PProxy.Player_Move(G,RmiContext.UnreliableSend,myPosition);
      
      
    • 요약

      • CreateP2PGroup()으로 클라이언트들이 직접 통신할 P2P 그룹을 만든다
      • OnP2PMemberJoin()으로 클라이언트들은 자기가 P2P 통신할 수 있음을 안다
      • RMI나 SendUserMessage()로 P2P 메시지를 보낸다
      • P2P RMI를 쓰려면 Proxy,Stub을 모두 NetClient에 Attach 해야한다
  • 스레드 모델

    • NetServer의 워커 스레드는 CPU 개수만큼 구동된다

      즉, CPU 개수만큼 스레드를 가진 스레드 풀이 기본으로 제공된다

    • 워커스레드를 1개로 설정하고 CPU 개수만큼 서버를 구동해야하는 경우

      • 서버 내부 데이터가 뮤텍스 1개로 보호되는 경우
      • 서버 내부 로직에서 DB나 파일 엑세스 등 디바이스 타임이 없는 경우
      • 서버 프로세스를 여러 개 띄울 수 있게 분산 서버로 개발하는 경우