// UdpSocket.h
#pragma once
#include <WinSock2.h>
#include <atomic>
#include <string>
#include <list>
#pragma comment(lib, "WS2_32")
typedef struct tagNetConfig
{
UINT port;
std::string ip;
} SNetConfig;
class UdpSocket
{
public:
void SetRemoteAddress(const SNetConfig& config);
bool CreateSocket();
bool CloseSocket();
bool SetSocketOpt();
bool IoCtlSocket();
bool Bind();
protected:
static std::atomic<bool> bRun;
SOCKET m_sock;
SOCKADDR_IN m_addrRemote;
};
// UdpSocket.cpp
#include "pch.h"
#include "UdpSocket.h"
std::atomic<bool> UdpSocket::bRun;
void UdpSocket::SetRemoteAddress(const SNetConfig& config)
{
memset(&m_addrRemote, 0, sizeof(SOCKADDR_IN));
m_addrRemote.sin_family = AF_INET;
m_addrRemote.sin_port = htons(config.port);
m_addrRemote.sin_addr.s_addr = inet_addr(config.ip.c_str());
}
bool UdpSocket::CreateSocket()
{
m_sock = socket(AF_INET, SOCK_DGRAM, 0);
if(m_sock == INVALID_SOCKET)
return false;
bRun = true;
return true;
}
bool UdpSocket::CloseSocket()
{
if(m_sock == INVALID_SOCKET || m_sock == SOCKET_ERROR)
return false;
if (0 != closesocket(m_sock))
return false;
bRun = false;
Sleep(100);
return true;
}
bool UdpSocket::SetSocketOpt()
{
int nRecvBuf = 1;
if (0 != setsockopt(m_sock, SOL_SOCKET, SO_RCVBUF, (char*)&nRecvBuf, sizeof(int)))
{
AfxMessageBox(_T("setsockopt failed"));
return false;
}
return true;
}
bool UdpSocket::IoCtlSocket()
{
u_long nMode = 1;
if (ioctlsocket(m_sock, FIONBIO, &nMode) == SOCKET_ERROR)
{
AfxMessageBox(_T("ioctlsocket failed"));
return false;
}
return true;
}
bool UdpSocket::Bind()
{
SOCKADDR_IN addrLocal;
memset(&addrLocal, 0, sizeof(SOCKADDR_IN));
addrLocal.sin_family = AF_INET;
addrLocal.sin_port = m_addrRemote.sin_port;
addrLocal.sin_addr.s_addr = inet_addr("127.0.0.1"); //inet_addr("192.168.1.100");
if (0 != bind(m_sock, (SOCKADDR*)&addrLocal, sizeof(SOCKADDR)))
return false;
return true;
}
// UdpService.h
#pragma once
#include "UdpSocket.h"
#define BUFF_LENGTH 1030
#define MAX_COUNT 100
class UdpService : public UdpSocket
{
public:
UdpService();
virtual ~UdpService();
public:
bool Open(const SNetConfig& config);
long ReceviceData(CString& strDataFromIP);
void SendData(const char* pData);
static DWORD WINAPI UdpSendFunction(LPVOID lpParameter);
static DWORD WINAPI UdpRoundRobin(LPVOID lpParameter);
static std::string WideCharToMultiByte(const CString& text);
private:
static bool InitWinsock();
protected:
HANDLE m_hThread = NULL;
HANDLE m_hSemaphore = NULL;
std::list<std::string> m_list;
fd_set m_fd;
timeval m_timeout;
int m_nRecvBytes;
char m_strBuffer[BUFF_LENGTH] = { 0 };
};
typedef struct tagThreadParameter
{
UdpService* pUdpService;
CListBox* pListBox;
} SThreadParameter;
// UdpService..cpp
#include "pch.h"
#include "UdpService.h"
UdpService::UdpService()
{
InitWinsock();
}
UdpService::~UdpService()
{
WSACleanup();
}
bool UdpService::Open(const SNetConfig& config)
{
if (!CreateSocket())
return false;
m_hThread = CreateThread(NULL, 0, UdpSendFunction, this, 0, NULL);
if (m_hThread == NULL)
return false;
m_hSemaphore = CreateSemaphore(NULL, 0, MAX_COUNT, NULL);
if (m_hSemaphore == NULL)
return false;
m_timeout.tv_sec = 0;
m_timeout.tv_usec = 0;
m_nRecvBytes = 0;
SetRemoteAddress(config);
if (!SetSocketOpt() || !IoCtlSocket() || !Bind())
return false;
return true;
}
long UdpService::ReceviceData(CString& strDataFromIP)
{
int nFromLen = sizeof(SOCKADDR_IN);
FD_ZERO(&m_fd);
FD_SET(m_sock, &m_fd);
int nSelectRecv = select(FD_SETSIZE, &m_fd, 0, 0, &m_timeout);
if (nSelectRecv == 0)
return -1;
if (nSelectRecv < 0)
{
// AfxMessageBox(_T("Listen failed"));
return -2;
}
memset(m_strBuffer, '\0', BUFF_LENGTH * sizeof(char));
m_nRecvBytes = 0;
m_nRecvBytes = recvfrom(m_sock, m_strBuffer, BUFF_LENGTH * sizeof(char), 0, (SOCKADDR*)&m_addrRemote, &nFromLen);
if (m_nRecvBytes != SOCKET_ERROR)
{
SOCKADDR_IN* pAddr = (SOCKADDR_IN*)&m_addrRemote;
BYTE* ip = (BYTE*)&pAddr->sin_addr.s_addr;
strDataFromIP.Format(_T("Data from IP: %d.%d.%d.%d\r\n"), ip[0], ip[1], ip[2], ip[3]);
return m_nRecvBytes;
}
return -3;
}
void UdpService::SendData(const char* pData)
{
m_list.push_back(std::string(pData));
ReleaseSemaphore(m_hSemaphore, MAX_COUNT, NULL);
}
DWORD WINAPI UdpService::UdpSendFunction(LPVOID lpParameter)
{
UdpService* pService = (UdpService*)lpParameter;
while (UdpService::bRun)
{
HANDLE& hSemaphore = pService->m_hSemaphore;
std::list<std::string>& list = pService->m_list;
if (hSemaphore == NULL)
return -1;
::WaitForSingleObject(hSemaphore, INFINITE);
if (!list.empty())
{
SOCKET& sock = pService->m_sock;
if (sock == INVALID_SOCKET || sock == SOCKET_ERROR)
{
AfxMessageBox(_T("invalide socket or socket error"));
return false;
}
std::string& strSend = list.front();
int nBufLen = (int)list.front().size();
SOCKADDR* pAddrRemote = (SOCKADDR*)&pService->m_addrRemote;
if (-1 == sendto(sock, strSend.c_str(), nBufLen, 0, pAddrRemote, sizeof(SOCKADDR)))
{
CString str;
str.Format(_T("UDP send to error\r\n"), strerror(errno));
::OutputDebugString(str);
}
list.pop_front();
}
Sleep(16);
}
return 0;
}
DWORD WINAPI UdpService::UdpRoundRobin(LPVOID lpParameter)
{
SThreadParameter* pParameter = (SThreadParameter*)lpParameter;
UdpService* pService = pParameter->pUdpService;
char* pBuffer = pService->m_strBuffer;
CListBox* pListBox = pParameter->pListBox;
CString strDataFromIP, strLength, strData;
while (UdpService::bRun)
{
long nRecvBytes = pService->ReceviceData(strDataFromIP);
if (-1 > nRecvBytes)
break;
if (-1 == nRecvBytes)
continue;
strLength.Format(_T("data length %ld, bytes"), nRecvBytes);
strData = CString(pBuffer);
CString str(strDataFromIP + _T(", data: '") + strData + _T("', ") + strLength);
pListBox->InsertString(0, str);
Sleep(16);
}
return 0;
}
std::string UdpService::WideCharToMultiByte(const CString& text)
{
int nLen = ::WideCharToMultiByte(CP_ACP, 0, text, text.GetLength(), NULL, 0, NULL, NULL);
char buff[BUFF_LENGTH] = { 0 };
::WideCharToMultiByte(CP_ACP, 0, text, text.GetLength(), buff, NULL, 0, NULL);
buff[nLen + 1] = '\0';
return std::string(buff);
}
bool UdpService::InitWinsock()
{
WORD nVersionRequested = MAKEWORD(2, 2);
WSADATA wsaData;
if (WSAStartup(nVersionRequested, &wsaData) != 0)
return false;
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wHighVersion) != 2)
{
WSACleanup();
return false;
}
return true;
}
// UdpDemoDlg.h
#pragma once
#include "UdpService.h"
// CUdpDemoDlg dialog
class CUdpDemoDlg : public CDialogEx
{
... ...
protected:
UdpService m_service;
virtual BOOL OnInitDialog();
public:
afx_msg void OnBnClickedSendButton();
afx_msg void OnDestroy();
};
// UdpDemoDlg.h
BOOL CUdpDemoDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
... ...
SNetConfig remoteConfig{ 7, "127.0.0.1" }; // "192.168.1.5"
if (!m_service.Open(remoteConfig))
return FALSE;
static SThreadParameter parameter { &m_service, (CListBox*)GetDlgItem(IDC_LIST) };
HANDLE hThread = CreateThread(NULL, 0, UdpService::UdpRoundRobin, (LPVOID)¶meter, 0, NULL);
if (hThread == NULL)
return FALSE;
return TRUE;
}
void CUdpDemoDlg::OnBnClickedSendButton()
{
CString text;
GetDlgItemText(IDC_EDIT, text);
m_service.SendData(_bstr_t(text));
}
void CUdpDemoDlg::OnDestroy()
{
CDialogEx::OnDestroy();
m_service.CloseSocket();
}