서버를 만들 때 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가 실패일 경우, 에러 메시지를 호출한다.
'공부 > 게임 서버' 카테고리의 다른 글
[게임 서버] SetBuffer를 RecvBuffer, SendBuffer로 따로 빼내기 #1 (0) | 2022.06.20 |
---|---|
[게임 서버] Listener, 네트워크 연결을 기다려보자 (0) | 2022.06.20 |
[게임 서버] 소켓 프로그래밍 BufferList를 사용하여 한 번에 Send (0) | 2022.06.18 |
[게임 서버] 아주아주 간단하게 만드는 소켓 프로그래밍 환경 (0) | 2022.06.17 |
[게임 서버] 파싱(Parsing)이란? (0) | 2022.06.04 |