공부/게임 서버

[게임 서버] Listener, 네트워크 연결을 기다려보자

돌멩이수프 2022. 6. 20. 14:11
728x90

서버와 클라이언트의 연결을 기다리는 용도의 Listener 클래스는 시작점인 Init, 기다리는 연결이 바로 실행될 수 있음을 확인하는 RegisterAccept, 연결 완료를 담당하는 OnAcceptCompleted, 연결을 받아들이는 Accept 함수로 구성된다.

 

 

Socket _listenSocket;
Action<Socket> _onAcceptHandler; // Accept가 완료됐을 때 어떻게 처리할 것인지 담당.

public void Init(IPEndPoint endPoint, Action<Socket> onAcceptHandler)
{
    _listenSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
    // AddressFamily == 네트워크 유형, Stream / Tcp == TCP 사용을 위한 설정
    _onAcceptHandler += onAcceptHandler;    

    _listenSocket.Bind(endPoint); // 소켓 정의

    _listenSocket.Listen(10); // 연결 요청 대기. 최대 대기 수 10

    SocketAsyncEventArgs args = new SocketAsyncEventArgs();
    // args 비동기 작업이 완료됐을 때(Compledted) 새로운 이벤트인 OnAcceptCompledted를 콜백방식으로 불러오자.
    args.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted);
    RegisterAccept(args);
}

 

Init은 endPoint와 onAcceptHandler를 인자로 받게 된다. onAcceptHandler는 말 그대로 Accept의 완료 처리를 담당한다. Init에서는 소켓을 정의하고, 기본적인 환경을 설정한다. _onAcceptHandler와 onAcceptHandler를 연결하는 +=는 델리게이트 체인이다. onAcceptHandler는 콜백 이벤트로 설정해주고 RegisterAccept(args)를 통해 연결을 기다리는 대기자 수를 확인하게된다.

 

 

void RegisterAccept(SocketAsyncEventArgs args)
{
    args.AcceptSocket = null; // 이벤트 재사용 시 기존 이벤트를 날려주고 실행하는 게 중요.

    // pending == 대기자수 체크.
    bool pending = _listenSocket.AcceptAsync(args); // 비동기 함수 사용 예약하기.
    if (pending == false) // 너~무 한가해서 대기자 수 하나도 없고 바로 비동기 함수 실행됨.
        OnAcceptCompleted(null, args);
}

 

확실한 초기화를 위해 기존 이벤트를 날려주고 pending을 통해 대기자 수를 체크한다. *pending이 false라면 바로 OnAcceptCompleted를 실행해준다.

 

* 비동기방식으로 Accept를 요청했는데 true 값이 반환된다는 건 비동기적으로 콜백 함수를 사용한다는 뜻이지만 false가 반환된다는 건 동기 방식으로 바로 Accept가 진행된다는 의미다.

 

 

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(args); // 난 다 끝났고, 다음 아이를 위해서 다시 예약
}

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

 

OnAcceptCompleted는 확실하게 연결이 진행됐을 경우 호출된다. SocketError를 확인해서 연결이 성공했다면 _onAcceptHandler를 Invoke해서 소켓을 허용한다. 이는 Socket clientSocket = _listener.Accept()와 같은 의미다. 연결이 실패할 경우 에러 메시지를 출력한다. 그리고 다시 다음 아규먼트를 RegisterAccept에 연결한다.

728x90