Thinking Different




 

Server 구조

 

자 그럼 서버 코드를 작성해보도록 하겠습니다. 서버 코드 클래스의 구조는 위와 같습니다.

서버의 구조는 Boost Asio 로 구성되며 윈도우 플랫폼에서는 내부적으로 Proactor 패턴의 IOCP로 작동됩니다.

 

각 클래스의 구조는 서버 클래스인 CServer와 각각의 세션 Entity 인 CSession, 그리고 접속하여 채팅하는 공간이 필요하므로 채팅방 클래스인 CChatRoom, 마지막으로 패킷데이터 관리를 위한 CMessage 클래스까지 4개의 클래스로 작성됩니다. 여기에 마지막으로 FlatBuffers 라이브러리가 사용되어 패킷 직렬화를 아주 쉽고 간단하게 도와줄 겁니다.

 

 

이 서버는 CServer에서 접속자를 기다리며 접속자가 접속 요청이 오면 Accep 하여 세션을 할당하고 할당된 세션의 유저 정보를 받은 다음 채팅방으로 접속하여 채팅방에서 메시지를 주고 받는 구조로 설계됩니다.

 

 

 

 

CMessage 클래스

 

자 그럼 공통적으로 서버와 클라이언트에서 사용되는 CMessage 클래스부터 작성하도록 하겠습니다. 통상적으로 FlatBuffers에서 사용하는 데이터 타입의 경우 uint_8_t * 타입이므로 해당 타입으로 작성되었습니다.

 

구조는 헤더 부분과 바디부분으로 나뉘어 헤더 부분에는 바디의 길이정보를 바디는 데이터를 담습니다.

CMessage 클래스 구조

Message.cpp

#pragma once

class CMessage
{
public:
    enum { header_length = 4 };
    enum { max_body_length = 512 };

public:
    CMessage() {}

    CMessage(const uint8_t* body, size_t body_size)
        : m_Body_length(body_size)
    {
        assert(body_size <= max_body_length);
        std::copy(body, body + body_size, this->body());
        encode_header();
    }

public:
    const uint8_t* data() const
    {
        return m_pData;
    }

    uint8_t* data()
    {
        return m_pData;
    }

    std::size_t length() const
    {
        return header_length + m_Body_length;
    }

    const uint8_t* body() const
    {
        return m_pData + header_length;
    }

    uint8_t* body()
    {
        return m_pData + header_length;
    }

    std::size_t body_length() const
    {
        return m_Body_length;
    }

    void body_size(std::size_t new_size)
    {
        assert(new_size <= max_body_length);
        m_Body_length = new_size;
    }

    bool decode_header()
    {
        char header[header_length + 1] = { 0, };
        strncat_s(header, (char*)m_pData, header_length);
        m_Body_length = std::atoi(header);
        if (m_Body_length > max_body_length)
        {
            m_Body_length = 0;
            return false;
        }
        return true;
    }

    void encode_header()
    {
        char header[header_length + 1] = { 0, };
        sprintf_s(header, "%4d", static_cast<int>(m_Body_length));
        memcpy(m_pData, header, header_length);
    }

private:
    uint8_t m_pData[header_length + max_body_length] = { 0, };
    std::size_t m_Body_length = 0;
};

 

기본 베이스는 boost asio 예제코드의 message.cpp에서 가져온 것이고 조금 변경하여 다듬었습니다.

별로 어려운 것은 없으며 데이터를 담고 헤더에는 데이터 길이를 담습니다.

 

헤더의 길이 정보를 담는 코드는 encode_header(), decode_header()가 담당하게 됩니다.