(C# : Server) SendBuffer

Posted by : at

Category : Charp-Server



SendBuffer는 아래와 같은 기능을 제공해야 한다

class Knight
{
    public int hp;
    public int attack;
}

class GameSession : Session
{
    public override void OnConnected(EndPoint endPoint)
    {
        // 예를들어 Knight의 정보가 있다면 이 정보를 byte로 변환해야한다
        Knight knight = new Knight() { hp = 100, attack = 10 };

        byte[] sendBuff = new byte[1024];
        // 바이트로 변환
        byte[] buffer = BitConverter.GetBytes(knight.hp);
        byte[] buffer2 = BitConverter.GetBytes(knight.attack);
        Array.Copy(buffer, 0, sendBuff, 0, buffer.Length);
        Array.Copy(buffer2, 0, sendBuff, buffer.Length, buffer2.Length);

        Send(sendBuff)

        // SendBuff는 이런 기능을 지원해 줘야함.

        // ...
    }
}

당장 여기서 설명할 부분은 아니지만 버퍼의 복사는 최소화 해야한다

byte[] sendBuff = new byte[1024];
// 바이트로 변환
byte[] buffer = BitConverter.GetBytes(knight.hp);
byte[] buffer2 = BitConverter.GetBytes(knight.attack);
Array.Copy(buffer, 0, sendBuff, 0, buffer.Length);
Array.Copy(buffer2, 0, sendBuff, buffer.Length, buffer2.Length);

위와 같은 복사를 SendBuffer내부에서 한다고 옮겨버리면 매번 Send를 호출하며 복사가 더 일어나게 됨. 이런 부분의 주의해서 만들어 보자
이런 복사가 중요한게 MMO의 경우 주변에 유저가 많을경우 Session을 통해 각 유저에게 데이터를 보낸다면 유저수만큼 복사가 일어나야할 경우가 발생할 수 있기 때문

결론은 하면 외부에서 버퍼를 할당후 그 버퍼를 사용하는 형태로 만들자

namespace ServerCore
{
    public class SendBufferHelper
    {
        public static ThreadLocal<SendBuffer> CurrentBuffer = new ThreadLocal<SendBuffer>(() => { return null; });

        public static int ChunkSize { get; set; } = 4096 * 100;

        public static ArraySegment<byte> Open(int reserveSize)
        {
            if (CurrentBuffer.Value == null)
                CurrentBuffer.Value = new SendBuffer(ChunkSize);

            if(CurrentBuffer.Value.FreeSize < reserveSize)
                CurrentBuffer.Value = new SendBuffer(ChunkSize);

            return CurrentBuffer.Value.Open(reserveSize);
        }

        public static ArraySegment<byte> Close(int usedSize)
        {
            return CurrentBuffer.Value.Close(usedSize);
        }
    }

    public class SendBuffer
    {
        byte[] _buffer;
        int _usedSize = 0;

        public int FreeSize { get { return _buffer.Length - _usedSize; } }

        public SendBuffer(int chunkSize)
        {
            _buffer = new byte[chunkSize];
        }

        public ArraySegment<byte> Open(int reserveSize)
        {
            if (reserveSize > FreeSize)
                return new ArraySegment<byte>();

            return new ArraySegment<byte>(_buffer, _usedSize, reserveSize);
        }

        public ArraySegment<byte> Close(int usedSize)
        {
            ArraySegment<byte> segment = new ArraySegment<byte>(_buffer, _usedSize, usedSize);
            _usedSize += usedSize;

            return segment;
        }
    }
}

사용은 이렇게

class Knight
{
    public int hp;
    public int attack;
}

class GameSession : Session
{
    public override void OnConnected(EndPoint endPoint)
    {
        Console.WriteLine($"[OnConnected] {endPoint}");

        Knight kinght = new Knight() { hp = 100, attack = 10 };

        // 보내는 부분
        ArraySegment<byte> openSegment = SendBufferHelper.Open(4096);
        byte[] buffer = BitConverter.GetBytes(kinght.hp);
        byte[] buffer2 = BitConverter.GetBytes(kinght.attack);
        Array.Copy(buffer, 0, openSegment.Array, openSegment.Offset, buffer.Length);
        Array.Copy(buffer2, 0, openSegment.Array, openSegment.Offset + buffer.Length, buffer2.Length);
        ArraySegment<byte> sendBuffer = SendBufferHelper.Close(buffer.Length + buffer2.Length);

        Send(sendBuffer);
        Thread.Sleep(1000);
        Disconnect();
    }

About Taehyung Kim

안녕하세요? 8년차 현업 C++ 개발자 김태형이라고 합니다. 😁 C/C++을 사랑하며 다양한 사람과의 협업을 즐깁니다. ☕ 꾸준한 자기개발을 미덕이라 생각하며 노력중이며, 제가 얻은 지식을 홈페이지에 정리 중입니다. 좀 더 상세한 제 이력서 혹은 Private 프로젝트 접근 권한을 원하신다면 메일주세요. 😎

Star
Useful Links