네트워크 프로그래밍 중에 어떻게 보면 가장 쉬우면서 어려운 개념이 왔습니다.
네트워크 프로그래밍에서 데이터를 보내거나 받을 때 어떤 함수를 사용했는지 기억나시나요?
네, send와 recv함수, 또는 read, write 함수를 사용하죠.

 

그리고 이들의 데이터를 전달하는 방식은 char / byte와 같은 1byte 데이터 배열을 사용합니다.
이제 이 char *을 데이터 흐름 (stream)이라고 정의합니다.
패킷을 이해하시기엔 아래와 같이 메모리 블록을 생각하시면 좋을 것 같군요.

 

이 방법은 심플하기는 하고 굉장히 고전적인 방법입니다. 코드로 표현하면 아래와 같군요.

 

초창기에 많이 사용했습니다만, 프로그램이 커지면서 다음과 같은 관리 문제가 발생하고 맙니다.


1. 콘텐츠 변경 등으로 패킷을 변경할 때, 일일이 사용하는 곳을 찾아 수정해야 합니다.
위의 예를 보시면 레벨 다음에 이름을 보내는데, 중간에 캐릭터 타입에 대한 정보를 넣어야 한다면? 어떻게 해야 할까요? 추가 정보는 무조건 뒤에 추가하면 점점 의미 없는 복잡한 단어가 난무하는 코드가 떠오르기 시작되는군요.


2. 패킷 데이터 변경, 당연히 서버만 변경하면 안됩니다. 변경내용을 클라이언트 파트에 알려 줘야 하고, 클라이언트의 부수적인 작업이 들어가야 합니다.


3. 예를 들어 패킷 경험치 항목이 현재 4byte(int32)로 쓰고 있는데, 유저들의 폭업으로 unsigned int32의 최대값 약 42억을 넘길 필요가 있다면 어떻게 해야 할까요?
unsigned long long 으로 int64_t 로 바꾸면 캐릭터 이름 앞부분이 침범당하겠네요.
이뿐만 아니라 프로그래머 실수로 int32_t 를 float으로 읽어 버렸다면? offset 변경을 수정하지 않으면 데이터가 밀리겠군요.


4. 이런 패킷 방식이면 정리가 잘 안됩니다. 작업 중 여러 패킷 데이터가 필요하고 그때그때 저런 식으로 패킷을 만들면 이전에 만들었던 패킷도 또 만들게 되는 경우가 발생합니다. (패킷이 보통 1천~3천개의 종류가 있고 초창기 개발진이 퇴사하면 이 패킷에 대해서는 어디 문서화 하기 전까지는 아무도 모르는 코드가 됩니다)

2번은 컨탠츠 추가 시 수반되어야 하는 일이니 그렇다 쳐도 3, 4번은 유지 보수 하는데, 막막해질 거 같네요. 연관성을 모두 조사해서 같이 바꿔줘야 하니까요.
위에 sprintf 예에서 3번째 변수 수정을 해야 한다고 생각해 보세요.
3번에 대응하기 위해서는 encode / decode를 알아서 해주는 클래스를 만들면 어떻게든 해결되지 않을까요? 이 모든 것을 깔끔하게 해결하기 위해 흔히 알고 계시는 패킷 제네레이터 프로그램을 만들어서 소스코드를 만드는 프로그램을 짜면 해결될 것 같네요.

 

그럼 이제 패킷이 있다고 하면, 패킷을 서버에서 어떻게 처리해야 하는지 생각해 봅시다.
이전 장의 네트워크 프로그램 장에서의 다이어그램은 패킷 데이터를 어떻게 처리할지 였고, 이것은 스트림 데이터를 어떻게 분석해서 어떻게 넣을지를 생각해 보았습니다.


간단히 설명하면, 온전한 스트림 데이터를 받고, 이를 분석해서, 맡은바 처리에 넘기면 간단히 해결될 것 같군요.
이제 아까 네트워크 장에 나온 클래스 다이어그램에 나온 것처럼 하나하나 클래스를 만들어 봅시다.

 

위 내용은 제가 집필한 "게임 서버 프로그래밍 입문" 책의 내용중 일부 부분에 대한 내용입니다.

전체 소스 코드와 책 구입에 대해서는 http://rosagigantea.tistory.com/589 에 링크 시켰습니다.

일반적인 네트워크 프로그래밍의 마지막 부분 IOPC 소스는 간단한 에코 서버를 구현한 예제들이 많은데요

대체로 이런 소스를 기능별로 잘 살펴보시면 크게 2가지로 구분되어 있습니다.

 

 

우선 윈도에서 통신을 구현하는 API가 있겠고, 각각의 클라이언트 세션 정보를 가지고 있는 객체들이 있습니다.

이 세션에 대해서 직접 제어해야 다른 인위적인 행위 (특정 세션에 데이터 송수신 및 접속 종료 같은)를 할 수 있겠군요.

위의 소스는 정말 간단히 핵심만 구현시켜놓은 형태로 볼 수 있으며, 이를 자동차로 바꿔 보면, 차체 프레임에 바퀴 끼고 엔진만 올린 상태로 볼 수 있습니다. 차야 굴러가겠지만, 실제 공도에 나왔다간 큰일 날 겁니다.
어쨌든 위의 마인드맵과 같이 각 분야를 객체로 표현해 봅시다, 그러면 어느 정도 코드 정리도 되고, 코드가 정리돼야 추가 기능 확장도 쉽겠죠.


그럼 각 분야별로 어떻게 만들어 보면 좋을지 생각해 봅시다.

1. 네트워크 API: 전체 네트워크를 통제하는 역할이 주로 이루어집니다.
2. 세션 정보: 세션은 2 단말기 간의 통신이 연결됨을 나타냅니다.
                   클라이언트에게서 오는 데이터를 받고 전송해야 하는 역할을 해야 합니다.
3. 2번에서 데이터를 받아서 그대로 돌려주면. 에코 서버겠죠?
   여기에 데이터를 변형하는 로직을 분리해서 서버 기능 확장을 하도록 합시다.

이렇게 하면 대략 아래와 같이 생각할 수 있을 것 같군요.

 

 

조금은 눈에 들어오시나요?
이제 마인드맵을 그려서 대략 그림이 나왔으니, 각각 설계해봅시다.
설계는 맨 처음 언급했듯이 간단한 Activity랑 Class 다이어그램을 그려 보시기 바랍니다. 그리고 이후 제가 한 것과 비교해 보는 것이 좋을 것 같네요. 



  

서버는 아래와 같이 처리가 이루어지면 좋을 것 같군요.
Active 다이어그램과 같이 에코 코드의 기본 옵션을 역할별로 나눠 기술했습니다.
여기서 ContetnsProcess, Package 같은 것은 이다음에 기술되어 있으니 같이 보시기 바랍니다.
다음은 Session 역할입니다.


  
어떻게 보면 꽤 당연한 것을 Active로 나타내니 왜 이것을 할 까란 생각이 드실지도 모르겠습니다만, Activity를 그려야 모듈에서 어떤 일이 필요한지 정리되고 이를 바탕으로 Class 다이어그램을 그려야 좀 더 정교하게 그릴 수 있습니다. 물론 구현하다 보니 생각지도 못한 기능을 구현할 때도 있습니다만, 이는 그때그때 다시 그리면 좋겠죠. 한입에 다 먹으려고 하면 체합니다.
지금 설계의 기능을 보시면 소켓 데이터를 가지고 있고, 각각을 구별한 id 값, 접근, 전송, 수신, 끊기 기능을 넣고 있습니다.


다음은 데이터를 어떻게 처리할 것인가에 대해서 생각해 보겠습니다.
데이터는 세션으로부터 와서 이걸 패키징하는 처리와 이 패키징된 데이터를 가지고 실제 프로그램 처리 하는 부분으로 나눠 생각해 봅시다.


패킷 흐름은 recv를 한 이후, 그 byte 데이터(stream이라고 표현합니다)를 패키지로 만들어 낸 뒤, 이것을 패킷 처리 전담 처리에 넣는 과정을 담았습니다.
패키지(Package)는 말 그대로 소포의 의미입니다. 왜 소포 개념을 넣었느냐면, 소포처럼 받은 사람, 보낸 사람의 정보(세션 데이터)와 내용 물(데이터)가 있어야 반대로 나에게 보낸 클라이언트에 결과값을 줄 수 있기 때문입니다.


객체 지향은 말 그대로 소스를 현실 사물과 투과시킴으로써 사람이 좀 더 이해하기 편함에 목적을 두었다고 생각합니다. 그러므로 이런 용어를 사용하였습니다 패키지화하지 않고 내용을 분석해 처리해도 되지만, 네트워크 IO 처리에 부하가 가는 것을 막기 위해 일반적으로는 따로 처리 프로세스를 나눠서 처리합니다.
(보통 소포 물은 집에 와서 확인하지 직접 우체국 가서 찾은 뒤, 내용물 확인하지는 않죠?)


패킷 프로세스 처리는 간단합니다.
초기환경 설정이라 했지만, 내부적으로는 서버마다 오는 패킷을 처리하는 함수들을 등록하는 과정이며, 이를 큐에서 적절히 꺼내 내부 함수에 넘겨주는 역할을 담당합니다.

 


위의 Active 다이어그램을 바탕으로 클래스 도식화를 해보았습니다.

ContentsProcess Packet을 받아서 처리하는 곳입니다.
밑에 ServerContetsProcess가 있는데, 서버마다 패킷을 받으면 처리할 내용을 담을 것입니다.
Packet도 마찬가지입니다. 이 앞 패킷 처리에서 더 자세히 다룰 예정입니다만, Packet을 상속받아 각각의 ContentsPacket들을 만들어 보낼 것입니다.
Stream이라는 것이 보이는데 이는 데이터를 시리얼라이징(직렬 화) 하는 데 도움을 주는 클래스이며, 역시 이다음 장에 자세히 기술하겠습니다.


아직 저 클래스만으로는 어떻게 돌아가는지 잘 파악이 안되실 거 같지만, 위의 클래스를 바탕으로 서버, 세션, 패킷 클래스를 만들어 보도록 하겠습니다.
그리고 네트워크 라이브러리이므로 다시 라이브러리 프로젝트에서 작업하도록 합시다.

 

위 내용은 제가 집필한 "게임 서버 프로그래밍 입문" 책의 내용중 일부 부분에 대한 내용입니다.

전체 소스 코드와 책 구입에 대해서는 http://rosagigantea.tistory.com/589 에 링크 시켰습니다.

+ Recent posts