6์ฅ ๊ฒ์ ๋คํธ์ํฌ ์์ง ํ๋ผ์ฐ๋๋ท
1. ๊ฒ์ ์๋ฒ, ๋คํธ์ํฌ ์์ง
๊ฒ์ ์๋ฒ์ ๋ฉํฐํ๋ ์ด์ด ์ฒ๋ฆฌ๋ฅผ ๊ฐ๋ฐํ ๋ ๋คํธ์ํน ์ฒ๋ฆฌ๋ฅผ ์ํด ๋ณดํต ์์ผ API๋ฅผ ์ด์ฉํ๋ค. ๊ทธ๋ฌ๋ ์์ผ API๋ง ์ด์ฉํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ๋ฒ๊ฑฐ๋ก์์ด ๋ฐ์ํ ์ ์๋ค.
- ์ด์์ฒด์ ๋ง๋ค ์์ผ API๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ์์ ์ฐจ์ด๊ฐ ์๋ค.
- ์์ผ API์์ ์ ๊ณตํ์ง ์๋ ๊ธฐ๋ฅ์ ์ง์ ๋ง๋ค์ด์ผ ํ๋ค.
๊ทธ๋์ ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ๊ฐ์ ํ๊ธฐ ์ํด ๋คํธ์ํฌ ์์ง์ ์ฌ์ฉํ๊ธฐ๋ ํ๋ค.
2. ๊ฐ๋ฐ ํ๊ฒฝ๊ณผ ๊ธฐ๋ณธ ๋ชจ๋
๋คํธ์ํฌ ์์ง ์ค ํ๋์ธ ํ๋ผ์ฐ๋๋ท์...
- ์์ ์ฉ ์ํํธ์จ์ด๋ก ๊ฐ์ธ์ด๋ ํน์ ์กฐ๊ฑด์ ์ธ๋ ๊ฐ๋ฐ์ฌ์์๋ ๋ฌด๋ฃ๋ก ์ฌ์ฉํ ์ ์๋ค.
- ํด๋ผ์ด์ธํธ-์๋ฒ ๊ฐ ๋คํธ์ํน๊ณผ ํด๋ผ์ด์ธํธ ๊ฐ ์ง์ ๋คํธ์ํน์ ๋จ์ํ๊ฒ ์ฌ์ฉํ ์ ์๊ฒ ํ๋ฉฐ ๋คํธ์ํฌ ์ํธํ, ์์ถ, ํ๋ฆ ์ ์ด ๋ฑ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
- ๊ฐ๋ฐ ์ธ์ด๋ C++, C#์ ์ง์ํ๋ค.
- ์๋ฒ ๋ชจ๋์ ์๋ ์๋ฒ์ ๋ฆฌ๋ ์ค๋ฅผ ์ง์ํ๊ณ ํด๋ผ์ด์ธํธ๋ ์๋, ๋ฆฌ๋ ์ค, iOS, ์๋๋ก์ด๋, ํ๋ ์ด์คํ ์ด์ 4๋ฅผ ์ง์ํ๋ค.
3. ๊ฒ์ ํด๋ผ์ด์ธํธ-์๋ฒ ํต์
๋คํธ์ํฌ ๋ชจ๋์ ํฌ๊ฒ ํด๋์ค 2๊ฐ๋ก ๋๋๋ค.
- NetServer ํด๋์ค: ๊ฒ์ ์๋ฒ์ ๋ฉ์ธ ๋ชจ๋๋ก ํด๋ผ์ด์ธํธ์ ์ฐ๊ฒฐ์ ๋ฐ์ผ๋ฉฐ ๋ฉ์์ง๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ์ญํ ์ ํ๋ค. ๋ ๊ฐ ํด๋ผ์ด์ธํธ์ ๋คํธ์ํฌ ์ํฉ ๋ฑ์ ์ด๋ํ ์ ์๋ค.
- NetClient ํด๋์ค: ๊ฒ์ ํด๋ผ์ด์ธํธ์์ ๋คํธ์ํฌ ๋ชจ๋๋ก ์๋ฒ์ ์ฐ๊ฒฐ์ ๋งบ์ ํ ๋ฉ์์ง๋ฅผ ์ฃผ๊ณ ๋ฐ์ ์ ์๋ค. ๋ ๋ค๋ฅธ ํด๋ผ์ด์ธํธ์ P2P ํต์ ๋ ๊ฐ๋ฅํ๋ค.

NetServer ์ธ์คํด์ค๋ฅผ ์์ฑํ๊ณ ํด๋ผ์ด์ธํธ ์ ์์ ๋ฐ์ผ๋ ค๋ฉด, ๋ค์ ์์ ์ด ํ์ํ๋ค.
- CNetServer.Create()๋ก ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ค.
- CNetServer.Start()๋ก ํด๋ผ์ด์ธํธ ์ ์์ ๋ฐ๋๋ค. ์ด๋ ๋งค๊ฐ๋ณ์๋ก ๋ฆฌ์ค๋ ํฌํธ ๋ฒํธ์ ํ๋กํ ์ฝ ๋ฒ์ ์ ๋ณด๋ธ๋ค.
- ํ๋กํ ์ฝ ๋ฒ์ ์ ์๋ฒ์ ํด๋ผ์ด์ธํธ๊ฐ ๋ชจ๋ ๋ง์ถ์ด์ผ ํ๋ค.
CNetServer* s = CNetServer::Create();
param.m_tcpPorts.Add(44444);
param.m_udpPorts.Add(44444);
param.m_protocolVersion = Guid(
{ 0x5dca93f4, 0x8133, 0x44a0,{ 0xb5, 0x7b,
0x75, 0x7d, 0x9c, 0x78, 0xd5, 0x2e } });
s->Start(param);
ํด๋ผ์ด์ธํธ๋ ์ธ์คํด์ค๋ฅผ ์์ฑํ๊ณ ์๋ฒ์ ์ ์ํ๋ ค๋ฉด, ๋ค์ ์์ ์ด ํ์ํ๋ค.
- CNetClient.Create()๋ก ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ค.
- CNetClient.Connect()๋ก ์๋ฒ์ ์ ์ํ๋ค. ์ด๋ ๋งค๊ฐ๋ณ์๋ก ์๋ฒ ์ฃผ์์ ํฌํธ ๊ฐ, ํ๋กํ ์ฝ ๋ฒ์ ์ ๋ณด๋ธ๋ค.
CNetClient* c = CNetClient::Create();
param.m_serverAddr = _PNT("my.game.com");
param.m_serverPort = 44444;
param.m_protocolVersion = Guid(...);
c->Connect(param);
์๋ฒ์ ํด๋ผ์ด์ธํธ ์ ์์ด ๋ค์ด์ค๋ฉด, ์๋ฒ ์ค๋ ๋ ํ์์ OnClientJoin() ์ด๋ฒคํธ ํจ์๋ฅผ ํธ์ถํ๋ค. OnClientJoin()์ ํด๋ผ์ด์ธํธ ์ ์์ด ๋ค์ด์์์ ์๋ฆฌ๋ ์ด๋ฒคํธ๋ก ํจ์ ์ฝ๋ฐฑ ํํ์ด๋ค. ์ฆ, ์ด๋ฒคํธ ํจ์๋ฅผ ์ด์ฉํ๋ ค๋ฉด, ๊ฐ๋ฐ์๋ ์ด ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ ํจ์๋ฅผ ๋ง๋ค์ด์ผ ํ๊ณ ํ๋ผ์ฐ๋๋ท์ ๊ทธ ํจ์๋ฅผ ํธ์ถํด ์ค๋ค.
s->OnClientJoin = [](CNetClientInfo* info) {
// info์๋ ์ ํด๋ผ์ด์ธํธ ์ ๋ณด๊ฐ ์๋ค.
...
};
์๋ฒ ์ ์์ด ์๋ฃ๋๋ฉด, ํด๋ผ์ด์ธํธ์์๋ OnJoinServerComplete() ์ด๋ฒคํธ ํจ์๋ฅผ ํธ์ถํ๋ค.
c->OnJoinServerComplete = [](ErrorInfo* result) {
if (result->m_errorType = = ErrorType_Ok) {
... // ์ฑ๊ณต ์ฒ๋ฆฌ
}
else {
print(result->ToString());
... // ์คํจ ์ฒ๋ฆฌ
}
};
์ผ๋ฐ์ ์ธ ๊ฒ์ ํด๋ผ์ด์ธํธ๋ ๋ฉ์ธ ๋ฃจํ๋ฅผ ๊ฐ์ง๋ค. ๋ฉ์ธ ๋ฃจํ์์๋ ๊ฒ์ ๋ก์ง ์ฒ๋ฆฌ์ ๋ ๋๋ง์ ํ๋๋ฐ, ์๋ฒ๋ ๋ค๋ฅธ ํด๋ผ์ด์ธํธ์์ ์จ ๋ฉ์์ง๋ฅผ ์ฒ๋ฆฌํ๊ฑฐ๋ ์ด๋ฒคํธ ํจ์๋ฅผ ํธ์ถ๋ฐ๋ ๊ฒ๋ ์ด ๋ฉ์ธ ๋ฃจํ ์ด๋๊ฐ์์ ํ ๊ฒ์ด๋ค. FrameMove() ํจ์๋ฅผ ํธ์ถํ๋ฉด, ์ง๊ธ๊น์ง ๋์ ๋ ์ด๋ฒคํธ๋ ์์ ๋ ๋ฉ์์ง์ ๋ํ ์ด๋ฒคํธ ์ฝ๋ฐฑ์ด ์ผ์ด๋๋ค.
c->FrameMove();
๋ค์์ผ๋ก ํด๋ผ์ด์ธํธ์ ์๋ฒ ๊ฐ ์ฐ๊ฒฐ ํด์ ์ด๋ค.

ํด๋ผ์ด์ธํธ์์ Disconnect() ํจ์๋ฅผ ํธ์ถํ๊ฑฐ๋ ์๋ฒ์ ํด๋ผ์ด์ธํธ ์ฐ๊ฒฐ์ด ์ค๊ฐ์ ๋๊ฒจ OnLeaceServer() ์ด๋ฒคํธ ํจ์๊ฐ ํธ์ถ๋๋ฉด, ์๋ฒ์์๋ OnClientLeave() ์ด๋ฒคํธ ํจ์๊ฐ ํธ์ถ๋๋ค.
c->OnLeaveServer = [](ErrorInfo* reason) {
// reason์๋ ์ ๋์ด์ก๋์ง๊ฐ ๋ด๊ฒจ ์๋ค.
...
};
s->OnClientLeave = [](CNetClientInfo* client,
ErrorInfo* reason,
const ByteArray& comment) {
// ์ฌ๊ธฐ์ ์ด๋ฒคํธ ์ฒ๋ฆฌ ์ฝ๋๋ฅผ ์ถ๊ฐํ๋ค.
};
4. ๋ฉ์์ง ์ฃผ๊ณ ๋ฐ๊ธฐ
ํ๋ผ์ฐ๋๋ท์์ ๋ฉ์์ง๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ๋ฐฉ๋ฒ์ ํฌ๊ฒ '์ ํต์ ์ธ ๋ฐฉ๋ฒ์ผ๋ก ๋ฐ์ด๋๋ฆฌ ๋ฐ์ดํฐ ์ฃผ๊ณ ๋ฐ๊ธฐ'์ '๋ค๋ฅธ ์ปดํจํฐ์ ์๋ ํจ์๋ฅผ ์๊ฒฉ์ผ๋ก ํธ์ถํ๊ธฐ'๊ฐ ์๋ค.
์ ํต์ ์ธ ๋ฐฉ๋ฒ์ผ๋ก ๋ฐ์ด๋๋ฆฌ ๋ฐ์ดํฐ ์ฃผ๊ณ ๋ฐ๊ธฐ๋ NetClient์ NetServer ๋ด SendUserMessage() ํจ์๋ก ์๋๋ฐฉ์๊ฒ ๋ฉ์์ง๋ฅผ ์ ์กํ๋ ๋ฐฉ๋ฒ์ด๋ค. ์ด๋ ๋งค๊ฐ๋ณ์๋ ๋ค์๊ณผ ๊ฐ๋ค.
- ๋๊ตฌ(HostID ๋๋ HostID array): ๋ฉ์์ง๋ฅผ ๋ณด๋ด๊ณ ์ ํ๋ ๋์
- ์ด๋ป๊ฒ(reliable, unreliable ๋ฑ): ์ด๋ค ๋ฐฉ๋ฒ์ผ๋ก ์๋๋ฐฉ์๊ฒ ๋ฉ์์ง๋ฅผ ์ ์กํ ์ง
- ๋ฌด์์(byte array): ๋ฉ์์ง
unsigned char data[100];
c->SendUserMessage(HostID_Server,
RmiContext::ReliableSend, data, 100);
s->SendUserMessage(ClientHostID,
RmiContext::UnreliableSend, data, 50);
HostID sendTo[10];
s->SendUserMessage(sendTo, 10,
RmiContext::UnreliableSend, data, 30);
๋ฉ์์ง๋ฅผ ์์ ํ๋ฉด OnReceiveUserMessage() ์ด๋ฒคํธ ํจ์๊ฐ ํธ์ถ๋๋ค.
c->OnReceiveUserMessage = [...]
(HostID sender, const RmiContext& rmiContext,
uint8_t* payload, int payloadLength)
{
... // ์์ ๋ ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ฅผ ํด ์ฃผ์.
};
5. ์์ดํ์ด ์ ๋ฃฐ๋ฌ ์ฐ๊ฒฐ ํธ๋์ค๋ฒ ๊ธฐ๋ฅ
๋ชจ๋ฐ์ผ ๊ฒ์์ ๊ฐ๋ฐํ ๋๋ ๋ฌด์ ๋คํธ์ํฌ๊ฐ ๋์ด์ง ์ ์๋ค๋ ์ ์ ๊ณ ๋ คํด์ผ ํ๋๋ฐ, ํ๋ผ์ฐ๋๋ท์ ์ฐ๊ฒฐ ์ ์ง ๊ธฐ๋ฅ์ ํตํด ์ด ๋ฌธ์ ๋ฅผ ๊ทน๋ณตํ ์ ์๋ค. ์ด๋ฅผ ์ฌ์ฉํ๋ฉด ์์ดํ์ด ์ง์ญ์ ๋ฒ์ด๋๊ฑฐ๋ ๋ฐ๋๋ก ์์ดํ์ด ์ง์ญ ์์ผ๋ก ๋ค์ด๊ฐ๋๋ผ๋ ์ฐ๊ฒฐ์ด ๋์ด์ง๋ ํ์ ์์ด ๊ฒ์์ ํ๋ ์ดํ ์ ์๋ค.
param.m_autoConnectionRecovery = true;
...
c->Connect(param);
๋คํธ์ํฌ ํต์ ์ด ์ผ์์ ์ผ๋ก ๋ฉ์ถ๊ณ ๋ค์ ํ๋ณต๋๋ฉด, ๊ทธ๋์ ์ฃผ๊ณ ๋ฐ์ง ๋ชปํ๋ ๋ฉ์์ง๋ฅผ ํ๊บผ๋ฒ์ ๋ฐ๋๋ค.
// ๋คํธ์ํฌ๊ฐ ์ผ์ ์ ์ง๋์์ ๋
c->OnServerOffline = [...](CRemoteOfflineEventArgs &args) {
// args์๋ ์ ์คํ๋ผ์ธ์ด ๋์๋์ง์ ๋ํ ์ ๋ณด๊ฐ ์๋ค.
...
};
// ๋คํธ์ํฌ๊ฐ ๋ค์ ํ๋ณต๋์์ ๋
c->OnServerOnline = [](CRemoteOnlineEventArgs &args) {
// args์๋ ์ฐ๊ฒฐ ํธ๋์ค๋ฒ๊ฐ ์๋ฃ๋ ํ ์ ๋ณด๊ฐ ๋ด๊ฒจ ์๋ค.
...
};
6. ์๊ฒฉ ๋ฉ์๋ ํธ์ถ
์์ ์ดํด๋ณธ ์ ํต์ ์ธ ๋ฐฉ๋ฒ์ผ๋ก ๋ฐ์ด๋๋ฆฌ ๋ฐ์ดํฐ ์ฃผ๊ณ ๋ฐ๊ธฐ๋ ์ก์์ ๋ฃจํด์ ์ง์ ๋ง๋ค์ด์ผ ํ๋ ๋ฒ๊ฑฐ๋ก์์ด ์๋ค. ํ๋ผ์ฐ๋๋ท์ RMI(์๊ฒฉ ๋ฉ์๋ ํธ์ถ(Remote Method Invocation))์ ์๋๋ฐฉ ์ปดํจํฐ ์์ ์๋ ํ๋ก๊ทธ๋จ์ ํน์ ํจ์๋ฅผ ๋ฉ๋ฆฌ์ ์คํํ๊ฒ ํ๋๋ฐ, ์ด๋ฅผ ์ฌ์ฉํ๋ฉด ๋์ฑ ํธ๋ฆฌํ๊ฒ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์ ์ ์๋ค.
- ์ก์ ์ ์ฒ๋ฆฌํ๋ ์ฝ๋์ ์์ ์ ์ฒ๋ฆฌํ๋ ์ฝ๋๋ฅผ ์ผ์ผ์ด ๊ตฌํํ ํ์๊ฐ ์๋ค.
- ์ก์์ ๋ฉ์์ง ํํ๊ฐ ๋ณ๊ฒฝ๋์์ ๋ ์ก์ ์ด๋ ์์ ์ ์ฒ๋ฆฌํ๋ ์ฝ๋๋ฅผ ์์ ํ๋ค๊ฐ ์ค์ํ ์ํ์ด ์๋ค.

RMI๋ ์๋์ผ๋ก ๋ง๋ค์์ด์ผ ํ๋ ์ฝ๋๋ฅผ ์๋์ผ๋ก ๋ง๋ค์ด์ค๋ค. ์ก์ ํ๋ ์ชฝ์์๋ ์ค์ ํจ์ ๋์ ์ก์ ์ ๋ด๋นํ๋ ์ฝ๋๊ฐ ์คํ๋๋๋ฐ, ์ก์ ์ ๋ด๋นํ๋ ์ฝ๋๋ 'ํจ์ ํธ์ถ์ ๋์ ํ๋ค'๋ผ๋ ์๋ฏธ๋ก proxy๋ผ๊ณ ๋ ํ๋ค. ๋ฐ๋๋ก ์์ ํ๋ ์ชฝ์์๋ ๋ฐ๋ ๋ฉ์์ง๋ฅผ ๋ถ์ํ์ฌ ์ค์ ํจ์๋ฅผ ํธ์ถํด์ค๋ค. ์์ ์ ๋ด๋นํ๋ ์ฝ๋๋ 'ํจ์๋ฅผ ํธ์ถํด์ฃผ๋ ๊ธฐ๋ฐ'์ด๋ผ๋ ์๋ฏธ๋ก stub๋ผ๊ณ ๋ ํ๋ค.
class SenderCode
{
// ์๋์ผ๋ก ์์ฑ๋๋ ์ฝ๋
Knight_Move(SendTo, position, motion)
{
Message msg;
msg.Write(ID_Knight_Move);
msg.Write(position);
msg.Write(motion);
Send(SendTo, msg);
}
}
class ReceiverCode
{
// ์๋์ผ๋ก ์์ฑ๋๋ ์ฝ๋
ProcessReceivedMessage(Message msg)
{
ID = msg.Read();
switch (ID)
{
case ID_Knight_Move:
position = msg.Read();
motion = msg.Read();
Knight_Move(position, motion);
break;
...
}
}
Knight_Move(position, motion)
{
// ์ฌ๊ธฐ์ ์ฝ๋๋ฅผ ์์ฑํ๋ค.
}
}
7. ํด๋ผ์ด์ธํธ๋ผ๋ฆฌ P2P ํต์
ํ๋ผ์ฐ๋๋ท์์๋ ์๋ฒ ์์ด ํด๋ผ์ด์ธํธ๋ผ๋ฆฌ ํต์ ํ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค. ์ด๋ฅผ P2P ํน์ ํผ์ดํฌํผ์ด ๋คํธ์ํน์ด๋ผ ํ๋ค.
- ์๋ฒ๊ฐ ์น์ธํด์ผ๋ง P2P ์ฐ๊ฒฐ์ด ๊ฐ๋ฅํ๋ค. ์๋ฒ์์ ํด๋ผ์ด์ธํธ 1๊ณผ 2๋ฅผ P2P ์ฐ๊ฒฐํ๋ผ๊ณ ์ง์ํ๋ฉด, ํด๋ผ์ด์ธํธ 1๊ณผ 2๋ ์๋ก๊ฐ P2P ์ฐ๊ฒฐ์ด ๋์์์ ์ฆ์ ์ ์ ์๋ค.
- ์ด๋๋ ํด๋ผ์ด์ธํธ๋ค์ ๋ฌถ์ด P2P ๊ทธ๋ฃน์ด๋ผ ๋ถ๋ฅธ๋ค. ์ด ํด๋ผ์ด์ธํธ๋ผ๋ฆฌ๋ง P2P ๋คํธ์ํน์ ํ ์ ์๋ค.

HostID list[2];
list[0] = C1;
list[1] = C2;
// ๋ ๋ฒ์งธ ์ธ์ = ๋ฐฐ์ด ํฌ๊ธฐ
G = s->CreateP2PGroup(list, 2);
8. ์ค๋ ๋ ๋ชจ๋ธ

ํ๋ผ์ฐ๋๋ท์ ์ค๋ ๋ ๋ชจ๋ธ์ ๋ ๊ฐ์ง ํํ๋ก ์ ๊ณตํ๋ค.
- ์๋ฒ ํ๋ก์ธ์ค ํ๋๊ฐ ์ค๋ ๋ ์ฌ๋ฟ ๊ฐ์ง๋ ํํ์ ์๋ฒ
- ์ฌ๋ฌ์ ์ฌ๋ฌ ์๋ฒ ํ๋ก์ธ์ค๊ฐ ๊ฐ๊ฐ ์ค๋ ๋ ํ๋๋ฅผ ๊ฐ์ง๋ ํํ์ ์๋ฒ
๊ธฐ๋ณธ๊ฐ์ผ๋ก NetServer์ ์์ปค ์ค๋ ๋(worker thread)๋ CPU ๊ฐ์๋งํผ ๊ตฌ๋ํ๋ค. ๋ค์ ๋งํด์ CPU ๊ฐ์๋งํผ ์ค๋ ๋๋ฅผ ๊ฐ์ง ์ค๋ ๋ ํ์ด ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณต๋๋ค.