C++/MFC

[MFC] 채팅 프로그램 - 서버 (1/3)

balabala 2021. 8. 25. 23:31
728x90
SMALL
- 설명 - 
프로젝트 명 : ChatServer(대화 상자)
① CSocket 클래스 이용 : 동기식(블로킹)으로 동작
② 서버 클라이언트 1:1 통신
③ 해당 글은 서버용 채팅 프로그램 만드는 법 설명

 

1. MFC 애플리케이션 옵션 설정
애플리케이션 종류 : 대화 상자
[고급 기능][Windows 소켓] 활성화

그림1. MFC 애플리케이션 옵션 설정

 

2. 다이얼로그 디자인 및 ID

그림2. 서버 프로그램 다이얼로그
그림3. 멤버변수 설정

 

3. CSocket 기반의 CSocServer, CSocCom MFC 클래스 생성
[프로젝트][클래스 마법사][클래스 추가] 버튼 옆의 ▼ 버튼[MFC 클래스] 선택클래스 생성
CSocServer : 서버용 소켓
CSocCom : 통신용 소켓

그림4. MFC 클래스 만드는 방법
그림5. CSocServer 클래스
그림6. CSocCom 클래스

4. CSocServer, CSocCom MFC 클래스에 가상 함수 추가
OnAccept(CSocServer) : 클라이언트 접속 요청 처리
OnReceive(CSocCom) : 데이터가 도착했다는 것을 알려줌

그림7. OnAccept 가상 함수 추가
그림8. OnRecieve 가상 함수 추가

 

5. stdAfx.h 헤더 파일 생성
CSocServer, CSocCom 헤더 파일 include
사용자 정의 메시지 선언
 - UM_ACCEPT : 메인 윈도우에 메시지를 보내기 위한 구별자. 즉, 메인 윈도우(여기서는 m_hWnd)에       
   SendMessage를 이용해 UM_ACCEPT라는 사용자 정의 메시지를 줌.
 - UM_RECEIVE : UM_ACCEPT와 비슷한 역할.

그림9. stdAfx.h 생성

  • stdAfx.h 소스 보기
더보기
#pragma once
#include <afxsock.h>            // 소켓 클래스 사용 위해 include
#include "SocServer.h"            // 서버용 소켓
#include "SocCom.h"                // 통신용 소켓

#define UM_ACCEPT WM_USER+1        // 메인 윈도우에 메시지를 보내기 위한 구별자. 즉, 메인 윈도우(여기서는 m_hWnd)에 SendMessage를 이용해 UM_ACCEPT라는 사용자 정의 메시지를 줌.
#define UM_RECEIVE WM_USER+2    // UM_ACCEPT와 비슷한 역할
 

 

6. CSocCom 클래스 작업
HWND m_hWnd : 메인 윈도우 핸들
Init() : 소켓 클래스와 메인 윈도우를 연결시킴
OnReceive() : 데이터가 도착했다는 것을 알려줌

그림10. SocCom.h 소스 코드

  • SocCom.h 소스 보기
더보기
#pragma once

// CSocCom 명령 대상
// 통신용 소켓
 #define UM_RECEIVE WM_USER+2

class CSocCom : public CSocket
{
public:
    CSocCom();
    virtual ~CSocCom();

    HWND m_hWnd;                            // 메인 윈도우 핸들
    void CSocCom::Init(HWND hWnd);            // 소켓 클래스와 메인 윈도우를 연결시킴    
    virtual void OnReceive(int nErrorCode);    // 데이터가 도착했다는 것을 알려줌
};
 
  • SocCom.cpp 소스 보기
더보기
// CSocCom 멤버 함수
// 소켓 클래스와 메인 윈도우를 연결시킴
void CSocCom::Init(HWND hWnd) {
    m_hWnd = hWnd;
}

// 데이터가 도착했다는 것을 알려주는 가상 함수
void CSocCom::OnReceive(int nErrorCode)
{
    // TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
    SendMessage(m_hWnd, UM_RECEIVE, 0, 0);
    CSocket::OnReceive(nErrorCode);
}
 
SMALL
7. CSocServer 클래스 작업
HWND m_hWnd : 메인 윈도우 핸들
CSocCom m_socCom : 연결 요청을 한 클라이언트 서버와 실제 연결이 되는 소켓
Init() : 소켓 클래스와 메인 윈도우를 연결시킴
OnAccept() : 클라이언트 접속 요청 처리
GetAcceptSocCom() : 통신 소켓 리턴

그림11. SocServer.h 소스 코드

  • SocServer.h 소스 보기
더보기
#pragma once

#include "SocCom.h"
#define UM_ACCEPT WM_USER+1

// CSocServer 명령 대상
// 서버용 소켓
class CSocServer : public CSocket
{
public:
    CSocServer();
    virtual ~CSocServer();

    CSocCom m_socCom;                        // 연결 요청을 한 클라이언트 서버와 실제 연결이 되는 소켓
    CSocCom* GetAcceptSocCom();                // 통신 소켓 리턴

    HWND m_hWnd;                            // 메인 윈도우 핸들
    void CSocServer::Init(HWND hWnd);        // 소켓 클래스와 메인 윈도우를 연결시킴    
    virtual void OnAccept(int nErrorCode);    // 클라이언트 접속 요청 처리
};
 
  • SocServer.cpp 소스 보기
더보기
// CSocServer 멤버 함수

// 소켓 클래스에서는 소켓의 메시지(On으로 시작하는 함수)만을 처리하고, 실제 동작은 메인 프로그램에서 수행
// 소켓 클래스와 메인 윈도우를 연결시키는 것이 필요 → Init 함수
void CSocServer::Init(HWND hWnd) {
    // 메인 윈도우 포인터 받기
    m_hWnd = hWnd;
}

// 클라이언트에서 접속 요청이 올 경우 OnAccept 함수가 호출됨
// OnAccept 함수가 호출되면 접속 요청할 한 소켓과 다른 소켓을 연결하기 위해 Accept 함수를 호출한 뒤 메인 윈도우에 OnAccept 함수가 호출되었다는 것을 알려줌
void CSocServer::OnAccept(int nErrorCode)
{
    // TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
    Accept(m_socCom);                        // m_socCom은 연결 요청을 한 클라이언트 서버와 실제 연결이 되는 소켓
    SendMessage(m_hWnd, UM_ACCEPT, 0, 0);
    CSocket::OnAccept(nErrorCode);
}

// 메인 윈도우에서는 m_socCom을 얻어서 통신을 처리
CSocCom* CSocServer::GetAcceptSocCom() {
    // 통신소켓을 return
    // 반환되는 통신 소켓은 클라이언트와 연결됨
    return &m_socCom;
}
 

 

8. ChatServerDlg에 서버용 소켓, 통신용 소켓 선언 및 OnInitDialog 메시지 함수 코딩
CSocServer m_socServer : 서버용 소켓
CSocCom* m_socCom : 통신용 소켓
OnInitDialog : 클라이언트 접속 기다리는 코드 추가(포트번호 5000)

그림12. ChatServerDlg.h 소스 코드
그림13. OnInitDialog 소스 코드

  • 소스 보기
더보기
    // TODO: 여기에 추가 초기화 작업을 추가합니다.
    m_socCom = NULL;
    // 서버 소켓을 생성(포트번호 5000)
    m_socServer.Create(5000);
    // 클라이언트의 접속을 기다림
    m_socServer.Listen();
    // 소켓 클래스와 메인 윈도우(여기에서는 CChatServerDlg)를 연결
    m_socServer.Init(this->m_hWnd);
 

 

9. ChatServerDlg에 메시지 함수 추가
OnAccept() : 클라이언트 접속 요청이 왔을때 실제 접속은 CSocServer 클래스의 OnAccept()에서 처리하고, 접속한 소켓은 GetAcceptSocCom을 이용해 얻어옴.
OnReceive() : 소켓 연결 이후 모든 통신을 해당 메시지 함수를 이용함.

그림14. ChatServerDlg.h 메시지 함수 추가
그림15. ChatServerDlg.cpp 메시지 함수 추가

  • 그림 14, 그림 15의 소스 보기
더보기
// ChatServerDlg.h
afx_msg LPARAM OnAccept(UINT wParam, LPARAM lParam);    // 클라이언트 접속 요청이 왔을 때 실행되는 메세지 함수
afx_msg LPARAM OnReceive(UINT wParam, LPARAM lParam);    // 클라이언트에서 오는 데이터 수신할때 실행되는 메세지 함수

// ChatSeverDlg.cpp - MESSAGE_MAP PART
ON_MESSAGE(UM_ACCEPT, OnAccept)    
ON_MESSAGE(UM_RECEIVE, OnReceive)
 
  • OnAccept(), OnReceive() 소스 보기
더보기
// 클라이언트 연결 요청이 왔기 때문에 Accept 함수로 접속
// 실제 접속을 담당하는 것은 CSocServer
// 이렇게 접속한 소켓은 GetAcceptSocCom을 이용해 얻어옴
// OnAccept 실행 이후 서버용 소켓인 m_socServer의 역활은 끝나고, 실제 모든 통신은 통신용 소켓인 m_socCom을 이용
LPARAM CChatServerDlg::OnAccept(UINT wParam, LPARAM lParam) {
    // 클라이언트에서 접속 요청이 왔을 때
    m_strStatus = "접속성공";

    // 통신용 소켓을 생선한 뒤
    m_socCom = new CSocCom;
    // 서버소켓과 통신소켓을 연결한다.
    m_socCom = m_socServer.GetAcceptSocCom();
    m_socCom->Init(this->m_hWnd);

    m_socCom->Send("접속성공", 256);    // 소켓이 연결되었다는 것을 클라이언트에 알리기 위해 Send 함수로“접속성공”이라는 문자열을 보냄

    UpdateData(FALSE);
    return TRUE;
}

// 데이터를 보내는 것은 소켓 클래스의 멤버 함수인 Send를 이용
// 데이터를 받을 때는 통신 소켓 클래스에 오버라이딩한 OnReceive 메시지 함수를 사용
LPARAM CChatServerDlg::OnReceive(UINT wParam, LPARAM lParam) {
    // 접속된 곳에서 데이터가 도착했을 때
    char pTmp[256];
    CString strTmp;
    memset(pTmp, '\0', 256);

    // 데이터를 pTmp에 받는다.
    m_socCom->Receive(pTmp, 256);
    strTmp.Format("%s", pTmp);

    // 리스트박스에 보여준다.
    int i = m_list.GetCount();
    m_list.InsertString(i, strTmp);
    return TRUE;
}
 

 

10. [전송] 버튼 클릭 메시지 함수 작업
[전송] 버튼 클릭 시 클라이언트에게 데이터 전송
IDC_BUTTON_SEND의 메시지 함수

그림16. [전송] 버튼 클릭 메시지 함수

  • 메시지 함수 소스 보기
더보기
// [전송] 버튼을 클릭했을 때
void CChatServerDlg::OnClickedButtonSend()
{
    // TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
    UpdateData(TRUE);
    char pTmp[256];
    CString strTmp;

    // pTmp에 전송할 데이터 입력
    memset(pTmp, '\0', 256);
    strcpy_s(pTmp, m_strSend);
    m_strSend = "";

    // 전송
    m_socCom->Send(pTmp, 256);

    // 전송한 데이터도 리스트박스에 보여준다.
    strTmp.Format("%s", pTmp);
    int i = m_list.GetCount();
    m_list.InsertString(i, strTmp);

    UpdateData(FALSE);
}
 

 

11. 실행화면


관련글
 

[MFC] 채팅 프로그램 - 클라이언트 (2/3)

- 설명 - 프로젝트 명 : ChatClient(대화 상자) ① CSocket 클래스 이용 : 동기식(블로킹)으로 동작 ② 서버 클라이언트 1:1 통신 ③ 해당 글은 클라이언트 채팅 프로그램 만드는 법 설명 1. MFC 애플리케이

balabala.tistory.com

 

 

[MFC] 채팅 프로그램 - 다중접속(멀티) (3/3)

- 설명 - ① 1, 2번에서 설명한 Server와 Client를 수정 ② 설정에 따라 서버만 모든 클라이언트와 통신 가능하거나, 클라이언트도 같은 클라이언트끼리 통신 가능 1. Client : 다이얼로그 디자인 및 ID

balabala.tistory.com

 

728x90
LIST