일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- streaming
- AWS
- Sharp
- Thumbnail
- 좌표
- 이미지프로세싱
- 디지털영상
- 리사이즈
- resize
- lightsail
- 노선
- 스트리밍서버
- Node
- S3
- 이미지
- 아날로그영상
- 샘플링
- 튜토리얼
- 시작하기
- 화소
- Bucket
- 프라우드넷
- 게임서버
- 버킷
- 지하철역
- nodejs
- multiparty
- ProudNet
- 이미지서버
- 데이터
- Today
- Total
Deep Studying
[프라우드넷] 채팅 서버 만들기(2) RMI 통신하기 본문
이번 포스트부터는 본격적으로 서버 프로그램을 만들어보도록 하겠습니다.
기본적인 RMI통신 내용을 설명하기 위해 아주 간단한 서버와 클라이언트를 만드는 것으로 시작하겠습니다.
프로젝트 초기 설정이 안되있다면 이전 포스트를 읽고 다시 봐주시기 바랍니다.
이전 포스트: 채팅 서버 만들기(1) 프로젝트 초기 설정하기
요구사항
- 메세지는 시스템 메세지와 채팅 메세지 두 종류이다.
- 클라이언트가 접속하면 모든 클라이언트에게 시스템 메세지를 보낸다.
- 클라이언트가 접속을 종료하면 모든 클라이언트에게 시스템 메세지를 보낸다.
- 클라이언트가 채팅을 입력하면 모든 클라이언트에게 채팅 메세지를 전달한다.
위 세 가지 기능에 필요한 요소들을 살펴보면
- 클라이언트가 접속하면 모든 클라이언트에게 시스템 메세지를 보낸다.
- 클라이언트가 접속을 종료하면 모든 클라이언트에게 시스템 메세지를 보낸다.
- 클라이언트가 채팅을 입력하면 모든 클라이언트에게 채팅 메세지를 전달한다.
빨간 글씨에 적힌 내용들이 중요 요소들입니다.
1. 클라이언트의 접속 처리
2. 클라이언트의 접속 종료 처리
3. 클라이언트에게 시스템 메세지 전송 ( S2C, Server to Client )
4. 서버에게 채팅 메세지 전송 ( C2S, Client to Server )
5. 클라이언트에게 채팅 메세지 전송 ( S2C, Server to Client )
목표는 위 다섯 가지 통신 요소를 처리하는 것으로 잡을 수 있습니다.
1번과 2번은 기본적으로 시스템이 처리해주는 기능입니다. 핸들러를 통해 로직을 추가해야합니다.
나머지는 PIDL에 등록하여 직접 stub과 proxy를 사용하는 기능들입니다.
PIDL파일 등록
다음과 같이 파일을 작성합니다.
global C2S 2000
{
Chat([in] string str);
}
global S2C 3000
{
NotifyChat([in] string str);
SystemChat([in] string str);
}
우선은 세가지 메소드 모두 string을 주고 받는 것으로 정의하겠습니다.
다 작성하셨다면 PIDL.bat 파일을 실행해줍니다.
> PIDL.bat 실행
Stub 등록하기
우선 내부 로직을 구현하기 전에 빈 함수로 Stub을 구성해줍니다.
S2C에 작성한 함수의 Stub은 클라이언트에서, C2S에서 작성한 함수의 Stub은 서버에서 처리합니다.
RPC 통신의 특성으로 요청을 받는 쪽에서 Stub을 실행, 로직을 처리한다고 기억하시면 됩니다.
Client > Program.cs
...
static void InitializeStub()
{
S2CStub.SystemChat = (HostID remote, RmiContext rmiContext, string str) =>
{
return true;
};
S2CStub.NotifyChat = (HostID remote, RmiContext rmiContext, string str) =>
{
return true;
};
}
...
Server > process/Process.cs
using Nettention.Proud;
namespace Server.process
{
internal class CommonProcess
{
static S2C.Proxy S2CProxy = new S2C.Proxy();
static C2S.Stub C2SStub = new C2S.Stub();
public void InitStub()
{
// Stub에 등록
C2SStub.Chat = Chat;
ServerLauncher.NetServer.AttachProxy(S2CProxy);
ServerLauncher.NetServer.AttachStub(C2SStub);
}
// Chat 함수 로직 작성
static public bool Chat(HostID remote, RmiContext rmiContext, string str)
{
return true;
}
}
}
SystemChat 구현
시스템 채팅을 받으면 클라이언트에서 해줄 일은 채팅을 출력해주는 것이 전부입니다.
Client > Program.cs
static void InitializeStub()
{
S2CStub.SystemChat = (HostID remote, RmiContext rmiContext, string str) =>
{
lock (g_critSec)
{
Console.WriteLine("[System] {0}", str);
}
return true;
};
...
}
여기서 lock은 사실 필요하진 않습니다. 공유 자원에 접근하는 일이 따로 없기 때문이죠.
하지만 일단은 Stub을 처리할 때는 lock을 넣는다 라고 생각하고 넘어가셔도 좋을 것 같습니다.
물론 lock을 빼고 Console.WriteLine( ) 함수만 작성하셔도 문제될게 전혀 없습니다.
이제 서버에서 S2CProxy.SystemChat( ) 함수를 실행하면 클라이언트에게 메세지가 전송될 것입니다.
해당 함수를 외부에서도 호출할 수 있도록 public 메서드를 하나 더 작성해줍니다.
Server > process/Process.cs
internal class CommonProcess{
...
public void SystemChat(string str)
{
S2CProxy.SystemChat(ServerLauncher.NetServer.GetClientHostIDs(), RmiContext.ReliableSend, str);
}
}
서버 어디서든 이 함수를 호출하면 모든 클라이언트에게 시스템 메세지를 전송할 수 있습니다.
NetServer.GetClientHostIDs( ) 는 ProudNet에서 제공하는 메서드로 접송중인 모든 HostID를 배열로 리턴합니다.
접속 / 접속종료 핸들러 등록
Handler.cs를 열어보면 미리 작성해둔 여러 핸들러들이 있습니다.
그 중에 사용할 것은 ClientJoinHandler와 ClientLeavehandler 입니다. 각각 클라이언트가 접속 / 접속종료 했을 때 서버에서 실행하는 핸들러들입니다. 아래 내용과 같이 메서드 안을 채워줍니다.
Server > Handler.cs
internal class Handler{
...
public void ClientJoinHandler(NetClientInfo clientInfo)
{
string message = string.Format("Host{0} entered", clientInfo.hostID);
Console.WriteLine(message);
Process.SystemChat(message);
}
public void ClientLeaveHandler(NetClientInfo clientInfo, ErrorInfo errorinfo, ByteArray comment)
{
string message = string.Format("Host{0} leaved", clientInfo.hostID);
Console.WriteLine(message);
Process.SystemChat(message);
}
...
}
동작은 단순합니다. 서버 콘솔에 메세지를 출력하고, 클라이언트에게 전송합니다.
실행
클라이언트 접속
왼쪽 위부터 클라이언트 프로그램을 실행하였습니다.
클라이언트를 켤 때마다 시스템 메세지가 출력되는 것을 볼 수 있습니다.
클라이언트 종료
마찬가지로 클라이언트를 종료할 때마다 시스템 메세지가 출력됩니다.
Chat 구현
이제 채팅 메세지를 처리를 해보겠습니다. 먼저 로직을 작성하기 전에 클라이언트에서 이를 테스트할 수 있게 하겠습니다. 클라이언트 Main문의 loop에 다음과 같은 내용을 작성합니다.
Client > Program.cs
...
while (keepWorkerThread)
{
string userInput = Console.ReadLine();
if (userInput == "q")
{
keepWorkerThread = false;
}
else
{
C2SProxy.Chat(HostID.HostID_Server, RmiContext.ReliableSend, userInput);
}
}
...
키보드 input을 받고 C2SProxy를 통해 Chat메세지를 서버로 보내는 내용입니다.
HostID.HostID_Server 은 서버의 Host ID로 해당 값을 인자로 넣으면 서버로 요청이 가도록 되어있습니다.
다음은 서버에서 Chat요청을 처리해줄 Stub을 작성해야합니다.
Server > process / Process.cs
internal class CommonProcess{
...
static public bool Chat(HostID remote, RmiContext rmiContext, string str)
{
Console.WriteLine(str);
S2CProxy.NotifyChat(ServerLauncher.NetServer.GetClientHostIDs(), rmiContext, str);
return true;
}
...
}
Chat 메세지를 받으면 시스템 메세지를 보내는 것 처럼 모든 유저에게 내용을 보냅니다.
이제 다시 클라이언트에서 NotifyChat을 받으면 처리할 stub을 구현해줍니다.
Client > Program.cs
class Program{
...
static void InitializeStub()
{
S2CStub.SystemChat = (HostID remote, RmiContext rmiContext, string str) =>
{
lock (g_critSec)
{
Console.WriteLine("[System] {0}", str);
}
return true;
};
S2CStub.NotifyChat = (HostID remote, RmiContext rmiContext, string str) =>
{
lock (g_critSec)
{
Console.WriteLine("> {0}", str);
}
return true;
};
}
...
}
내용은 역시 단순 출력 밖에 없습니다. SystemChat과 똑같으나 구분을 위해 출력 포맷만 다르게했습니다.
실행
오른쪽 콘솔에서 키보드 입력을 넣으면
서버는 물론 다른 클라이언트들도 메세지를 출력하는 것을 볼 수 있습니다.
마무리
이번 포스트에서는 가장 단순한 형태의 RMI메세지를 처리하는 것을 해보았습니다.
또한 서버에서 클라이언트에게 메세지를 BroadCast하는 것도 해보았습니다.
그러나 채팅서비스에는 유저의 닉네임이 들어가야 누가 메세지를 보냈는지 알 수 있습니다.
이를 구현하기 위해 다음 포스트에서는 유저 클래스를 추가하고 이를 이용해 RMI 통신을 해보겠습니다.
다음 포스트: 채팅 서버 만들기(3) Marshaler 이용하기
'게임서버' 카테고리의 다른 글
[프라우드넷] 채팅 서버 만들기(4) Dictionary 사용해보기 / Room으로 관리하기 (0) | 2022.01.13 |
---|---|
[프라우드넷] 채팅 서버 만들기 (3) Marshaler 이용하기 (2) | 2022.01.12 |
[프라우드넷] 채팅 서버 만들기(1) 프로젝트 초기 설정하기 (0) | 2022.01.10 |
[프라우드넷] RPC 통신 이해하기(1) - 용어 및 개념 정의 (0) | 2022.01.09 |
[프라우드넷] RPC 통신 이해하기(2) - PIDL 파일에 대해 (0) | 2022.01.09 |