프라우드넷
1. 개발 환경과 기본 모듈
NetServer Class
게임 서버의 메인 모듈. 클라이언트의 연결을 받으며,클라이언트와 메시지를
주고받음
NetClient Class
게임 클라이언트에서 네트워크 모듈. 서버로 연결을 맺은 후 메시지 주고받기
를 수행. 다른 클라이언트와 P2P 통신 가능
2. 클라이언트 - 서버 통신
- 클라이언트
클라이언트가 서버에 접속하는 법
- Create()
- Connect()
접속 성공여부 확인
- OnJoinServerComplete 이벤트
클라이언트가 연결 해제하는 법
- DisConnect()
- OnLeaveServer 이벤트
- 서버
서버가 클라이언트를 연결시키는 법
- Create()
- Start()
클라이언트 접속 확인
- OnClientJoin 이벤트
클라이언트가 연결을 해제했을 때
- OnClientLeave 이벤트
이벤트에 체인 걸 함수들은 직접 생성해야함!
- 서버 구동 의사 코드
// 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);
- 클라이언트 서버 접속 의사 코드
NetClient c = new NetClient();
//param은 실제코드에서 NetConnectionParam , EndPoint/Version 설정
param.serverAddr = "192.168...";
param.serverPort = 44444;
//Server와 동일한 값을 넣어줌
param.protocolVersion = Guid.From({...});
s.Connect(param);
- 이벤트 처리 코드
c.JoinServerCompleteHandler = (result) =>
{
if(result.m_errorType == ErrorType.ok)
{
// 성공처리
}else
{
print(result.ToString());
}
}
s.ClientJoinHandler = (Info) =>
{
// Info에는 새 클라이언트 정보가 있다.
...
};
- 메인 루프와 콜백함수
서버나 다른 클라이언트의 메시지 처리는 메인루프 어딘가에서 한다
NetClient.FrameMove()
FrameMove()를 호출했던 때 이후부터 지금까지 누적된 이벤트나 수신된 메시지에 대한
이벤트 콜벡이 일어남
2.메시지 주고받기
주고 받는 방법
- 바이너리 데이터 주고받기
- 다른 컴퓨터에 있는 함수를 원격으로 호출하기
바이너리 데이터 주고받기
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() 이벤트 함수 호출
원격 메서드 호출
프라우드넷의 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나 파일 엑세스 등 디바이스 타임이 없는 경우
- 서버 프로세스를 여러 개 띄울 수 있게 분산 서버로 개발하는 경우
'게임 서버 프로그래밍 > 게임 서버 프로그래밍 교과서' 카테고리의 다른 글
#8. NOSQL (1) | 2020.02.17 |
---|---|
#7. 데이터베이스의 기초 (0) | 2020.02.17 |
#5. 게임 네트워크 (0) | 2020.02.17 |
#4. 게임 서버와 클라이언트 (0) | 2020.02.17 |
#3. 소켓 프로그래밍 (0) | 2020.02.17 |