일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- MFC 채팅 프로그램
- Python
- 안드로이드 Firebase
- OpenCV IMAGE
- 파이썬
- MFC 소켓 프로그래밍
- OpenCV 흰색 픽셀
- OpenCV 픽셀
- MFC
- c#
- c++ ifstream
- MFC 채팅 예제
- Android 알람 앱
- 안드로이드 스튜디오
- OpenCV pixel
- C++ 채팅
- 안드로이드 파이어베이스 facebook
- OpenCV 이미지 처리
- MFC TCP/IP
- Kotlin
- OpenCV 이미지
- MFC 채팅
- 안드로이드
- 코틀린
- c++ 쓰레드
- OpenCV 검은색 픽셀
- 안드로이드 데이터베이스
- 안드로이드 firebase 회원가입
- c++ ofstream
- 안드로이드 스튜디오 알람
- Today
- Total
개발자 승학
MFC 소켓 프로그래밍 - 채팅 프로그램(서버, 클라이언트) 본문
일반적인 소켓 프로그래밍은 TCP/IP 소켓 프로그래밍을 말합니다.
이 글은 채팅 서버 / 클라이언트 예제입니다.
서버 - 클라이언트의 접속을 대기 / 수신한 후 연결된 모든 클라이언트에게 메시지를 전달.
클라이언트 - 서버에 접속하여 메시지를 전달하거나 다른 클라이언트의 메시지를 수신.
[서버예제]
1.
프로젝트 이름 ChatServer.
대화상자기반으로 프로젝트를 생성.
프로젝트를 생성하셨다면 다이얼로그에 채팅기록을 볼 수 있는 컨트롤을 배치하겠습니다.
리소스뷰 -> DIalog -> IDD_CHATSERVER_DIALOG에 List Box를 배치하고
이름을 m_List로 변수추가를 해줍니다.
※ 변수추가방법
위 사진처럼 List Box내에 우클릭하여 변수추가를 클릭 후 변수 이름에 m_List로 설정해주시면 변수추가가 완료됩니다.
2.
CListenSocket 클래스와 CClientSocket 클래스를 추가합니다.
CListenSocket 클래스를 CAsyncSocket을 CClientSocket 클래스는 CSocket 클래스를 상속받아 생성합니다.
솔루션 탐색기 우클릭 -> 추가 -> 클래스 -> MFC클래스
위 사진처럼 생성합니다.
CAsyncSocket 클래스는 입출력이 비동기적으로 이루어지는 소켓 - 클라이언트 접속을 기다림
CSocket클래스는 동기적으로 이루어지는 소켓
3.
[CChatServerDlg.h]
// ChatServerDlg.h : 헤더 파일
//
#pragma once
#include "afxwin.h"
#include "ListenSocket.h"
// CChatServerDlg 대화 상자
class CChatServerDlg : public CDialogEx
{
// 생성입니다.
public:
CChatServerDlg(CWnd* pParent = NULL); // 표준 생성자입니다.
CListenSocket m_ListenSocket;
ListenSocket.h를 include하고 CListenSocket 클래스 객체를 멤버로 추가합니다.
4.
[CListenSocket.h]
#pragma once
// CListenSocket 명령 대상입니다.
class CListenSocket : public CAsyncSocket
{
public:
CListenSocket();
virtual ~CListenSocket();
CPtrList m_ptrClientSocketList;
};
CPtrList 클래스 객체를 멤버로 추가합니다.
5.
[CListenSocket.cpp]
클래스뷰 -> CListenSocket 우클릭 -> 속성 -> 재정의(초록색 아이콘) -> OnAccept 재정의
void CListenSocket::OnAccept(int nErrorCode)
{
// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
CClientSocket* pClient = new CClientSocket;
if (Accept(*pClient))
{
pClient->SetListenSocket(this);
m_ptrClientSocketList.AddTail(pClient);
}
else
{
delete pClient;
AfxMessageBox(L"ERROR: Failed to accept new client!");
}
CAsyncSocket::OnAccept(nErrorCode);
}
CClientSocket 클래스를 사용하기위해 CListenSocket.cpp 상단에
#include "ClientSocket.h" 를 추가합니다.
CAsyncSocket 클래스의 OnAccept() - 클라이언트의 TCP접속이 있을 때 자동으로 호출됩니다.
6.
CloseClientSocket 멤버함수 추가
void CListenSocket::CloseClientSocket(CSocket* pClient)
{
POSITION pos;
pos = m_ptrClientSocketList.Find(pClient);
if (pos != NULL)
{
if (pClient != NULL)
{
pClient->ShutDown();
pClient->Close();
}
m_ptrClientSocketList.RemoveAt(pos);
delete pClient;
}
}
특정 클라이언트와의 접속이 종료되었을 때 통신을 끝내고 클래스 객체를 소멸시키는 역할을 합니다.
7.
SendChatDataAll 멤버함수 추가
void CListenSocket::SendChatDataAll(TCHAR* pszMessage)
{
POSITION pos;
pos = m_ptrClientSocketList.GetHeadPosition();
CClientSocket* pClient = NULL;
while (pos != NULL)
{
pClient = (CClientSocket*)m_ptrClientSocketList.GetNext(pos);
if (pClient != NULL)
pClient->Send(pszMessage, lstrlen(pszMessage) * 2);
}
}
연결된 모든 클라이언트에게 동일한 메시지를 전송합니다.
8.
[CClientSocket.h]
// CClientSocket 명령 대상입니다.
class CClientSocket : public CSocket
{
public:
CClientSocket();
virtual ~CClientSocket();
void SetListenSocket(CAsyncSocket * pSocket);
CAsyncSocket* m_pListenSocket;
};
[CClientSocket.cpp]
// CClientSocket
CClientSocket::CClientSocket()
{
m_pListenSocket = NULL;
}
CListenSocket 클래스 객체의 주소를 저장하는 멤버 m_pListenSocket를 추가하고 생성자에서 NULL로 초기화합니다.
9.
[CClientSocket.cpp]
// CClientSocket 멤버 함수
void CClientSocket::SetListenSocket(CAsyncSocket* pSocket)
{
m_pListenSocket = pSocket;
}
10.
[CClientSocket.cpp]
클래스뷰 -> CClientSocket우클릭 -> 속성 -> 재정의(초록색 아이콘) -> OnClose 재정의
void CClientSocket::OnClose(int nErrorCode)
{
// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
CSocket::OnClose(nErrorCode);
CListenSocket* pServerSocket = (CListenSocket*)m_pListenSocket;
pServerSocket->CloseClientSocket(this);
}
CListenSocket 클래스를 사용하기 위해 상단에 #include "ListenSocket.h"를 추가합니다.
CAsyncSocket 클래스의 OnClose() 함수는 연결이 종료되는 시점에 호출됩니다.
11.
[CClientSocket.cpp]
클래스뷰 -> CClientSocket우클릭 -> 속성 -> 재정의(초록색 아이콘) -> OnReceive 재정의
void CClientSocket::OnReceive(int nErrorCode)
{
// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
CString strTmp = L"";
CString strIPAdress = L"";
UINT uPortNumber = 0;
TCHAR szBuffer[1024];
::ZeroMemory(szBuffer, sizeof(szBuffer));
GetPeerName(strIPAdress, uPortNumber);
if (Receive(szBuffer, sizeof(szBuffer)) > 0)
{
CChatServerDlg* pMain = (CChatServerDlg*)AfxGetMainWnd();
strTmp.Format(_T("[%s:%d] : %s"), strIPAdress, uPortNumber, szBuffer);
pMain->m_List.AddString(strTmp);
pMain->m_List.SetCurSel(pMain->m_List.GetCount() - 1);
CListenSocket* pServerSocket = (CListenSocket*)m_pListenSocket;
pServerSocket->SendChatDataAll(szBuffer);
}
CSocket::OnReceive(nErrorCode);
}
CAsyncSocket 클래스의 OnReceive() 메서드는 클라이언트로부터 정보를 수신해야 할 때 호출됩니다.
12.
[CChatServerDlg.cpp]
// CChatServerDlg 메시지 처리기
BOOL CChatServerDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 시스템 메뉴에 "정보..." 메뉴 항목을 추가합니다.
// IDM_ABOUTBOX는 시스템 명령 범위에 있어야 합니다.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 이 대화 상자의 아이콘을 설정합니다. 응용 프로그램의 주 창이 대화 상자가 아닐 경우에는
// 프레임워크가 이 작업을 자동으로 수행합니다.
SetIcon(m_hIcon, TRUE); // 큰 아이콘을 설정합니다.
SetIcon(m_hIcon, FALSE); // 작은 아이콘을 설정합니다.
// TODO: 여기에 추가 초기화 작업을 추가합니다.
if (m_ListenSocket.Create(21000, SOCK_STREAM))
{
if (!m_ListenSocket.Listen())
{
AfxMessageBox(L"ERROR: Listen() return FALSE");
}
}
else
{
AfxMessageBox(L"ERROR: Failed to create server socket!");
}
return TRUE; // 포커스를 컨트롤에 설정하지 않으면 TRUE를 반환합니다.
}
CAsyncSocket 클래스의 Create() 메서드는 소켓을 생성하는 역할을 합니다.
13.
[CChatServerDlg.cpp]
클래스뷰 -> CChatServerDlg우클릭 -> 속성 -> 메시지 -> OnDestroy() 추가
void CChatServerDlg::OnDestroy()
{
CDialogEx::OnDestroy();
// TODO: 여기에 메시지 처리기 코드를 추가합니다.
POSITION pos;
pos = m_ListenSocket.m_ptrClientSocketList.GetHeadPosition();
CClientSocket* pClient = NULL;
while (pos != NULL)
{
pClient = (CClientSocket*)m_ListenSocket.m_ptrClientSocketList.GetNext(pos);
if (pClient != NULL)
{
pClient->ShutDown();
pClient->Close();
delete pClient;
}
}
m_ListenSocket.ShutDown();
m_ListenSocket.Close();
}
CClientSocket 클래스를 사용하기 위해 상단에 #include "ClientSocket.h"를 추가합니다.
모든 클라이언트와의 연결을 종료하고 CClientSocket 클래스 객체를 소멸시킵니다.
while문은 연결된 CClientSocket 클래스 객체를 관리하는 목록을 청므부터 끝까지 탐색하고 소켓을 닫은 후
CClientSocket 클래스 객체를 소멸시킵니다.
다음 포스트에서 클라이언트 예제를 올리겠습니다.
'it > MFC' 카테고리의 다른 글
MFC 소켓 프로그래밍 - 채팅 프로그램(서버, 클라이언트) (6) | 2019.10.11 |
---|