编译环境:系统:WIN7,Visual C++ 2015,创建ClientDlg对话框应用程序和创建ServerDlg的对话框程序
1、利用MFC创建一个CLidarReceiveDlg的对话框程序
2、设计发送端与接收端的界面,由于UDP通信是无连接,所以同一个程序即可当发送端与可当接收端
图1、发送端与接收端的界面
3、给控件增加变量和处理函数,具体如下:
public:
CListBox m_Message; //消息接收变量
CIPAddressCtrl m_LocalIP; //本地IP地址变量
CEdit m_LocalPort; //本地端口变量
CIPAddressCtrl m_OtherIP; //目的地地址IP变量
CEdit m_OtherPort; //目的地地址端口变量
CEdit m_SendMessage; //发送信息变量
afx_msg void OnBnStartInternect(); //启动网络按键
afx_msg void OnBnStopInternect(); //停止网络按键
afx_msg void OnBnSendMessage(); //发送信息按键
afx_msg void OnBnStop(); //关闭软件按键
afx_msg void OnBnAbout();
public:
SOCKET Client;
SOCKET ServerSocket; //定义Socket套接字
SOCKADDR_IN m_SocketServer; //本地套接字通用地址结构
SOCKADDR_IN m_SocketTo; //目的地套接字通用地址结构
MSG m_message;
int m_SocketLen; //SOCKADDR_IN结构体的大小
private:
BYTE m_SetIP[4]; //IP地址
public:
afx_msg void OnBnRecordClean(); //ClistBox框清空按键
LRESULT OnReadClose(WPARAM wParam, LPARAM lParam); //接收数据函数
CButton m_StartKey; //启动按键变量
CButton m_StopKey; //停止按键变量
CButton m_SendKey; //发送数据变量
4、添加控件的处理函数
void CLidarReceiveDlg::OnBnStartInternect() //网络启动函数,对应“启动”按键
{
// TODO: 在此添加控件通知处理程序代码
BYTE MIP[4]; //定义IP字节
char *CharIP;
CString PortString, LocalIPString, LocalPort, InternectPort;
int ErrorCode;
DWORD m_ip;
WSADATA WsaData;
UpdateData(TRUE);
if (m_LocalIP.IsBlank())
{
MIP[0] = m_SetIP[0];
MIP[1] = m_SetIP[1];
MIP[2] = m_SetIP[2];
MIP[3] = m_SetIP[3];
m_LocalIP.SetAddress(MIP[0], MIP[1], MIP[2], MIP[3]); //设置预先设置的IP地址
LocalIPString.Format(_T("%d.%d.%d.%d"), MIP[0], MIP[1], MIP[2], MIP[3]);
m_LocalPort.SetWindowTextW(_T("2235")); //设置预先设置的端口
m_LocalIP.EnableWindow(FALSE);
m_LocalPort.EnableWindow(FALSE);
}
else
{
m_LocalIP.GetAddress(MIP[0], MIP[1], MIP[2], MIP[3]); //获取IP控件中的IP地址
m_LocalIP.GetAddress(m_ip);
LocalIPString.Format(_T("%d.%d.%d.%d"), MIP[0], MIP[1], MIP[2], MIP[3]);
m_LocalPort.GetWindowTextW(PortString); //获取端口号
}
if (WSAStartup(MAKEWORD(2, 2), &WsaData)) //启动网络
{
//AfxMessageBox(_T("网络初始化失败"));
m_Message.AddString(_T("初始化Socket失败"));
m_Message.SetTopIndex(m_Message.GetCount() - 1);
WSACleanup();
return;
}
m_Message.AddString(_T("创建Socket......"));
m_Message.SetTopIndex(m_Message.GetCount() - 1);
ServerSocket = socket(PF_INET,SOCK_DGRAM,0); //创建一个Socket对象
if (ServerSocket == INVALID_SOCKET)
{
m_Message.AddString(_T("创建失败......"));
m_Message.SetTopIndex(m_Message.GetCount() - 1);
return;
}
USES_CONVERSION;
CharIP = T2A(LocalIPString); //把IP地址转化成Char类型
//strcpy(CharIP,(LPCTSTR)LocalIPString);
m_LocalPort.GetWindowTextW(LocalPort); //获取端口的端口号
m_SocketServer.sin_family = AF_INET;
m_SocketServer.sin_addr.S_un.S_addr = inet_addr(CharIP); //把IP地址赋予IP通用结构体
m_SocketServer.sin_port = htons(_ttoi(LocalPort)); //把端口号赋予IP地址通盈结构体
m_SocketLen = sizeof(m_SocketServer); //获取IP地址通用结构体的大小
if (SOCKET_ERROR == bind(ServerSocket, (LPSOCKADDR)&m_SocketServer,sizeof(m_SocketServer))) //绑定IP地址
{
m_Message.AddString(_T("绑定地址失败......"));
m_Message.SetTopIndex(m_Message.GetCount() - 1);
return;
}
ErrorCode = WSAAsyncSelect(ServerSocket,m_hWnd,WM_CLIENT_READCLOSE,FD_READ); //设置接收回调函数
if (ErrorCode == SOCKET_ERROR)
{
m_Message.AddString(_T("WSAAsyncSelect函数出错!!!"));
m_Message.SetTopIndex(m_Message.GetCount() - 1);
return;
}
m_Message.AddString(_T("客户端网络初始化成功!!!"));
m_Message.SetTopIndex(m_Message.GetCount() - 1);
InternectPort.Format(_T("IP:%d.%d.%d.%d 端口:%d"), MIP[0], MIP[1], MIP[2], MIP[3], _ttoi(LocalPort));
m_Message.AddString(InternectPort);
m_Message.SetTopIndex(m_Message.GetCount() - 1);
this->SetWindowTextW(_T("本应用进程:"+InternectPort+""));
m_StartKey.EnableWindow(FALSE);
m_LocalIP.EnableWindow(FALSE);
m_LocalPort.EnableWindow(FALSE);
//m_SocketServer.sin_addr.s_addr = inet_addr(LocalIPString);
}
void CLidarReceiveDlg::OnBnStopInternect() //网络停止函数对应“停止”按键
{
// TODO: 在此添加控件通知处理程序代码
closesocket(ServerSocket); //关闭网络
WSACleanup();
m_Message.AddString(_T("本网络进程停止运行......"));
m_Message.SetTopIndex(m_Message.GetCount() - 1);
m_StartKey.EnableWindow(TRUE);
m_LocalIP.EnableWindow(TRUE);
m_LocalPort.EnableWindow(TRUE);
}
void CLidarReceiveDlg::OnBnSendMessage() //发送信息处理函数对应“发送”按键
{
// TODO: 在此添加控件通知处理程序代码
BYTE m_IP[4];
CHAR *CharMessage, *CharIP;
CString SendIP, m_SendMsg,IPString,m_Port;
//UpdateData(TRUE);
m_SendMessage.GetWindowTextW(m_SendMsg); //获取发送的信息
//m_Message.AddString(_T("发送信息为空!!!"));
if (m_SendMsg.IsEmpty()) //判断发送的信息是否为空
{
m_Message.AddString(_T("发送信息为空!!!"));
m_Message.SetTopIndex(m_Message.GetCount() - 1);
return;
}
m_OtherPort.GetWindowTextW(m_Port); //获取目的地的端口号
USES_CONVERSION;
CharMessage = T2A(m_SendMsg); //把发送的信息的格式转化为Char类型
m_OtherIP.GetAddress(m_IP[0], m_IP[1], m_IP[2], m_IP[3]); //
if (m_OtherIP.IsBlank())
{
m_Message.AddString(_T("IP地址为空!!!"));
m_Message.SetTopIndex(m_Message.GetCount() - 1);
return;
}
if (m_Port.IsEmpty())
{
m_Message.AddString(_T("端口号为空!!!"));
m_Message.SetTopIndex(m_Message.GetCount() - 1);
return;
}
IPString.Format(_T("%d.%d.%d.%d"), m_IP[0], m_IP[1], m_IP[2], m_IP[3]);
CharIP = T2A(IPString); //把发送地址转化成Char类型
m_SocketTo.sin_family = AF_INET;
m_SocketTo.sin_addr.S_un.S_addr = inet_addr(CharIP);
m_SocketTo.sin_port = htons(_ttoi(m_Port));
sendto(ServerSocket, CharMessage,strlen(CharMessage),0, (LPSOCKADDR)&m_SocketTo,sizeof(m_SocketTo)); //发动信息
m_Message.AddString(_T("发送:"+ m_SendMsg +""));
m_Message.SetTopIndex(m_Message.GetCount() - 1);
}
void CLidarReceiveDlg::OnBnStop() //对应“退出”按键
{
// TODO: 在此添加控件通知处理程序代码
CDialogEx::OnCancel(); //关闭软件
}
void CLidarReceiveDlg::OnBnAbout() //对应“关于”按键
{
// TODO: 在此添加控件通知处理程序代码
CAboutDlg aboutdlg;
aboutdlg.DoModal();
}
void CLidarReceiveDlg::OnBnRecordClean() //对应“清空记录”按键
{
// TODO: 在此添加控件通知处理程序代码
m_Message.ResetContent(); //清空接收信息
}
5、UDP接收数据通过VC的宏映射机制接收信息
为了网络能够异步的接收信息需定义一个映射宏,该宏对应void CLidarReceiveDlg::OnBnStartInternect() 函数中的
WSAAsyncSelect(ServerSocket,m_hWnd,WM_CLIENT_READCLOSE,FD_READ)函数,其中WM_CLIENT_READCLOSE
就是自定义的宏。
自定义消息宏的步骤:
在头文件stdafx.h中定义:#define WM_CLIENT_READCLOSE WM_USER + 102
在CLidarReceiveDlg.cpp文件中BEGIN_MESSAGE_MAP与END_MESSAGE_MAP之间添加
ON_MESSAGE(WM_CLIENT_READCLOSE,OnReadClose),其中OnReadClose函数为宏WM_CLIENT_READCLOSE
的映射函数。
在CLidarReceiveDlg.h中定义LRESULT OnReadClose(WPARAM wParam, LPARAM lParam)映射函数
最后在在CLidarReceiveDlg.cpp文件中定义OnReadClose函数的实现如下所示:
LRESULT CLidarReceiveDlg::OnReadClose(WPARAM wParam, LPARAM lParam)
{
char rechar[5000] = {0}; //设置接收的缓冲区
CString stringchar;
int charlen; //接收的字节数
if (WSAGETSELECTERROR(lParam)) //判断网络接收是否出错
{
m_Message.AddString(_T("网络出错......"));
m_Message.SetTopIndex(m_Message.GetCount() - 1);
return 0;
}
switch (WSAGETSELECTEVENT(lParam)) //
{
case FD_READ:
charlen = recvfrom(ServerSocket, rechar, 5000, 0, (LPSOCKADDR)&m_SocketServer, (int*)&m_SocketLen); //接收数据
if (charlen == SOCKET_ERROR)
{
m_Message.AddString(_T("接收失败......"));
m_Message.SetTopIndex(m_Message.GetCount() - 1);
return 0;
}
rechar[charlen] = '\0'; //缓冲区接收数据的最后一个字符后面加上一个结束符
stringchar = rechar; //把缓冲区里面的char字符串转化成CString字符类型
stringchar.Format(_T("收到:%s"), stringchar);
m_Message.AddString(stringchar); //把接收到的字符显示出来
m_Message.SetTopIndex(m_Message.GetCount() - 1);
break;
default:
break;
}
return 0;
}
6、编译调试
图2编译成功之后的界面
图3、相互发送信息