세션이라는 단어는 웹 서비스 개발을 할 때 '쿠키와 세션' 이라는 키워드로 처음 접했었습니다.
세션은 클라이언트 인증을 통해 누군지 식별하고, 그에 따른 권한을 주는 기술로 사용되었습니다.
인증은 대부분 로그인을 할 때 진행됩니다.
게임 서버를 공부하면서도 세션이라는 키워드를 동일하게 사용하는 것을 알게되었습니다.
단순히 인증과 인가를 위해 세션을 사용하는게 아니라 다른 의미가 있을 것 같다고 생각했습니다.
그래서 이번 게임 서버를 공부하면서 세션에 대한 개념도 확립하기 위해 이렇게 공부하게 되었습니다.
컴퓨터 과학에서, 특히 네트워크 분야에서 반영구적이고 상호작용적인 정보 교환을 전제하는 둘 이상의 통신 장치나 컴퓨터와 사용자 간의 대화나 송수신 연결상태를 의미하는 보안적인 다이얼로그(dialogue) 및 시간대를 가리킨다. 따라서 세션은 연결상태를 유지하는 것보다 연결상태의 안정성을 더 중요시 하게 된다.
<위키백과 - 세션 (컴퓨터 과학)>
게임 서버에서 세션은 클라이언트와 서버 간의 상호작용을 추적하고 유지하기 위한 개념입니다.
세션은 주로 멀티플레이어 게임 및 온라인 게임에서 사용됩니다.
세션의 중요한 의미는 아래와 같습니다.
제가 세션을 이해했던 부분은 인증과 인가(권한 부여)만 있었다는 것을 알게되었습니다.
게임 서버를 강의를 보며 구축하면서 게임 상태 저장과 통신 및 데이터 교환을 주로 다뤄보고 있습니다.
이런 클라와 서버의 상호작용을 유지하고 추적하는 것이 세션이라는 것을 알게되었습니다.
그렇다면 게임 서버에서 이 세션을 담당하는 예제 코드는 어떻게 되어있는지 알아봅시다.
개념을 보기만 하는 것보다는 코드를 보는게 더 기억에 남고 응용할 수 있기 때문에 정리해봤습니다.
public abstract class Session
{
Socket _socket;
int _disconnected = 0;
public void Start(Socket socket)
{
// 세션 소켓을 초기화 합니다.
_socket = socket;
// 비동기 소켓 통신을 위해 이벤트를 생성합니다.
SocketAsyncEventArgs recvArgs = new SocketAsyncEventArgs();
// 비동기 소켓 작업이 완료되었을 때 호출될 이벤트를 설정합니다.
// 콜백 함수 => OnRecvCompleted
recvArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnRecvCompleted);
// 버퍼의 사이즈를 세팅합니다.
recvArgs.SetBuffer(new byte[1024], 0, 1024]);
RegisterRecv(recvArgs);
}
// Buff를 Send합니다.
public void Send(byte[] sendBuff)
{
_socket.Send(sendBuff);
}
// 소켓을 셧다운하고 Close 합니다.
public void Disconnect()
{
// 멀티 스레드 환경에서의 안정성을 위해 Lock 설정을 추가합니다.
// _disconnected 플래그 값을 활용
if (Interlocked.Exchange(ref _disconnected, 1) == 1)
return;
_socket.Shutdown(SocketShutdown.Both);
_socket.Close();
}
#region 네트워크 통신
void RegisterRecv(SocketAsyncEventArgs args)
{
// 비동기로 소켓 Receive를 처리합니다.
// args 객체에 결과를 저장하고 작업이 완료되면 Completed 이벤트를 트리거하여 완료된 상태를 알립니다.
// 비동기 소켓 작업이 실패하면 직업 OnRecvCompleted 함수를 호출합니다.
bool pending = _socket.ReceiveAsync(args);
if (pending == false)
OnRecvCompleted(null, args);
}
// 비동기 소켓 통신에서 데이터 수신 작업이 완료되면 호출되는 이벤트 핸들러
void OnRecvCompleted(Object sender, SocketAsyncEventArgs args)
{
// 수신된 데이터의 바이트 수가 0보다 크면 성공
if (args.BytesTransferred > 0 && args.SocketError == SocketError.Success)
{
// TODO
try
{
string recvData = Encoding.UTF8.GetString(args.Buffer, args.Offset, args.BytesTransferred);
Console.WriteLine($"[From Client] {recvData}");
RegisterRecv(args);
}
catch (Exception e)
{
Console.WriteLine($"OnRecvCompleted Failed {e}");
}
}
else
{
// TODO Disconnect
}
}
#endregion
}
Session은 Listner에서 Accept 이 후에 진행되는 작업입니다.
(Listener 작업에 대한 내용은 링크를 누르시면 볼 수 있습니다.)
둘 다 네트워크 통신에서 데이터를 전송하는 데 사용됩니다.
직역하면 Receive는 받다, Send는 보내다가 됩니다.
소켓 통신에서 두 버퍼는 데이터의 임시 저장 및 전송 관련 작업에 사용됩니다.
개발자는 각 용도에 맞게 버퍼의 사이즈와 주고 받을 데이터를 미리 정하고 코드를 작성합니다.
소켓 통신에서 버퍼를 사용하는 이유는 데이터 전송의 효율성과 안정성을 높이기 위해서입니다.
작은 데이터 패킷을 하나씩 전송하는 것보다, 데이터를 버퍼에 쌓아서 한 번에 보내는 경우 레이턴시(지연 시간)를 줄일 수 있습니다.
이런 데이터 패킷은 블록 단위로 처리하기 때문에 효율성을 올려주고 패킷 손실 또는 오류를 처리하기 쉽습니다.
(재전송이나 오류 복구 메커니즘을 통한 데이터 손실 최소화)
이런 버퍼의 사이즈는 개발자가 직접 설정할 수 있는데,
어떻게 설정 하는지에 따라서도 성능 차이가 발생 한다고 합니다.