서버를 만드는 방식은 여기를 참고해주세요
오늘은 클라이언트를 만들어 보도록 하겠습니다. MFC 프로젝트를 아래와 같이 하나 추가해 주세요.
stdafx.h 맨 위에 아래와 같이 define을 해줍니다.
#pragma once
#define _WINSOCK_DEPRECATED_NO_WARNINGS // 정의
대화상자 헤더파일에 아래와 같이 SOCKET 변수를 추가합니다.
SOCKET m_socketClient;
리소스 뷰에서 연결, 보내기, 종료 버튼을 생성 합니다. ( 더블 클릭하여 이벤트 처리 함수까지 생성하게 합니다. )
연결 버튼 이벤트 함수에 아래와 같이 코딩합니다.
void CASyncSockClientDlg::OnBnClickedButton1()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
WSADATA wsdata;
int iRes = ::WSAStartup( MAKEWORD( 0x02, 0x02 ), &wsdata );
// 소켓 만들기
m_socketClient = ::socket( PF_INET, SOCK_STREAM, 0 );
iRes = ::WSAAsyncSelect( m_socketClient, m_hWnd, 10000, FD_CONNECT | FD_READ | FD_WRITE | FD_CLOSE );
// 서버에 연결
SOCKADDR_IN servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = inet_addr( "192.168.0.5" );
servAddr.sin_port = htons( 1234 );
iRes = ::connect( m_socketClient, (LPSOCKADDR)&servAddr, sizeof( servAddr ) );
}
클라이언트가 접속을 완료하고 패킷을 수신하고, 종료되었을 때 메시지를 받기위해 아래와 같이 WindowProc 함수를 추가합니다.
그리고 내부에 아래와 같이 코딩합니다.
LRESULT CASyncSockClientDlg::WindowProc( UINT message, WPARAM wParam, LPARAM lParam )
{
if ( 10000 == message )
{
SOCKET hSocket = (SOCKET)wParam;
switch ( lParam )
{
case FD_CONNECT:
// 연결 됨
break;
case FD_READ:
// 패킷 수신
{
char cBuff;
memset( &cBuff, 0, sizeof( cBuff ) );
int iLen = ::recv( hSocket, &cBuff, sizeof( cBuff ), 0 );
}
break;
case FD_WRITE:
// 송신 가능함
break;
case FD_CLOSE:
// 종료
int iRes = ::closesocket( hSocket );
break;
}
}
return CDialogEx::WindowProc( message, wParam, lParam );
}
보내기 버튼 함수에 아래와 같이 코딩합니다.
void CASyncSockClientDlg::OnBnClickedButton2()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
char cBuff = 1;
int iSend = ::send( m_socketClient, &cBuff, sizeof( cBuff ), 0 );
if ( SOCKET_ERROR == iSend )
{
// Send error
}
}
종료 버튼 함수에 아래와 같이 코딩합니다.
void CASyncSockClientDlg::OnBnClickedButton3()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
int iRes = ::closesocket( m_socketClient );
::WSACleanup();
}
클라이언트도 마찬가지로 WSAAsyncSelect로 논블로킹 모드로 변환 시켜주는게 핵심입니다.
int
WSAAPI
WSAAsyncSelect(
_In_ SOCKET s, // 소켓
_In_ HWND hWnd, // 네트워크 이벤트가 발생할때 메시지를 받을 윈도우 핸들
_In_ u_int wMsg, // 네트워크 이벤트가 발생할때 수신되는 메시지
_In_ long lEvent // 네트워크 이벤트
);
클라이언트를 만들 때 참고해야할 네트워크 이벤트는 아래와 같습니다.
// FD_READ: 데이터 수신이 가능하면 윈도우 메시지를 발생시킨다.
// FD_WRITE: 데이터 송신이 가능하면 윈도우 메시지를 발생시킨다.
// FD_CLOSE: 상대가 접속을 종료하면 윈도우 메시지를 발생시킨다.
// FD_CONNECT: 접속이 완료되면 윈도우 메시지를 발생시킨다.
// FD_OOB: OOB 데이터가 도착하면 윈도우 메시지를 발생시킨다.
저번 글에 만들었던 서버 프로그램과 이번에 만든 클라이언트 프로그램을 실행 했을 때 순서는 아래와 같습니다.
- 서버 소켓 생성
- 클라이언트 접속
- 서버 or 클라이언트에서 패킷 전송
- 서버 or 클라이언트에서 소켓 종료
저번 서버 글도 그렇고 이번 클라이언트도 작동하게만 만들어 놓아서 뭔가 좀 아쉽습니다.
그래서 제가 추가적으로 수정한 파일을 첨부하였습니다. 아래 파일을 다운받으셔서 참고하세요.