26
EasyGameServer Source 협찬 by sm9

Easy gameserver

  • Upload
    -

  • View
    270

  • Download
    3

Embed Size (px)

Citation preview

Page 1: Easy gameserver

EasyGameServerSource 협찬 by sm9

Page 2: Easy gameserver

무엇을 알아볼까?

• 소스 출처 : https://github.com/zeliard/EasyGameServer

• 채팅을 뿌려주는 서버가 어떻게 작동하는지 원리를 알아본다

• 중요한 함수들 위주로 알아보자

Page 3: Easy gameserver

1. EasyServer.cpp (_tmain 1/4 )

전역변수 선언

g_AcceptedSocket

Exception이 발생하면

ExceptionFilter 작동

ClientManager

DatabaseMager 초기화

WSDATA 초기화

ListenSocket 생성

Page 4: Easy gameserver

1. EasyServer.cpp (_tmain 2/4 )

옵션 결정

ListenSocket을

Bind

ListenSocket을

Listen

EventHandle 생성

Page 5: Easy gameserver

1. EasyServer.cpp (_tmain 3/4 ) 스레드를 생성

ClientHandlingThread

함수를 실행한다.

SUSPENDED 상태다

DB Thread 생성

SUSPENDED 상태

전역 변수로 선언한

소켓에 accept 시도

Blocking 이므로 대기

Queue에 connectio이

있어야 넘어간다.

Accept 되면 Thread

Security 인자에 signal

을 줘서 실행시킨다

Page 6: Easy gameserver

1. EasyServer.cpp (_tmain 4/4 )

서버가 종료되기 전에 할당된 자원을 해제한다

윈속은 WSACleanup()을 쓴다.

Page 7: Easy gameserver

1. EasyServer.cpp (ClientHandlingThread 1/2 )

WaitableTimer를 생성한다.

이 Thread는 10 밀리초마다 TimeProc을

수행하는 타이머를 설정하게 된다.

두 번째 인자는 최초 대기 시간이다.

100 nanosecond 단위이다.

세 번째 인자는 Timer 주기를 나타낸다.

millisecond 단위이다.

Page 8: Easy gameserver

1. EasyServer.cpp(ClientHandlingThread 2/2 )

WaitForSingleObjectEx 함수의 마지막 인자가 true

이므로 APC 큐에 뭔가 추가되면(연결이 되면) 호출

그렇지 않으면 대기 시간이 무한대이다

WaifForSingleObjectEx의 반환값이 WAIT_OBJECT_0

라면 signaled 상태라는 뜻이다.

이때 ClientManager는 새로운 ClientSession을 만든다.

g_AcceptedSocket은 지금 연결된 client 소켓 정보를

담고 있으므로 session 별로 고유한 소켓을 줄 수 있다.

ClientSession이 생성되었다면 연결 처리(OnConnect())

Page 9: Easy gameserver

1. EasyServer.cpp (TimeProc)

이 함수는 ClientManager에게 주기적으로 할 일을

시킨다.

TimeProc을 호출하는 타이머가 10 밀리초 주기를

가지기 때문에 OnPeriodWork() 역시 10 밀리초

주기로 호출된다.

Page 10: Easy gameserver

1. EasyServer.cpp (DatabaseHandlingThread)

DatabaseManager에게 DB 작업을 수행시킨다.

DB 부분은 자세히 분석하지 않을 예정이다.

EasyGameServer에서는 DB 입출력이 그렇게 많지 않다.

개발하면서 필요하면 추가로 DB 부분을 알아보도록 하자.

Page 11: Easy gameserver

2. ClientManager (CreateClient)

Singleton

새로운 연결이 있으면 CreateClient

함수를 부르고 소켓 정보를 할당

ClientSession이 생성되고 나면

List 형태로 담고 있는 mClientList에

저장한다.

이후부터

Main함수는 ClientManager를 통해

ClientSession에 일을 시킨다.

Page 12: Easy gameserver

2. ClientManager (BroadcastPacket)

ClientSession 전체를 돌면서

패킷을 발송한다.

채팅 서버에서는 이 함수에서

각각의 클라이언트에게 대화를

중계한다.

Page 13: Easy gameserver

2. ClientManager (BroadcastPacket)

GetTickCount()로 시스템 시간을 받아서

이전에 확인한 값과 1초 이상 차이나면

1초마다 할 일들을 수행한다.

CollectGarbageSession()에서

연결이 끊어진 세션을 정리하고,

ClientPeriodWork()에서

각각의 Client 마다 일을 시킨다.

Page 14: Easy gameserver

2. ClientManager(CollectGarbageSessions)

ClientSession을 전부 돌면서

응답이 없고 비동기 I/O중이 아닌

세션들을 vector에 모아놓는다.

이렇게 모아놓은 ClientSession은

고유한 socket정보로 구분되므로

연결을 끊고 List에서 삭제한다.

Page 15: Easy gameserver

2. ClientManager (ClientPeriodWork)

심플하게 ClientSession 리스트를 돌면서

전부 OnTick()을 실행시키는 함수

주기적으로 할 일을 OnTick()에 넣어두면

ClientPeriodWork()때 전부 실행된다

Page 16: Easy gameserver

2. ClientManager (FlushClientSend)

ClientSession List를 순회하면서 SendFlush()

함수를 실행시키는데, 버퍼에 쌓인 데이터를

전송하는 함수다.

여기서 실패하면 연결이 끊어진 것이다.

Page 17: Easy gameserver

3. ClientSession (OnConnect)

소켓을 넌블러킹으로 바꾸지 않으면 서버 전체가 블로킹 될 수 있다.

Nagle 알고리즘을 끄면 서버 부하는 늘어나지만 실시간성은 높아진다. 채팅 서버이므로 끈다.

바로 PostRecv() 함수로 이어진다.

Page 18: Easy gameserver

3. ClientSession (PostRecv)

mRecvBuffer는 Circular Buffer.

소스를 열어보면 버퍼를 두 개

부분으로 나눠 적절히 교체한다

잘못 짜면 바로 터진다(…)

WSARecv 함수는 연결된 소켓

에서 데이터를 받아온다.

비동기 입출력이 가능하다.

WSABUF에 값이 저장되며, 여러

개의 버퍼를 지정할 수도 있다.

RecvCompletion Callback함수가

등록되어 있다.

Page 19: Easy gameserver

3. ClientSession (RecvCompletion)

데이터 읽기가 끝나면

OnRead() 함수로 정리하고

다시 PostRecv()로 돌아간다

Page 20: Easy gameserver

3. ClientSession (OnRead 1/2)

RecvBuffer에는 Client에서

받은 데이터가 들어있다.

이 데이터는 패킷이므로 약속된

패킷 헤더 정보를 통해 전송이

제대로 됐는지 확인할 수 있다.

제대로 패킷을 받았다면 계속

진행한다.

Page 21: Easy gameserver

3. ClientSession (OnRead 2/2)

패킷 type에 따라 다른 처리를 해 준다

로그인 패킷이면 DB에 값을 쓴다.

채팅 패킷이면 BroadCast 준비를 한다.

패킷을 보낸 플레이어의 id, 이름, 채팅

내용을 패킷에 담고 방송한다.

지금은 두 종류 패킷 밖에 없으므로 이

외에는 에러로 간주한다.

Page 22: Easy gameserver

3. ClientSession (SendRequest)

Broadcast를 실행하면 ClientSession들은

SendRequest 함수를 실행한다.

SendRequest 자체는 서버의 버퍼에만

패킷 정보를 쓰는 과정이다.

ClientManager에는 이렇게 ClientSession

버퍼에 저장된 패킷을 전송하라는 Flush

ClientSend 함수가 있다. 이 함수는

Manager에서 주기적으로 불려오므로

쌓인 패킷들도 주기적으로 전송된다.

Page 23: Easy gameserver

3. ClientSession (SendFlush)

PostRecv와 거의 같은 구조다.

다만 WSASend 함수를 쓰므로

소켓으로 데이터를 보낸다는 점

입출력이 끝나고 CallBack 함수로

SendCompletion을 호출하는 점이

다르다

Page 24: Easy gameserver

3. ClientSession (SendFlush)

비동기 I/O 카운터를 하나 줄이고 OnWriteComplete 함수를 부르는 것이 이 함수의 본업이다

Page 25: Easy gameserver

3. ClientSession (OnWriteComplete)

전송 완료한 데이터를 버퍼에서 덜어내는 일을 한다.

Page 26: Easy gameserver

끝!

• 나머지 함수들은?• 채팅 이외의 기능을 구현하려면 본격적으로 중요해진다

• 특히 DB 연동하는 부분은 캐릭터 좌표 등을 저장하고 있어서 일부러 언급을 피했지만 채팅에 게임까지 들어가면 이 부분도 봐야 함

• 그 외• Windows 시스템 프로그래밍 좀 더 봐야겠다

• MSDN도