聊天客户端与服务端改进

Jun 2, 2015 00:31 · 1197 words · 3 minute read MFC

更新:

  • 增加网络连接时的错误判断,实现错误识别并显示错误窗口
  • 增加用户登录功能(未连接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
	}