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();
}