Receive Buffer

이승한·2023년 12월 30일
0

Server

목록 보기
3/4

Receive Buffer

이번에 알아볼 것은 우리가 서버에서 Receive할 때 버퍼를 만들어서 받아줘야하는데

이것을 어떻게 구현해볼까? 에 대한 내용이다


  1. 생성자로 bufferSize를 받아서 ArraySegment 생성
  2. 이 buffer에서 _readPos와 _WritePos 로 Data를 다루기
  3. DataSize는 현재 내가 사용하고 있는 Data의 Size ( 현재 사용한 공간 끝 - 데이터의 시작)
  4. FreeSize는 내가 사용 할 수 있는 Size (전체 공간 - 현재 사용한 공간 끝)
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; } }
}

  1. ReadSegment : 현재 사용중인 범위
public ArraySegment<byte> ReadSegment
{
	get { return new ArraySegment<byte>(_buffer.Array,_buffer.Offset + _readPos, DataSize); }
}

  1. WriteSegment : 다음에 Receive할때 어디서부터 어디까지가 유효범위인지 남는 범위
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] [] [] [] [] [] [] [] []
    }
}

  1. ReadPos 커서 이동
public bool OnRead(int numOfBytes)
{
	//유효범위를 초과한 경우
    if(numOfBytes > DataSize)
    	return false;
    
    _readPos += numOfBytes;
    return true;
}

  1. WritePos 커서 이동
public bool OnWrite(int numOfBytes)
{
	//유효범위를 초과한 경우
    if(numOfBytes > FreeSize)
    	return false;
    
    _writePos += numOfBytes;
    return true;
}

Receive할때 코드의 흐름을 보면

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

0개의 댓글