게임 구조 게임 구조 설계(18)

4845 단어
원문 주소:https://blog.csdn.net/erlib/article/details/24303123
서버 공통 구성 요소 구현 - 루프 버퍼
메시지 대기열 자물쇠 호출이 너무 잦은 문제는 해결된 셈이다. 또 다른 고민은 아마도 이 너무 많은 메모리 분배와 방출 작업일 것이다.빈번한 메모리 분배는 시스템 비용을 증가시켰을 뿐만 아니라 메모리 조각도 끊임없이 증가하여 우리 서버의 장기적인 안정적인 운행에 매우 불리하다.아마도 우리는 SGI STL에 첨부된 작은 메모리 분배기와 같은 메모리 탱크를 사용할 수 있을 것이다.그러나 이런 엄격한 선진적인 선출 순서에 따라 처리하는 데 블록 크기가 작지 않고 블록 크기도 통일되지 않은 메모리 분배 상황에 대해 말하자면 고리형 버퍼라고 하는 방안을 더 많이 사용한다. mangos의 네트워크 코드에도 이런 것이 있는데 그 원리도 비교적 간단하다.
마치 두 사람이 둥근 탁자를 둘러싸고 쫓고 있는 것과 같다. 달리는 사람은 인터넷 IO 라인에 의해 제어되고 데이터를 쓸 때 이 사람은 앞으로 뛴다.쫓는 사람은 논리 라인이고, 쫓는 사람을 쫓을 때까지 계속 쫓는다.따라잡으면 어떡하지?그것은 읽을 데이터가 없으니 우선 좀 기다려라. 뛰는 사람이 앞으로 몇 걸음 뛰고 쫓아가면 게임을 할 수 없겠지.그럼 쫓는 사람이 너무 느리게 뛰면 뛰는 사람이 한 바퀴 돌다가 쫓는 사람을 따라잡을 수 있을까요?그럼 잠깐 쉬세요.만약 계속 이렇게 거꾸로 쫓아다닌다면, 당신은 더 빨리 달리는 추격자를 바꿀 수밖에 없을 것입니다. 그렇지 않으면 이 게임은 정말 계속할 수 없을 것입니다.
앞에서 우리는 특히 엄격한 선진적인 선출 순서에 따라 처리하는 것을 강조했는데 이것은 고리형 버퍼의 사용이 반드시 준수해야 하는 요구이다.즉, 모두들 규정을 준수해야 한다. 쫓는 사람은 책상 위에서 건너갈 수 없고, 뛰는 사람도 당연히 반대로 뛰는 것을 허락하지 않는다.왜 그런지 더 이상 설명할 필요가 없겠지.
고리형 버퍼는 빈번하게 메모리를 분배하지 않고 대부분의 상황에서 메모리의 반복적인 사용도 우리로 하여금 더욱 적은 메모리 블록으로 더 많은 일을 할 수 있게 하는 좋은 기술이다.
네트워크 IO 라인에서, 우리는 모든 연결을 위해 고리형 버퍼를 준비하여 수신된 데이터를 임시로 저장하여 반봉지와 접착 상황에 대처할 것이다.패키지 해제 및 복호화가 끝난 후에 우리는 이 데이터 패키지를 논리 루트 메시지 대기열에 복사할 것입니다. 만약에 우리가 하나의 대기열만 사용한다면 여기도 고리형 버퍼가 될 것입니다. IO 루트는 안으로 쓰고 논리 루트는 뒤에서 읽고 서로 쫓습니다.그러나 만약에 우리가 앞에서 소개한 최적화 방안을 사용한 후에 이곳은 고리형 버퍼가 더 이상 필요하지 않을 것이다. 적어도 우리는 그들이 고리형이라는 것을 더 이상 필요로 하지 않을 것이다.왜냐하면 우리는 같은 대기열에 대해 더 이상 동시에 읽고 쓰는 상황이 나타나지 않기 때문이다. 모든 대기열은 다 쓴 후에 논리 라인에 맡기고, 논리 라인을 다 읽은 후에 대기열을 비운 후에 IO 라인에 맡기고, 고정된 크기의 버퍼를 쓰면 된다.괜찮아, 이렇게 좋은 기술은 다른 곳에서도 꼭 쓸 거야.
서버 공통 구성 요소 구현 – 하청 방식
앞에서 줄곧 데이터를 수신할 때의 처리 방법을 말했는데 우리는 전문적인 IO 라인을 이용하여 완전한 메시지 패키지를 수신한 후에 메인 라인의 메시지 대기열에 넣어야 하지만 메인 라인이 어떻게 데이터를 보내는지 아직 연구한 적이 없다.
일반적으로 가장 직접적인 방법은 논리 루틴이 언제 데이터를 보내고 싶으면 관련 socket API를 호출하여 보내는 것이다. 이것은 서버의 유저 대상에 연결된 socket 핸들을 저장하도록 요구한다.그러나 직접send 호출은 때때로 문제가 발생할 수 있다. 예를 들어 시스템의 전송 버퍼가 꽉 차서 막히는 경우나 일부 데이터만 보내는 경우도 있다.우리는 발송할 데이터를 먼저 캐시할 수 있습니다. 이렇게 하면 발송되지 않은 것을 만났을 때, 논리 라인의 다음 처리 때 이어서 발송할 수 있습니다.
데이터 캐시를 고려하면 여기에는 두 가지 실현 방식이 있다. 하나는 모든 유저를 위해 버퍼를 준비하는 것이다. 다른 하나는 전역적인 버퍼만 있고 보낼 데이터가 전역 버퍼에 들어갈 때 이 데이터가 어느 socket에 보내졌는지 가리키는 것이다.만약에 전역 버퍼를 사용한다면 우리는 한 걸음 더 나아가 하나의 독립된 라인을 사용하여 데이터 송신을 처리할 수 있다. 이것은 논리 라인이 데이터에 대한 처리 방식과 유사하다. 이 독립 송신 라인도 하나의 메시지 대기열을 유지할 수 있다. 논리 라인이 데이터를 송신할 때도 이 대기열에 데이터를 넣고 전송 라인 순환 패키지를 보내서send 호출을 실행한다.이때의 막힘도 논리 라인에 아무런 영향을 주지 않을 것이다.
두 번째 방식을 채택하면 최적화 방안도 첨부할 수 있다.일반적으로 방송 메시지에 대해 말하자면 주위 유저에게 보내는 데이터는 모두 완전히 같다. 만약에 우리가 모든 유저에게 버퍼 대기열을 사용하는 방식을 사용한다면 이 패키지는 여러 부를 복사해야 하고 전체 전송 대기열을 사용할 때 우리는 이 정보를 한 번만 입력하고 이 패키지가 어떤 socket에 보내야 하는지 가리키면 된다.이 최적화에 대한 설명은 구름바람이 서버 연결을 설명하는 블로그 글에서도 흥미로운 것은 읽어볼 수 있다.
서버 공통 구성 요소 구현-상태기
State 모델의 디자인 의도와 실현은 디자인 모델에서 발췌하지 않는다. 우리는 게임 서버 프로그래밍에서 State 디자인 모델을 어떻게 사용하는지 볼 뿐이다.
우선 mangos의 코드부터 보면 로그인 서버가 클라이언트가 보낸 메시지를 처리할 때 이런 구조체를 사용했다는 것을 알 수 있다.
  struct AuthHandler
  {
    eAuthCmd cmd;
    uint32 status;
    bool (AuthSocket::*handler)(void);
  };

이 구조체는 모든 메시지 코드의 처리 함수와 필요한 상태 표지를 정의하고 현재 상태가 요구를 충족시킬 때만 지정한 처리 함수를 호출합니다. 그렇지 않으면 이 메시지 코드의 출현은 비합법적입니다.이 status 상태 표지의 정의는 하나의 매크로로 두 가지 유효한 표지가 있습니다. STATUS_CONNECTED 및 STATUS_AUTHED, 즉 인증되지 않은 통과와 인증된 통과입니다.이 상태 표지의 변화는 운행할 때 이루어진다. 정확히 말하면 어떤 메시지를 받고 정확하게 처리한 후에 바뀌었다.
디자인 모델에서 State 모델에 대한 설명을 살펴보자. 그 중에서 State 모델의 적용 상황에 대한 설명은 작업에 방대한 다분지의 조건 문장이 있고 이런 분지는 이 대상의 상태에 의존한다. 이 상태는 보통 하나 이상의 매거변수로 나타난다.
묘사된 상황은 우리가 여기에서 처리해야 할 상황과 이렇게 비슷하다. 아마도 우리는 한번 해 볼 수 있을 것이다.그러면 State 모드가 제공하는 해결 방안이 어떤지 다시 한 번 봅시다. State 모드는 모든 조건을 하나의 독립된 클래스에 나누어 넣습니다.
이곳의 두 상태 표지는 두 가지 상태만 구분하기 때문에 우리는 두 개의 독립된 종류만 필요로 하고 두 가지 상태를 표시하면 된다.그리고 State 모드의 설명에 따라 우리는context 클래스, 즉 상태기 관리 클래스를 필요로 하여 현재의 상태 클래스를 관리해야 한다.대략적인 코드는 다음과 같습니다.
   :
  StateBase
  {
    void Enter() = 0;
    void Leave() = 0;
    void Process(Message* msg) = 0;
  };
   :
  MachineBase
  {
    void ChangeState(StateBase* state) = 0;

    StateBase* m_curState;
  };

우리의 논리 처리 클래스는 MachineBase에서 파생됩니다. 데이터 패키지를 꺼낸 후에 현재 상태 처리에 맡기고 앞에서 설명한 두 상태 클래스는StateBase에서 파생됩니다. 각 상태 클래스는 이 상태 표지에서 처리해야 할 메시지만 처리합니다.상태 변환을 할 때 MachineBase의 ChangeState () 방법을 호출하여 상태기 관리 클래스가 어떤 상태로 전환해야 하는지 표시합니다.따라서 상태 클래스 내부에 상태 관리자 클래스의 지침을 저장해야 합니다. 이것은 상태 클래스가 초기화될 때 전송될 수 있습니다.구체적인 실현 세부 사항은 지나치게 묘사하지 않겠다.
상태기 사용은 복잡한 판단 문장을 피했지만 새로운 번거로움을 가져왔다.우리가 상태 전환을 진행할 때 일부 현장 데이터를 낡은 상태 대상에서 새로운 상태 대상으로 옮겨야 할 수도 있다. 이것은 인터페이스를 정의할 때 고려해야 한다.만약 복사를 실행하기를 원하지 않는다면, 여기에 공유된 현장 데이터도 상태 클래스에 넣을 수 있으며, 단지 이렇게 사용할 때 그다지 우아하지 않을 수도 있다.
같은 디자인 모델에서 묘사한 바와 같이 모든 모델은 이미 문제가 있는 또 다른 해결 방안이다. 즉, 이것은 유일한 해결 방안이 아니다.우리가 오늘 토론한 State 모드에서 로그인복이 처리하는 두 가지 상태로 말하자면 mangos가 사용하는 역행 처리 함수를 사용하는 방법이 더욱 간단할 수 있지만 시스템의 상태 수량이 증가하고 상태 표지도 많아질 때 State 모드는 특히 중요하다.
예를 들어 게임 서버에서 유저의 상태 관리, 그리고 NPC 인공지능을 실현할 때의 각종 상태 관리 등은 앞으로의 주제로 남겨 두자.

좋은 웹페이지 즐겨찾기