[그림 1]
이번에는 방송 서버의 다른 기능을 좀 부각해서 설명을 이어 갑니다. 바로 데이터베이스를 비동기로 처리하는 내용입니다. 지난 포스트의 클래스 다이어그램에서는 표시되지 않았지만, 룸서버는 데이터베이스를 처리하는 TDatabase 클래스와 상호 협조를 하고 있습니다.
TDatabase는 일반적인 데이터베이스 시스템일 수도 있고, 현재 서비스에서처럼 http 프로토콜을 이용해서 사용자 정보 등을 쿼리해 올 수도 있습니다.
일단 예제로 로그인 과정을 한 번 살펴보도록 하겠습니다.
[소스 1] 로그인 과정
procedure TRoomServer.rp_Login(AConnection: TConnection; ACustomData: DWord; AData: pointer; ASize: integer); var ValueList : TValueList; pPacket : PRoomPacket absolute AData; begin if AConnection.IsLogined then Exit; AConnection.UserName := 'Loging...'; ValueList := TValueList.Create; try ValueList.Text := pPacket^.ToString(ASize); if ValueList.Integers['Version'] <> ROOM_SERVER_VERSION then begin sp_ErVersion(AConnection); Exit; end; AConnection.LocalIP := ValueList.Values['LocalIP']; FDatabase.Execute(TDatabaseRequestLogin.Create(Self, AConnection, ValueList.Values['LoginString'])); finally ValueList.Free; end; end;
22: 라인에 보면 FDatabase(TDatabase의 객체 레퍼런스)의 Execute() 메소드를 이용해서 로그인 요청을 하고는 결과를 처리하지 않고 패킷 처리를 완료합니다. 그리고 파라메터로 TDatabaseRequestLogin의 객체를 넘겨주고 있습니다. 그리고 TDatabaseRequestLogin 클래스의 생성자의 맨 앞에 있는 파라메터는 자기 자신을 가르키도록 되어 있습니다.
TDatabaseRequestLogin는 로그인 결과를 Self 객체에게 피드백을 해주는 방식을 취합니다. 콜백 함수와 같은 것 입니다.
[그림 1] Job Flow - DB에 대한 비동기 처리
[그림 1]의 Job Flow를 설명하면 다음과 같습니다.
procedure TRoomServer.DatabaseRequestLogin(AConnection: TConnection; AID: integer; AResult: TValueList); var RoomUnit : TRoomUnit; ErrorMsg : string; UserLimit : integer; IsLogined : boolean; begin if AConnection.IsLogined then Exit; IsLogined := AResult.Booleans['result']; AConnection.RoomID := AResult.Values['room_id']; AConnection.UserID := AResult.Values['user_id']; AConnection.UserName := AResult.Values['user_name']; AConnection.UserLevel := AResult.Integers['user_level']; // TODO: 임시 코드 예전 웹 서비스에 맞춰서 추가 함 if AResult.Booleans['owner'] then AConnection.UserLevel := TUserLevel.ADMIN; ErrorMsg := AResult.Values['error_msg']; UserLimit := AResult.Integers['user_limit']; AConnection.UserID := TIdURI.URLDecode(AConnection.UserID); AConnection.UserID := StringReplace(AConnection.UserID, '%20', ' ', [rfReplaceAll]); ErrorMsg := TIdURI.URLDecode(ErrorMsg); ErrorMsg := StringReplace(ErrorMsg, '%20', ' ', [rfReplaceAll]); if not IsLogined then begin sp_ErLogin(AConnection, ErrorMsg); Exit; end; RoomUnit := FRoomList.FindRoomByRoomID(AConnection.RoomID); if RoomUnit = nil then begin sp_ErLogin(AConnection, '강의실에 접속하는 중 에러가 발생하였습니다.'#13#10'잠시 후 다시 시도하시기 바랍니다.'); Exit; end; if (UserLimit > 0) and (RoomUnit.ConnectionList.Count >= UserLimit) then begin sp_ErLogin(AConnection, '강의실 정원을 초과하였습니다.'); Exit; end; if (AConnection.UserLevel < TUserLevel.ADMIN) and RoomUnit.CheckBlackList(AConnection) then begin sp_ErLogin(AConnection, '강퇴 된 사용자는 당분간 강의실을 이용하실 수가 없습니다.'); Exit; end; AConnection.IsLogined := IsLogined; RoomUnit.UserIn(AConnection); end;
[소스 2]에 구현된 DatabaseRequestLogin 메소드는 IDatabaseRequestLogin 인터페이스의 메소드입니다. 즉, TRoomServer는 DB 처리에 대한 Call back을 받기 위해서, IDatabaseRequestLogin 상속 받아서 구현해야 합니다.
[소스 3]은 IDatabaseRequestLogin와 TDatabaseRequestLogin의 선언부 입니다.
[소스 3]
type IDatabaseRequestLogin = interface ['{9B615467-9024-41C8-8E92-CD40B64AF68E}'] {* 로그인 요청에 대한 데이터 처리 결과 통보 @param AConnection 처리를 요청한 (연결) 객체 @param AID 처리를 요청한 객체의 아이디 @param AResult 처리 결과 데이터 } procedure DatabaseRequestLogin(AConnection:TConnection; AID:integer; AResult:TValueList); end; TDatabaseRequestLogin = class (TDatabaseRequest) protected procedure Execute; override; public Receiver: IDatabaseRequestLogin; Connection : TConnection; ConnectionID : integer; LoginString : string; /// JSon 포멧의 RoomID, UserID, UserPass, etc {* @param AReceiver 처리를 요청한 그리고 결과를 메시지를 받을 객체 @param AConnection 처리를 요청한 (연결) 객체 @param ALoginString 로그인을 위한 정보 } constructor Create(AReceiver:IDatabaseRequestLogin; AConnection:TConnection; ALoginString:string); reintroduce; end;
데이터베이스 처리는 로그인 뿐 아니라, 게임 등에서는 승패 정보 등을 저장하고 순위 정보를 쿼리해오는 등의 다양한 일을 하게 됩니다. 저는 모든 요청 사항에 대하여 따로 클래스와 인터페이스를 작성하여 처리하고 있습니다.
클래스 상속과 인터페이스 구현의 차이 #2 (6) | 2013.12.12 |
---|---|
PC 화면 압축과 실시간 전송에 대한 연구 (0) | 2013.10.20 |
HiMyTV Player 만들기 #3 - Core Module에 대한 이해 계속 (0) | 2013.07.13 |
HiMyTV Player 만들기 #2 - Core Module에 대한 이해 (0) | 2013.07.13 |
The Core of HiMyTV Player (0) | 2013.07.13 |