티스토리 뷰
Visual Studio 2012
소켓으로 통신하는 채팅 서버 프로그램을 만들어보자.
서버 프로그램에서 사용하는 소켓은 두 종류가 있다.
- Listen 소켓
- Client 매칭 소켓1. MFC 프로젝트 생성
- 새 프로젝트 - MFC 응용 프로그램 선택
- 대화상자 기반으로 선택한다
아래 창에선 다음과 같이 Windows 소켓에 체크표시하고 '마침' 누름
2. 소켓에 해당되는 클래스 만들기
클래스 마법사에서 'MFC 클래스' 클릭
클래스 추가 - 기본클래스는 CSocket으로 지정
Listen 소켓 클래스 이름(CServerSocket)
Client 매칭 소켓 클래스 이름(CClientSocket)
3. Listen 소켓 클래스 구현
- Listen할 때 필요한 함수
SetWnd : Sendmessage 활용을 위한 메인의 핸들을 받는 함수
OnAccept : 클라이언트가 서버와 연결 시 처리 시의 함수이다.
- OnAccept 함수는 Client 매칭되는 소켓 클래스를 구현할 때 같이 구현한다.
함수 및 핸들 변수 선언
// ServerSocket.h : 헤더 파일입니다.
//
class CServerSocket : public CSocket
{
...
void SetWnd(HWND hWnd);
HWND m_hWnd;
virtual void OnAccept(int nErrorCode);
};
- 함수 구현
// ServerSocket.cpp : 구현 파일입니다.
//
// CServerSocket 멤버 함수
void CServerSocket::SetWnd(HWND hWnd)
{
m_hWnd = hWnd;
}
void CServerSocket::OnAccept(int nErrorCode)
{
// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
CSocket::OnAccept(nErrorCode);
}
4. Client 매칭 소켓 클래스 구현
- 필요 함수
- SetWnd : Sendmessage 활용을 위한 메인의 핸들을 받는 함수
- OnReceive : 서버 통신 받을 시의 처리 함수
- OnClose : 소켓 해제 시의 처리 함수
- 함수 및 핸들 변수 선언
// ClientSocket.h : 헤더 파일입니다.
//
class CClientSocket : public CSocket
{
...
public:
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: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
CString strTmp = _T(""), strIPAddress = _T("");
UINT uPortNumber = 0;
TCHAR strBuffer[1024];
ZeroMemory(strBuffer, sizeof(strBuffer));
GetPeerName(strIPAddress, uPortNumber);
if(Receive(strBuffer, sizeof(strBuffer)) > 0) { // 전달된 데이터(문자열)가 있을 경우
strTmp.Format(_T("[%s : %d]: %s"), strIPAddress, uPortNumber, strBuffer);
}
CSocket::OnReceive(nErrorCode);
}
void CClientSocket::OnClose(int nErrorCode)
{
// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
CSocket::OnClose(nErrorCode);
}
5. Listen 소켓에서 OnAccept을 통해 Client 매칭 소켓 연결하기
- 헤더 추가
// ServerSocket.h : 헤더 파일입니다. // #include "ClientSocket.h"
- OnAccept 구현
// ServerSocket.cpp : 구현 파일입니다.
//
void CServerSocket::OnAccept(int nErrorCode)
{
// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
CClientSocket* pClient = new CClientSocket; //Client 소켓 포인터 추가
if(Accept(*pClient)) //Listen에서 클라이언트 접속을 확인하면
{
pClient->SetWnd(m_hWnd); //Client소켓에 메인핸들 연결
}
else //클라이언트 접속 문제시
{
delete pClient;
AfxMessageBox(_T("ERROR : Failed can't accept new Client!"));
}
CSocket::OnAccept(nErrorCode);
}
6. 메인 클래스 구현
- 대화상자 설정
아래과 같이 구상한다. 리스트박스와 관련된 소스들은 추후에 Sendmessage 적용하면서 구현함.
- IDC_LIST_CLIENT : 접속한 클라이언트 표시용 리스트박스
- IDC_LIST_MSG : 메시지 표시용 리스트박스
- 헤더 설정
// socServerDlg.h : 헤더 파일
//
#include "ServerSocket.h"
class CsocServerDlg : public CDialogEx
{
...
// 구현입니다.
private:
CClientSocket *m_pClientSocket;
CServerSocket *m_pServerSocket;
public:
CListBox m_list_client; //IDC_LIST_CLIENT
CListBox m_list_msg; //IDC_LIST_MSG
CPtrList m_ptrClientSocketList; //For manage Client Sockets
};
- 소스 구현
// socServerDlg.cpp : 구현 파일
//
#define PORT 9999 //임의의 포트 설정
void CsocServerDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST_CLIENT, m_list_client);
DDX_Control(pDX, IDC_LIST_MSG, m_list_msg);
}
BOOL CsocServerDlg::OnInitDialog()
{
...
// TODO: 여기에 추가 초기화 작업을 추가합니다.
//Listen 소켓 초기화
m_pServerSocket = new CServerSocket;
m_pServerSocket->SetWnd(this->m_hWnd);
//소켓 Listen하기
m_pServerSocket->Create(PORT);
m_pServerSocket->Listen();
return TRUE; // 포커스를 컨트롤에 설정하지 않으면 TRUE를 반환합니다.
}
7. 대화상자 컨트롤 작동 구현
- Sendmessage를 활용해야 하는 경우(사용자 메시지 함수 사용)
- WM_ACCEPT_SOCKET : 클라이언트가 서버에 접속할 때 IDC_LIST_CLIENT에 접속 표시
- WM_CLEINT_MSG_RECV : 클라이언트가 서버로 메시지 보낼 때 IDC_LIST_MSG에 메시지 표시
- WM_CLIENT_CLOSE : 클라이언트가 서버와 연결 해제 시의 대처
- 사용자 메시지 정의
WM_ACCEPT_SOCKET은 Listen 소켓 클래스에서 정의
// ServerSocket.h : 헤더 파일입니다. // #define WM_ACCEPT_SOCKET WM_USER+1
나머지 메시지들은 Client 매칭 소켓 클래스에서 정의
// ClientSocket.h : 헤더 파일입니다. // #define WM_CLIENT_MSG_RECV WM_USER+2 #define WM_CLIENT_CLOSE WM_USER+3
- 각 소켓 클래스에서 메인으로 메시지 보내기
WM_ACCEPT_SOCKET : OnAccept()함수
// ServerSocket.cpp : 구현 파일입니다.
//
void CServerSocket::OnAccept(int nErrorCode)
{
// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
...
SendMessage(m_hWnd, WM_ACCEPT_SOCKET, 0, (LPARAM)pClient);
CSocket::OnAccept(nErrorCode);
}
WM_CLEINT_MSG_RECV : OnReceive()함수
WM_CLIENT_CLOSE : OnClose()함수
// ClientSocket.cpp : 구현 파일입니다.
//
void CClientSocket::OnReceive(int nErrorCode)
{
// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
...
//Main Window에 Send
SendMessage(m_hWnd,WM_CLIENT_MSG_RECV,0,(LPARAM)((LPCTSTR)strTmp));
CSocket::OnReceive(nErrorCode);
}
void CClientSocket::OnClose(int nErrorCode)
{
// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
SendMessage(m_hWnd, WM_CLIENT_CLOSE, 0, (LPARAM)this);
CSocket::OnClose(nErrorCode);
}
- 메인에서 메시지 핸들 함수 구현
사용자 지정 메시지 추가
- 클래스 마법사 - 클래스 이름(DLG클래스로) - 메시지탭 - 사용자 지정 메시지 추가
위에 적용한 3개의 사용자 지정 메시지를 각각 추가하기
- 사용자 지정 메시지 함수 구현
OnAcceptSocket 함수
// socServerDlg.cpp : 구현 파일
//
afx_msg LRESULT CsocServerDlg::OnAcceptSocket(WPARAM wParam, LPARAM lParam)
{
CString str;
m_pClientSocket = (CClientSocket*) lParam;
m_ptrClientSocketList.AddTail(m_pClientSocket);
str.Format(_T("Client (%d)"), (int)(m_pClientSocket));
m_list_client.InsertString(-1,str);
m_pClientSocket = NULL;
delete m_pClientSocket;
return 0;
}
OnClientMsgRecv 함수
// socServerDlg.cpp : 구현 파일
//
afx_msg LRESULT CsocServerDlg::OnClientMsgRecv(WPARAM wParam, LPARAM lParam)
{
LPCTSTR lpszStr = (LPCTSTR)lParam;
//BroadCasting
POSITION pos = m_ptrClientSocketList.GetHeadPosition();
while(pos != NULL)
{
CClientSocket* pClient = (CClientSocket*)m_ptrClientSocketList.GetNext(pos);
if(pClient!=NULL)
{
//UNICODE 사용하면 기존 메모리크기 *2 해야함
//Client에 Broadcasting하기
pClient->Send(lpszStr, lstrlen(lpszStr) * 2);
}
}
//m_list_msg에 메시지 추가
m_list_msg.InsertString(-1,lpszStr);
m_list_msg.SetCurSel(m_list_msg.GetCount()-1);
return 0;
}
OnClientClose 함수
// socServerDlg.cpp : 구현 파일
//
afx_msg LRESULT CsocServerDlg::OnClientClose(WPARAM wParam, LPARAM lParam)
{
CClientSocket *pClient = (CClientSocket *)lParam;
CString str;
UINT idx=0;
POSITION pos = m_ptrClientSocketList.Find(pClient);
if(pos!=NULL)
{
//m_list_client에서 해당 Client 삭제
str.Format(_T("Client (%d)"), (int)pClient);
idx = m_list_client.SelectString(-1, (LPCTSTR)str);
m_list_client.DeleteString(idx);
//CPtrList에서도 해당 Client 삭제
m_ptrClientSocketList.RemoveAt(pos);
}
return 0;
}
소스코드 참조 : https://github.com/jjj0214/MFC_Samples/tree/master/socServer
끝
'Programming > MFC(C++)' 카테고리의 다른 글
| [MFC] 멀티스레드 사용하여 소켓통신 채팅 서버 프로그램 만들기 (9) | 2018.07.20 |
|---|---|
| [MFC] 소켓통신 채팅 클라이언트 프로그램 만들기 (14) | 2018.07.19 |
| [MFC] 콘솔창 대신 디버그창 활용하기(OutputDebugString) (0) | 2018.07.16 |
| [MFC] 자식 다이얼로그에서 다른 자식 다이얼로그로 메시지 보내기(SendMessage) : 핸들 사용 (0) | 2018.07.13 |
| [MFC] 자식 다이얼로그에서 부모와 자식에게 메시지 보내기(SendMessage) : 클래스포인터 사용 (0) | 2018.07.13 |
- Total
- Today
- Yesterday
- MFC ADO
- #자동업데이트
- MFC
- 해외송금확인
- ADODB
- sqlite3
- ADO
- Modeless
- SendMessage
- 부가가치세
- 프로세스이름변경
- c++
- 소켓
- 모달리스
- 청년내일채움공제 만기신청
- MSSQL
- 전자신고변환
- 모달리스 다이얼로그
- 청년내일채움공제
- 전자신고파일설명서
- 법정동코드
- MFC Modeless
- MXCOMPONENT
- 부가가치세전산매체
- CMFCBUTTON
- 스쿠트항공 환불받기
- Sticky Notes Loading
- 항공알파벳
- 스티커메모로드중
- 세금계산서합계표양식
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |