Session

์›๋ž˜๋ฒŒ๋ ˆยท2022๋…„ 8์›” 20์ผ
0
post-custom-banner

๐ŸŒž ๋‚š์‹œ๋Œ€๋ฅผ ์—ฌ๋Ÿฌ๊ฐœ๋ฅผ ๋‘์ž

  • Listener ํด๋ž˜์Šค์˜ Init
    class Listener
    {
        Socket _listenerSocket;
        Action<Socket> _onAcceptHandler;
        public void Init(IPEndPoint endPoint, Action<Socket> onAcceptHandler)
        { 
            //๋ฌธ์ง€๊ธฐ
            _listenerSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            _onAcceptHandler += onAcceptHandler;

            //๋ฌธ์ง€๊ธฐ ๊ต์œก
            _listenerSocket.Bind(endPoint);

            //์˜์—…์‹œ์ž‘
            //backlog : ์ตœ๋Œ€ ๋Œ€๊ธฐ์ˆ˜
            _listenerSocket.Listen(10);

            for(int i = 0; i<10;i++)
            {
                SocketAsyncEventArgs args = new SocketAsyncEventArgs();
                args.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted);
                RegisterAccept(args);
            }
        }

SocketAsyncEventArgs๋ฅผ ์—ฌ๋Ÿฌ๊ฐœ ๋งŒ๋“ค์–ด ์ฃผ๊ณ , RegisterAccept๋ฅผ ์—ฌ๋Ÿฌ๋ฒˆ ์‹คํ–‰ํ•œ๋‹ค.


๐ŸŒž ๋น„๋™๊ธฐ๋Š” ์–ด๋–ป๊ฒŒ ์ผ์–ด๋‚ฌ๋Š”๊ฐ€?

  • ํ˜„์žฌ ํ”„๋กœ๊ทธ๋žจ์€ Main์—์„œ ๋นˆ while๋ฌธ์„ ๋ฌดํ•œ์ • ๋„๋Š” ์ž‘์—…์„ ํ•˜๊ณ  ์žˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์–ด๋–ป๊ฒŒ Connect๊ฐ€ ๋ฐœ์ƒ ํ–ˆ์„ ๋•Œ, OnAcceptCompleted๋ฅผ ์‹คํ–‰ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ธ๊ฐ€?

์ž‘์—…์ž ์Šค๋ ˆ๋“œ์˜ ์ƒ์„ฑ์ด ํ•ด๋‹ต์ด๋‹ค.


OnAcceptCompleted์— Break Point๋ฅผ ๋‘๊ณ , ๋””๋ฒ„๊ทธ๋ฅผ ์‹คํ–‰ ํ•  ๊ฒฝ์šฐ ์œ„ ์‚ฌ์ง„๊ณผ ๊ฐ™์ด '์ฃผ ์Šค๋ ˆ๋“œ' ์ด์™ธ์— ์ƒˆ๋กœ์šด '์ž‘์—…์ž ์Šค๋ ˆ๋“œ' ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ด๋ ‡๋“ฏ AcceptAsync ๋ฉ”์†Œ๋“œ์— ๋”ฐ๋ผ SocketAsyncEventArgs์˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋Š” ์ƒˆ๋กœ์šด ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰ ๋œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

๐ŸŒผ ์œ„ ์ƒํ™ฉ์˜ ๋ฌธ์ œ์ 

  • '์ฃผ ์Šค๋ ˆ๋“œ' ์™€ '์ž‘์—…์ž ์Šค๋ ˆ๋“œ' ๋Š” ํ•จ๊ป˜ ์‹คํ–‰๋˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋งŒ์•ฝ์— ๋™์‹œ๋‹ค๋ฐœ์ ์œผ๋กœ ๊ฐ™์€ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๋Š” ๊ฒฝ์šฐ Race Condition ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

OnAcceptCompleted ๋ถ€๋ถ„์€ red Zone ์ฆ‰ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ๋ฅผ ์—ผ๋‘ํ•˜๊ณ  ์ฝ”๋”ฉ์„ ํ•ด์•ผํ•˜๋Š” ๋ถ€๋ถ„์œผ๋กœ ์ƒ๊ฐ์„ ํ•œ ์ƒํƒœ์—์„œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•  ๊ฒƒ์ด๋‹ค.


๐ŸŒž Server์˜ Receive๋ฅผ ๋น„๋™๊ธฐ๋กœ

using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;

namespace ServerCore
{
    class Session
    {   
        Socket _socket;
        public void Init(Socket socket)
        {
            _socket = socket;

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

        }

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

        }

        void OnRecvCompleted(object obj, SocketAsyncEventArgs args)
        {
            if(args.BytesTransferred != 0 && args.SocketError==SocketError.Success)
            {
                try
                {
                    string recvData = Encoding.UTF8.GetString(args.Buffer, args.Offset, args.BytesTransferred);
                    Console.WriteLine($"[From Client] {recvData}");

                    RegisterRecv(args);
                }
                catch(Exception e)
                {
                    Console.WriteLine($"OnRecvCompleted Failed {e}");
                }
            }
            else
            {
                //TODO Disconect
            }
        }
    }
}

์•ž์„  ์ˆ˜์—…์—์„œ ํ–ˆ๋˜ Accept๋ฅผ ํ•˜๋Š” ๋ถ€๋ถ„๊ณผ ๋น„์Šทํ–ˆ๋‹ค.
SocketAsyncEventArgs ์„ ์–ธ, Completed ์ถ”๊ฐ€, RegisterAsync๋ฅผ ์‹คํ–‰ํ•ด์ฃผ๋Š” Init

ReceiveAsync์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ๋Š” RegisterRecv

Completed์— ์ถ”๊ฐ€ ๋  OnRecvCompleted

๋‹ค๋ฅธ ์ ์ด๋ผ๊ณ  ํ•˜๋ฉด, Args์— Data๋ฅผ ๋ฐ›์•„ ์ค„ Buffer๋ฅผ ์ถ”๊ฐ€ํ•œ ์ ์ด๋‹ค.

๊ทธ๋ฆฌ๊ณ  OnRecvCompleted ๋ถ€๋ถ„์—์„œ args.BytesTransferred > 0 ๋ณด๋‹ค ํฐ ๊ฒฝ์šฐ์—๋งŒ ์กฐ๊ฑด๋ฌธ์„ ํ†ต๊ณผํ•˜๊ฒŒ ํ–ˆ๋Š”๋ฐ ๊ทธ ์ด์œ ๋Š” Disconnect์™€ ๊ฐ™์€ ๊ฒƒ์„ Receive๋ฅผ ํ•˜๋ฉด 0 byte๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Š” ํ†ต๊ณผ ์‹œ์ผœ์ฃผ์ง€ ์•Š๊ธฐ ์œ„ํ•ด์„œ ๋„ฃ๋Š”๋‹ค.


๐ŸŒž ์ž„์‹œ Disconnect์™€ Send

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

        public void Disconnect()
        {
            _socket.Shutdown(SocketShutdown.Both);
            _socket.Close();
        }

๐ŸŒผ Disconnect์˜ ๋ฌธ์ œ์ 

  • ๋ฉ€ํ‹ฐ์“ฐ๋ ˆ๋“œ ํ™˜๊ฒฝ์ด๊ธฐ ๋•Œ๋ฌธ์— ํ˜„์žฌ Disconnect ๋ถ€๋ถ„์ด ๋‘๋ฒˆ ์ด์ƒ ํ˜ธ์ถœ์ด ๋  ์ˆ˜ ์žˆ๋Š” ์ƒํ™ฉ์ด๋‹ค. Disconnect ๋œ ์ƒํƒœ์—์„œ ๋‹ค์‹œ ํ•œ ๋ฒˆ Disconnect๋ฅผ ํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋ถ€๋ถ„์„ ํ•œ๋ฒˆ๋งŒ ํ•˜๊ฒŒ๋” ์ˆ˜์ •ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.
        public void Disconnect()
        {
            if (Interlocked.Exchange(ref _disconnected, 1) == 1)
                return;


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

๐ŸŒž Send๋ฅผ ๋น„๋™๊ธฐ์‹์œผ๋กœ

Send์˜ ๋น„๋™๊ธฐ์‹์€ ๊ธฐ์กด ์•ž์—์„œ ํ–ˆ๋˜ ๋ฐฉ์‹๊ณผ๋Š” ์กฐ๊ธˆ ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค.
์™œ๋ƒํ•˜๋ฉด, ์•ž์„œ ํ–ˆ๋˜ Accept์™€ Receive์˜ ๊ฒฝ์šฐ Client์—์„œ ์‹ ํ˜ธ๊ฐ€ ์˜ค๋Š” ๊ฒƒ์„ ๋ฐ›๋Š” ๊ฒƒ์ด๋ผ๋ฉด, Send๋Š” ๋ฐ˜๋Œ€๋กœ Server์—์„œ ์‹ ํ˜ธ๋ฅผ ๋ณด๋‚ด๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace ServerCore
{
    class Session
    {
        Socket _socket;
        int _disconnected = 0;
        SocketAsyncEventArgs _sendArgs = new SocketAsyncEventArgs();
        Queue<byte[]> sendQueue = new Queue<byte[]>();
        bool _pending = false;
        object _lock = new object();
        public void Start(Socket socket)
        {
            _socket = socket;

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

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

            RegisterRecv(recvArgs);

        }

        #region ๋„คํŠธ์›Œํฌํ†ต์‹ 

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

        }

        void OnSendCompleted(object obj, SocketAsyncEventArgs args)
        {
            lock (_lock)
            {
                if (args.BytesTransferred > 0 && args.SocketError == SocketError.Success)
                {
                    try
                    {
                        _pending = false;
                        if (sendQueue.Count > 0)
                            RegisterSend();
                        else
                            _pending = false;
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine($"OnSendCompleted Failed {e}");
                    }
                }
                else
                {
                    Disconnect();
                }
            }
        }

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

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

        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)
                OnRecvCompleted(null, args);

        }

        void OnRecvCompleted(object obj, SocketAsyncEventArgs args)
        {
            if(args.BytesTransferred != 0 && args.SocketError==SocketError.Success)
            {
                try
                {
                    string recvData = Encoding.UTF8.GetString(args.Buffer, args.Offset, args.BytesTransferred);
                    Console.WriteLine($"[From Client] {recvData}");

                    RegisterRecv(args);
                }
                catch(Exception e)
                {
                    Console.WriteLine($"OnRecvCompleted Failed {e}");
                }
            }
            else
            {
                Disconnect();
            }
        }
    }
    #endregion
}

Send๋ฅผ ๋น„๋™๊ธฐ์‹์œผ๋กœ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•ด์„œ ๊ธฐ์กด์— Accept์™€ Async์˜ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด, SocketAsyncEventArgs๋ฅผ ์žฌ์‚ฌ์šฉ ํ•  ์ˆ˜ ์—†๊ณ  Send๋ฅผ ํ˜ธ์ถœ ๋ฐ›์„ ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•˜๊ฒŒ ๋œ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๋จผ์ € SocketAsyncEventArgs๋ฅผ ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜๋กœ ์„ ์–ธ์„ ํ•˜๊ณ  ์ด๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๋ถ€๋ถ„์„ Start ๋ฉ”์†Œ๋“œ์— ๋„ฃ์–ด์ฃผ์—ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  Async ํ•จ์ˆ˜์˜ ๊ฒฝ์šฐ ์Šค๋ ˆ๋“œ๋ฅผ ๋‹ค๋ฃจ๋Š” ๋ถ€๋ถ„์ด๊ธฐ ๋•Œ๋ฌธ์— ์œ ์ €๋‹จ์—์„œ ๋ช…๋ น์„ ํ•  ๋ฟ ์•„๋‹ˆ๋ผ OS๋‹จ์—์„œ๋„ ๋ช…๋ น์„ ํ•˜๊ฒŒ ๋œ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๋ฆฌ์†Œ์Šค ๋˜ํ•œ ๋งŽ์ด ๋จน๊ฒŒ ๋˜๋Š”๋ฐ, ๊ธฐ์กด์— Accept ์™€ Receive์˜ ๊ฒฝ์šฐ ๊ฐฏ์ˆ˜๋ฅผ ๋Š˜๋ ค๋ด์•ผ ๋ช‡๊ฐœ ๋˜์ง€ ์•Š์•˜์ง€๋งŒ, Send์˜ ๊ฒฝ์šฐ ๋งŒ์•ฝ์— ์œ ์ €๊ฐ€ ์ฒœ๋ช…, ๋งŒ๋ช…์ด ๋˜์–ด์„œ ๊ทธ ๋ชจ๋“  ์‚ฌ๋žŒ๋“ค์ด SendAsync๋ฅผ ํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด ์„œ๋ฒ„์— ๋ถ€ํ•˜๊ฐ€ ์—„์ฒญ๋‚˜๊ฒŒ ๋“ค์–ด์˜จ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ์กฐ์ ˆํ•˜๊ธฐ ์œ„ํ•ด์„œ ์‹ค์ œ๋กœ ์‹คํ–‰๋˜๋Š” SendAsync๋Š” ํ•˜๋‚˜์ž„์„ ์œ ์ง€ํ•˜๋ฉด์„œ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ Send๊ฐ€ ๋“ค์–ด์˜ค๋ฉด Send์— ์•„๊ทœ๋จผํŠธ ๊ฐ’์ธ sendBuff๋ฅผ sendQueue์— ์ €์žฅํ•˜์—ฌ ๋‚˜์ค‘์— ์ƒˆ๋กœ์šด SendAsync์—์„œ ์ฒ˜๋ฆฌํ•ด์ฃผ๊ฒŒ๋” ๋งŒ๋“ค์–ด์ฃผ์—ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์œ„์˜ ๋ฐฉ๋ฒ•์€ ์‹ค์ œ๋กœ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ๋‹ค๊ณ  ๋ณด๊ธฐ๋Š” ์–ด๋ ต๋‹ค. ์–ด์ฐจํ”ผ ๊ฒฐ๊ตญ์—” SendAsync๋ฅผ Send๊ฐ€ ๋“ค์–ด์˜จ ๋งŒํผ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฒ•์€ ์•„๋ž˜์—์„œ ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.


๐ŸŒž Send๋ฅผ ์ข€ ๋” Compactํ•˜๊ฒŒ


๐ŸŒผ SocketAsyncEventArgs.BufferList

  • ์ด ๋ฉ”์†Œ๋“œ๋Š” SetBuffer์˜ ๋ฆฌ์ŠคํŠธ ๋ฒ„์ „์œผ๋กœ Buffer๋ฅผ ๋ฆฌ์ŠคํŠธ ํ˜•์‹์œผ๋กœ ์ €์žฅ์„ ํ•˜์—ฌ, ํ•œ๊บผ๋ฒˆ์— Async๋ฅผ ์‹คํ–‰ํ•˜๊ฒŒ ํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ์œ„์—์„œ ๋ฌธ์ œ์˜€๋˜ Async๋ฅผ ์—ฌ๋Ÿฌ๋ฒˆ ํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐ ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.
  • ์ฃผ์˜์‚ฌํ•ญ : SetBuffer์™€ BufferList๋Š” ๋‘˜ ์ค‘ ํ•˜๋‚˜๋งŒ ์‚ฌ์šฉ์„ ํ•ด์•ผ ํ•œ๋‹ค. ๋งŒ์•ฝ์— ๋‘ ๊ฐœ ๋ชจ๋‘ ๊ฐ’์ด ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

๐ŸŒผ Dequeue ๋ถ€๋ถ„์„ ์ˆ˜์ •ํ•˜์ž

void RegisterSend()
       {
           _pending = true;

           List<ArraySegment<byte>> list = new List<ArraySegment<byte>>();

           while(_sendQueue.Count > 0)
           {
               byte[] buff = _sendQueue.Dequeue();
               list.Add(new ArraySegment<byte>(buff, 0, buff.Length));
           }

           _sendArgs.BufferList = list;

           bool pending = _socket.SendAsync(_sendArgs);
           if (pending == false)
               OnSendCompleted(null, _sendArgs);
       }
  • ๊ธฐ์กด์— ์žˆ๋˜ Dequeue ๋ถ€๋ถ„์€ sendQueue์—์„œ ๊ฐ’๋“ค์„ ํ•˜๋‚˜์”ฉ ๋นผ์คฌ๋‹ค. ์ด๋ฒˆ์—๋Š” BufferList๋ฅผ ์ด์šฉ ํ•  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด BufferList์— sendQueue ๋‚ด๋ถ€์— ์žˆ๋Š” ๋ชจ๋“  ๊ฐ’๋“ค์„ ์˜ฎ๊ธธ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— while๋ฌธ์„ ๋Œ๋ฉด์„œ sendQueue์˜ Count๊ฐ€ 0์ด ๋ ๋•Œ๊นŒ์ง€ ์ž„์˜์˜ buff ๋ฆฌ์ŠคํŠธ๋ฅผ ๋‘์–ด ๊ทธ์ชฝ์— ์ €์žฅ์„ ํ•  ๊ฒƒ์ด๋‹ค.

    ์™œ ๋ฐ”๋กœ BufferList์— Add ํ•˜์ง€ ์•Š๊ณ , ์ž„์˜์˜ BufferList๋ฅผ ๋‘๋Š”๊ฐ€?
    ํ˜„์žฌ List๋Š” ๋‹จ์ˆœํžˆ byteํ˜• Buffer๋ฅผ ๋ฐ›๊ณ  ์žˆ์ง€ ์•Š๋‹ค. ์ด๊ฒƒ์€ BufferList ๋˜ํ•œ ๊ฐ™์€๋ฐ, ArraySegment๋Š” ๋ฐฐ์—ด ๋‚ด๋ถ€์˜ ๋ถ€๋ถ„์„ ๋‹ด๋Š” ๊ตฌ์กฐ์ฒด์ด๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ๊ธฐ์กด์— ์•Œ๋˜ byteํ˜• List์™€๋Š” ๋‹ค๋ฅผ ์ˆ˜๋„ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๊ณ , ์‹ค์ œ๋กœ ๋‹ค๋ฅด๋‹ค.
    ์ด BufferList์— ๊ฒฝ์šฐ '=' ์—ฐ์‚ฐ์ž๋ฅผ ํ†ตํ•ด์„œ๋งŒ ๊ฐ’๋“ค์„ ์ œ๋Œ€๋กœ ๋ณต์‚ฌ ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ์œ„์™€ ๊ฐ™์ด ์ž„์˜์˜ BufferList๋ฅผ ๋‘๋Š” ์ž‘์—…์„ ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace ServerCore
{
    class Session
    {
        Socket _socket;
        int _disconnected = 0;
        SocketAsyncEventArgs _sendArgs = new SocketAsyncEventArgs();
        SocketAsyncEventArgs _recvArgs = new SocketAsyncEventArgs();
        List<ArraySegment<byte>> _pendingList = new List<ArraySegment<byte>>();
        Queue<byte[]> _sendQueue = new Queue<byte[]>();
        bool _pending = false;
        object _lock = new object();
        public void Start(Socket socket)
        {
            _socket = socket;

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

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

            RegisterRecv();

        }

        #region ๋„คํŠธ์›Œํฌํ†ต์‹ 

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

        }

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

                        Console.WriteLine($"Transferred bytes : {_sendArgs.BytesTransferred}");

                        if (_sendQueue.Count > 0)
                            RegisterSend();
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine($"OnSendCompleted Failed {e}");
                    }
                }
                else
                {
                    Disconnect();
                }
            }
        }

        void RegisterSend()
        {
            _pendingList.Clear();

            while(_sendQueue.Count > 0)
            {
                byte[] buff = _sendQueue.Dequeue();
                _pendingList.Add(new ArraySegment<byte>(buff, 0, buff.Length));
            }

            _sendArgs.BufferList = _pendingList;

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

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


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

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

        }

        void OnRecvCompleted(object obj, SocketAsyncEventArgs args)
        {
            if(args.BytesTransferred != 0 && args.SocketError==SocketError.Success)
            {
                try
                {
                    string recvData = Encoding.UTF8.GetString(args.Buffer, args.Offset, args.BytesTransferred);
                    Console.WriteLine($"[From Client] {recvData}");

                    RegisterRecv();
                }
                catch(Exception e)
                {
                    Console.WriteLine($"OnRecvCompleted Failed {e}");
                }
            }
            else
            {
                Disconnect();
            }
        }
    }
    #endregion
}
  • ์ด๋ฐฉ์‹์œผ๋กœ Send์˜ ๋ชจ๋“  ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ๋‹ค๊ณ  ๋ณด๊ธฐ๋Š” ์–ด๋ ต๋‹ค.
    Buffer์— ๋ณด๋‚ผ ๊ฐ’๋“ค์„ ๋ชจ์•„์„œ ๋ณด๋‚ด๊ธด ํ–ˆ์ง€๋งŒ,
    ์•„์ง ์ด์ •๋„๋กœ ํŒจํ‚ท์„ ๋ชจ์•„ ๋ณด๋ƒˆ๋‹ค ํ•˜๊ธฐ์—” ์ข€ ๊ทธ๋ ‡๋‹ค.
    ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์—”์ง„ ์ชฝ์ด๋‚˜, ํด๋ผ์ชฝ์—์„œ ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ์•„๋‹Œ
    ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ณด๋‚ด๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ์œผ๋Š” ์—„์ฒญ ํฐ ๋ฒ„ํผ๋ฅผ ๋‘๊ณ 
    ๊ทธ ๋ฒ„ํผ๋ฅผ SendAsyncํ•˜๋Š” ๋ฐฉ์‹๋“ฑ์„ ๊ณ ๋ คํ•ด ๋ณผ ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

๐ŸŒž SessionHandler

  • Handler๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ, Engine๊ณผ ์ปจํ…์ธ ๋ฅผ ๋ถ„๋ฆฌ์‹œํ‚ค์ž

  • Session์—์„œ์˜ ํ–‰๋™์„ ํ•  callback์„ ๋‹ด๋‹นํ•˜๋Š” ๋ถ€๋ถ„์ธ SessionHandler๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž. ์ „์— Accept์˜ ๊ฒฝ์šฐ์—๋„ OnAcceptHandler๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์–ด์„œ, Accept๊ฐ€ ์ผ์–ด๋‚˜์„œ ์™„๋ฃŒ๋œ ๊ฒฝ์šฐ์— callback์œผ๋กœ ํ•  ์ผ์„ ์‹คํ–‰ํ•ด์ฃผ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ๊ฒƒ์ด๋‹ค.

  • Handler๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๋Š” ๋ฐฉ๋ฒ•์—๋Š” ๋‘๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค.
    ์ฒซ๋ฒˆ์งธ๋Š” class๋ฅผ ํ•˜๋‚˜ ์„ ์–ธํ•˜์—ฌ ๊ฑฐ๊ธฐ์— Handler๋ฅผ ๋ชจ์•„์„œ ๊ด€๋ฆฌํ•˜๋Š” ๋ฒ•
    ๋‘๋ฒˆ์จฐ๋Š” Session์„ ์ƒ์†๋ฐ›๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

์—ฌ๊ธฐ์„œ๋Š” ๋‘๋ฒˆ์งธ ๋ฐฉ๋ฒ•์œผ๋กœ ํ•ด๋ณด๊ฒ ๋‹ค.

namespace ServerCore
{
    abstract class Session
    {
        public abstract void OnConnected(EndPoint endPoint);
        public abstract void OnRecv(ArraySegment<byte> buffer);
        public abstract void OnSend(int numOfBytes);
        public abstract void OnDisconnect(EndPoint endPoint);
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace ServerCore
{

    class GameSession : Session
    {
        public override void OnConnected(EndPoint endPoint)
        {
            Console.WriteLine($"OnConnected : {endPoint}");

            byte[] sendBuff = Encoding.UTF8.GetBytes("Welcome to MMORPG Server !");
            Send(sendBuff);

            Thread.Sleep(1000);
            Disconnect();
        }

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

        public override void OnRecv(ArraySegment<byte> buffer)
        {
            string recvData = Encoding.UTF8.GetString(buffer.Array, buffer.Offset, buffer.Count);
            Console.WriteLine($"[From Client] {recvData}");
        }

        public override void OnSend(int numOfBytes)
        {
            Console.WriteLine($"Transferred bytes : {numOfBytes}");
        }
    }
    class Program
    {
        static Listener _listener = new Listener();
        static void Main(string[] args)
        {
            //DNS(Domain Name Service
            string host = Dns.GetHostName();
            IPHostEntry ipHost = Dns.GetHostEntry(host);
            IPAddress ipAddr = ipHost.AddressList[0];
            IPEndPoint endPoint = new IPEndPoint(ipAddr, 7777);

            _listener.Init(endPoint, () => { return new GameSession(); });
                
            while (true)
            {
                ;
            }
        }

    }
}
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace ServerCore
{
    class Listener
    {
        Socket _listenerSocket;
        Func<Session> _sessionFactory;
        public void Init(IPEndPoint endPoint, Func<Session> sessionFactory)
        { 
            //๋ฌธ์ง€๊ธฐ
            _listenerSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            _sessionFactory += sessionFactory;

            //๋ฌธ์ง€๊ธฐ ๊ต์œก
            _listenerSocket.Bind(endPoint);

            //์˜์—…์‹œ์ž‘
            //backlog : ์ตœ๋Œ€ ๋Œ€๊ธฐ์ˆ˜
            _listenerSocket.Listen(10);

            for(int i = 0; i<10;i++)
            {
                SocketAsyncEventArgs args = new SocketAsyncEventArgs();
                args.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted);
                RegisterAccept(args);
            }
        }

        void RegisterAccept(SocketAsyncEventArgs args)
        {
            args.AcceptSocket = null;

            bool pending = _listenerSocket.AcceptAsync(args);
            if (pending == false)
                OnAcceptCompleted(null, args);
                   
        }

        void OnAcceptCompleted(object sender, SocketAsyncEventArgs args)
        {
            if (args.SocketError == SocketError.Success)
            {
                Session session = _sessionFactory.Invoke();
                session.Start(args.AcceptSocket);
                session.OnConnected(args.AcceptSocket.RemoteEndPoint);
            }
            else
                Console.WriteLine(args.SocketError.ToString());

            RegisterAccept(args);
        }


        public Socket Accept()
        {
            return _listenerSocket.Accept();
        }
    }
}

์—ฌ๊ธฐ์„œ ํ•˜๋‚˜ ์˜๋ฌธ์ธ ์ ์ด์—ˆ๋˜ ๋ถ€๋ถ„์€ SessionFactory ๋ถ€๋ถ„์ด์—ˆ๋Š”๋ฐ, Func ๋Œ€๋ฆฌ์ž๋ฅผ ํ†ตํ•ด์„œ SessionFactory์— GameSession์„ ๋Œ€์ž…ํ•˜๋Š” ์—ฐ์‚ฐ์„ ํ•˜์˜€๋‹ค. ๊ทผ๋ฐ ์ด ๋ถ€๋ถ„์—์„œ Session ํด๋ž˜์Šค์— SessionFactory๊ฐ€ ๋“ค์–ด๊ฐˆ ์ˆ˜๊ฐ€ ์žˆ๋‚˜? ๋ผ๋Š” ์ƒ๊ฐ์„ ํ–ˆ๋Š”๋ฐ,
์ถ”์ƒํด๋ž˜์Šค์˜ ๊ฒฝ์šฐ ์›๋ž˜ Session์œผ๋กœ ์„ ์–ธํ•ด์„œ ์‚ฌ์šฉ ํ•  ์ˆ˜๊ฐ€ ์—†์ง€๋งŒ, ์—…์บ์ŠคํŒ…์„ ํ†ตํ•ด์„œ๋Š” ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ๋๋‹ค.

profile
ํ•™์Šตํ•œ ๋‚ด์šฉ์„ ๋‹ด์€ ๋ธ”๋กœ๊ทธ ์ž…๋‹ˆ๋‹ค.
post-custom-banner

0๊ฐœ์˜ ๋Œ“๊ธ€