基于CAsyncSocket类模拟TCP网络传输之客户端

时间:2024-03-26 12:12:56

本例程对应的服务端请看https://blog.csdn.net/qq_34911636/article/details/88254711

本例程通信的流程和操作请看https://blog.csdn.net/qq_34911636/article/details/88256403

编译环境:系统:WIN7,Visual C++ 2015,创建ClientDlg对话框应用程序

1、基于MFC创建对话框应用程序,在创建对话框应用程序时“windows套接字”选项需要打钩

基于CAsyncSocket类模拟TCP网络传输之客户端

二、创建ClientSocket类,继承CAsyncSocket类

项目->添加类->选择MFC类->添加,如下图所示:

基于CAsyncSocket类模拟TCP网络传输之客户端

类名:ClientSocket,基类选择:CAsyncSocket,点击完成,这时在项目中生成ClientSocket.h;ClientSocket.cpp两个文件

三、重写CAsyncSocket类的回调函数OnConnect(int nErrorCode);OnClose(int nErrorCode);OnReceive(int nErrorCode)等

打开类视图,选中左边ClientSocket类,点击右边工具栏的属性,在用鼠标点击对应的OnConnect,OnClose,OnReceive

等函数,这时在ClientSocket.h文件中生成virtual void OnConnect(int nErrorCode); virtual void OnClose(int nErrorCode); 

virtual void OnReceive(int nErrorCode)三个函数,如下图示:

基于CAsyncSocket类模拟TCP网络传输之客户端

四、客户端界面的搭建,如下图所示:

基于CAsyncSocket类模拟TCP网络传输之客户端

五、添加控件的变量和处理函数

CClientDlg.h头文件中的变量:

ClientSocket *m_ClientSocket; //Socket指针
CIPAddressCtrl m_IP;  //IP控件的变量

CString m_Port;   //端口控件变量(String类型)
afx_msg void OnInternectConnect();  //“连接”按键对应的处理函数
afx_msg void OnInternectBreak();  //“断开”按键对应的处理函数
afx_msg void OnMessageSend();   //“发送”按键对应的处理函数
afx_msg void OnInternectQuit(); //“退出”按键对应的处理函数
CListBox m_Message;  //接收信息列表框的变量(Control类型)
CString m_Word;  //发送信息编辑框的变量(String类型)
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
CButton m_Connect;  //“连接”按键对应的变量(Control类型)
CButton m_Break;    //“断开”按键对应的变量(Control类型)
CButton m_SendClean; //“发送清除”按键的变量(Control类型)
CButton m_SendMessage; //“发送”按键对应的变量(Control类型)
CButton m_RecordClean;  //“清除记录”按键对应的变量(Control类型)
CEdit m_SendWord;  //发送信息编辑框的变量(Control类型)
afx_msg void OnBnSendClean();  //“发送清除”按键对应的处理函数
afx_msg void OnBnRecordClean(); //“记录清除”按键对应的处理函数
CEdit m_ControlPort;   //端口控件变量(Control类型)

CClientDlg.cpp文件中添加的处理函数

void CClientDlg::OnInternectConnect()
{
    // TODO: 在此添加控件通知处理程序代码
    BYTE IPADDRESS[4];
    CString IpString;
    UINT port;
    UpdateData(TRUE);  
    port = _ttoi(m_Port);  //把端口数据转换成整形变量
    m_IP.GetAddress(IPADDRESS[0],IPADDRESS[1],IPADDRESS[2],IPADDRESS[3]);  //获取IP地址
    if (!(m_IP.IsBlank()))  //判断是否有输入IP地址
    {
        IpString.Format(_T("%d.%d.%d.%d"), IPADDRESS[0], IPADDRESS[1], IPADDRESS[2], IPADDRESS[3]);  //把IP地址转换成字符串类型
        m_ClientSocket->Create();  //创建Socket
        m_ClientSocket->Connect(IpString, port);  //连接网络

        m_Break.EnableWindow(TRUE);  //使断开按键有效
        m_SendMessage.EnableWindow(TRUE); //使发送按键有效
        m_Connect.EnableWindow(FALSE);  //使连接按键失效
        m_IP.EnableWindow(FALSE);  //使IP控件输入无效
        m_ControlPort.EnableWindow(FALSE);  //使断开无效
    }

    //CButton *cbutton = (CButton*)GetDlgItem(IDC_CONNECT);
    //HWND hbutton = cbutton->GetSafeHwnd();
    CDC *buttonpdc = GetDlgItem(IDC_CONNECT)->GetDC();
    buttonpdc->SetBkMode(TRANSPARENT);
    buttonpdc->SetBkColor(RGB(255,0,0));
    ReleaseDC(buttonpdc);
}

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

    m_ClientSocket->Close();  //断开网络连接
     
    m_IP.EnableWindow(TRUE);  //使IP控件有效
    m_ControlPort.EnableWindow(TRUE);  //使断开有效
    m_Connect.EnableWindow(TRUE);  //使连接按键有效
    m_Message.AddString(_T("已断开连接"));  
    m_Message.SetTopIndex(m_Message.GetCount() - 1);
}

void CClientDlg::OnMessageSend()  
{
    // TODO: 在此添加控件通知处理程序代码
    CString sendmessage;
    UpdateData(TRUE);

    if (m_Word.GetLength() != 0)  //判断发送信息框是否有信息要发送
    {
        m_ClientSocket->Send(m_Word, 2*m_Word.GetLength()); //发送信息
        sendmessage.Format(_T("发送:%s"), m_Word); 
        m_Message.AddString(sendmessage);
        m_Message.SetTopIndex(m_Message.GetCount() - 1);
    }
}

void CClientDlg::OnInternectQuit()
{
    // TODO: 在此添加控件通知处理程序代码
    m_ClientSocket->Close();  //退出网络连接
    CDialog::OnCancel();
}

void CClientDlg::OnBnRecordClean()
{
    // TODO: 在此添加控件通知处理程序代码
    m_Message.ResetContent();  //清除列表框内容
}

void CClientDlg::OnBnSendClean()
{
    // TODO: 在此添加控件通知处理程序代码
    m_SendWord.SetWindowTextW(_T(""));  //清除发送框内容
}

六、ClientSocket.cpp中添加以下代码

void ClientSocket::OnConnect(int nErrorCode)  //Socket的回调函数,若TCP连接成功,则调用该函数
{
    // TODO: 在此添加专用代码和/或调用基类
    
    if (nErrorCode)
    {
        AfxMessageBox(_T("网络连接失败"));
        return;
    }

    ((CClientDlg*)(AfxGetApp()->m_pMainWnd))->m_Message.AddString(_T("网络连接成功"));
    ((CClientDlg*)AfxGetApp()->m_pMainWnd)->m_Message.SetTopIndex(((CClientDlg*)AfxGetApp()->m_pMainWnd)->m_Message.GetCount() + 1);

    CAsyncSocket::OnConnect(nErrorCode);
}

void ClientSocket::OnClose(int nErrorCode)  //Socket的回调函数,若TCP网络断开,则调用该函数
{
    // TODO: 在此添加专用代码和/或调用基类
    ((CClientDlg*)AfxGetApp()->m_pMainWnd)->m_Message.AddString(_T("服务器端断开连接"));
    ((CClientDlg*)AfxGetApp()->m_pMainWnd)->m_Message.SetTopIndex(((CClientDlg*)AfxGetApp()->m_pMainWnd)->m_Message.GetCount() - 1);

    CAsyncSocket::OnClose(nErrorCode);
}

void ClientSocket::OnReceive(int nErrorCode)  //Socket的回调函数,若TCP收到信息,则调用该函数接收信息
{
    // TODO: 在此添加专用代码和/或调用基类
    char RBYTE[200] = {NULL};
    CString RWord;
    int n = Receive(RBYTE, 200);
    RBYTE[n] = _T('\0');
    RWord.Format(_T("收到:%s"), RBYTE);

    ((CClientDlg*)AfxGetApp()->m_pMainWnd)->m_Message.AddString(RWord);
    ((CClientDlg*)AfxGetApp()->m_pMainWnd)->m_Message.SetTopIndex(((CClientDlg*)AfxGetApp()->m_pMainWnd)->m_Message.GetCount() - 1);

    CAsyncSocket::OnReceive(nErrorCode);
}

在上述的代码中((CClientDlg*)(AfxGetApp()->m_pMainWnd))的作用;

其中AfxGetApp()获取的是应用程序类的指针,比如利用vs2010或vc6.0自动生成一个mfc工程,假设名称为Pro,若是基于单文档的程序,里面一定有CPro、CProDoc、CProView还有CMainFrame这几个类,其中,CPro代表了这个应用程序运行实例的对象,该类里面有很多成员变量,其中有不少代表窗口的变量,而m_pMainWnd正是其中的一个变量,它代表了程序的主体窗口,且它是属于CMainFrame类。MFC提供的应用程序设计理念就是文档、框架、视图  结构,这三个结构是由三个类来体现的,分别是上面提到的CProDoc(文档,代表程序的数据)、CMainFrame(框架,代表应用程序界面的框架窗口)、CProView(视图,代表应用程序的客户区界面部分),MFC基于单文档或多文档的应用程序,都是基于这个结构来实现的。而者三个结构成员是由应用程序实例的类来统一管理的,这就是上面所说的CPro类。AfxGetCpp()正是返回了这个类的指针。虽然m_pMainWnd和AfxGetCpp()的返回值都是指针,但他们的指向不同。

所以上述代码“((CClientDlg*)(AfxGetApp()->m_pMainWnd))”作用是在ClientSocket类中调用整个函数的实例,并且指向该实例的句柄,其该实例的句柄就是所看到程序运行的窗口,在通过指针引用到实例中的具体变量上,例如“ ((CClientDlg*)AfxGetApp()->m_pMainWnd)->m_Message.AddString(RWord);”就是对窗口的列表框加载字符串操作。

 

七、运行代码

基于CAsyncSocket类模拟TCP网络传输之客户端