게임 서버 프로그래밍 입문 (7) - 패킷 처리에 대해서
네트워크 프로그래밍 중에 어떻게 보면 가장 쉬우면서 어려운 개념이 왔습니다.
네트워크 프로그래밍에서 데이터를 보내거나 받을 때 어떤 함수를 사용했는지 기억나시나요?
네, 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 에 링크 시켰습니다.