티스토리 뷰

Visual Studio 2012


이번엔 소켓으로 통신하는 채팅 클라이언트 프로그램을 만들어보자.


클라이언트 프로그램은 클라이언트 담당 소켓만 있으면 된다.


1. MFC 프로젝트 생성


  • 새 프로젝트 - MFC 응용 프로그램 선택


  • 대화상자 기반 선택


  • Windows 소켓 선택


2. Client 소켓 만들기


  • 클래스 마법사에서 'MFC 클래스' 선택


  • CClientSocket 클래스 추가 - 기본클래스는 CSocket으로 지정



3. CClientSocket 클래스 구현


- 필요 함수

  1. SetWnd : Sendmessage 활용을 위한 메인의 핸들을 받는 함수
  2. OnReceive : 서버 통신 받을 시의 처리 함수
  3. OnClose : 소켓 해제 시의 처리 함수

- 사용자 메시지

  1. WM_CLIENT_RECV : 서버에서 응답 온 메시지 표시하기 위해

  • 함수 및 핸들 변수 선언
    • // ClientSocket.h : 헤더 파일입니다.
      //
      
      #define WM_CLIENT_RECV WM_USER+1
      
      class CClientSocket : public CSocket
      {
      public:
      	CClientSocket();
      	virtual ~CClientSocket();
      	void SetWnd(HWND hWnd);
      	HWND m_hWnd;
      	virtual void OnReceive(int nErrorCode);
      	virtual void OnClose(int nErrorCode);
      };
      


      • 함수 구현
      // ClientSocket.cpp : 구현 파일입니다.
      //
      
      void CClientSocket::SetWnd(HWND hWnd)
      {
      	m_hWnd = hWnd;
      }
      
      void CClientSocket::OnReceive(int nErrorCode)
      {
      	// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
      	TCHAR szBuffer[1024];
      	ZeroMemory(szBuffer, sizeof(szBuffer));
      
      	if(Receive(szBuffer, sizeof(szBuffer)) > 0) { 
      		SendMessage(m_hWnd, WM_CLIENT_RECV,0,(LPARAM)szBuffer);
      	}
      
      	CSocket::OnReceive(nErrorCode);
      }
      
      void CClientSocket::OnClose(int nErrorCode)
      {
      	// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
      	ShutDown();
      	Close();
      
      	CSocket::OnClose(nErrorCode);
      
      	AfxMessageBox(_T("ERROR:Disconnected from server!"));
      	PostQuitMessage(0);
      }


      4. 메인 클래스 구현


      • 대화상자 설정

      아래과 같이 구상한다.



      - IDC_LIST_MSG : 메시지 표시용 리스트박스

      - IDC_EDIT_SEND : 보낼 메시지

      - IDC_BTN_SEND : 메시지 SEND 버튼


      • 헤더 설정
      // socClientDlg.h : 헤더 파일
      //
      
      #include "ClientSocket.h"
      
      // CsocClientDlg 대화 상자
      class CsocClientDlg : public CDialogEx
      {
      	...
      private:
      	CClientSocket m_ClientSocket;
      
      // 구현입니다.
      public:
      	CListBox m_list_msg;
      	CEdit m_edit_send;
      	afx_msg void OnBnClickedBtnSend();
      protected:
      	afx_msg LRESULT OnClientRecv(WPARAM wParam, LPARAM lParam);  //사용자 메시지 함수 추가
      };


      • 소스 구현

      초기 설정


      // socClientDlg.cpp : 구현 파일 // #define PORT 9999 void CsocClientDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_EDIT_SEND, m_edit_send); //IDC_EDIT_SEND 컨트롤 변수 추가 DDX_Control(pDX, IDC_LIST_MSG, m_list_msg); //IDC_LIST_MSG 컨트롤 변수 추가 } BEGIN_MESSAGE_MAP(CsocClientDlg, CDialogEx) ... ON_MESSAGE(WM_CLIENT_RECV, &CsocClientDlg::OnClientRecv) ... END_MESSAGE_MAP() BOOL CsocClientDlg::OnInitDialog() { ... // TODO: 여기에 추가 초기화 작업을 추가합니다. m_ClientSocket.SetWnd(this->m_hWnd); m_ClientSocket.Create(); if(m_ClientSocket.Connect(_T("127.0.0.1"), PORT) == FALSE) { AfxMessageBox(_T("ERROR : Failed to connect Server")); PostQuitMessage(0); return FALSE; } return TRUE; // 포커스를 컨트롤에 설정하지 않으면 TRUE를 반환합니다. }


      버튼 클릭 시

      void CsocClientDlg::OnBnClickedBtnSend()
      {
      	// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
      	CString str;
      	UpdateData(TRUE);
      	m_edit_send.GetWindowTextW(str);
      	m_ClientSocket.Send((LPVOID)(LPCTSTR)str,str.GetLength() *2);
      	m_edit_send.SetWindowTextW(_T(""));
      	UpdateData(FALSE);
      }
      


      사용자 메시지 핸들 함수

      afx_msg LRESULT CsocClientDlg::OnClientRecv(WPARAM wParam, LPARAM lParam)
      {
      	LPCTSTR lpszStr = (LPCTSTR)lParam;
      
      	m_list_msg.InsertString(-1,lpszStr);
      	m_list_msg.SetCurSel(m_list_msg.GetCount()-1);
      	return 0;
      }


      5. 테스트!


      0


      소스코드 참조: https://github.com/jjj0214/MFC_Samples/tree/master/socClient


      댓글