버퍼를 하나씩 보내는게 아닌, BufferList를 활용하여 버퍼들을 한 번에 담아 보내는 방법으로 수정을 하고, 그 과정 중에서 배열의 일부분을 받을 수 있는 ArraySegment< T >를 배워 적용하였다.
Send를 할 때는 지난 번 구현한 것에서 처리를 하면 되지만 Recv 할 때는 이렇게 직접 처리하는 것이 아닌, 어떤 메시지를 받았다는 것을 알리고(콜백) 처리 하는 곳을 따로 구분해두어야 한다.
기존 lister에서는 이벤트 처리가 하나의 방법이 될 수 있다. listner의 Init()에서 Action 값을 연결 해주고
동작을 담당할 내역을 가진 함수를 만들어 준 뒤
Main 함수에서 Init를 실행시키면서, 위에서 동작의 내용이 담긴 함수를 보내줄 경우 Init를 통해 listner에 연결하여 필요한 내역들을 등록하고
유저가 접속했을 때 이벤트 핸들러에 등록 된 녀석을 Invoke를 통해 불러 "Welcome to Justin Server" 라는 문구가 출력되는 구조이다.
하지만 이번에는 이벤트 처리가 아닌 상속 처리로 진행할 것이다. 일단 어떤 것들이 필요한지 정리한다.
현재 제작중인 서버 코어는 라이브러리와 같이 사용될 예정이기에, 굳이 내부적으로 돌아가는 건 만지지 않고
Send와 같이 전송할 때 사용될 수 있는 몇 가지의 함수를 추가 제작할 예정이다.
클라이언트가 접속했다는 것을 알려주는 알림 역할을 하며, 어떤 클라이언트에서 접속했는지 주소를 위해 EndPoint를 매개변수로 받는다.
public void OnConnected(EndPoint end);
이번 파트에서 중요한 내용으로 클라이언트에서 패킷을 보내고, 내가 받았을 때 알려주기 위한 용도로 사용된다.
배열로 받아도 되고, 지난 번 배운 ArraySegment가능 하다. 하지만 나중엔 큰 패킷으로 한 번에 받을 것이다.
public void OnRecv(ArraySegment<byte> buffer);
OnSend()는 성공적으로 보낸 것을 알려줄 함수, OnDisconnected()는 연결이 끊긴 것을 알려주는 함수이다.
public void OnSend(int numOfBytes);
public void OnDisconnected(EndPoint end);
위에서 추가 제작이 필요한 메소드들을 추상 함수로 제작한다. Class 또한 추상 클래스로 변경 시켜주어야 한다.
abstract class Session
{
public abstract void OnConnected(EndPoint end);
public abstract void OnRecv(ArraySegment<byte> buffer);
public abstract void OnSend(int numOfBytes);
public abstract void OnDisconnected(EndPoint end);
...
}
메인 Program이 있는 곳에 override 하여 잘 배치 해준 뒤에 작업을 시작한다.
그러면 기존에 Session을 생성하여 사용했던 부분이 abstract Class가 되면서 사용할 수 없어, 오류가 발생할 텐데 이를 새로 생성한 GameSession 클래스로 변경해준다.
이제 제작한 함수들을 연결하고, 각 함수별로 동작하는 걸 옮겨 주는 과정을 거친다.
가장 쉬운 OnDisconnected 같은 경우는 그냥 종료하는 시점에 해당 소켓이 연결되어 있는 주소를 가져온다.
실제 패킷을 성공적으로 받았을 경우 동작하는 부분에 OnRecv 이벤트를 연결 해주고, 인자로 기존에 하드코딩식으로 사용하던 방식을 넣는다.
다만 여기서 'X' 표시 된 부분은 복사해서 OnRecv에서 활용하자. 그러면 이렇게 코드들이 자기랑 안 맞는다고 투덜될 텐데, 새로 받게될 buffer을 이용해 인터페스만 변경해주면 된다.
아래와 같이 변경. 이번 작업에 핵심은 엔진과 콘텐츠를 분리해주는 것이다. 그래서 나중에는 이렇게 분리한 Game Session 을 생성하여 각각 사용하도록 할 것이다.
string recvDa =
Encoding.UTF8.GetString(
buffer.Array, buffer.Offset, buffer.Count);
아주 간단하게 Send 하던 시점에서 출력하던 메시지를 옮겨 주면 끝이다.
OnConnected가 되는 부분은 Session에서 작업하지 않고, listner 제작 시에 진행했었기에 그 부분(아래 이미지 영역)을 건드려 줘야해서 조금 복잡할 수 있다.
이 부분을 보면 위에서 기존에 이와 유사한 방법 이벤트 에서 설명했듯이 이벤트 핸들러를 통해 동작하고 있다.
그 이벤트 핸들러의 동작 중 일부를 가져와 이 곳에서 동작시키는 방안을 사용한다.
다만 session을 만드는 부분이 나중에는 MMO Session 이런 식으로 다른 세션 또한 들어갈 수 있기에 이렇게 Game만을 위해 구성해놓는건 좋지 않다.
사용하는 곳에서 어떤 세션인지 정해주는 방식으로 변경해보자.
기존 Action으로 작업하던 부분을 Func로 변경하여 어떤 Session을 사용할 것인지 뱉어주는 방식으로 변경한다.
Action과 Func의 차이
- Action은 파라미터만 있는 대리자로 return 값이 없다.
- Func는 파라미터, 리턴 모두 있지만 꼭 리턴을 해주어야한다.
나중에야 매니저 같은 걸 따로 만들어서 생성할 세션을 관리 할 수 있지만, 지금은 간단하게 람다로 필요한 세션만 생성해 보내준다.
그 뒤에 Session을 new로 생성하는 것이아닌 Factory를 통해 받아온 값을 넣어주어 session을 생성 한다. Func 타입이 다른 Session들도 처리하기 위해 가장 상위인 Session으로 되어있기에 이곳에서도 session으로 받아와 처리한다.
마지막으로 OnAcceptEventHandler라는 곳에서 작동하던 안내 문자 처리도 OnConnected로 변경하여 처리 해주면된다.
Start만 멋지게 하고, 연결되기 전에 클라이언트에서 연결을 끊어 버릴 경우 args.AcceptSocket을 찾는 거 자체가 오류가 될 수 있어 수정을 해주어야한다.
서버는 작게 만들고 테스트 하면서 이런 저런 오류를 찾아 해결하는 과정이 필요하다.
학습은 최대한 빠르게 진행하고, 반복학습이 중요하다고 팁을 주셨다. 지금은 모르면 엄청 찾아보고 완벽히 이해한 뒤에 다음 강의를 듣곤 했는데 그 방법 보다는 속도가 더 필요한 때 인거 같다.
확실히 지금 이해를 했다 하더라도 금방 잊어버리기에 우선 반복을 통해 어느정도는 외우고 있는 것도 도움이 될 듯 싶다.
앞으로 정리는 더 간단하게 하고 강의 듣는 양을 늘려보고자 한다.