서버 공부

[서버 공부]11. Listener

myjeongjun 2025. 1. 12. 01:07

#11. 소켓프로그래밍에서 block 계열의 함수안 Aceept ,Receive, Send 를 사용해서 구현해보았다.

 

하지만 Aceept같은 경우에는 아주 드물게 block계열로 구현할순 있으나 Receive,Send같은 함수의 경우 데이터가 수신되거나 전송될때까지 스레드를 멈춘다는건 너무 비효율적이다. 

그러므로 이제 차례대로 Accept부터 비동기적으로 처리해보고자한다.

 

우선 따로 Listener 클래스를 정의해준다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;
namespace ServerCore
{
    internal class Listener
    {
        Socket _listemSocket;
        Action<Socket> _onAcceptHandler;

        public void Init(IPEndPoint endPoint, Action<Socket> onAcceptHandler) 
        {
            _listemSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            _onAcceptHandler += onAcceptHandler;

            _listemSocket.Bind(endPoint);

            //backlog : 최대 대기수
            _listemSocket.Listen(10);

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

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

            bool pending = _listemSocket.AcceptAsync(args);
            if (!pending) //운좋게 실행하자마자 Accept성공
            {
                OnAcceptCompleted(null, args);
            }
        }

        void OnAcceptCompleted(object sender , SocketAsyncEventArgs args) 
        {
            if (args.SocketError == SocketError.Success) 
            {
                _onAcceptHandler.Invoke(args.AcceptSocket);
            }
            else 
            {
                Console.WriteLine(args.SocketError.ToString());
            }

            RegisterAccept(args);
        }
    }
}

 

하나씩 보자면

        public void Init(IPEndPoint endPoint, Action<Socket> onAcceptHandler) 
        {
            _listemSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            _onAcceptHandler += onAcceptHandler;

            _listemSocket.Bind(endPoint);

            //backlog : 최대 대기수
            _listemSocket.Listen(10);

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

backlog 설정까지는 동일하나 그 이후에 SocketAsyncEventArgs클래스의 args를 만들어준다. 이건 비동기 소켓작업,이벤트를 관리하기 위해 제공하는 클래스이다.

 

args.Completed는 비동기 작업이 완료되었을때 호출되는 이벤트로 OnAcceptCompleted함수를 등록해주었다.

다음으로 RegisterAccept함수를 실행해주는데

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

            bool pending = _listemSocket.AcceptAsync(args);
            if (!pending) //운좋게 실행하자마자 Accept성공
            {
                OnAcceptCompleted(null, args);
            }
        }

_listemSocket.AcceptAsync(args);는 아직 비동기 작업이 진행중일때 true고 진행중이지 않을때 false를 주는데

만약 이것이 false로 들어왔다는말은 실행하자마자 Accept에 성공했다는 말이 된다.

왜냐하면 결국에 이 함수가 실행되는 이유는 어떠한 요청이 왔기때문에 실행되는것인데 처리를 하지않고있다는건 매우 빠르게 처리되었다는 의미와 같기때문이다.

        void OnAcceptCompleted(object sender , SocketAsyncEventArgs args) 
        {
            if (args.SocketError == SocketError.Success) 
            {
                _onAcceptHandler.Invoke(args.AcceptSocket);
            }
            else 
            {
                Console.WriteLine(args.SocketError.ToString());
            }

            RegisterAccept(args);
        }

 

그리고 작업이 완료될때마다 이벤트로 등록했던 이함수를 불러주면 비동기적으로 처리하는 효과를 얻는다.