강의

멘토링

로드맵

Inflearn brand logo image

인프런 커뮤니티 질문&답변

랄프로님의 프로필 이미지
랄프로

작성한 질문수

[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버

Send관련 질문 드려요

해결된 질문

작성

·

285

0

안녕하세요!

강의 너무 잘 보고 있습니다!  보다가 궁금한 점이 생겨서 질문 드려요!

1. SetBuffer가 아닌 BufferList를 이용하여 Send를 하시던데, 두가지 사이의 성능적으로 차이가 크게 있을까요?

2. SendBuffer를 Session이 가진것이 아니라 외부에 만들어서 이 Buffer로 브로드케스팅하시던데, 이 경우 Buffer를 재활용 하고 싶으면 모든 Session들이 Send를 다 끝마친 후(SendCompleted)가 되겠죠?  혹시 그 전에 버퍼를 건들면 영향이 없을까요?

3.  SendBuffer를 재활용하는 방법에 대해 잠깐 언급하시고 그럴꺼면 C++로 하는게 낫다고 말씀주셨는데,  C#의 경우 외부에서 SendBuffer를 만들때 Buffer를 재활용할 수 있는 방법이 있을지.. 생각하신 부분이 있으시면 가르침 좀 부탁드릴게요. 제가 생각했을때는 Bytes 풀을 만들고 쪼개서 할당해주고 싶은데, Buffer를 풀에 반환하는 타이밍이나 방법이 떠오르지 않아 질문 드립니당.

답변 기다리겠습니다!

답변 1

1

Rookiss님의 프로필 이미지
Rookiss
지식공유자

안녕하세요,

1)
게임 서버에서 고질적으로 부하를 먹는 부분은 여럿 있지만
특히나 네트워크 IO 부분의 부하를 무시 못합니다.
Send를 보내면 해당 부분 처리를 위한 Context-Switching이 일어나고
커널 모드로 전환해야 하는데, 그렇다는 것은 어떻게든 Send 보내는 횟수를 줄이는 편이 유리하겠죠.
BufferList로 처리해주면 이 부분을 개선할 수 있고
실제 List로 연결해준 버퍼를 순회하면서 커널 쪽 버퍼에 복사를 하게 됩니다.
참고로 C++ IOCP 서버에도 WSABuf 하나를 보내는 것이 아니라
여럿을 리스트로 꼽아줄 수 있는데 비슷하다고 생각하시면 됩니다.

2)
이해하신 것이 맞습니다.
Session별로 SendBuffer를 두는 경우 방법도 있는데,
직관적이고 이해하기도 쉽지만 실제 패킷 Broadcasting이 일어나면
각 Session의 SendBuffer에다가 내용물을 일일히 복사를 해줘야 하는
어마무시한 복사 비용이 일어나게 됩니다.
유저들이 모이면 모일 수록 불필요한 복사 비용이 일어나게 되겠죠.
따라서 Broadcasting이 빈번한 MMO에서는
외부에서 SendBuffer를 관리하고, 필요한 Session마다 꼽아주는 형태가 훨씬 더 효율적이라고 볼 수 있습니다.
이 경우 Session들이 Send를 마쳐야 정상적으로 회수가 가능합니다.
Send를 비동기로 보냈으니, 어느 시점에 완료되었는지 예상할 수가 없고
실질적으로 클라 쪽에서 데이터를 받을 수 있을 때
유저 레벨 버퍼 (우리의 SendBuffer)에서 커널 레벨 버퍼로 내용물을 복사한 후,
야금 야금 전송을 하게 됩니다
그런데 그 전에 버퍼를 건드리면 당연히 오염된 정보가 전송될테니 절대 하면 안 됩니다.
이 부분은 C++/C# 상관없이 동일합니다.

3)
C++ IOCP 서버에서 보통 SendBuffer를 풀링해서,
Session마다 Send를 보내는 시점에 SendBuffer RefCount를 늘리고(atomic),
Send가 완료되면 SendBuffer RefCount를 줄이면서(atomic)
RefCount가 0이 되는 시점에 SendBuffer가 풀에 돌아가게 구현하면 간단합니다.
C#의 경우 의외로 이 부분 처리가 훨씬 까다로운데
RefCount 처리가 알아서 되고 있고 0이 되면 언~~젠가 어느 시점에 GC에 의해 메모리가 회수 되기 때문입니다.
2)의 내용 때문에 풀링을 하고 싶다면 우리 마음대로 할 수는 없고
반드시 RefCount 관리를 해서 모든 Session의 Send가 완료 되기를 기다리는 수 밖에 없습니다.
SendBuffer 래퍼 클래스를 적당히 수정하고 GC.SuppressFinalize와 Finalizer(소멸자)를 조합해서
GC에 의해 회수되는 시점을 가로채서 풀에다가 다시 넣어주는 방식을 일단 고려할 수 있을 것 같은데
직접 해보고 테스트를 해보진 않아서 딱히 추천을 드릴 수는 없을 것 같네요.

감사합니다.

랄프로님의 프로필 이미지
랄프로

작성한 질문수

질문하기