MFC中基于TCP/UDP协议的网络聊天室(ODBC链接MySQL、基于对话框、简易网络聊天室、TCP/UDP协议)

MFC中基于TCP/UDP协议的网络聊天室(ODBC链接MySQL、基于对话框、简易网络聊天室、TCP/UDP协议)

前言

这个代码是MFC中基于TCP/UDP协议的简易网络聊天室,主要实现的功能是不同客户端之间可以互相发送消息, 而且所有消息都存储在数据库中,客户端用户可以对自己所发送过的消息进行查询。服务器可以对客户端进行相应的管理。程序链接了MySQL数据库,VS和数据的链接这块儿不在赘述。
程序代码链接 https://download.csdn.net/download/eq_cyc/11966503

环境说明

编译环境:VS2012
数据库:MySQL

核心代码

登录界面

这块儿是一个简单的登录界面,UDP协议和TCP协议的基本相同,主要算法在三次登录和链接数据库。先上图:

在这里插入图片描述
登录代码:


void dlg::OnBnClickedlogin()
{
GetDlgItem(IDC_EDIT1)->GetWindowText(user1);
GetDlgItem(IDC_EDIT2)->GetWindowText(password);
//将两个控件里的值作为数据库查询条件
if(user1.IsEmpty()||password.IsEmpty())
{
	AfxMessageBox(_T("用户名或密码不能为空!"));
	return;
}

//进行数据库查询,user和password一致则执行登录操作
UpdateData();
m_num++;
CString strSQL;
strSQL.Format(_T("select * from serverlogin where name ='%s' and password = '%s'"),user1,password);

if(!recordset.Open(AFX_DB_USE_DEFAULT_TYPE,strSQL))
{
	MessageBox(_T("打开数据库失败!"),_T("数据库错误"),MB_OK);
	return ;
}

if(recordset.GetRecordCount()==0)
{
	if(m_num == 3)
	{
		MessageBox(_T("密码错误3次,自动退出!"));
		exit(0);
	}
	else{
		recordset.Close();
		MessageBox(_T("密码错误,请重新输入!"));
		password="";
		UpdateData(FALSE);
	}
}
else{
	//如果组合框控件选择的是TCP_Client,则登录到页面1***********************************
	if (m_cb.GetCurSel()==0)
	{
		MessageBox(_T("登录成功TCP_Server!"));
		recordset.Close();
		m_dataBase.Close();
		CDialog::OnOK();
		ClineDlg dlg;
		theApp.m_pMainWnd = &dlg;
		dlg.DoModal();
	}	//如果组合框控件选择的是UDP_Client,则登录到页面2***********************************
	else if(m_cb.GetCurSel()==1)
	{
		MessageBox(_T("登录成功UDP_Server!"));
		recordset.Close();
		m_dataBase.Close();
		//关闭登录窗口
		CDialog::OnOK();
		UDPDlg dlg;
		theApp.m_pMainWnd = &dlg;
		dlg.DoModal();				
	}
}
}

数据库配置

在这里插入图片描述

注册界面

在这里插入图片描述

TCP服务端

在这里插入图片描述
TCP协议服务器端这一套必须写全了:
1.Creat()Create 方法已经包含了 Bind 方法,如果是以 Create 方法创建socket的前提下不能再调用 Bind ,要不一定出错。
2.Listen()
3.Accept()

新建一个类,继承CSocket类,类名为CServerSocket,
CServerSocket核心代码:


//添加上线用户,套接字接受到连接请求时触发该事件
//以通知侦听套接字它可以通过调用accept成员函数接受挂起的连接请求。
void CServerSocket::OnAccept(int nErrorCode)
{
	m_pDlg->AddClient();  //调用对话框类的AddClient方法:添加新的连接用户
	CSocket::OnAccept(nErrorCode);
}

// 删除下线用户,套接字关闭时触发该事件
void CServerSocket::OnClose(int nErrorCode)
{
	m_pDlg->RemoveClient(this); //RemoveClient:移除客户端
	CSocket::OnClose(nErrorCode);
}

// 接收数据,,套接字上有数据被接受时触发该事件
void CServerSocket::OnReceive(int nErrorCode)
{
	m_pDlg->RecvData(this);  //调用对话框类的RecvData方法:接受客户端发来的消息
	CSocket::OnReceive(nErrorCode);
}


void CServerSocket::OnSend(int nErrorCode)
{
	// TODO: 在此添加专用代码和/或调用基类

	CSocket::OnSend(nErrorCode);
}

创建服务器端的套接字Creat(),并且监听Listen(),打开服务器

void ClineDlg::startServer()
{
	CString cport;
	//获取服务器的端口号
	GetDlgItemText(IDC_SERVERPORT,cport);
	//端口号的字符串转换为数字
	int port = _ttoi(cport);
	//如果没有填写端口号,则默认的端口号为8080
	if (port == 0)
	{
		m_port = 8080;
	}
	else
	{
		m_port = port;
	}

	//如果当前服务器的状态为开启,则关闭服务器m_connect初始为false
	if (m_connect)
	{
		//关闭服务器的socket
		delete m_listenSocket;
		m_listenSocket = NULL;
		m_connect = false;
		SetDlgItemText(IDC_BN_SERVER_START, _T("打开服务器"));
		UpdateEvent(_T("系统关闭服务器."));
		//关闭控件的只读状态
		m_editPort.SetReadOnly(FALSE);
		//设置“发送”按钮为不可用
		m_buttonSend.EnableWindow(FALSE);
		return;
	}
	//否则就打开服务器
	m_listenSocket = new CServerSocket();
	m_listenSocket->m_pDlg = this;
	m_listenSocket->m_userName = _T("TCP服务器");
	
	// 指定对话框为主对话框,不能少了这句
	UpdateData(true);
	// 创建服务器的套接字,IP地址默认本机IP
	if (!m_listenSocket->Create(m_port))	//bind绑定端口
	{
		AfxMessageBox(_T("创建套接字错误!"));
		m_listenSocket->Close();
		return;
	}
	if (!m_listenSocket->Listen())
	{
		AfxMessageBox(_T("监听失败!"));
		m_listenSocket->Close();
		return;
	}
	m_connect = true;
	SetDlgItemText(IDC_BN_SERVER_START, _T("关闭服务器"));
	//更新聊天窗口中的消息
	UpdateEvent(_T("系统打开服务器."));
	//将服务器端口号的控件设置为只读状态
	if(port == 0)
	{
		m_editPort.SetWindowTextW(_T("8080"));
	}
	else 
	{
		m_editPort.SetWindowTextW(cport);
	}
	m_editPort.SetReadOnly(TRUE);
	//设置“发送”按钮为可用
	m_buttonSend.EnableWindow(TRUE);
}

接收由客户端发来的消息

void ClineDlg::RecvData(CServerSocket* pSocket)
{
	char* pData = NULL;
	pData = new char[1024];
	memset(pData, 0, sizeof(char)* 1024);
	UCHAR leng = 0;
	CString str;
	//Receive为CSocket中的方法,用于服务器接收消息
	//pData接受数据的缓冲区
	//1024:缓冲区的长度
	//0:函数调用模式
	if (pSocket->Receive(pData,1024,0) != SOCKET_ERROR)
	{
		str = pData;
		//接受消息后可以将消息转发给所有客户端或者指定客户端translateMsg()转发
		translateMsg(str, pSocket);
	}
	delete[] pData;
	pData = NULL;
}

添加新的客户端,调用Accept()函数

void ClineDlg::AddClient()
{
	CServerSocket *pSocket = new CServerSocket;  //创建一个套接字
	pSocket->m_pDlg = this;
	m_listenSocket->Accept(*pSocket);  //接受客户端套接字连接
	//CAsyncSocket::AsyncSelect调用此成员函数以请求套接字的事件通知。
	pSocket->AsyncSelect(FD_READ | FD_WRITE | FD_CLOSE);
	//AddTail将新元素的列表添加到此列表的结尾。
	m_clientList.AddTail(pSocket);  //将套接字添加到列表容器中
	m_userCount = m_clientList.GetCount();
	UpdateData(false);
}

给客户端发送消息:

void ClineDlg::SendMSG()
{
	CString  msg;
	//获取消息框中的字符串
	GetDlgItemText(IDC_EDITMSG, msg);
	//获取当前选中的用户的下标(比如选中了某个人或者选中了“所有人”)
	int selectIndex = m_users.GetCurSel();
	if(selectIndex == -1)
	{
		MessageBox(_T("请在聊天室中选择发送对象!"));
		return ;
	}
	if(msg == _T(""))
	{
		MessageBox(_T("请输入要发送的内容!"));
		return ;
	}
	CString usermsg;
	//获取该下标的字符串名称
	m_users.GetText(selectIndex, usermsg);
	if (usermsg == "所有人")
	{
		usermsg = _T("MEVERY") + msg;
	}
	else
	{
		usermsg = _T("M") + usermsg + _T(" ") + msg;
	}
	translateMsg(usermsg, this->m_listenSocket);
}

TCP客户端

TCP协议的客户端代码,重点是创建套接字Creat(),和连接服务器Connect()。然后利用Send()函数给服务器发送消息,利用Receive函数来接受消息,并且将接受到的信息进行拆分,分别在聊天室列表和聊天窗口进行显示。接受到了服务器端发送的消息后,同时会将消息进行存储。数据存储在MySQL数据库中,显示历史聊天记录是对数据库进行查询的一个操作,按用户名进行查询,将查询出来的结果显示在聊天记录窗口中。

在这里插入图片描述
下面是核心代码,进行讲解:
连接服务器,会进行客户端套接字的创建和连接工作。

void ClineClientDlg::ConnectServer()
{
	GetDlgItemText(IDC_USERNAME, m_userName);
	//如果已经连接,则断开服务器
	if (m_connect)
	{
		//关闭并且删除客户端的socket
		m_connect = false;
		m_pSock->Close();
		delete m_pSock;  //删除套接字
		m_pSock = NULL;
		m_ConPC.SetWindowTextW(_T("连接服务器"));
		m_buttonSend.EnableWindow(FALSE);
		m_buttonShowHistory.EnableWindow(FALSE);
		UpdateData(false);
		return;
	}
	//否则连接服务器
	else
	{
		//在这里创建新的socket
		m_pSock = new CClientSocket();
		//创建套接字
		if (!m_pSock->Create())
		{
			AfxMessageBox(_T("创建套接字失败!"));
			return;
		}
	}
	//获得ip控件的ip
	BYTE f1, f2, f3, f4;
	((CIPAddressCtrl*)GetDlgItem(IDC_SERVERIPADDRESS))->GetAddress(f1, f2, f3, f4);
	m_strServerip.Format(_T("%d.%d.%d.%d"), f1, f2, f3, f4);
	m_port = GetDlgItemInt(IDC_SERVERPORT);
	m_strServerip = _T("127.0.0.1");
	//TCP服务器为8080
	//用m_strServerip和m_port来验证是否成功连接到服务器
	if (!m_pSock->Connect(m_strServerip, m_port))
	{
		AfxMessageBox(_T("连接服务器失败!"));
		return;
	}
	else
	{
		AfxMessageBox(_T("连接服务器成功!"));
		//成功连接上了服务器!!
		m_connect = true;
		int n = 0;
		CString userName;
		userName = _T("MUSERNAME") + m_userName;
		//宽字节转多字节,确定要发送的字符串将占据多少个宽度的char
		n = WideCharToMultiByte(CP_OEMCP, 0, userName, -1, NULL, 0, 0, FALSE);
		char* pBuff = new char[n];
		memset(pBuff, 0, n);
		//宽字节转多字节,将要发送的字符串转化为字节流
		WideCharToMultiByte(CP_OEMCP, 0, userName.GetBuffer(0), n, pBuff, n, 0, FALSE);
		//通过socket发送多字节流连接
		m_pSock->SendMSG(pBuff, n);
		delete[] pBuff;
		m_ConPC.SetWindowTextW(_T("断开服务器"));
		m_buttonSend.EnableWindow(TRUE);
		m_buttonShowHistory.EnableWindow(TRUE);
		UpdateData(false);
	}
}

给服务器发送消息:

void ClineClientDlg::SendMsg()
{
	CString  msg;
	//获得消息框里的字符串
	GetDlgItemText(IDC_EDIT_MSG, msg);
	int selectIndex = m_userslist.GetCurSel();
	if(selectIndex == -1)
	{
		MessageBox(_T("请在聊天室中选择发送对象!"));
		return ;
	}
	if(msg == _T(""))
	{
		MessageBox(_T("请输入要发送的消息内容!"));
		return ;
	}
	CString usermsg;
	m_userslist.GetText(selectIndex, usermsg);
	if (usermsg == "所有人")
	{
		usermsg = _T("MEVERY") + msg;
	}
	else
	{
		usermsg = _T("M") + usermsg + _T(" ") + msg;
	}
	if (!usermsg.IsEmpty())
	{
		//未连接服务器则不执行
		if (!m_connect)
		{
			return;
		}
		UpdateData(true);
		GetDlgItemText(IDC_EDIT_MSG, m_DataSend);
		if (m_DataSend != "")
		{
			int n = 0;
			m_DataSend = usermsg;
			//通过将宽字节转换成多字节来获得多字节的位数
			n = WideCharToMultiByte(CP_OEMCP, 0, m_DataSend, -1, NULL, 0, 0, FALSE);
			char* pBuff = new char[n];
			//初始化数组
			memset(pBuff, 0, n);
			//将宽字节转换成多字节
			WideCharToMultiByte(CP_OEMCP, 0, m_DataSend.GetBuffer(0), n, pBuff, n, 0, FALSE);
			//发送消息
			m_pSock->SendMSG(pBuff, n);
			//2019-10-26
			delete[] pBuff;
		}
	}
}

每更改一次聊天窗口的内容,就更新数据库对应的聊天记录

void ClineClientDlg::updateDataBase(CString newStr)
{
	if(m_pSock == NULL)
	{
		return ;
	}
	CTime curTime;
	curTime = CTime::GetCurrentTime(); //获取系统时间
	CString strCurTime;                 
	strCurTime=curTime.Format(_T("%Y-%m-%d %H:%M:%S"));
	CString str;
	str.Format(_T("insert history value('%s','%s','%s')"),m_userName,newStr,strCurTime);
	m_dataBase.ExecuteSQL(str);
}

更新聊天室列表

void ClineClientDlg::UpdateUsers(CString str, int index)
{
	str = str.Right(str.GetLength()-strlen("MUPDATEUSERLIST"));
	CStringArray *strArray = translateUsersStr(str);
	int size = strArray->GetSize();
	m_userslist.ResetContent();
	m_userslist.AddString(_T("所有人"));
	for (int i = 0; i < size; i++)
	{
		m_userslist.AddString(strArray->GetAt(i));
	}
}

UDP服务端

UDP与TCP最大的区别是UDP是无连接的网络协议 ,因此每次发送消息的时候只需要对方的IP和端口号就可以发送过去了。UDP服务器需要创建套接字,但是并不需要监听和连接,因此UDP服务器狭义上来讲也可以是一个客户端,它主要是进行一个消息转发的功能。所有的客户端同时给这个端口为8080(8080可以自己设置)的服务器发送消息,服务器收到消息后进行转发

核心代码:

打开服务器:进行套接字的创建Create(m_port,SOCK_DGRAM,NULL),类型为SOCK_DGRAM数据报格式。

void UDPDlg::OnBnClickedBnServerStart()
{
CString cport;
	//获取服务器的端口号
	GetDlgItemText(IDC_SERVERPORT,cport);
	//端口号的字符串转换为数字
	int port = _ttoi(cport);
	//如果没有填写端口号,则默认的端口号为8080
	if (port == 0)
	{
		m_port = 8080;
	}
	else
	{
		m_port = port;
	}
	//如果当前服务器的状态为开启,则关闭服务器,,,m_connect初始为false
	if (m_connect)
	{
		//关闭服务器的socket
		delete m_pServerSocket;
		m_pServerSocket = NULL;
		m_connect = false;
		SetDlgItemText(IDC_BN_SERVER_START, _T("打开服务器"));
		UpdateEvent(_T("系统关闭服务器."));
		//关闭控件的只读状态
		m_editPort.SetReadOnly(FALSE);
		//设置“发送”按钮为不可用
		m_buttonSend.EnableWindow(FALSE);
		return;
	}
	//否则就打开服务器
	// 创建服务器的套接字,IP地址默认本机IP
	m_pServerSocket = new UDPServerSocket();
	m_pServerSocket->m_pdlg = this;
	m_pServerSocket->m_userName = _T("UDP服务器");
	UpdateData(true);
	m_pServerSocket->Create(m_port,SOCK_DGRAM,NULL);  //创建本地套接口
	m_connect = true;
	SetDlgItemText(IDC_BN_SERVER_START, _T("关闭服务器"));
	//更新聊天窗口中的消息
	UpdateEvent(_T("系统打开服务器."));
	if(port == 0)
	{
		m_editPort.SetWindowTextW(_T("8080"));
	}
	else 
	{
		m_editPort.SetWindowTextW(cport);
	}	
	//将服务器端口号的控件设置为只读状态
	m_editPort.SetReadOnly(TRUE);
	//设置“发送”按钮为可用
	m_buttonSend.EnableWindow(TRUE);
}

接受客户端的连接,并且对接受到的数据进行处理。用户名添加在聊天室列表中,消息更新在聊天窗口。UDP协议的接受用的是ReceiveFrom(这块儿也不同与TCP的Receive),发送消息用的是SendTo(不同于TCP协议的Send)

void UDPDlg::OnBnClickedSendmsg()
{
	CString  msg;
	//获取消息框中的字符串
	GetDlgItemText(IDC_EDITMSG, msg);
	//获取当前选中的用户的下标(比如选中了某个人或者选中了“所有人”)
	int selectIndex = m_users.GetCurSel();
	if(selectIndex == -1)
	{
		MessageBox(_T("请在聊天室中选择发送对象!"));
		return ;
	}
	if(msg == _T(""))
	{
		MessageBox(_T("请输入要发送的内容!"));
		return ;
	}
	CString usermsg;
	//获取该下标的字符串名称
	m_users.GetText(selectIndex, usermsg);
	if (usermsg == "所有人")
	{
		usermsg = _T("MEVERY") + msg;
	}
	else
	{
		usermsg = _T("M") + usermsg + _T(" ") + msg;
	}
	translateMsg(usermsg,this->m_pServerSocket, m_ClientAddr);
			//发送消息

}
void UDPDlg::RecvData(UDPServerSocket* pSocket)
{
	CString str;
	TCHAR buff[4096];  //定义接受数据的缓冲区
	int nRead;
	ClientAddr  sendServerAddr;

	nRead = pSocket -> ReceiveFrom(buff,4096,sendServerAddr.strIPAddr,sendServerAddr.uiPort);

	CString tmpstr = buff;
	if (tmpstr.Left(9) == _T("MUSERNAME"))
	{
		//保存用户Socket,以便后来发消息时使用
		m_clientList.AddTail(pSocket);
		tmpstr = tmpstr.Mid(9);//去掉前缀"MUSERNAME",取出新登录的用户名
		sendServerAddr.strUserName = tmpstr;
		m_ClientAddrList.Add(sendServerAddr);
	}
	else
	{
		for (int i = 0; i < m_ClientAddrList.GetSize(); i++)
		{
			ClientAddr& tempClient = m_ClientAddrList.ElementAt(i);
			if (tempClient.strIPAddr==sendServerAddr.strIPAddr && tempClient.uiPort == sendServerAddr.uiPort)
			{
				sendServerAddr.strUserName = tempClient.strUserName;
				break;
			}
		}
	}

	if (nRead != SOCKET_ERROR)
	{
		str=buff;
		translateMsg(str, pSocket, sendServerAddr);
	}
	
	if(nRead == SOCKET_ERROR){
	    return;
	}
}

其余部分都和TCP协议的服务器端代码类似,这块儿不在给出。

UDP客户端

UDP客户端和服务端的代码类似,也是进行套接字的创建,消息的发送。具体代码如下:

连接服务器

void UDPClientDlg::OnBnClickedLnetoserver()
{
	//获得ip控件的ip
	BYTE f1, f2, f3, f4;
	((CIPAddressCtrl*)GetDlgItem(IDC_SERVERIPADDRESS))->GetAddress(f1, f2, f3, f4);
	m_strServerip.Format(_T("%d.%d.%d.%d"), f1, f2, f3, f4);
	m_port = GetDlgItemInt(IDC_SERVERPORT);
	GetDlgItemText(IDC_USERNAME, m_userName);
	//如果已经连接,则断开服务器
	if (m_connect)
	{
		//关闭并且删除客户端的socket
		m_connect = false;
		m_pClientSock->Close();
		delete m_pClientSock;  //删除套接字
		m_pClientSock = NULL;
		m_ConPC.SetWindowTextW(_T("连接服务器"));
		m_buttonSend.EnableWindow(FALSE);
		m_buttonShowHistory.EnableWindow(FALSE);
		UpdateData(false);
		return;
	}
	//否则连接服务器
	else
	{
		CString m1,m2,m3;
		GetDlgItemText(IDC_SERVERIPADDRESS,m1);
		GetDlgItemText(IDC_USERNAME,m2);
		GetDlgItemText(IDC_SERVERPORT,m3);

		if (m1.IsEmpty()||m2.IsEmpty()||m3.IsEmpty())
		{
			AfxMessageBox(_T("请输入IP和Port"));
		}
		else
		{	
				//创建套接字
			m_pClientSock = new UDPClientSocket(this); //初始化套接字的成员变量
			//创建套接字
			if (!m_pClientSock -> Create(m_port,SOCK_DGRAM))  //创建套接字,SOCK_DGRAM:数据报套接字
			{
				AfxMessageBox(_T("创建套接字失败!"));
				return;
			}
					AfxMessageBox(_T("创建套接字成功!!!"));
			SocketAddr myAddr;

			AfxMessageBox(_T("连接服务器成功!"));
		
			//成功连接上了服务器!!
			m_connect = true;
			UpdateData(TRUE);

//*******************************************************************************************
			//通过socket发送数据报连接
			CString userName;
			userName = _T("MUSERNAME") + m_userName;

			m_pClientSock->SendTo((LPCTSTR)userName, userName.GetLength()*8, m_ServerAddr.uiPort, m_ServerAddr.strIPAddr);
//*******************************************************************************************
			m_ConPC.SetWindowText(_T("断开服务器"));
			m_buttonSend.EnableWindow(TRUE);
			m_buttonShowHistory.EnableWindow(TRUE);
			UpdateData(false);
		}		
	}
}

发送消息

void UDPClientDlg::OnBnClickedSendmsg()
{
	CString  msg;
	//获得消息框里的字符串
	GetDlgItemText(IDC_EDIT_MSG, msg);
	int selectIndex = m_userslist.GetCurSel();
	if(selectIndex == -1)
	{
		MessageBox(_T("请在聊天室中选择发送对象!"));
		return ;
	}
	if(msg == _T(""))
	{
		MessageBox(_T("请输入要发送的消息内容!"));
		return ;
	}
	CString usermsg;
	m_userslist.GetText(selectIndex, usermsg);
	if (usermsg == "所有人")
	{
		usermsg = _T("MEVERY") + msg;
	}
	else
	{
		usermsg = _T("M") + usermsg + _T(" ") + msg;
	}
	if (!usermsg.IsEmpty())
	{
		UpdateData(true);
		GetDlgItemText(IDC_EDIT_MSG, m_DataSend);
		if (m_DataSend != "")
		{
			int n = 0;
			m_DataSend = usermsg;
			//通过将宽字节转换成多字节来获得多字节的位数
			n = WideCharToMultiByte(CP_OEMCP, 0, m_DataSend, -1, NULL, 0, 0, FALSE);
			char* pBuff = new char[n];
			//初始化数组
			memset(pBuff, 0, n);
			//将宽字节转换成多字节
			WideCharToMultiByte(CP_OEMCP, 0, m_DataSend.GetBuffer(0), n, pBuff, n, 0, FALSE);
			//发送消息
			m_pClientSock->SendTo(
				(LPCTSTR)m_DataSend,
				m_DataSend.GetLength()*8,
				m_ServerAddr.uiPort,
				m_ServerAddr.strIPAddr);
			delete[] pBuff;
		}
	}
}

接受消息

void UDPClientDlg::ReceiveText()
{
	TCHAR buff[4096];  //定义接受数据的缓冲区
	int nRead;
	SocketAddr  sendClientAddr;
	nRead = m_pClientSock -> ReceiveFrom(buff,4096,sendClientAddr.strIPAddr,sendClientAddr.uiPort);
	if(nRead == SOCKET_ERROR){
	    return;
	}
	buff[nRead]  =  _T('\0');  //设置结束标志
	CString str(buff);
	//如果服务器通知更新聊天室列表
	if (-1 != strUpdate(str))
	{
		((UDPClientDlg *)AfxGetMainWnd())->UpdateUsers(str, strUpdate(str));
	}
	//否则就更新聊天窗口内容
	else
	{			
		//向聊天窗口添加接受到的信息
		CStringW  strTemp(buff);
		CStringW chartMsg;
		this->GetDlgItemTextW(IDC_EDIT_RECIEVEMSG,chartMsg);
		chartMsg += strTemp + _T("\r\n");
		this->SetDlgItemTextW(IDC_EDIT_RECIEVEMSG,chartMsg);
	}
}

运行结果

TCP协议运行结果
在这里插入图片描述
UDP运行结果
在这里插入图片描述
数据库数据截图
在这里插入图片描述
在这里插入图片描述

总结

第一次写博文,难免有错误之处,忘批评指正。本文TCP协议部分代码借鉴了MFC小程序 简易网络聊天室 博文,共同学习进步。

里面包含聊天室的客户端和服务器端的源文件和一份完整的设计报告。 一、 系统概要 本系统能实现基于VC++的网络聊天室系统。有单独的客户端、服务器端。 服务器应用程序能够接受来自客户端的广播,然后向客户端发送本机的IP与服务端口,让客户端接入到服务器进行聊天,检测用户名是否合法(重复),服务器责接收来自客户端的聊天信息,并根据用户的需求发送给指定的人或所有人,能够给出上线下线提示。客户端能够发出连接请求,能编辑发送信息,可以指定发给单人或所有人,能显示聊天人数,上线下线用户等。 二、 通信规范的制定 服务请求规范: 服务器端: (1) 创建一个UDP的套接字,接受来自客户端的广播请求,当请求报文内容为“REQUEST FOR IP ADDRESS AND SERVERPORT”时,接受请求,给客户端发送本服务器TCP聊天室的端口号。 (2) 创建一个主要的TCP协议的套接字负责客户端TCP连接 ,处理它的连接请求事件。 (3)在主要的TCP连接协议的套接字里面再创建TCP套接字保存到动态数组里,在主要的套接字接受请求后 ,就用这些套接字和客户端发送和接受数据。 客户端: (1) 当用户按“连接”按钮时,创建UDP协议套接字,给本地计算机发广播,广播内容为“REQUEST FOR IP ADDRESS AND SERVERPORT”。 (2)当收到服务器端的回应,收到服务器发来的端口号后,关闭UDP连接。根据服务器的IP地址和端口号重新创建TCP连接。 故我思考:客户端一定要知道服务器的一个端口,我假设它知道服务器UDP服务的端口,通过发广播给服务器的UDP服务套接字,然后等待该套接字发回服务器TCP聊天室服务的端口号,IP地址用ReceiveForom也苛刻得到。 通信规范 通信规范的制定主要跟老师给出的差不多,并做了一小点增加: (增加验证用户名是否与聊天室已有用户重复,在服务器给客户端的消息,增加标志0) ① TCP/IP数据通信 --- “聊天”消息传输格式 客户机 - 服务器 (1)传输“用户名” STX+1+用户名+ETX (2) 悄悄话 STX+2+用户名+”,”+内容+ETX (3) 对所有人说 STX+3+内容+ETX 服务器- 客户机 (0)请求用户名与在线用户名重复 //改进 STX+0+用户名+EXT (1)首次传输在线用户名 STX+1+用户名+ETX (2)传输新到用户名 STX+2+用户名+ETX (3)传输离线用户名 STX+3+用户名+ETX (4)传输聊天数据 STX+4+内容+ETX (注:STX为CHR(2),ETX 为CHR(3)) 三、 主要模块的设计分析 四、 系统运行效果 (要求有屏幕截图) 五、 心得与体会
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值