개발은 하는건가..

MFC UDP Socket Base class 본문

C, C++, MFC

MFC UDP Socket Base class

수동애비 2022. 7. 19. 10:51
반응형

# Header

#pragma once
#include <afxsock.h>  

#define MAX_DATA_LENGTH		1500

class CUdpSocket
{
public:
	CUdpSocket();
	~CUdpSocket();

	friend class CUdpServer;

	enum EResultCode {
		EUDP_NO_OPERATION = -2,
		EUDP_GENERAL_FAIL = -1,		
		EUDP_SUCCESS = 0,
		EUDP_INVALID_DATA,
		EUDP_MAX_DATA_LENGTH_OVER,
		EUDP_CANCELED = WSAEINTR,			// blocking operation canceled by WSACancelBlockingCall()
		EUDP_BAD_SOCKET = WSAEBADF,			// bad socket descriptor
		EUDP_PERMISSON_DENIED = WSAEACCES,	// Permission denied
		EUDP_BAD_ADDRESS = WSAEFAULT,		// Bad Address
		EUDP_ADDR_IN_USE = WSAEADDRINUSE,	// addr in used
		EUDP_NET_DOWN = WSAENETDOWN,		// network system fail		
		EUDP_NET_RESET = WSAENETRESET,		// ttl expires		
		EUDP_CONN_RESET = WSAECONNRESET,	// remote disconn or connect not avail.
		EUDP_TIMEOUT = WSAETIMEDOUT,		// send/recv timeout		
	};

	virtual void		AssignSocket(SOCKET sock, BOOL bCloseCurrentSocket = TRUE);
	virtual SOCKET		OpenSocket();
	virtual void		CloseSocket();

	void				SetSockAddr(SOCKADDR_IN* pAddr);

		
	EResultCode			SendData(__in BYTE* pData, int nDataLen);
	EResultCode			RecvData(__out BYTE* pRecvBuffer, __inout int& nRecvBuffLen, UINT nTimeoutMs = 0);
	
	char*				GetLastErrorMsg();
	CStringW			GetLastErrorMsgW(BOOL bRemoveCrLf = FALSE);
	EResultCode			ConvertToResultCode(int wsaErrCode);

	inline SOCKADDR_IN*	GetSockAddr() { return &m_SocketAddr; };
	inline int			GetSockAddrSize() { return sizeof(m_SocketAddr); };
	inline SOCKET		GetSocket() { return m_Socket; };
	
	static inline BOOL	IsHexChar(WCHAR c) { return (c >= L'0' && c <= L'9') || (c >= L'a' && c <= L'f') || (c >= L'A' && c <= L'F'); };
	static inline BOOL	IsSkipChar(WCHAR c) { return (c == L'\n' || c == L'\r' || c == L' ' || c == L'\t' || c == NULL); };

	static BOOL			ConvertHexStrToBinary(char *pszHex, BYTE *pOutBuffer, __inout int &nOutBufferLen);
	static BOOL			ConvertHexStrToBinary(WCHAR *pwszHex, BYTE *pOutBuffer, __inout int &nOutBufferLen);
	static void			ConvertBinaryToHexStr(BYTE *pData, int nDataLen, __out WCHAR *pwszOutBuff, int nOutBuffSize, int lineSplit = -1);
	static void			ConvertBinaryToDisplayHexStr(BYTE *pData, int nDataLen, __out WCHAR *pwszOutBuff, int nOutBuffSize, BOOL bTabIndent = TRUE);

	static CString		GetIpString(SOCKADDR_IN *pAddr);


private:		
	SOCKET				m_Socket = NULL;
	WSADATA				m_WsaData;
	SOCKADDR_IN			m_SocketAddr;

	char				m_szLastErrMsg[MAX_PATH] = { NULL, };	
};


# Cpp 

#include "pch.h"
#include "UdpSocket.h"



CUdpSocket::CUdpSocket()
{	
	int nRes = WSAStartup(MAKEWORD(2, 2), &m_WsaData);

	if (nRes != 0) {
		ASSERT(0);
		TRACE("CUdpSocket::CUdpSocket() - WSAStartup (Err:%d)\n", nRes);
		return;
	}	

	ZeroMemory(&m_SocketAddr, sizeof(SOCKADDR_IN));
}

CUdpSocket::~CUdpSocket()
{
	CloseSocket();

	if (m_WsaData.wVersion != 0) {
		WSACleanup();
	}
}


SOCKET CUdpSocket::OpenSocket()
{
	if (m_Socket == NULL) {
		m_Socket = socket(AF_INET, SOCK_DGRAM, 0);	// IPPROTO_UDP
	}

	if (m_Socket == NULL) {
		TRACE("CUdpSocket::OpenSocket() - Socket (Err:%d)\n", GetLastErrorMsg());
		ASSERT(0);		
	}

	return m_Socket;
}

void CUdpSocket::SetSockAddr(SOCKADDR_IN* pAddr)
{
	if (pAddr != NULL) {
		m_SocketAddr = *pAddr;
	}
}


void CUdpSocket::AssignSocket(SOCKET sock, BOOL bCloseCurrentSocket) {
	if (m_Socket != NULL && bCloseCurrentSocket == TRUE) {
		closesocket(m_Socket);
	}

	m_Socket = sock;
}

CUdpSocket::EResultCode CUdpSocket::SendData(BYTE* pData, int nDataLen)
{
	if (pData == NULL) {
		ASSERT(0);
		return EUDP_INVALID_DATA;		
	}

	int nRes = 0;
	EResultCode eResult = EUDP_SUCCESS;
	SOCKET sock = OpenSocket();

	if (sock == SOCKET_ERROR) {
		return EUDP_GENERAL_FAIL;
	}

	nRes = sendto(sock, (char*)pData, nDataLen, 0, (SOCKADDR*)&m_SocketAddr, sizeof(m_SocketAddr));
	
	if (nRes < nDataLen) {
		eResult = ConvertToResultCode(WSAGetLastError());
	}
	
	return eResult;
}


CUdpSocket::EResultCode CUdpSocket::RecvData(__out BYTE* pRecvBuffer, __inout int& nRecvBuffLen, UINT nTimeoutMs)
{
	EResultCode eRes = EUDP_SUCCESS;
	SOCKET sock = OpenSocket();
	int nRecvSize = 0;
	int nSockAddrSize = sizeof(m_SocketAddr);

	if (sock == -1) {
		return EUDP_GENERAL_FAIL;
	}
		
	setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&nTimeoutMs, sizeof(nTimeoutMs));

	nRecvSize = recvfrom(sock, (char*)pRecvBuffer, nRecvBuffLen, 0, (SOCKADDR*)&m_SocketAddr, &nSockAddrSize);

	if (nRecvSize == SOCKET_ERROR) {
		TRACE("CUdpSocket::RecvData() - recvfrom (Err:%s)\n", GetLastErrorMsg());
		eRes = ConvertToResultCode(WSAGetLastError());
	}
	else {
		nRecvBuffLen = nRecvSize;
	}
		   	 
	return eRes;
}


void CUdpSocket::CloseSocket()
{
	if (m_Socket != NULL) {
		// child 클래스에서 m_Socket 체크 루틴에서 우선 걸러지도록 m_Socket = NULL 후 closesocket() 하도록 함.
		SOCKET tmpSock = m_Socket;
		m_Socket = NULL;
		closesocket(tmpSock);
	}
}


char* CUdpSocket::GetLastErrorMsg()
{
	m_szLastErrMsg[0] = NULL;

	FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		WSAGetLastError(),
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		m_szLastErrMsg,
		MAX_PATH,
		NULL);

	return m_szLastErrMsg;
}

CStringW CUdpSocket::GetLastErrorMsgW(BOOL bRemoveCrLf)
{
	CStringW strW;
	strW = GetLastErrorMsg();	

	if (bRemoveCrLf == TRUE) {
		strW.Replace(L"\r\n", L"");
	}

	return strW;
}


CUdpSocket::EResultCode CUdpSocket::ConvertToResultCode(int wsaErrCode)
{
	if (wsaErrCode == 0) {
		return EUDP_SUCCESS;
	}
	else if (wsaErrCode > WSABASEERR) {
		return (EResultCode)wsaErrCode;
	}

	return EUDP_GENERAL_FAIL;
}


// Hexstring 형식은 반드시 2자리로 구성되어야 하며 공백,\r,\n 은 자동 skip 처리 됨. 
BOOL CUdpSocket::ConvertHexStrToBinary(char *pszHex, BYTE *pOutBuffer, __inout int &nOutBufferLen)
{
	WCHAR wString[4096] = { NULL, };
	size_t convertedChars = 0;

	mbstowcs_s(&convertedChars, wString, 4096, pszHex, _TRUNCATE);

	return ConvertHexStrToBinary(wString, pOutBuffer, nOutBufferLen);
}

BOOL CUdpSocket::ConvertHexStrToBinary(WCHAR *pwszHex, BYTE *pOutBuffer, __inout int &nOutBufferLen)
{
	WCHAR szHex[4] = { NULL, };
	WCHAR *pPos = pwszHex;
	int nSrcLen = (int)wcslen(pwszHex);
	WCHAR *pEndPos = pPos + nSrcLen;
	int nIncLen = 0;

	if (nSrcLen < 2 || nSrcLen >(MAX_DATA_LENGTH * 4)) {
		ASSERT(0);
		TRACE("CUdpSocket::ConvertHexStrToBinary() - Max data length over.\n");
		return FALSE;
	}

	while (pEndPos > pPos) {
		if (IsSkipChar(*pPos) == TRUE) {
			pPos++;
			continue;
		}

		szHex[0] = *pPos++;
		szHex[1] = *pPos++;

		if (IsHexChar(szHex[0]) == FALSE || IsHexChar(szHex[1]) == FALSE) {
			return FALSE;
		}

		pOutBuffer[nIncLen] = (BYTE)wcstoul(szHex, NULL, 16);
		nIncLen++;

		if (nIncLen >= nOutBufferLen) {
			break;
		}

	}

	nOutBufferLen = nIncLen;

	return TRUE;
}

void CUdpSocket::ConvertBinaryToHexStr(BYTE *pData, int nDataLen, __out WCHAR *pOut, int nOutBuffSize, int lineSplit)
{
	CStringW strHex;

	pOut[0] = NULL;

	for (int i = 0; i < nDataLen; i++) {
		if (lineSplit > 4) {
			if (i > 0 && (i % lineSplit) == 0) {
				wcscat_s(pOut, nOutBuffSize, L"\r\n");
			}
		}

		strHex.Format(L"%02X ", pData[i]);

		if ((i + 1) * 3 < nOutBuffSize) {
			wcscat_s(pOut, nOutBuffSize, strHex.GetBuffer(0));
		}
		else {
			break;
		}
	}
}


void CUdpSocket::ConvertBinaryToDisplayHexStr(BYTE *pData, int nDataLen, __out WCHAR *pOut, int nOutBuffSize, BOOL bTabIndent)
{
	CStringW strHex;
	CStringW strTmp;
	CStringW strTab = (bTabIndent) ? L"\t" : L"";

	strHex = strTab + L"offset | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\r\n" +
		strTab + L"========================================================\r\n";

	for (int i = 0; i < nDataLen; i++) {
		if ((i % 16) == 0) {
			strTmp.Format(strTab + L"0x%04X | ", i);

			if (i == 0) {
				strHex.Append(strTmp);
			}
			else {
				strHex.Append(L"\r\n");
				strHex.Append(strTmp);
			}
		}

		strTmp.Format(L"%02X ", pData[i]);
		strHex.Append(strTmp);
	}

	strHex.Append(L"\r\n" + strTab + L"========================================================");

	wcsncpy_s(pOut, nOutBuffSize, strHex.GetBuffer(0), nOutBuffSize - 1);

	strHex.ReleaseBuffer();
}

CString CUdpSocket::GetIpString(SOCKADDR_IN *pAddr)
{	
	CString strIp;
	char szIP[INET_ADDRSTRLEN];

	strIp = inet_ntop(AF_INET, &(pAddr->sin_addr), szIP, INET_ADDRSTRLEN);	   		
	
	return strIp;
}

 

Comments