티스토리 뷰
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
- c++
- MSSQL
- 부가가치세
- 세금계산서합계표양식
- 모달리스 다이얼로그
- 청년내일채움공제 만기신청
- 소켓
- 법정동코드
- 청년내일채움공제
- 부가가치세전산매체
- Modeless
- CMFCBUTTON
- sqlite3
- 스쿠트항공 환불받기
- MFC
- 전자신고변환
- 모달리스
- MXCOMPONENT
- 전자신고파일설명서
- MFC ADO
- #자동업데이트
- 해외송금확인
- Sticky Notes Loading
- ADO
- 항공알파벳
- 프로세스이름변경
- ADODB
- MFC Modeless
- SendMessage
- 스티커메모로드중
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |