공부/게임 서버

[게임 서버] Listener의 반대 개념, Connector에 대해서

돌멩이수프 2022. 6. 20. 13:53
728x90

서버를 만들 때 Listener라는 이름으로 클라이언트의 연결 요청을 기다리는 클래스를 만들게 된다. 그 반대 입장으로 서버에서 서버와 클라이언트의 연결을 직접 하는 클래스를 만들어야 하는데 내가 배운 강의에서는 그 이름을 Connector라 한다.

 

Connector를 따로 만드는 가장 큰 이유 2가지는

1️⃣ 서버 코어 안에서 클라이언트와 연결하는 일이 필요할 때 범용적으로 사용하기 위해,

2️⃣ 나중에 게임 서버를 본격적으로 만들 때 몬스터 관리, 아이템 관리 등 여러가지 서버가 생기게 되는데 이때 그 서버를 서로 연결하고자 할 때 사용하기 위해서다.

 

Connector는 전체적으로 Listener와 흡사한 구조를 갖고 있다. Connector를 호출할 때 사용하게 되는 Connect 함수, 연결을 예약하는 용도의 RegisterConnect 함수, 연결이 완료됐음을 표시하는 OnConnectCompleted 함수로 이루어져 있다.

 

 

Func<Session> _sessionFactory;

public void Connect(IPEndPoint endPoint, Func<Session> sessionFactory)
{
    // 휴대폰 설정
    Socket socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
    _sessionFactory = sessionFactory;
    SocketAsyncEventArgs args = new SocketAsyncEventArgs();
    args.Completed += OnConnectCompleted; // Completed 발생시 OnConnectCompleted 이벤트 호출
    args.RemoteEndPoint = endPoint;
    args.UserToken = socket;

    RegisterConnect(args);
}

 

강의에서 만든 Connect는 이런 모양이다. Connect는 client에서 호출할 host endPoint와 필요한 session을 고를 수 있는 용도의 sessionFactory를 인자로 받게 된다.

이때 사용하는 args.UserToken은 socket을 받기 위해 사용됐다. Connector 산하에 전역으로 사용할 수 있도록 Socket _socket;같은 모양으로 socket을 선언할 수 있지만 이는 경제적이지 않다. 프로그램을 실행하면 여러 번 반복하여 서버를 호출하는 상황이 생기는데 이때마다 Socket _socket;을 생성하는 것은 비효율적이다. 그래서 이벤트를 활용하여 userToken에 이미 있는 socket을 잠시 저장했다가 바로 꺼내 쓰는 편이 더 좋다.

 

 

void RegisterConnect(SocketAsyncEventArgs args)
{
    Socket socket = args.UserToken as Socket;
    if (socket == null)
        return;

    bool pending = socket.ConnectAsync(args);
    if (pending == false)
        OnConnectCompleted(null, args); // 대기자 수가 없이 바로 입장에 성공하면 직접 Connect해줌.
}

 

위에서도 말했듯이 RegisterConnect는 연결이 필요한 인수를 넣어 지금 바로 연결할 수 있는 상황인지 확인하는 용도다. bool pending = socket.ConnectAsync(args)를 통해 지금 바로 Connect가 된다면, (즉, *pending이 false.) RegisterConnect에서 직접 OnConnectCompleted를 호출하게 된다. 보통은 Connect에 있는 args.Completed += OnConnectCompleted;에서 콜백 방식으로 OnConnectCompleted가 호출된다.

 

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

 

 

void OnConnectCompleted(object sender, SocketAsyncEventArgs args)
{
    if (args.SocketError == SocketError.Success)
    {
        // sessionFactory에서 어떤 session을 생성할지 받은 후 실행
        Session session = _sessionFactory.Invoke(); 
        // session 상에서 필요한 Socket을 ConnectSocket으로 넘겨줌.
        session.Start(args.ConnectSocket); 
        session.OnConnected(args.RemoteEndPoint);
    }
    else
    {
        Console.WriteLine($"OnConnectCompleted Fail : {args.SocketError}");
    }
}

 

OnConnectCompleted는 이 말에서도 알 수 있듯이 Connect가 진행됐을 경우 호출된다. 우선, SocketError를 확인해서 SocketError.Success가 출력됐는지 확인한다. Connect가 성공했다면 필요한 session을 생성하고 그 session을 실행, 연결한다. SocketError가 실패일 경우, 에러 메시지를 호출한다.

728x90