[C#][Server] 9. SendBuffer
C# Socket Programming TIL: SendBuffer 클래스 도입
1. 진행 상황
- 데이터 송신 시,
SendBufferHelper를 통해SendBuffer를 동적으로 할당하여 이를 기반으로 송신 작업을 수행하도록 구현하였다.
2. 주요 변경 사항
1) SendBuffer 클래스 구현
- 송신 데이터를 임시로 저장하고 이를 관리하는 역할을 수행한다.
- 데이터 크기와 메모리 관리 효율성을 고려해 동적 버퍼를 활용.
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);
}
}
2) SendBufferHelper 클래스 구현
- 중앙화된 버퍼 관리:
SendBufferHelper는ThreadLocal을 사용해 각 스레드에서 독립적인SendBuffer를 관리하도록 돕는다. 이를 통해 데이터를 동적으로 생성하고 각 스레드별로 처리 가능하다. - 동적 버퍼 할당과 재사용:
Open(size)메서드를 통해 필요한 크기의 버퍼를 할당하고,Close(size)메서드로 실제 데이터를 송신 가능한 상태로 반환한다. 이 방식은 메모리 복사를 줄여준다. - 사용 예시: 데이터를 송신하기 전,
Open으로 데이터를 기록할 버퍼를 열고,Close로 이를 송신 가능한ArraySegment<byte>로 변환하여 효율적으로 관리한다.
public class SendBuffer
{
byte[] _buffer;
int _usedSize = 0;
public SendBuffer(int chunkSize)
{
_buffer = new byte[chunkSize];
}
public int FreeSize { get { return _buffer.Length - _usedSize; } }
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;
}
}
3) GameSession의 송신 부분 변경
변경 전
- 송신 데이터를 직접 생성하고, 배열 복사 등을 처리.
byte[] buffer = Encoding.UTF8.GetBytes("Hello World!");
Send(new ArraySegment<byte>(buffer, 0, buffer.Length));
변경 후
- 테스트를 위해 Knight 클래스 생성.
SendBufferHelper를 사용하여 버퍼를 동적으로 생성하고 송신하도록 변경.
class Knight
{
public int hp;
public int attack;
}
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);
3. ThreadLocal의 사용
역할
ThreadLocal<T>는 각 스레드에서 독립적으로 데이터를 유지할 수 있는 스레드 로컬 저장소를 제공한다.SendBuffer는 각 스레드에서 별도로 관리되어, 동시 작업 시 데이터 충돌을 방지한다.
코드 분석
public static ThreadLocal<SendBuffer> CurrentBuffer = new ThreadLocal<SendBuffer>(() => { return null; });
ThreadLocal<SendBuffer>:- 각 스레드마다 독립적인
SendBuffer인스턴스를 유지.
- 각 스레드마다 독립적인
(() => { return null; }):- 초기화 콜백으로, 각 스레드가
ThreadLocal에 접근할 때 실행되어 기본값을 설정한다. - 여기서는 초기값으로
null을 반환하도록 설정.
- 초기화 콜백으로, 각 스레드가
4. 마무리
- 송신 데이터 관리 효율화:
- 동적 버퍼를 활용하여 송신 데이터를 효율적으로 관리.
- 스레드 안정성 확보:
ThreadLocal을 통해 송신 버퍼를 각 스레드에서 독립적으로 관리하여 동시 작업 시 충돌 방지.