模拟UDP通信的发送与接收

编译环境:系统: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、相互发送信息                        

 

 

 

 

 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值