ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • IOCP게임서버 동기화 패킷처리
    프로그래밍/네트워크 프로그래밍 2016. 10. 12. 06:02
    반응형



    윈도우로 c++게임서버를 만들고 api정도에서 움직임 처리를 패킷으로 처리하는 포트폴리오입니다.

    클라이언트는 2개만 띄워서 테스트를 하였고 많은 패킷을 받아서 빠르게 처리를 하는 서버를

    구현하는데 목적을 두었습니다.

    IOCP이용해서 게임서버를 만들었습니다.


    클라이언트 C++

    서버 C++

    Winsock

    VisualStudio2015 ,IOCP,c/c++



    NetLIS.zip








    NetLIS ServerManual

     

    0.       NetLIS 서버 프로그램 소개.

    서버프로그램 NetLIS IOCP를 사용한 멀티 쓰레드 환경의 서버 프로그램입니다.

    코드생성을 하실 때 다중쓰레드(/Mtd)를 사용하여 주십시오.

    IOCP를 사용하였으므로 XP이하의 운영체제에서는구동이 불가능 합니다.

    함수호출에 따라서 서버기능을 수행하기도하고 클라이언트 기능을 수행하기도 합니다.

    매우 단순한 구조로 되어있으며확장성을 고려하여 최소한의 기능만을 집어넣었습니다.

     

    -라이센스-

    이 프로그램의 저작권은 팀 크리에이터서버팀에게 있으며 문의는 carrylose@naver.com으로 해주시기 바랍니다.(그렇지만 향후 2년간 제가 군대를 가므로 불가능합니다만;;)

    마음대로 수정/삭제 하셔도 무방합니다만코드의 맨 위쪽의 주석은 삭제하시면 안됩니다.

    상업적 사용을 금합니다.

     

    1.       사용 준비

    l 이름공간

    Using namespace NetLIS;

    NetLIS라는 이름의 이름공간을 사용합니다.

    모두 사용하시려면 위의 코드를 넣어주십시오.

    l 객체의 선언

    INetLIS* g_pNetLIS = NULL;

    INetLIS라는 객체의 포인터를 선언합니다.

    이 포인터를 사용하여 서버의 모든 조작(서버 시작서버종료패킷 전송 등)

     

    2.       객체 생성과 파괴

    *생성

    g_pNetLIS = createNetLISDevice();

           createNetLISDevice()함수를 호출하여 객체를 생성과 초기화를 합니다객체의 포인터는 위의 INetLIS를 사용합니다.

     

    *파괴

    g_pNetLIS->drop();

    drop() 함수를 호출하여 객체를 파괴합니다.

    이것으로 모든 쓰레드 종료메모리 삭제등을 모두 수행합니다프로그램 종료시 반드시 호출하여 주십시오.

     

    3.       본격적인 사용

           이 프로그램은 함수 호출에 따라서 서버프로그램이 되기도 하고 클라이언트 프로그램이 되기도 합니다.

           객체의 생성과 초기화를 완료한 후 다음 함수를 호출하여 주십시오.

       

    g_pNetLIS->CreateServer();

    서버의 생성에 성공하면 TRUE, 실패시 FALSE를리턴합니다.

    이 함수를 호출하면 해당 어플리케이션은 서버로 동작합니다호출즉시 IOCP객체의 생성접속 수락 쓰레드를 깨워서 기동시킵니다.

          서버/클라이언트를구분하는 플래그 (이름공간 NetLIS 안의 전역 변수 g_bIsMaker) TRUE가 됩니다.

     

           g_pNetLIS->ConnectServer(127.0.0.1, 9200);

    서버에 접속하는 함수 입니다.

    접속에 성공하면 TRUE, 실패시 FALSE를리턴합니다.

    첫번째 인수는 서버의 IP, 두 번째 인수는 포트번호(9200번으로고정)를 넣어주시면 됩니다.

     

    4. 패킷의 송신수신

    모든 준비가 끝났으므로 패킷을 송/수신하는 과정에 대해서 알아보겠습니다.

    NetLIS프로그램은 각 클라이언트들을 Index를 사용하여 구분합니다.

    인덱스는 서버에 접속하는 즉시 부여받습니다.

    0번은무조건 마스터(서버)이며,접속 순서대로 1 2 3 이 됩니다.

    서버와 접속이 해제되면 새로운 접속자는 해당 인덱스를 다시 사용할수있습니다.

     

    -     패킷의 생성

    패킷을 전송하려면 패킷을 일단 만들어야 합니다.

    어떤 데이터를 보내고 받을 건인지를 생각 한 후에해당 데이터에 알맞은 이름으로

    Structlist.h 파일에 구조체(클래스도 상관없습니다)를 만들어줍니다.

     

    )

    Struct sItemPacket

    {

           SHEADERsHeader;

           Int            nItemIndex;

           Int            nUserIndex;

    };

     

    첫줄의 SHEADER sHeader;는 패킷의 헤더 입니다.

    반드시 제일 첫줄에 써주셔야 합니다.

    혹은 상속을 받으셔야 합니다.

    이 사항을 지키지 않을 경우 정상적으로 작동되지 않습니다.

    이 아랫줄에 보내고 받을 원하는 데이터를 선언해주십시오.

     

    NetLIS 프로그램은패킷을 WorkerThread에서 수신 합니다.

    따라서 수신된 패킷의 처리를 WorkerThread에서 바로 해주시거나,

    혹은 main쓰레드와 공통적으로 사용하는 전역 변수를 만들어 처리하는 방법이 있습니다.

     

    두 번째 방법을 사용하시려면 Structlist.hsItemPacket(혹은 독자가 만든 패킷)을 전역으로 선언해 주십시오.

     

    다음으로 패킷을 선언하였으니 이제 남은건 송신과 수신입니다.

     

    먼저 송신하는 방법은 다음과 같습니다.

    1.     iNetLIS.h / NetWork.h 에 적절한 이름으로 함수를 만듭니다.

    현재 예제의 경우 SendItemPacket() 정도가 적당하겠습니다.

    사용하는 인자는 위에서 선언한 패킷 구조체 형을 사용하거나

    전송에 필요한 정보를 사용하시면 되겠습니다.

    Ex)

    Void SendItemPacket ( sItemPacket& sData);

     

    2.     함수의 구현

    전송시 서버와 클라이언트의 경우 전송 대상이 다릅니다.

    간단하게 if문을 사용합니다.

        if(g_bIsMaker)// 서버로 동작하는 경우

        {

    for(int i=1; i<10; i++) // 전송대상 : 1~10까지의 인덱스를가진 소켓(클라)

    {

    if(m_pClientInfo[i].m_bJoin == TRUE)// 전송조건 : 해당 인덱스가 접속및 연결되어있는 경우

    {

    g_pPacketProcess->MakePacket(&m_pClientInfo[i],(char*)&data, sizeof(SUNIT_INFO), 0, GAME); // g_pPacketProcess객체를 사용하여 패킷을 생성한다

    //첫번째 인수는 소켓두번째는 보낼 데이터세번째는 데이터의 사이즈네번째는 패킷의 번호다섯번째는 보내는 논리적 위치로비게임 으로 구분)

     

    m_pClientInfo[i].SendPacket();// 준비가 끝났으므로 해당 소켓에 전송한다.

    }

    }

        }

        Else // 클라이언트로 동작하는 경우

        {

    g_pPacketProcess->MakePacket(m_pChannelServerConnecter,(char*)&data, sizeof(SUNIT_INFO), 0, GAME);// 위와 마찬가지 이지만 대상이 서버이다.

     

    m_pChannelServerConnecter->SendPacket();//서버에 패킷 전송.

        }

    전송 함수는 대략 이런식으로 전송합니다.

     

    수신의 경우는 조금 복잡할지도 모르겠네요.

     

    PacketProc.h       파일을 열어봅시다.

    그 중에서 중간쯤에 나오는 주석을 보면

    로비 패킷 프로세스 함수 / 룸 패킷 프로세스 함수 /게임 패킷 프로세스 함수

    이렇게 써있는데 이게 뭔가 하면저 위에 전송 부분에서 패킷 생성함수의 마직막 인자에따라서 수신 시 다른 함수를 호출하게 되는 원리 입니다.

     

    그러므로 해당 논리적 위치로비//게임 )에 패킷 수신 함수를 만들어 줍시다.

    여기서 주의할 사항은 다음 양식을 맞춰 주셔야 합니다.


    // 로비

    int PacketLogIn();

    // 

    int PacketConnectRoom(cConnection*pServer, cConnection* pClient, int index);

    // 게임

    int PacketCharacterInfo(cConnection*pServer, cConnection* pClient, int index);

     

    위 함수들과 같이논리적 위치에 따라서 리턴형과 인수를 동일하게 해주셔야 합니다이름은 원하는대로 지어도 상관 없습니다.

     

    수신 함수의 구현.

    예제로는 캐릭터 정보 패킷을 사용하도록 하겠습니다.

        //! ReceiveCharacter Information Packet

        int CPacketProc::PacketCharacterInfo(cConnection*pServer, cConnection* pClient, int index)

        {

              {

                               //! 이건 동기화 코드 입니다.

                   cSync::Ownerlock(m_csInterger); 


                              /* 

                              이 코드를 통해서 메인쓰레드와 워커 쓰레드가공통으로 사용하는 전역 데이터 g_sUnitInfo의 내용 

                              을 업데0이트 해줍니다.

                              여기서 업데이트된 데이터는메인 루프에서 렌더링할 때 사용하게 됩니다. 

                              워커 쓰레드에서 수신 즉시처리하고 싶은 경우 이 코드 대신에 데이터 처리 코드를 넣어주시면 됩니다.
                              */


                   memcpy(&g_sUnitInfo,&m_szBuffer, m_nDataLen);

     

              }

     

              if(g_bIsMaker) . 서버(방장이라면

              {

                   //!Send!

                              // !  모든 클라이언트에게 받은 데이터를 그대로 가공없이 재전송 해줍니다.

                   for(inti=1; i<10; i++) 

                   {

                          if(pClient[i].m_bJoin== TRUE && i != index)

                          {

                                 //!make packet and send to it!

                                 MakePacket(&pClient[i],(char*)&g_sUnitInfo, sizeof(SUNIT_INFO), 0, 

                                            GAME);

                                 pClient[i].SendPacket();

                          }

     

                   }

              }

                      //! 클라이언트라면 그냥 끝납니다.

              return 0;

        }

     

    기본적인 구조는 다음과 같습니다.

     

    패킷 정의

     

    서버의 경우

    패킷 전송(모든클라이언트에게) -> 클라이언트수신 -> 데이터 처리 -> 종료

     

    클라이언트의 경우

    패킷 서버에게 전송 -> 서버 수신 -> 수신한데이터 처리 -> 다른 모든 클라이언트에게 패킷 전송 ->다른 클라이언트가 패킷 수신 -> 다른 클라이언트가 수신된 패킷을 처리 -> 종료.

     

    대략 이런 과정을 거치게 되는데 이 순서나 처리 과정을 미리 생각 하신 후 적절한 함수의 호출을 해주시면 에러 없이 될거라 생각됩니다.(아마도..-_-..;;)

    반응형

    '프로그래밍 > 네트워크 프로그래밍' 카테고리의 다른 글

    파일패치 서버  (1) 2016.10.12
    html와 Node.js 웹소켓서버  (3) 2016.10.12
    [포트폴리오]리니지 네트워크  (4) 2014.07.05
    게임서버 만들기 3  (0) 2014.06.25
    서버만들기 2  (0) 2014.06.25
Designed by Tistory.