更新:

  • 增加网络连接时的错误判断,实现错误识别并显示错误窗口
  • 增加用户登录功能(未连接SQL版本)
  • 增加更改昵称功能
  • 本文基于上一篇博文 《MFC实现简单聊天客户端与服务端》

效果显示

客户端

重载OnReceive函数,用于接收数据

    void CMySocket::OnReceive(int nErrorCode)
    {
        CChatClientDlg* dlg = (CChatClientDlg*)AfxGetApp()->GetMainWnd();

        //获得返回的登陆状态
        Receive(dlg->buff, 200, 0);
        CString temp = dlg->buff;
        int index = 0;
        int sum = temp.GetLength();
        index = temp.Find('&')+1;
        serInf = temp.Mid(index, sum - index); 
        //AfxMessageBox(serInf);  调试信息
        if (index >= 1)
            dlg->isLogin = ConfirmLogin();
        if (temp[0] != '$')  //屏蔽验证消息,不在聊天记录内输出
        {   
        CString temp1 = "server: ";
        CString temp = dlg->buff;
        CString str;
        dlg->m_tm = CTime::GetCurrentTime();
        str = dlg->m_tm.Format("%X");
        temp1 = str + temp1;
        temp1 += temp;
        dlg->m_list.AddString(temp1);
        dlg->m_list.UpdateData(FALSE);
        }

        CAsyncSocket::OnReceive(nErrorCode);
    }

重载OnConnect函数用于连接时的处理

    void CMySocket::OnConnect(int nErrorCode)
    {
        CChatClientDlg* dlg = (CChatClientDlg*)AfxGetApp()->GetMainWnd();
        CString str;
        CString errinf = "【ERROR】";
        //判断是否连接成功
        if (0 != nErrorCode)
        {
            switch (nErrorCode)
            {
            case WSAEADDRINUSE:
                //AfxMessageBox(_T("The specified address is already in use.\n"));
                errinf += "The specified address is already in use.";
                break;
            case WSAEADDRNOTAVAIL:
                //AfxMessageBox(_T("The specified address is not available from ")
                //  _T("the local machine.\n"));
                errinf += "The specified address is not available from the local machine.";
                break;
            case WSAEAFNOSUPPORT:
                //AfxMessageBox(_T("Addresses in the specified family cannot be ")
                //  _T("used with this socket.\n"));
                errinf += "Addresses in the specified family cannot be used with this socket.";
                break;
            case WSAECONNREFUSED:
                //AfxMessageBox(_T("The attempt to connect was forcefully rejected.\n"));
                errinf += "The attempt to connect was forcefully rejected.";
                break;
            case WSAEDESTADDRREQ:
                //AfxMessageBox(_T("A destination address is required.\n"));
                errinf += " A destination address is required.";
                break;
            case WSAEFAULT:
                //AfxMessageBox(_T("The lpSockAddrLen argument is incorrect.\n"));
                errinf += "The lpSockAddrLen argument is incorrect.";
                break;
            case WSAEINVAL:
                //AfxMessageBox(_T("The socket is already bound to an address.\n"));
                errinf += "The socket is already bound to an address.";
                break;
            case WSAEISCONN:
                //AfxMessageBox(_T("The socket is already connected.\n"));
                errinf += "The socket is already connected.";
                break;
            case WSAEMFILE:
                //AfxMessageBox(_T("No more file descriptors are available.\n"));
                errinf += "No more file descriptors are available.";
                break;
            case WSAENETUNREACH:
                //AfxMessageBox(_T("The network cannot be reached from this host ")
                //  _T("at this time.\n"));
                errinf += "The network cannot be reached from this host at this time.";
                break;
            case WSAENOBUFS:
                //AfxMessageBox(_T("No buffer space is available. The socket ")
                //  _T("cannot be connected.\n"));
                errinf += "No buffer space is available. The socket cannot be connected.";
                break;
            case WSAENOTCONN:
                //AfxMessageBox(_T("The socket is not connected.\n"));
                errinf += "The socket is not connected.";
                break;
            case WSAENOTSOCK:
                //AfxMessageBox(_T("The descriptor is a file, not a socket.\n"));
                errinf += "The descriptor is a file, not a socket.";
                break;
            case WSAETIMEDOUT:
                //AfxMessageBox(_T("The attempt to connect timed out without ")
                //  _T("establishing a connection. \n"));
                errinf += "The attempt to connect timed out without establishing a connection.";
                break;
            default:
                TCHAR szError[256];
                _stprintf_s(szError, _T("OnConnect error: %d"), nErrorCode);
                errinf = "OnConnect error: %d";
                AfxMessageBox(szError); 
                break;
            }
            dlg->m_info.SetWindowTextA(errinf);
            //dlg->m_list.AddString(errinf);
            AfxMessageBox(_T("Please confirm your information and retry"));
            //更新控件状态
        }
        else if (dlg->isLogin=="1")
        {   
            dlg->m_tm = CTime::GetCurrentTime();
            str = dlg->m_tm.Format("%X");
            str += "\n 与服务器连接成功";
            dlg->m_info.SetWindowTextA(str);
            //dlg->m_list.AddString(str);
        }
        CAsyncSocket::OnConnect(nErrorCode);
    }

返回登陆状态

    CString CMySocket::ConfirmLogin()
    {
        CChatClientDlg* dlg = (CChatClientDlg*)AfxGetApp()->GetMainWnd();
        //解码
        if (serInf =="0") //0means err
        {
            AfxMessageBox(_T("用户ID或密码错误\n"));
            //增加控件清空
            dlg->password.SetWindowTextA("");
            return "0";
        }
        else if (serInf == "1") //1 means suc
        {
            AfxMessageBox(_T("登陆成功\n"));
            return "1";
        }
        else  
        {
            return "-1";  //-1 means err
        }
    }

调用登陆函数向服务器发送账户信息

        void  CMySocket::mLogin(CString userID, CString password)  //显示登陆信息是否成功等
    {
        CChatClientDlg* dlg = (CChatClientDlg*)AfxGetApp()->GetMainWnd();
        //格式化用户id和密码
        if (userID.IsEmpty() || password.IsEmpty())
            AfxMessageBox(_T("用户ID或密码不能为空\n"));
        else
        {
            CString userIDF = "&" + (CString)userID + "$" + (CString)password;;
            Send(userIDF, 200, 0);
        }
    }

    ##主函数中调用登陆
            //登陆
        CString userID;
        CString password;
        GetDlgItem(IDC_userID)->GetWindowText(userID);
        GetDlgItem(IDC_password)->GetWindowText(password);
        (m_client)->mLogin(userID, password);

服务器

重载OnConnect函数用于连接时的处理

    // CClientSocket 成员函数
    void CClientSocket::OnReceive(int nErrorCode)
    {
        CChatServerDlg* dlg = (CChatServerDlg*)AfxGetApp()->GetMainWnd();
        //获取时间
        dlg->m_tm = CTime::GetCurrentTime();
        CString str;
        str = dlg->m_tm.Format("%X");
        //接收数据
        Receive(dlg->buff, 200, 0);
        CString temp1 = dlg->buff;
        
        int sum = temp1.GetLength();
        //解码登陆信息
        int indexUser=0 ;
        int indexPass = 0;
        //for (int i = 0; i <sum ; i++)
        indexUser = temp1.Find('&')+1;
        //for (int i = 0; i <sum; i++)
        indexPass = temp1.Find('$')+1;

        userIDget = temp1.Mid(indexUser, indexPass-2);  //获得目标ID
        userPassget = temp1.Mid(indexPass, sum - indexPass); //获得目标密码
        if (indexPass>1)
            dlg->m_list.AddString("[Client] ID: "+userIDget +" P: "+ userPassget);

        //验证登陆信息
        if ((!userIDget.IsEmpty()) && (!userPassget.IsEmpty()))
            ConfirmLogin();

        //解码收到的数据,消息类
        int index = 0;
        for (int i=0; i < 3; i++)
        {
            index = temp1.Find('#', index);
            index++;
        }

        int count = sum - index;
        CString message = temp1.Mid(index, count);
        CString temp2 = "客户端:";
        temp2 = str + temp2;
        temp2 += message;
        dlg->m_list.AddString(temp2);

        CAsyncSocket::OnReceive(nErrorCode);
    }

以下方法用于验证用户信息,可替换为sql

    //验证登陆信息
    void CClientSocket::ConfirmLogin()
    {

        if (userIDget == "bupt")
        {
            canLogin = "$zwk&1";
            Send(canLogin, 20);
        }
        else
        {
            canLogin = "$zwk&0";
            Send(canLogin, 20);
        }

        //other
    }