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

이제 Peer 프로그램을 구현합시다. EH 메신저 솔루션에 Peer 이름의 Win32 프로젝트를 만드세요.

Peer 프로젝트에 리소스를 추가하여 시작 대화상자의 화면을 구성합시다.

[그림 7.20] IDD_START 대화상자
[그림 7.20] IDD_START 대화상자
번호 컨트롤 ID 종류 번호 컨트롤 ID 종류 번호 컨트롤 ID 종류
1 IDD_START 대화상자 3 IDE_PW Edit 5 IDB_REG button
2 IDE_ID Edit 4 IDE_NAME Edit 6 IDB_LOGIN button

[표 7.1] 시작 대화상자 컨트롤 ID

그리고 로긴 후에 메시지와 파일을 주고받는 메인 대화상자를 추가 편집하세요.

[그림 7.21] 메인(IDD_MAIN) 대화상자
[그림 7.21] 메인(IDD_MAIN) 대화상자
 그리고 파일을 드래그 드랍(마우스로 끌어서 넣기)할 수 있게 MainDlg의 Accept Files 속성을 True로 지정하세요.

[그림 7.22] Accept Files 속성 설정
[그림 7.22] Accept Files 속성 설정
 먼저 필요한 헤더 파일을 포함하세요.

로긴 상태의 다른 사용자 계정의 정보를 기억하기 위한 자료구조는 map을 사용할게요.

사용하기 편하게 타입 재지정하세요.

로긴 상태의 다른 사용자 계정 정보를 기억할 변수를 선언하세요. 여기에서는 편의상 전역에 선언할게요.

로긴 성공하면 로긴한 아이디 정보와 다른 사용자 계정 정보를 수신할 포트 번호, 숏 메시지 수신 포트 번호, 파일 수신 포트 번호에 대응하는 변수를 선언하세요.

여기에서는 다른 계정의 상태를 수신하는 것과 숏 메시지 수신하는 부분은 WSAAsyncSelect 모델을 사용할 것입니다. 이에 따라 사용자 정의 메시지를 정의하세요.

진입점에서는 윈속 초기화 후에 시작 대화상자를 가동하고 닫힐 때 로긴 성공일 때는 메인 대화 상자를 띄워줍니다. 물론 종료하기 전에 윈속 해제화를 해야겠죠.

시작 대화 상자는 컨트롤에 의한 메시지 처리기만 정의합니다.

시작 대화상자의 메시지 처리기에서는 가입 버튼을 눌렀을 때와 로긴 버튼을 눌렀을 때 대화 상자를 닫기했을 때 처리를 담당합니다.

먼저 가입 버튼을 눌렀을 때 수행할 함수를 작성합시다.

입력한 계정 정보를 얻어옵니다.

가입 요청을 합니다.

가입 요청 결과를 수신합니다. 만약 수신한 메시지가 가입 요청 응답이 아니면 이를 사용자에게 통보합니다. 그렇지 않다면 가입 성공 여부를 알려줍니다.

로긴 버튼을 눌렀을 때 수행할 함수를 작성합시다.

계정 정보를 얻어옵니다.

로긴 요청 메시지를 전송합니다.

응답 메시지를 수신합니다. 만약 수신한 메시지가 로긴 요청 응답 메시지가 아니면 사용자에게 통보합니다. 로긴 요청 응답 메시지가 맞다면 결과를 사용자에게 통보합니다.

닫기를 눌렀을 때는 대화상자를 종료합니다.

이번에는 Main 대화상자의 콜백 프로시저를 작성합시다. 초기화 및 사용자의 컨트롤 사용에 관한 처리기와 파일을 드래그 드롭했을 때 처리기, WSAAsyncSelect 모델을 사용하면서 정의한 사용자 정의 메시지에 관한 처리기가 필요합니다.

먼저 Main 대화상자의 초기에 처리해야 할 작업을 정의합시다.

파일 수신할 디렉토리를 생성하세요.

1초 주기로 Keep Alive를 전송할 수 있게 타이머를 생성하세요.

다른 계정의 상태 정보를 수신하기 위한 소켓을 생성한 후에 WSAAyncSelect 함수로 FD_ACCEPT에 관해 설정합니다. 숏 메시지에 관한 소켓도 마찬가지입니다.

파일을 수신할 소켓을 생성합니다. 파일 수신은 쓰레드로 처리합시다. 다른 계정의 상태 정보나 숏 메시지는 하나의 작업을 위해 쓰레드를 사용하는 것은 처리할 작업에 비해 낭비적인 요소가 있지만 파일을 수신하는 작업 정도라면 쓰레드를 사용하는 것인 낭비라고 볼 수는 없을 거예요.

최초 Keep Alive에서 자신의 정보를 전송합니다.

파일 수신 소켓에 연결 요청을 수락하는 쓰레드 진입점에서는 accept loop으로 구성합니다. 그리고 accept 후에는 파일 수신하는 부분을 별도의 쓰레드로 처리합시다.

파일을 수신하는 부분은 다른 프로그램에서도 사용할 만한 부분이라 별도의 함수를 만들어 호출할게요. 이 부분(recv_file 함수)은 뒤에서 설명할게요.

Keep Alive 타이머가 발생하면 Keep Alive 메시지를 전송하세요. 이를 통해 논리적인 세션을 유지할 것입니다.

Main 대화상자에서 사용자에 의한 제어는 로그 아웃 요청과 탈퇴 요청, 메시지 전송 및 대화상자 닫기가 있겠죠.

로그 아웃 요청에서는 LogOutReq 메시지를 전송 후에 대화 상자를 종료하세요.

탈퇴 요청에서는 UnRegReq 메시지를 전송 후에 대화 상자를 종료하세요.

숏 메시지 전송에서는 사용자가 입력한 메시지와 선택한 계정 정보를 얻어와서 상대 숏메시지 수신 소켓에 연결한 후에 숏 메시지를 전송합니다.

파일을 전송하는 부분에서도 쓰레드를 사용할 거예요. 이 때 쓰레드 전달 인자로 전송할 파일 경로명과 소켓 정보가 필요합니다. 이에 간단한 구조체를 정의하세요.

파일을 드래그 드롭했을 때 처리하는 함수를 작성합시다.

파일을 보낼 상대를 선택하였는지 확인하세요.

상대 파일 수신 소켓에 연결한 후에 전송할 파일 정보를 얻습니다.

쓰레드로 파일 명과 연결한 소켓 정보를 갖고 있는 파라미터를 전달하세요.

파일 전송 부분도 다른 프로그램에서 재사용할 수 있는 부분입니다. 이 부분(send_file 함수)도 뒤에서 설명할게요.

상대 계정 정보 수신 소켓이나 숏 메시지 수신 소켓에 연결 요청이 오면 accept 호출과 WSAAsyncSelect 설정합니다.

상대 계정 정보 소켓에 메시지 수신 혹은 연결 종료가 왔을 때 처리할 처리기입니다.

상대 계정 정보를 수신한 후에 새로운 로긴한 계정 정보인지 로그 아웃한 계정 정보인지에 따라 처리합니다.

상대 계정이 로그 아웃하였을 때는 리스트 목록을 리셋한 후에 로긴한 계정 정보만 다시 목록에 추가하세요.

숏 메시지 수신 소켓에 메시지 수신 혹은 연결 종료가 왔을 때 처리할 처리기입니다.

메시지를 수신하였을 때 수신한 메시지를 리스트에 추가합니다.

다음은 send를 wrapping 한 함수입니다.

다음은 recv를 wrapping한 함수입니다.

파일을 수신하는 함수입니다. 입력 인자로 소켓과 수신한 파일을 기록할 디렉토리 정보를 받습니다.

먼저 파일 이름을 수신합니다.

디렉토리 계정과 함께 경로를 설정하여 파일을 쓰기 모드로 엽니다. 앞에서 Win32 API 파일 입출력으로 파일 매핑을 하였으니 여기에서는 C언어 표준 입출력 함수를 이용할게요. 어떤 것을 선택하더라도 관계 없어요. 여기에서는 단지 서로 다른 함수를 사용하는 것을 익히는 것을 추구할 뿐입니다.

파일 길이를 수신합니다.

파일 정보 조각을 수신할 때는 1024 바이트 단위로 수신할게요.

만약 수신해야 할 길이가 1024 바이트보다 크면 1024바이트 수신하고 파일에 기록합니다.

마지막 파일 정보 조각을 수신하여 파일에 기록합니다.

수신한 정보를 리스트에 추가합니다.

파일 전송 부분은 수신 부분의 역으로 생각하세요.

이상으로 EH 메신저 프로젝트를 마칠게요. 보다 나은 형태로 품질을 개선하는 것은 여러분의 몫으로 남길게요.