Receive Buffer
이번에 알아볼 것은 우리가 서버에서 Receive할 때 버퍼를 만들어서 받아줘야하는데
이것을 어떻게 구현해볼까? 에 대한 내용이다
public class RecvBuffer
{
// 0 1 2 3 4 5 6 7 8 9
// [] [] [] [] [] [] [] [] [] []
ArraySegment<byte> _buffer;
int _readPos;
int _writePos;
public RecvBuffer(int bufferSize)
{
_buffer = new ArraySegment<byte>(new byte[bufferSize], 0 ,bufferSize);
}
public int DataSize { get { return _writePos - _readPos; } }
public int FreeSize { get { return _buffer.Count - _writePos; } }
}
public ArraySegment<byte> ReadSegment
{
get { return new ArraySegment<byte>(_buffer.Array,_buffer.Offset + _readPos, DataSize); }
}
public ArraySegment<byte> WriteSegment
{
get { return new ArraySegment<byte>(_buffer.Array, _buffer.Offset + _writePos, FreeSize); }
}
public void Clean()
{
//중간중간 정리해서 버퍼의 처음으로 readPos,writePos 옮기기, 데이터 옮기기
//why? 만약에 2바이트씩 처리한다면 3바이트를 받았을 때 한바이트씩 뒤로 밀리게 되는데
//계속 밀리다보면 나중에 FreeSize가 없어지니 중간중간에 한번씩 처음으로 옮겨줌
int dataSize = DataSize;
if(dataSize ==0)
{
//클라에서 보내준 데이터를 모두 처리한 상태
//남은 데이터가 없으면 복사하지않고 커서 위치만 리셋
}
else
{
//데이터가 남아있는 상태 (r~w에 데이터)
//시작 위치로 복사
//[] [] [r] [] [w] [] [] [] [] []
Array.Copy(_buffer.Array, _buffer.Offset + _readPos, _buffer.Array, _buffer.Offset, dataSize);
_readPos = 0;
_writePos = dataSize;
//옮긴 후
//[r] [] [w] [] [] [] [] [] [] [] []
}
}
public bool OnRead(int numOfBytes)
{
//유효범위를 초과한 경우
if(numOfBytes > DataSize)
return false;
_readPos += numOfBytes;
return true;
}
public bool OnWrite(int numOfBytes)
{
//유효범위를 초과한 경우
if(numOfBytes > FreeSize)
return false;
_writePos += numOfBytes;
return true;
}
void RegisterRecv()
{
_recvBuffer.Clean();
ArraySegment<byte> segment = _recvBuffer.WriteSegment;
_recvArgs.SetBuffer(segment.Array, segment.Offset, segment.Count);
bool pending = _socket.ReceiveAsync(_recvArgs);
if(pending == false)
{
OnCompleted(null, _recvArgs);
}
}
void OnCompletedRecv(object sender, SocketAsyncEventArgs args)
{
if(args.BytesTransferred > 0 && args.SocektError == SocketError.Sucess)
{
try
{
//Write커서 이동
if(_recvBuffer.OnWrite(args.BytesTransferred) == false)
{
//false인 경우는 없다 그래도 혹시 모르니
DisConnect();
return;
}
//컨텐츠 쪽으로 데이터를 넘겨주고 얼마나 처리했는지 받는다.
//데이터를 처리했거나 보류한 상태
int processLen = OnRecv(_recvBuffer.ReadSegment);
if(processLen < 0 || _recvBuffer.DataSize < processLen)
{
Disconnect();
return;
}
//처리한 Read커서 이동
if(_recvBuffer.OnRead(processLen) == false)
{
Disconnect();
return;
}
//조건문을 다 통과했으면, 데이터를 다 처리했거나 아직 남아있는 상태인데
//남은 데이터가 있으면 RegisterRecv로 가서 Clean으로 0번배열로 밀어주고
//다시 Receive할 것이다.
RegisterRecv();
}
catch(Exception e)
{
Console.WriteLine($"OnCompletedRecv Failed! {e}");
}
}
else
{
Disconnect();
}
}