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();
}
}