🌞 SendBuffer?
SendBuffer에서 중요한 점은 SendBufferHelper 클래스 였다.
SendBuffer의 경우 ReceiveBuffer와는 달리 쓰고 읽는 영역에 대하여 사용한 버퍼를 제거하고 앞으로 옮기는 Clear 메소드의 역할을 할 수가 없다.
그이유는 Send는 여러 클라이언트에게 이루어지기 때문인데, 만약에 어떤 데이터를 A라는 사용자가 받았다는 이유로 삭제를 하게 되면 B라는 유저가 만약에 그 데이터를 사용 중이 었다면 크래쉬가 발생 할 것이다.
그렇기 때문에 SendBuffer의 경우에는 큰 뭉탱이 버퍼를 선언하여 그곳에 데이터를 쌓는 식으로 만들게 된다.
이러한 작업들은 여러 쓰레드에서 일어날 것이다. 그렇다면 같은 SendBuffer를 이용하기 때문에 lock을 걸어주지 않으면, 크래쉬가 일어날 것이다. 하지만 여기서 lock을 사용하게 되면, 성능상으로 느려지기 때문에 Helper 클래스에서 SendBuffer를 TLS영역으로 선언하여 각 쓰레드 별로 SendBuffer를 가져서 lock없이 데이터를 저장하고 Send하게끔 해주었다.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
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 null;
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;
}
}
}
public override void OnConnected(EndPoint endPoint)
{
Console.WriteLine($"OnConnected : {endPoint}");
Knight knight = new Knight() { hp = 100, attack = 10 };
ArraySegment<byte> openSegment = SendBufferHelper.Open(4096);
byte[] buffer = BitConverter.GetBytes(knight.hp);
byte[] buffer2 = BitConverter.GetBytes(knight.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> sendBuff = SendBufferHelper.Close(buffer.Length + buffer2.Length);
Send(sendBuff);
Thread.Sleep(1000);
Disconnect();
}