Socket 学习之 MFC:简单通信 UDP 双向

最终界面如图所示:

Socket 学习之 MFC:简单通信 UDP 双向

 

不管客户端还是服务器,头文件添加爱

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <windows.h>

#include <string>
#include <iostream>
#include <thread>


//using namespace std;

#pragma comment(lib, "ws2_32.lib")

用到 C++ 11 的 THREAD,VS2013 以上版本,本例用 VS2017 编译。

 

 

客户端:.H 文件

public:
	afx_msg void OnBnClickedBtsend();
	afx_msg void OnBnClickedBtstart();
	afx_msg void OnBnClickedBtstop();

	bool bRunning;

	std::thread  threadid;
	void  GetDataThread();

	int PrintToList(CString info, bool bclient = true);

public:		
	SOCKET			sockClient;
	SOCKADDR_IN		addrServ;

 

客户端:CPP 文件


int CUDPCLIENTDlg::PrintToList(CString info, bool bclient)
{
	SYSTEMTIME stime;
	CString strtime;
	GetLocalTime(&stime);
	strtime.Format(_T("%02d:%02d:%02d"), stime.wHour, stime.wMinute, stime.wSecond);

	//获取当前记录条数
	CListCtrl * list = (CListCtrl*)GetDlgItem(IDC_LTGET);
	CString role = bclient ? _T("客户端说") : _T("服务器说");
	int nIndex = list->GetItemCount();
	list->InsertItem(nIndex, strtime);
	list->SetItemText(nIndex, 1, role);
	list->SetItemText(nIndex, 2, info);
	list->SendMessage(WM_VSCROLL, SB_BOTTOM, NULL);

	return 0;
}


void CUDPCLIENTDlg::GetDataThread()
{
	int length = sizeof(SOCKADDR);
	
	while (bRunning == true)
	{
		char recvBuf[100] = { 0 };
		int len = recvfrom(sockClient, recvBuf, 100, 0, (SOCKADDR*)&(addrServ), &length);
		if (len <= 0 || std::string(recvBuf).length() <= 0)
		{
			OutputDebugStringA("FAIL RECV\n");
			Sleep(10);
			continue;
		}
		PrintToList(CString(recvBuf), false);
		OutputDebugStringA(recvBuf);

		Sleep(10);
	}
}

void CUDPCLIENTDlg::OnBnClickedBtsend()
{
	// TODO: 在此添加控件通知处理程序代码

	CString csinfo;
	GetDlgItem(IDC_ETSEND)->GetWindowTextW(csinfo);
	string infodata = CT2A(csinfo);
	sendto(sockClient, infodata.c_str(), infodata.length(), 0, (SOCKADDR*)&addrServ, sizeof(SOCKADDR));

	PrintToList(csinfo, true);
	OutputDebugStringA(CT2A(csinfo));
}


void CUDPCLIENTDlg::OnBnClickedBtstart()
{
	// TODO: 在此添加控件通知处理程序代码
	WORD wVersionRequested = MAKEWORD(1, 1);
	WSADATA	wsaData;
	int err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0)
	{
		string info = "WSASTAER FAILED WITH ERROR : ";
		info = info.append(to_string(err));
		OutputDebugStringA(info.c_str());
		AfxMessageBox(CString(info.c_str()));
		return;
	}

	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
	{
		string info = "COULD NOT FIND A USABLE VERSION OF WINSOCK.DLL";
		OutputDebugStringA(info.c_str());
		AfxMessageBox(CString(info.c_str()));
		WSACleanup();
		return;
	}
	else
	{
		string info = "THE WINSOCK.DLL 2.2 WAS FOUND OKAY";
		OutputDebugStringA(info.c_str());
	}

	//创建SOCKET对象
	sockClient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (sockClient == INVALID_SOCKET)
	{
		string info = "SOCKET FAILED WITH ERROR : ";
		info = info.append(to_string(WSAGetLastError()));
		OutputDebugStringA(info.c_str());
		AfxMessageBox(CString(info.c_str()));
		return;
	}

	//获取IP地址
	CIPAddressCtrl * pIpctr = (CIPAddressCtrl*)GetDlgItem(IDC_ETIP);	
	if (pIpctr->IsBlank() == true)
	{
		string info = "IP ADDRESS IS BLANK";
		OutputDebugStringA(info.c_str());
		AfxMessageBox(CString(info.c_str()));
		WSACleanup();
		return;
	}

	DWORD dwip;
	pIpctr->GetAddress(dwip);
	WORD hiWord = HIWORD(dwip);
	WORD loWord = LOWORD(dwip);
	BYTE nf1 = HIBYTE(hiWord);
	BYTE nf2 = LOBYTE(hiWord);
	BYTE nf3 = HIBYTE(loWord);
	BYTE nf4 = LOBYTE(loWord);

	CString csip;
	csip.Format(_T("%d.%d.%d.%d"), nf1, nf2, nf3, nf4);

	CString csport;
	GetDlgItem(IDC_ETPORT)->GetWindowTextW(csport);
	USHORT dwport = atoi(CT2A(csport));

	memset(&addrServ, 0, sizeof(addrServ));
	addrServ.sin_addr.S_un.S_addr = inet_addr(CT2A(csip));
	addrServ.sin_family = AF_INET;
	addrServ.sin_port = htons(dwport);

	GetDlgItem(IDC_BTSTART)->EnableWindow(FALSE);
	GetDlgItem(IDC_BTSTOP)->EnableWindow(TRUE);
	GetDlgItem(IDC_BTSEND)->EnableWindow(TRUE);

	struct timeval tv;
	tv.tv_sec = 1;
	tv.tv_usec = 0;
	setsockopt(sockClient, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));

	bRunning = true;

	threadid = std::thread(&CUDPCLIENTDlg::GetDataThread, this);
	threadid.detach();
}


void CUDPCLIENTDlg::OnBnClickedBtstop()
{
	// TODO: 在此添加控件通知处理程序代码

	bRunning = false;

	closesocket(sockClient);
	WSACleanup();

	GetDlgItem(IDC_BTSTART)->EnableWindow(TRUE);
	GetDlgItem(IDC_BTSTOP)->EnableWindow(FALSE);
	GetDlgItem(IDC_BTSEND)->EnableWindow(FALSE);

	//等待线程结束
	if (threadid.joinable() == true)
	{
		threadid.join();
	}
}

 

 

 

服务器:.H 文件

public:
	afx_msg void OnBnClickedBtsend();
	afx_msg void OnBnClickedBtstart();
	afx_msg void OnBnClickedBtstop();

	bool bRunning;

	std::thread  thid;
	void GetDataThread();

	int PrintToList(CString info, bool bclient = false);

public:	
	SOCKET		 sockSrv;		
	SOCKADDR_IN  addrClient;

 

服务器:CPP 文件

int CUDPSERVERDlg::PrintToList(CString info, bool bclient)
{
	SYSTEMTIME stime;
	CString strtime;
	GetLocalTime(&stime);
	strtime.Format(_T("%02d:%02d:%02d"), stime.wHour, stime.wMinute, stime.wSecond);

	//获取当前记录条数
	CListCtrl * list = (CListCtrl*)GetDlgItem(IDC_LTGET);
	CString role = bclient ? _T("服务器说") : _T("客户端说");
	int nIndex = list->GetItemCount();
	list->InsertItem(nIndex, strtime);
	list->SetItemText(nIndex, 1, role);
	list->SetItemText(nIndex, 2, info);
	list->SendMessage(WM_VSCROLL, SB_BOTTOM, NULL);

	return 0;
}


void CUDPSERVERDlg::GetDataThread()
{
	int length = sizeof(SOCKADDR);
	
	while (bRunning == true)
	{
		char recvBuf[100] = { 0 };
		int len = recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR*)&(addrClient), &length);
		if (len <= 0 || std::string(recvBuf).length() <= 0)
		{
			Sleep(10);
			continue;
		}		
		PrintToList(CString(recvBuf), false);
		OutputDebugStringA(recvBuf);

		Sleep(10);
	}
}

void CUDPSERVERDlg::OnBnClickedBtsend()
{
	// TODO: 在此添加控件通知处理程序代码
	
	size_t length = sizeof(SOCKADDR);
	CString csinfo;
	GetDlgItem(IDC_ETSEND)->GetWindowTextW(csinfo);
	std::string infodata = CT2A(csinfo);
	int ret = sendto(sockSrv, infodata.c_str(), infodata.length(), 0, (SOCKADDR *)&addrClient, length);
	if (ret < 0)
	{
		//重新连接
	}
	PrintToList(csinfo, true);
	OutputDebugStringA(CT2A(csinfo));
}


void CUDPSERVERDlg::OnBnClickedBtstart()
{
	// TODO: 在此添加控件通知处理程序代码
	WORD wVersionRequested = MAKEWORD(1, 1);
	WSADATA	 wsaData;
	int err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0)
	{
		std::string info = "WSASTAER FAILED WITH ERROR : ";
		info = info.append(std::to_string(err));
		OutputDebugStringA(info.c_str());
		AfxMessageBox(CString(info.c_str()));
		return;
	}

	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
	{
		std::string info = "COULD NOT FIND A USABLE VERSION OF WINSOCK.DLL";
		OutputDebugStringA(info.c_str());
		AfxMessageBox(CString(info.c_str()));
		WSACleanup();
		return;
	}
	else
	{
		std::string info = "THE WINSOCK.DLL 2.2 WAS FOUND OKAY";
		OutputDebugStringA(info.c_str());
	}

	sockSrv = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); //创建一个socket句柄;
	if (sockSrv == INVALID_SOCKET)
	{
		std::string info = "SOCKET FAILED WITH ERROR : ";
		info = info.append(std::to_string(WSAGetLastError()));
		OutputDebugStringA(info.c_str());
		AfxMessageBox(CString(info.c_str()));
		return;
	}
		
	CString csport;
	GetDlgItem(IDC_ETPORT)->GetWindowTextW(csport);
	USHORT dwport = atoi(CT2A(csport));

	SOCKADDR_IN  addrServ;
	memset(&addrServ, 0, sizeof(addrServ));
	addrServ.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addrServ.sin_family = AF_INET;
	addrServ.sin_port = htons(dwport);

	struct timeval tv;
	tv.tv_sec = 1;
	tv.tv_usec = 0;
	setsockopt(sockSrv, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
		
	auto ret = bind(sockSrv, (SOCKADDR*)&addrServ, sizeof(SOCKADDR));
	if (ret == SOCKET_ERROR)
	{
		std::string info = "BIND FAILED WITH ERROR : ";
		info = info.append(std::to_string(WSAGetLastError()));
		OutputDebugStringA(info.c_str());
		AfxMessageBox(CString(info.c_str()));
		return;
	}
		
	bRunning = true;

	GetDlgItem(IDC_BTSTART)->EnableWindow(FALSE);
	GetDlgItem(IDC_BTSTOP)->EnableWindow(TRUE);
	GetDlgItem(IDC_BTSEND)->EnableWindow(TRUE);

	thid = std::thread(&CUDPSERVERDlg::GetDataThread, this);
	thid.detach();
}


void CUDPSERVERDlg::OnBnClickedBtstop()
{
	// TODO: 在此添加控件通知处理程序代码

	bRunning = false;

	closesocket(sockSrv);
	WSACleanup();
	
	GetDlgItem(IDC_BTSTART)->EnableWindow(TRUE);
	GetDlgItem(IDC_BTSTOP)->EnableWindow(FALSE);
	GetDlgItem(IDC_BTSEND)->EnableWindow(FALSE);

	//等待线程结束
	if (thid.joinable() == true)
	{
		thid.join();
	}
}