서버/서버 이론

Listener 구현

잼잼재미 2025. 4. 12. 21:38

Listener 구현


이전에 예시로 만든 소켓 프로그래밍 구조는 문제점이 있다. 그것은 바로 블로킹 방식을 사용해서 받아올 것이 없을 때, 코드가 멈출 수 있는 것이다. 그래서 비동기 방식으로 수정하는 보완이 필요하다.

 

 

 

우선 ServerCore에 Listener class를 추가하자.

 

 

Listener

using System.Net;
using System.Net.Sockets;

namespace ServerCore
{
    class Listener
    {
        private Socket _listenSocket;
        private Action<Socket> _onAcceptHandler;

        public void Init(IPEndPoint endPoint, Action<Socket> onAcceptHandler)
        {
            _listenSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            _onAcceptHandler += onAcceptHandler;
            
            // Bind (문지기 교육)
            _listenSocket.Bind(endPoint);
            
            // Listen (영업 시작)
            // backlog : 최대 대기 수 
            _listenSocket.Listen(10);

            SocketAsyncEventArgs args = new SocketAsyncEventArgs();
            args.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted);
            RegisterAccept(args);
        }

        void RegisterAccept(SocketAsyncEventArgs args)
        {
            // 이벤트를 재사용 하기 때문에 null값 입력
            args.AcceptSocket = null; 
            
            // 비동기 방식으로 프로그램이 종료되지 않도록 함
            // 바로 접속하면 직접 OnAcceptCompleted 를 실행
            bool pending = _listenSocket.AcceptAsync(args);
            if (!pending)
            {
                OnAcceptCompleted(null,args);
            }
        }

        // 멀티쓰레드로 실행되는 부분, 공유 자원을 사용하는 경우, 멀티쓰레드 동기화 문제를 반드시 해결해야 됨
        void OnAcceptCompleted(object sender,SocketAsyncEventArgs args)
        {
            if (args.SocketError == SocketError.Success)
            {
                // Socket clientSocket = _listener.Accept(); 와 같은 역할
                _onAcceptHandler.Invoke(args.AcceptSocket);
            }
            else
            {
                Console.WriteLine(args.SocketError.ToString());
            }
            
            // 모든 처리 후, 다시 RegisterAccept 실행
            RegisterAccept(args);
        }
    }
}

 

 

Main

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace ServerCore
{
    class Program
    {
        private static Listener _listener = new Listener();

        static void OnAcceptHandler(Socket clientSocket)
        {
            try
            {
                // 수신
                byte[] recvBuff = new byte[1024];
                int recvBytes = clientSocket.Receive(recvBuff);
                string recvData = Encoding.UTF8.GetString(recvBuff, 0, recvBytes);
                Console.WriteLine($"[From Client] {recvData}");

                // 발신
                byte[] sendBuff = Encoding.UTF8.GetBytes("Welcome to YoungJune Server!");
                clientSocket.Send(sendBuff);

                // 퇴장
                clientSocket.Shutdown(SocketShutdown.Both);
                clientSocket.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
        }

        static void Main(string[] args)
        {
            //DNS (Domain Name System)
            string host = Dns.GetHostName();
            IPHostEntry ipHost = Dns.GetHostEntry(host);
            IPAddress ipAddr = ipHost.AddressList[0];
            IPEndPoint endPoint = new IPEndPoint(ipAddr, 7777);


            _listener.Init(endPoint, OnAcceptHandler);
            Console.WriteLine("Listening...");

            while (true)
            {

            }

        }
    }
}

'서버 > 서버 이론' 카테고리의 다른 글

Session 구현  (0) 2025.04.13
소켓 프로그래밍 기초  (0) 2025.04.09
네트워크 기초 이론  (0) 2025.04.07
Thread Local Storage (TLS)  (0) 2025.04.06
Lock 구현  (0) 2025.03.29