(C# : Server) Session - 2

Posted by : at

Category : Charp-Server



Recv까지 구현된 코드는 아래와 같다

// ServerCore
// Session.cs

class Session
{
    Socket _socket;
    int _disconnected = 0;

    public void Start(Socket socket)
    {
        _socket = socket;
        SocketAsyncEventArgs recvArgs = new SocketAsyncEventArgs();
        recvArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnRecvComplete);
        recvArgs.SetBuffer(new byte[1024], 0/*시작인덱스*/, 1024);

        RegisterRecv(recvArgs);
    }

    public void Send(byte[] sendBuff)
    {
        _socket.Send(sendBuff);
    }

    public void Disconnect()
    {
        if(Interlocked.Exchange(ref _disconnected, 1) == 1)
            return;

        _socket.Shutdown(SocketShutdown.Both);
        _socket.Close();
    }

    void RegisterRecv(SocketAsyncEventArgs args)
    {
        bool pending = _socket.ReceiveAsync(args);
        if(pending == false)
            OnRecvComplete(null, args);
    }

    void OnRecvComplete(object sender, SocketAsyncEventArgs args)
    {
        if(args.BytesTransferred > 0 && args.SocketError ==SocketError.Success)
        {
            try{
                string recvData = Encoding.UTF8.GetString(args.Buffer, args.Offset, args.BytesTransferred);
                RegisterRecv(args);
            }
            catch()
            {

            }
        }
        else
        {
            Disconnect();
        }
    }
}

Send를 구현해보자

// ServerCore
// Session.cs

class Session
{
    Socket _socket;
    int _disconnected = 0;

    // ...

    /*
    Recv와는 달리 Send는 호출되는 시점이 정해져있지 않다
    (Recv의 경우 시작과 동시에 Start로 동작, Recv이후 다시 Register)
    Send는 언제 Register하고 Send이후 언제다시 Register를 해야할까?
    */
    public void Send(byte[] sendBuff)
    {
        // 문제 1. SocketAsyncEventArgs를 재사용할 수 없음
        SocketAsyncEventArgs sendArgs = new SocketAsyncEventArgs();
        sendArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnSendCompleted);
        sendArgs.SetBuffer(sendBuff, 0, sendBuff.Length);

        RegisterSend(sendArgs);
    }

    // ...

    void RegisterSend(SocketAsyncEventArgs args)
    {
        // 문제 2. Send이후 Register과정에서 SendAsync를 호출하는데
        // 커널단에서 처리하는 함수라 속도가 느림
        bool pending = _socket.SendAsync(args);
            OnSendCompleted(null, args);
    }

    // ...

해결해보자

// ServerCore
// Session.cs

class Session
{
    Socket _socket;
    int _disconnected = 0;

    // Queue에 보낼 데이터를 미리 담아두자
    Queue<byte[]> _sendQueue = new Queu<byte[]>();
    bool _pending = false;
    SocketAsyncEventArgs _sendArgs = new SocketAsyncEventArgs();
    object _lock = new object();

    // ...

    public void Start(Socket socket)
    {
        _socket = socket;
        SocketAsyncEventArgs recvArgs = new SocketAsyncEventArgs();
        recvArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnRecvComplete);
        recvArgs.SetBuffer(new byte[1024], 0/*시작인덱스*/, 1024);

        // Send 델리게이터를 시작시점에 연결
        _sendArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnSendCompleted);

        RegisterRecv(recvArgs);
    }

    public void Send(byte[] sendBuff)
    {
        lock(_lock)
        {
            _sendQueue.Enqueue(sendBuff);
            if(_pending == false)
                RegisterSend();
        }
    }

    // ...

    void RegisterSend()
    {
        _pending = true;
        byte[] buff = _sendQueue.Dequeue();
        _sendArgs.SetBuffer(buff, 0, buff.Length);

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

    // ...

    void OnSendComplete(object sender, SocketAsyncEventArgs args)
    {
        lock(_lock)
        {
            if(args.BytesTransferred > 0 && args.SocketError ==SocketError.Success)
            {
                try{
                    // SendQueue안에 남아있는지 확인
                    if(_sendQueue.Count > 0)
                    {
                        RegisterSend();
                    }
                    else
                        _pending = false;
                }
                catch()
                {

                }
            }
            else
            {
                Disconnect();
            }
        }
    }

About Taehyung Kim

안녕하세요? 8년차 현업 C++ 개발자 김태형이라고 합니다. 😁 C/C++을 사랑하며 다양한 사람과의 협업을 즐깁니다. ☕ 꾸준한 자기개발을 미덕이라 생각하며 노력중이며, 제가 얻은 지식을 홈페이지에 정리 중입니다. 좀 더 상세한 제 이력서 혹은 Private 프로젝트 접근 권한을 원하신다면 메일주세요. 😎

Star
Useful Links