7.5.4 EH 메신저 – StsSVC 구현 [TCP/IP 소켓 프로그래밍 with 윈도우즈]

StsSVC를 구현합시다. EH 메신저 솔루션에 StsSVC 이름의 콘솔 응용 프로젝트를 만드세요. 그리고 이 책에서는 StsSVC를 구현한 이후에 테스트 프로젝트를 만들고 테스트하는 부분은 다루지 않을게요.

StsSVC 프로젝트에 Program.cpp 파일을 추가하여 소스를 구현합시다.

StsSVC에서는 주기적으로 최근에 수신한 KeepAlive 메시지와 현재 시간을 비교하는 작업이 필요합니다. 이 부분은 Win32 API에서 제공하는 타이머를 사용합시다. 이를 위해 Windows.h 파일을 포함합니다.

시간을 얻어오는 부분은 사용하기 편한 time 함수를 사용합시다. 이를 위해 time.h 파일을 포함합니다.

사용자에게 수신한 계정 정보를 보관하기 위해 map과 list를 사용합시다.

수신한 계정 정보와 마지막 KeepAlive 시간을 하나의 구조체로 정의합시다.

사용자 id를 키로 KARecord 개체를 값으로 하는 map을 KAMap, 반복자를 KAIter 이름으로 재지정합시다.

KARecord 개체를 보관하는 list를 KAList, 반복자를 KALIter 이름으로 재지정합시다.

전역 변수로 KAMap 형식 변수를 선언하여 계정 정보를 관리합시다.

진입점 main 함수에서는 윈속 초기화 후에 Sts 서버를 가동하고 윈속을 해제화합니다.

Sts 서버 가동 함수를 작성합시다.

Sts 서버에서는 주기적인 작업이 필요합니다. 이를 위해 윈도우 클래스를 등록합시다.

그리고 윈도우를 생성합니다. 대신 사용자와 상호 작용할 필요는 없어서 화면에 시각화는 하지 않습니다.

메시지 루프도 필요하겠죠.

윈도우 콜백 프로시져에서는 생성할 때 Sts 서버 소켓을 생성하고 타이머를 설정하는 부분이 필요합니다. 이를 수행하는 OnCreate 함수를 만들어 사용합시다.

OnCreate 함수를 작성합시다.

Sts 서버에서는 KeepAlive를 수신하는 부분과 주기적인 타이머로 확인하는 부분이 필요합니다. 수신하는 부분은 별도의 쓰레드에서 수행하게 합시다.

주기적인 타이머를 설정합니다. 이 책에서는 1초 주기로 할게요.

KeepAlive를 수신하는 쓰레드의 진입점 함수를 작성합시다.

STS_PORT를 인자로 TCP 서버를 가동합니다.

메시지를 수신하였을 때 이를 처리하는 부분은 별도의 쓰레드에서 수행하기로 합시다.

메시지를 수신하였을 때 처리하는 쓰레드 진입점 함수를 작성합시다.

쓰레드 인자로 전달받은 소켓을 형식 변환합니다.

그리고 메시지를 수신합니다.

수신한 메시지가 KeepAlive가 아니면 오류입니다.

수신한 메시지를 KeepAlive 메시지로 변환합니다.

만약 수신한 메시지의 stsport값이 0이면 처음 수신한 KeepAlive 메시지는 아닙니다. 이 때는 수신한 시간을 기록합니다. 이 부분은 별도의 함수로 작성합시다.

그렇지 않다면 처음 수신한 KeepAlive 메시지입니다. 이에 관한 처리도 별도의 함수로 작성합시다.

수신한 KeepAlive 시간을 기록하는 함수를 작성합시다.

계정 정보를 관리하는 배열에서 KARecord 개체를 참조합니다.

만약 KARecord 개체가 없다면 계정 정보가 없다는 것인데 이러한 시나리오는 나올 수 없습니다. 그렇지만 개발자의 논리적 버그로 이와 같은 문제가 발생했다면 빠르게 확인할 수 있게 오류 메시지를 출력합시다. 별도의 시스템 구동 로그 파일을 만들어서 파일에 기록하는 것도 좋은 방법이겠죠. 여기에서는 단순하게 콘솔에 출력하기로 할게요.

현재 시간으로 가장 최근에 수신한 KeepAlive 시간을 설정합니다.

처음으로 KeepAlive 메시지를 수신하였을 때 처리하는 함수를 작성합시다.

수신한 메시지로 KARecord 개체를 생성합니다.

처음으로 KeepAlive 메시지를 수신하면 DbmSVC에게 상태 변화 요청 메시지를 전송합니다. 이 때 상태값은 STS_LOGGED입니다.

계정 관리 map의 시작 위치와 끝 위치를 구합니다.

보관한 KARecord 개체를 참조할 변수를 선언합시다.

순차적으로 위치를 이동하면서 보관한 계정 정보를 참조합니다.

만약 참조한 정보가 참이면 기존 계정에게 새로운 계정 정보를 전송하고 새로운 계정에게 기존 계정 정보를 전송합니다. 이 부분은 별도의 함수로 작성합시다.

계정 관리 map 에 새로운 계정 정보를 보관합니다.

계정 정보를 전송하는 함수를 작성합시다.

먼저 전송할 OtherUserInfo 메시지를 생성합니다.

수신할 계정에 연결하여 메시지를 전송합니다.

주기적으로 마지막 수신한 KeepAlive 시간과 현재 시간을 비교하여 약속한 시간 범위 내에 KeepAlive 메시지를 보내지 않은 계정을 강제 로그 아웃 처리하는 함수를 작성합시다.

현재 시각을 구합니다.

강제 로그 아웃 처리할 계정 정보를 보관할 리스트 변수를 선언합시다.

계정 정보를 보관하는 map의 시작 위치와 끝 위치를 구합니다.

보관한 계정 정보를 참조할 변수를 선언합니다.

순차적으로 보관한 계정 정보를 참조합니다.

만약 계정 정보가 참일 때 현재 시간과 가장 최근에 수신한 KeepAlive 시간의 차이를 구합니다.

그리고 그 차이가 약속한 시간(이 책에서는 3초)보다 크면 강제 로그 아웃할 대상이므로 계정 정보를 보관한 map은 0으로 설정하고 list에 계정 정보를 추가합니다.

강제 로그 아웃할 계정 정보를 보관한 리스트의 시작 위치와 끝 위치를 구합니다.

순차적으로 위치를 이동하여 강제 로 그아웃 처리합니다. 강제 로그 아웃 처리는 별도의 함수로 작성합시다.

강제로 로그 아웃 처리하는 함수를 작성합시다.

먼저 계정 정보의 모든 포트 번호를 0으로 설정합시다. Peer에서 수신한 계정 정보의 포트 번호가 0이면 상대가 로그 아웃한 것으로 판별할 것입니다.

Dbm 서비스에 연결하여 상태를 변경 요청합니다. 이 때 변경할 상태는 STS_REG입니다.

로긴 상태의 계정 정보를 보관하는 map의 시작 위치와 끝 위치를 구합니다.

보관한 계정 정보를 참조할 변수를 선언합시다.

순차적으로 보관한 계정 정보를 참조합니다.

참조한 계정 정보가 있다면 해당 계정에게 강제 로그 아웃할 계정 정보를 전송합니다.