TCP/IP 프로그래밍 - 범용 소켓 클래스

2020. 12. 31. 12:40·C, C++, MFC

범용으로 사용할 수 있는 소켓 클래스 예제이다.

 

서버 소켓 클래스 (CServerSocket)

서버 소켓에 해당하는 기능을 CServerSocket 클래스에 구현한다.

CSocket 클래스에서 상속 받은 클래스로, 서버에서 클라이언트의 접속 요청을 받아들여 클라이언트와 서버의 데이터 소켓을 연결시켜주는 역할을 한다. 실제 데이터를 주고 받는 데는 관여하지 않는다.

 

CServerSocket.h

#if !defined(AFX_SERVERSOCKET_H__C33E76A0_1957_42F1_8202_3B5DD3C9C380__INCLUDED_)
#define AFX_SERVERSOCKET_H__C33E76A0_1957_42F1_8202_3B5DD3C9C380__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// ServerSocket.h : header file
//


#define UM_ACCEPT (WM_USER+10)

/////////////////////////////////////////////////////////////////////////////
// CServerSocket command target

class CServerSocket : public CSocket
{
// Attributes
public:
	void Init(CWnd *pWnd, int nPortNum);

// Operations
public:
	CServerSocket();
	virtual ~CServerSocket();

// Overrides
public:
	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CServerSocket)
	public:
	virtual void OnAccept(int nErrorCode);
	//}}AFX_VIRTUAL

	// Generated message map functions
	//{{AFX_MSG(CServerSocket)
		// NOTE - the ClassWizard will add and remove member functions here.
	//}}AFX_MSG

// Implementation
protected:
	CWnd *m_pWnd;
};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_SERVERSOCKET_H__C33E76A0_1957_42F1_8202_3B5DD3C9C380__INCLUDED_)

 

CServerSocket.cpp

// ServerSocket.cpp : implementation file
//


#include "pch.h"
#include "ServerSocket.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CServerSocket

CServerSocket::CServerSocket()
{
}

CServerSocket::~CServerSocket()
{
}


// Do not edit the following lines, which are needed by ClassWizard.
#if 0
BEGIN_MESSAGE_MAP(CServerSocket, CSocket)
	//{{AFX_MSG_MAP(CServerSocket)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()
#endif	// 0

/////////////////////////////////////////////////////////////////////////////
// CServerSocket member functions

// 서버 소켓 초기화
void CServerSocket::Init(CWnd *pWnd, int nPortNum)
{
	m_pWnd = pWnd;
	Create(nPortNum);	// 서버 소켓 초기화
	Listen();		// 접속 요청 감지
}

// 접속 요청이 있을 때마다 메시지 전송
void CServerSocket::OnAccept(int nErrorCode) 
{
	m_pWnd->SendMessage(UM_ACCEPT);		// 메인 윈도우에 UM_ACCEPT 메시지를 보냄
	CSocket::OnAccept(nErrorCode);
}

 

Init 함수는 접속 요청이 들어왔을 때, 어떤 윈도우에 메시지를 보내 줄 것인지, 접속 요청을 위해서 어떤 포트 번호를 사용할 것인지를 인자로 받는다.

OnAccept 함수는 클라이언트로부터 접속 요청이 들어오면 호출되는데, 이 함수를 재정의하면 윈도우의 포인터 m_pWnd를 이용하여 메인 윈도우에 UM_ACCEPT 메시지를 보내준다. 그러면 접속을 받아들이는 일이 메인 윈도우에서 가능하게 된다. 이 과정을 완료하면 서버 소켓의 임무는 끝이다.

 

 

데이터 소켓 클래스 (CDataSocket)

데이터 소켓에 해당하는 기능을 CDataSocket 클래스에 구현한다.

CSocket 클래스에서 상속 받은 클래스로 실제 데이터를 주고 받는데 관련된 기능을 수행한다. 또한 이 클래스는 클라이언트 소켓으로 서버에 접속 요청을 할 수 있다.

 

CDataSocket.h

#if !defined(AFX_DATASOCKET_H__DD8B4738_C2F2_4A7D_BA47_F43AA0571A53__INCLUDED_)
#define AFX_DATASOCKET_H__DD8B4738_C2F2_4A7D_BA47_F43AA0571A53__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// DataSocket.h : header file
//


#define UM_DATARECEIVE (WM_USER+11)
class CData;

/////////////////////////////////////////////////////////////////////////////
// CDataSocket command target

class CDataSocket : public CSocket
{
// Attributes
public:
	void Init(CWnd *pWnd);
	void Send(CData *pData);
	void Receive(CData *pData);
	void operator << (CData &data);
	void operator >> (CData &data);
	void Flush();

	CArchive	*m_pArchiveIn;
	CArchive	*m_pArchiveOut;

// Operations
public:
	CDataSocket();
	virtual ~CDataSocket();

// Overrides
public:
	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CDataSocket)
	public:
	virtual void OnReceive(int nErrorCode);
	virtual void OnClose(int nErrorCode);
	//}}AFX_VIRTUAL

	// Generated message map functions
	//{{AFX_MSG(CDataSocket)
		// NOTE - the ClassWizard will add and remove member functions here.
	//}}AFX_MSG

// Implementation
protected:
	CWnd		*m_pWnd;
	CSocketFile *m_pFile;
};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_DATASOCKET_H__DD8B4738_C2F2_4A7D_BA47_F43AA0571A53__INCLUDED_)

 

CDataSocket.cpp

// DataSocket.cpp : implementation file
//


#include "pch.h"
#include "DataSocket.h"
#include "Data.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CDataSocket

CDataSocket::CDataSocket()
{
}

CDataSocket::~CDataSocket()
{
}


// Do not edit the following lines, which are needed by ClassWizard.
#if 0
BEGIN_MESSAGE_MAP(CDataSocket, CSocket)
	//{{AFX_MSG_MAP(CDataSocket)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()
#endif	// 0

/////////////////////////////////////////////////////////////////////////////
// CDataSocket member functions

// 데이터 소켓 초기화
void CDataSocket::Init(CWnd *pWnd)
{
	m_pWnd = pWnd;
	m_pFile = new CSocketFile(this);
	m_pArchiveIn = new CArchive(m_pFile, CArchive::load);	// 수신
	m_pArchiveOut = new CArchive(m_pFile, CArchive::store);	// 송신
}

// 데이터 수신
void CDataSocket::Receive(CData * pData)
{
	if(m_pArchiveIn != NULL)
		pData->Serialize(*m_pArchiveIn);
}

// Receive 함수 호출
void CDataSocket::operator >>(CData & data)
{
	Receive (&data);
}

// 데이터 송신
void CDataSocket::Send(CData * pData)
{
	if(m_pArchiveOut != NULL)
		pData->Serialize(*m_pArchiveOut);
}

// 통신 버퍼에 저장되어 있는 내용을 바로 네트워크로 전송
void CDataSocket::Flush()
{
	if(m_pArchiveOut != NULL)
		m_pArchiveOut->Flush();
}

// Send 함수 호출
void CDataSocket::operator <<(CData & data)
{
	Send (&data);
	Flush();
}

// 새로운 데이터가 수신되면 호출됨
void CDataSocket::OnReceive(int nErrorCode) 
{
	m_pWnd->SendMessage(UM_DATARECEIVE);	// 메인 윈도우에 메시지를 보냄
	CSocket::OnReceive(nErrorCode);
}

// 연결된 소켓의 접속이 끊어지면 호출됨
void CDataSocket::OnClose(int nErrorCode) 
{
	if(m_pFile != NULL) {
		delete m_pFile;
		m_pFile = NULL;
	}
	if(m_pArchiveIn != NULL) {
		m_pArchiveIn->Abort();
		delete m_pArchiveIn;
		m_pArchiveIn = NULL;
	}
	if(m_pArchiveOut != NULL) {
		m_pArchiveOut->Abort();
		delete m_pArchiveOut;
		m_pArchiveOut = NULL;
	}	
	
	CSocket::OnClose(nErrorCode);
}

 

CArchive, CSocketFile 클래스와 연계되어 CData 클래스에 저장되어 있는 데이터를 송수신한다. 따라서 CArchieve 클래스와 CSocketFile 클래스의 포인터를 멤버변수로 가진다.

Init 함수는 새로운 데이터가 수신되었을 때 메시지를 전송 받을 윈도우의 포인터를 인자로 받는다. 그리고 CSocket, CSocketFile, CArchive 클래스가 맞물리도록 만들어 준다. CArchive 클래스는 데이터 수신용과 송신용을 각각 따로 만든다.

Receive 함수는 데이터를 수신하고자 할 때 사용한다. >> 연산자를 이용하여 Receive 함수를 호출할 수 있게 만들었다.

Send 함수는 데이터를 송신하고자 할 때 사용한다. << 연산자를 이용하여 Send 함수를 호출할 수 있게 만들었다. Send 함수를 이용하여 데이터를 송신한다고 해서 이 데이터가 바로 네트워크를 통해 전송되는 것은 아니고, 소켓이 관리하는 송신용 버퍼에 저장되기만 한다. 데이터가 실제 네트워크를 통해 전송될 때는 여러 오버헤드가 붙게 되는데 너무 작은 양의 데이터를 보내게 되면 실제 데이터보다 오버헤드가 더 커지는 상황이 발생하게 된다. 따라서 전송 효율을 높이기 위해서 데이터가 어느 정도 길이 이하일 때는 전송하지 않고 저장해 두었다가 어느 정도 길이 이상 되면 전송을 한다.
Flush 함수는 현재 통신 버퍼에 저장되어 있는 데이터를 바로 네트워크를 통해 전송한다.

OnReceive 함수는 새로운 데이터가 수신될 때마다 호출되는데 실제 데이터를 수신하는 일은 여기서 직접 하지 않고, 메인 윈도우에 메시지를 보주기만 한다.

OnClose 함수는 연결된 소켓의 접속이 끊어지면 호출된다. 동적으로 할당했던 CArchive 클래스와 CSocketFile 오브젝트를 삭제한다.

 

 

데이터 구조 클래스 (CData)

전송될 데이터는 CData 클래스에 캡슐화한다.

통신 프로그램이 서로간에 데이터를 주고 받으려면 양쪽에서 데이터 포맷이 같아야 한다. 통신 프로그램마다 주고 받는 데이터의 포맷은 달라질 것이므로, 프로그램에 따라 이 클래스를 변경해주면 된다. 

 

CData.h

#if !defined(AFX_DATA_H__98BA7ABB_2D49_49AA_9F38_6BCB606E3839__INCLUDED_)
#define AFX_DATA_H__98BA7ABB_2D49_49AA_9F38_6BCB606E3839__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// Data.h : header file
//



/////////////////////////////////////////////////////////////////////////////
// CData command target

class CData : public CObject
{
// Attributes
public:

// Operations
public:
	CData();
	virtual ~CData();

	virtual void Serialize(CArchive &ar);

	CString m_strData;	// 텍스트 데이터만 전송하므로

// Overrides
public:

// Implementation
protected:
};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_DATA_H__98BA7ABB_2D49_49AA_9F38_6BCB606E3839__INCLUDED_)

 

 

CData.cpp

// Data.cpp : implementation file
//


#include "pch.h"
#include "Data.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CData

CData::CData()
{
}

CData::~CData()
{
}

/////////////////////////////////////////////////////////////////////////////
// CData member functions

// 데이터 전송
void CData::Serialize(CArchive &ar)
{
	if(ar.IsStoring())
	{
		// 네트워크로 송신
		ar << m_strData;
	}
	else
	{
		// 네트워크에서 수신
		ar >> m_strData;
	}
}

 

Serialize 함수를 이용하여 데이터를 전송한다. 이 함수는 CObject 클래스에서 상속을 받아 재정의되어야 한다. 전송해야 할 데이터의 포멧이 달라지면 CData 클래스의 멤버 변수의 내용을 바꿔주고 Serialize 함수에서 멤버 변수들을 차례로 송신하고 수신하도록 만들어 주기만 하면 된다.

 

SocketClass.zip
0.01MB

 

 

 

저작자표시 비영리 변경금지 (새창열림)

'C, C++, MFC' 카테고리의 다른 글

모달(Modal)과 모달리스(Modeless)  (0) 2021.02.05
TCP/IP 프로그래밍 - 채팅 프로그램  (0) 2021.01.06
TCP/IP 프로그래밍 - 소켓 통신  (0) 2020.12.19
ODBC를 이용한 MFC 데이터베이스 프로그래밍  (1) 2020.12.02
Visual Studio 2008 MFC 프로젝트 생성  (0) 2020.11.30
'C, C++, MFC' 카테고리의 다른 글
  • 모달(Modal)과 모달리스(Modeless)
  • TCP/IP 프로그래밍 - 채팅 프로그램
  • TCP/IP 프로그래밍 - 소켓 통신
  • ODBC를 이용한 MFC 데이터베이스 프로그래밍
김고파
김고파
채워나가는 중
  • 김고파
    개발자 김고파
    김고파
  • 전체
    오늘
    어제
    • all
      • C, C++, MFC
        • 따배C++
      • Qt
      • OpenCV
      • Data Structure
      • Dev Tools
      • JAVA
  • 링크

    • github
    • 네이버블로그
    • BOJ
  • 인기 글

  • hELLO· Designed By정상우.v4.10.3
김고파
TCP/IP 프로그래밍 - 범용 소켓 클래스
상단으로

티스토리툴바