session

ㅋㅋ·2022년 10월 29일

csharp게임서버

목록 보기
10/16
  • initialize
public void Start(Socket socket)
{
    _socket = socket;

    _recvArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnRecvCompleted);
    _recvArgs.SetBuffer(new byte[1024], 0, 1024);

    RegisterRecv();

    _sendArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnSendCompleted);
    _sendArgs.BufferList = _pendingList;
    // bufferlist에 직접적으로 Add를 사용하여 추가할 시 에러 발생
}

listner가 보내준 소켓을 저장하여 사용

receiver가 사용할 이벤트 변수와

sender가 사용할 이벤트 변수를 나눠서 사용


  • receiver
void RegisterRecv()
{
    _recvBuffer.Clean();

    ArraySegment<byte> segment = _recvBuffer.ReceiveSegment;
    _recvArgs.SetBuffer(segment.Array, segment.Offset, segment.Count);

    bool pending = _socket.ReceiveAsync(_recvArgs);
    if (pending == false)
    {
        OnRecvCompleted(null, _recvArgs);
    }
}

void OnRecvCompleted(object sender, SocketAsyncEventArgs args)
{
    if (0 < args.BytesTransferred && args.SocketError == SocketError.Success)
    {
        try
        {
            if (_recvBuffer.OnWrite(args.BytesTransferred) == false)
            {
                Disconnect();
                return;
            }

            int processLength = OnReceive(_recvBuffer.DataSegment);
            if (processLength < 0 || _recvBuffer.DataSize < processLength)
            {
                Disconnect();
                return;
            }

            if (_recvBuffer.OnRead(processLength) == false)
            {
                Disconnect();
                return;
            }
                    
            RegisterRecv();
        }
        catch (Exception e)
        {
            Console.WriteLine($"OnRecvCompleted error: {e.Message}");
        }
    }
    else
    {
        Disconnect();
    }
}

receiver는 lisnter와 마찬가지로

ReceiveAsync를 실행 후 이벤트가 완료되거나 pending되지 않을 경우

OnRecvCompleted 콜백 함수가 실행되도록 함


  • sender
void RegisterSend()
{
    while (_sendQueue.Count > 0)
    {
        _pendingList.Add(_sendQueue.Dequeue());
    }

    _sendArgs.BufferList = _pendingList;

    bool pending = _socket.SendAsync(_sendArgs);
    if (pending == false)
    {
        OnSendCompleted(null, _sendArgs);
    }
}

void OnSendCompleted(object sender, SocketAsyncEventArgs args)
{
    lock (_lock)
    {
        if (0 < args.BytesTransferred && args.SocketError == SocketError.Success)
        {
            try
            {
                _pendingList.Clear();

                OnSend(args.BytesTransferred);

                if (0 < _sendQueue.Count)
                {
                    RegisterSend();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine($"OnSendCompleted error: {e.Message}");
            }
        }
        else
        {
            Disconnect();
        }
    }
}

public void Send(ArraySegment<byte> segment)
{
    lock (_lock)
    {
        _sendQueue.Enqueue(segment);
        if (_pendingList.Count == 0)
        {
            RegisterSend();
        }
    }
}

패킷을 보내고 받는 함수는 커널을 통해야 하기 때문에 비교적 느린 편이다.

그렇기 때문에 한번 send할 때 패킷을 모아 보내는 것이 유리하다.

_pendingList는 send를 직접적으로 호출하는 스레드와

send가 완료되고 비동기적으로 불린 스레드가 동시에 접근할 수 있기 때문에 lock을 사용


  • session 사용
class GameSession : Session
{
    public override void OnConnected(EndPoint endPoint)
    {
        Packet packet = new Packet();
        packet.size = 4;
        packet.packetID = 7;

        Console.WriteLine($"OnConnected : {endPoint}");
        for (int i = 0; i < 5; i++)
        {
            byte[] buffer1 = BitConverter.GetBytes(packet.size);
            byte[] buffer2 = BitConverter.GetBytes(packet.packetID);

            ArraySegment<byte> openSegment = SendBufferHelper.Open(4096);
            Array.Copy(buffer1, 0, openSegment.Array, openSegment.Offset, buffer1.Length);
            Array.Copy(buffer2, 0, openSegment.Array, openSegment.Offset + buffer1.Length, buffer2.Length);

            ArraySegment<byte> segment = SendBufferHelper.Close(packet.size);

            Send(segment);
        }
    }

    public override void OnDisconnected(EndPoint endPoint)
    {
        Console.WriteLine($"Disconnected: {endPoint}");
    }

    public override int OnReceive(ArraySegment<byte> buffer)
    {
        string recvData = Encoding.UTF8.GetString(buffer.Array, buffer.Offset, buffer.Count);
        Console.WriteLine($"From server: {recvData}");
        return buffer.Count;
    }

    public override void OnSend(int numOfBytes)
    {
        Console.WriteLine($"transfered byte {numOfBytes}");
    }
}

서버 코어에서 구현한 session을 상속받아 이벤트 함수들을 구현하여 사용

session에서 이벤트 핸들러들을 받아서 해당 이벤트를 호출하는 것도 가능

0개의 댓글