(C# : Server) Session - 3

Posted by : at

Category : Charp-Server



현재까지 구현된 코드

// ServerCore
// Session.cs

class Session
{
    Socket _socket;
    int _disconnected = 0;

    Queue<byte[]> _sendQueue = new Queu<byte[]>();
    bool _pending = false;
    SocketAsyncEventArgs _sendArgs = new SocketAsyncEventArgs();
    SocketAsyncEventArgs _recvArgs = 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);

        _sendArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnSendCompleted);

        RegisterRecv();
    }

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

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

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

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

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

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

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

            }
        }
        else
        {
            Disconnect();
        }
    }

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

최적화가 필요한 부분이 있다

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

    // SendAsync는 커널을 거드리는 함수이기에 속도가 느리다
    bool pending = _socket.SendAsync(_sendArgs));
    OnSendCompleted(null, _sendArgs);
}
List<ArraySegment<byte>> _pendingList = new List<ArraySegment<byte>>();

// ...

void RegisterSend()
{
    // byte[] buff = _sendQueue.Dequeue();
    // _sendArgs.SetBuffer(buff, 0, buff.Length);
    // 기존에는 SetBuffer로 버퍼를 지정해 보내줬지만
    // 버퍼의 리스트로 보내는 방법이 있다
    while(_sendQueue.Count > 0)
    {
        byte[] buff = _sendQueue.Dequeue();
        _pendingList.Add(new ArraySegment<byte>(buff, 0, buff.Length));
        // _sendArgs.BufferList.Add(new ArraySegment<byte>(buff, 0, buff.Length));
        // 참고로 위와 같이 Add를 해서 보내면 안됨(MSDN에 안된다고 나옴, 왜그런지는 잘;; MS에 물어봐야할 듯)

        /*
        개선점1 : 여기서 패킷이 너무 많을경우 너무 오래 잡고 있을 수 있음(이후 개선)
        */
    }

    _sendArgs.BufferList = _pendingList;

    // SendAsync는 커널을 거드리는 함수이기에 속도가 느리다
    bool pending = _socket.SendAsync(_sendArgs);
    if(pending == false)
        OnSendCompleted(null, _sendArgs);
}

// ...

    public void Send(byte[] sendBuff)
    {
        /*
        개선점2 : send가 필요한 데이터를 즉시 send하면 너무 많은 send가 일어난다.
        즉시 보내야할 데이터가 아닌경우 모아서 보내게 해야함
        */

        lock(_lock)
        {
            _sendQueue.Enqueue(sendBuff);
            if(_pendingList.Count == 0)
                // 대기중인 List가 없기에 Send를 Regist
                RegisterSend();
        }
    }

// ...

void OnSendComplete(object sender, SocketAsyncEventArgs args)
{
    lock(_lock)
    {
        if(args.BytesTransferred > 0 && args.SocketError ==SocketError.Success)
        {
            try{
                _sendArgs.BufferList = null;
                _pendingList.Clear();

                if(_sendQueue.Count > 0)
                    RegisterSend();
                
            }
            catch()
            {

            }
        }
        else
        {
            Disconnect();
        }
    }
}

About Taehyung Kim

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

Star
Useful Links