현재강좌 : Winsock 프로그래밍 이전: 5.3 윈속 채팅 서버


5.4 윈속 채팅 클라이언트

▶ 윈속 채팅 클라이언트 프로그램 WChat_Client.c는 사용자가 키보드에서 입력한 문자열을 서버에게 전송하고 서버가 보내온 메시지를 화면에 출력하는 기능을 수행한다.

▶ 그림 5-3은 채팅 클라이언트 프로그램의 실행 예이다 (그림 5-3을 구성하는 리소스 파일 Client.rc는 Visual C로 작성하였다).

그림 5-3 채팅 클라이언트 프로그램 실행 예

5.4.1 프로그램 주요부분 설명

(1) 헤더 파일과 전역변수

▶ WChat_Client.c는 다음과 같은 헤더 파일과 전역변수를 필요로 한다.

#include <windows.h> /* 윈도우 응용 프로그램의 마스터 헤더 파일 */

#include <stdlib.h> /* 문자 처리를 위한 헤더 파일 */

#include <Winsock.h> /* 윈속 관련 상수 및 함수 선언 */

#include "Client.h" /* 리소스 파일 */

HINSTANCE hInst; /* 현재 윈도우의 인스턴스 */

WNDPROC OldProc; /* 서브클래싱을 하기 위해 저장할 기존의 프로시져 */

char msg[512]; /* 입출력용 데이터 */

SOCKET m_s; /* 소켓번호 */

SOCKADDR_IN m_addr; /* 인터넷 타입의 소켓주소 구조체 */

HWND hParent; /* Parent 윈도우의 핸들 */

(2) WM_INITDIALOG의 처리

▶ 윈도우가 처음 생설될 때 발생하는 메시지인 WM_ INITDIALOG의 처리부에서는 다음과 같이 윈속의 초기화(InitSocket())와 다음에 설명한 에디트 컨트롤 서브클래싱(subclassing)을 수행한다.

case WM_INITDIALOG:

memset(msg, '\0', sizeof(msg));

if(!InitSocket(hWnd))

return FALSE;

OldProc = (WNDPROC) SetWindowLong (GetDlgItem(hWnd, IDC_EDIT),

GWL_WNDPROC, (LONG)EditProc);

return TRUE;

(3) 서브클래싱

▶ 클라이언트 프로그램은 다이얼로그 박스로부터 실행이 시작되는데 에디트 컨트롤(edit control)을 통하여 데이터를 입력받는다.

▶ 에디트 컨트롤에서는 사용자가 리턴('\n')까지 입력한 문자열을 받아, 사용자 정의 메시지인 WM_ASYNC를 이용하여 처리한다. 이를 위하여 서브클래싱을 사용한다.

▶ 서브클래싱이란 윈도우에서 수행되던 프로시져를 사용자 정의 프로시져로 연결하여 원하는 작업을 하다가 그 작업이 완료되면 다시 원래의 프로시져로 되돌아오는 기능을 말한다.

▶ 본 예제에서는 서브클래싱을 이용하여 사용자가 리턴키를 입력한 사실을 Parent 윈도우에 알려준다.

▶ 아래의 프로그램 코드는 현재의 프로시져를 OldProc로 잠시 저장하고 사용자 프로시져인 EditProc를 등록하는 것을 나타낸다.

OldProc = (WNDPROC) SetWindowLong (GetDlgItem(hWnd, IDC_EDIT),

GWL_WNDPROC, (LONG)EditProc);

▶ 위에서 IDC_EDIT는 에디트 컨트롤의 ID이며, EditProc는 에디트 컨트롤을 처리하기 위해 새로 정의한 프로시져 이름이다.

(4) WM_CHAR 메시지 처리

▶아래는 EditProc의 구현 내용으로, 사용자가 키보드로 글자를 입력하였을 때 발생하는 메시지 WM_CHAR를 처리한다.

▶ 리턴키를 제외한 다른 문자들은 모두 원래의 프로시져로 보내며(CallWindowProc() 호출) 사용자가 리턴키를 누르면 지금까지 에디트 컨트롤에 저장된 데이터를 GetWindowText() 함수를 사용하여 얻어온 후 PostMessage()를 사용하여 사용자 정의 메시지 WM_ASYNC를 발생시킨다.

LRESULT CALLBACK EditProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {

switch(iMsg) {

case WM_CHAR:

if(wParam == VK_RETURN) { /* 리턴키 입력 */

/* 현재 에디트 컨트롤에 저장된 문자열을 얻는다 */

GetWindowText(hWnd, (LPTSTR)msg, sizeof(msg));

/* WM_ASYNC 메시지 발생 */

PostMessage(hParent, WM_ASYNC, m_s,

WSAMAKESELECTREPLY(FD_WRITE, 0));

SetWindowText(hWnd, "");

break;

}

}

/* 리턴키가 아니면 원래의 프로시져로 문자열을 입력 */

return CallWindowProc(OldProc, hWnd, iMsg, wParam, lParam);

}

(5) WM_COMMAND의 처리

▶ "종료" 버튼이 눌릴 때 발생하는 메시지로 소켓을 종료하기 위하여 WSACleanup()을 호출하고 윈도우에 종료 메시지 WM_DESTROY를 보내기 위하여 Destroy- Window() 함수를 호출한다.

case WM_COMMAND:

switch(wParam) {

case IDC_EXIT: /* 종료버튼 입력 처리 */

closesocket(m_s);

m_s = INVALID_SOCKET; /* 소켓을 원상태로 초기화 */

WSACleanup();

DestroyWindow(hWnd); /* WM_DESTROY 발생 */

return TRUE;

}

break;

(6) WM_DESTROY의 처리

▶ 위의 DestroyWindow()에 의해 윈도우 내부적으로 발생하는 메시지로서 윈도우를 종료시킬 때 사용된다.

▶ 아래에서 PostQuitMessage() 함수는 윈도우 메시지 큐에 종료 메시지를 전송한다.

case WM_DESTROY:

PostQuitMessage(0);

return TRUE;

 

(7) WM_ASYNC의 처리

▶ 서버와의 연결, 연결종료, 데이터 송수신 등은 시스템의 I/O 변화시 발생하는 메시지 WM_ASYNC를 이용하여 처리된다.

▶ 발생한 이벤트의 종류를 구분하기 위하여 매크로 WSA- GETSELECTEVENT()를 사용한다.

▶ WChat_Client.c에서 처리해야 할 이벤트의 종류는 FD_ CONNECT, FD_CLOSE, FD_WRITE, FD_READ이며 이들을 각각 처리하는 것을 아래에 보였다.

case WM_ASYNC:

hListBox = GetDlgItem(hWnd, IDC_LIST);

switch(WSAGETSELECTEVENT(lParam)) {

case FD_CONNECT:

/* 서버와 연결 처리 */

case FD_CLOSE:

/* 소켓 종료 처리 */

case FD_WRITE:

/* 채팅 데이터 송신 처리 */

case FD_READ:

/* 채팅 데이터 수신 처리 */

default:

break;

}

WChat_Client.c 프로그램


현재강좌 : Winsock 프로그래밍 이전: 5.3 윈속 채팅 서버