【项目】— 基于TCP的高并发聊天系统

目录

一、TCP聊天系统的初步介绍

1.TCP聊天系统

2.实现目标

3、各个功能的消息流传图

客⼾端登录注册消息流转图

客⼾端添加好友信息流转图

客⼾端聊天消息流转图

服务端处理请求消息流转图

二、服务端介绍

准备工作

1、服务端模块划分

2、数据库模块的设计

2.1数据库表的设计

2.2MSQL-CAPI

设置连接对应的字符集:

2.3数据库代码设计

2.3.1返回数据库中存放的所有用户的信息

2.3.2插入新的用户信息(注册)

2.3.3根据用户ID查询当前用户好友的ID并用fri向量储存

2.3.4根据用户ID查询好友信息,并用Json结构储存

3、用户管理模块的设计

3.1描述用户信息类:UserInfo

3.2管理用户信息类:组织用户的信息

3、3业务接口:

4、线程安全的队列

5、网络通信模块的设计

6、自定义消息

6.1Json Value 对象的认识:

6.2Json序列化和反序列化

6.2使⽤Json数据格式封装⾃定义消息

7、业务处理模块的设计

三、客户端介绍

准备工作

MFC环境搭建:

jsoncpp编译:

1、win-tcp封装

2、消息队列

3、MFC基于对话框编程(VS2019)

3.1注册界⾯ 及功能实现

3.2登录界⾯ 及功能实现

3.3 添加好友 及功能实现

3.4 聊天界⾯及功能实现


一、TCP聊天系统的初步介绍

1.TCP聊天系统

一款基于TCP的聊天系统,实现客户端与客户端点对点通信。

2.实现目标

注册、登录、聊天和添加好友四大功能。

3、各个功能的消息流传图

客⼾端登录注册消息流转图

客⼾端添加好友信息流转图

客⼾端聊天消息流转图

服务端处理请求消息流转图

二、服务端介绍

准备工作

安装Jsoncpp;(应该是0.10.5版本)

yum install -y jsoncpp
yum install -y jsoncpp-devel

1、服务端模块划分

2、数据库模块的设计

数据库模块是与数据库打交道,处于最底层,负责维护我们所需要的数据库

功能:连接数据库,存储用户信息,查找用户信息

2.1数据库表的设计

一共有两张表:

user表:存放用户信息(用户ID(主键)、昵称、学校、电话、密码)

Create Table: CREATE TABLE `user` (
`userid` int(11) NOT NULL,
`nickname` varchar(20) NOT NULL,
`school` varchar(20) NOT NULL,
`telnum` char(11) NOT NULL,
`passwd` varchar(100) NOT NULL,
`m_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`userid`),
UNIQUE KEY `telnum` (`telnum`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

friendinfo表:存放用户好友信息(用户ID(外键)和好友ID)

Create Table: CREATE TABLE `friendinfo` (
`userid` int(11) NOT NULL,
`friend` int(11) NOT NULL,
KEY `userid` (`userid`),
KEY `friend` (`friend`),
CONSTRAINT `friendinfo_ibfk_1` FOREIGN KEY (`userid`) REFERENCES `user` (`userid`),
CONSTRAINT `friendinfo_ibfk_2` FOREIGN KEY (`friend`) REFERENCES `user` (`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2.2MSQL-CAPI

初始化mysql操作句柄:

MYSQL *mysql_init(MYSQL *mysql);

函数含义:分配或初始化与mysql_real_connect()相适应的MYSQL对象。如果mysql是NULL指 针,该函数将分配、初始化、并返回新对象。否则,将初始化对象,并返回对象的地址。

连接mysql服务端:

MYSQL *
mysql_real_connect(MYSQL *mysql, //mysql操作句柄
                   const char *host, //服务端IP地址
                   const char *user, //⽤⼾名
                   const char *passwd, //密码
                   const char *db, //数据库
                   unsigned int port, //端⼝
                   const char *unix_socket, //是否使⽤本地域套接字
                   unsigned long client_flag //数据库标志位, 通常为0, 采⽤默认属性
                   )

函数含义:连接mysql服务端,如果连接成功,在返回的是MYSQL操作句柄, 失败返回NULL

设置连接对应的字符集:

int
mysql_set_character_set(MYSQL *mysql,
                        const char *csname)

函数含义:⽤于设置当前连接的默认字符集;

执⾏sql语句:

int
mysql_query(MYSQL *mysql, //mysql操作句柄
            const char *stmt_str //执⾏的sql语句
            )

函数含义:执⾏sql语句。 成功返回0, 失败返回⾮0;

获取结果集:

MYSQL_RES *
mysql_store_result(
MYSQL *mysql // mysql操作句柄
)

函数含义:获取查询的结果, 称之为结果集。成功, 返回MYSQL_RES的指针。 失败返回NULL。 注意事项:需要调⽤

获取结果集⾏数:

my_ulonglong
mysql_num_rows(MYSQL_RES *result)

函数含义:获取结果集的⾏数

获取结果集的下⼀⾏:

MYSQL_ROW
mysql_fetch_row(MYSQL_RES *result)

函数含义:获取结果集的下⼀⾏内容

释放结果集内存:

void
mysql_free_result(MYSQL_RES *result)

释放操作句柄:

void
mysql_close(MYSQL *mysql)

注意:编译的时候要加上链接库

头⽂件包含:#include <mysql/mysql.h>
链接时 : -L /usr/lib64/mysql -lmysqlclient

2.3数据库代码设计

2.3.1返回数据库中存放的所有用户的信息

2.3.2插入新的用户信息(注册)

2.3.3根据用户ID查询当前用户好友的ID并用fri向量储存

2.3.4根据用户ID查询好友信息,并用Json结构储存

3、用户管理模块的设计

功能:用于维护和管理每个用户信息

3.1描述用户信息类:UserInfo

3.2管理用户信息类:组织用户的信息

用户管理模块初始化的时候就需要从数据库当中获取所有用户的信息,还有每个用户的好友信息,并将这些信息放于user_map当中去,方便后续的查询

3、3业务接口:

处理注册请求、登录请求、发送消息、添加好友请求

4、线程安全的队列

消息池当中使⽤vector来保存消息,vector这个容器并不是线程安全, STL当中的容器都是线程不安全 *保证线程安全的机制:  互斥锁+条件变量

5、网络通信模块的设计

功能:接受新连接、监听文件描述符、接收数据和发送应答

初始化TCP通信:

主线程接受连接 & 往epoll当中添加多个⽂件新连接的⽂件描述符

接收线程:

发送线程:

6、自定义消息

什么是自定义消息:只有当服务端和客户端发送的消息格式统一的时候,才能正常的进行通信。消息格式不统一的话,就会导致,驴头不对马嘴,导致错误。

6.1Json Value 对象的认识:

Json就是一个key  :value键值对的一个东西,包含多动类型

json 数据类型:对象,数组,字符串,数字

对象:使⽤花括号 {} 括起来的表⽰⼀个对象。

数组:使⽤中括号 [] 括起来的表⽰⼀个数组。

字符串:使⽤常规双引号 "" 括起来的表⽰⼀个字符串

数字:包括整形和浮点型,直接使⽤。

为了更好的理解Json cpp,有以下测试demo:

6.2Json序列化和反序列化

TCPSocket编程的时候,内存不连续的结构体是不能直接用send发送的,需要使用序列化,将内存组合起来,放到一块连续的内存当中去,然后再发送。反序列化就是将连续内存中的数据解析出来,变为原有的结构。

6.2使⽤Json数据格式封装⾃定义消息

7、业务处理模块的设计

功能:处理注册、登录、添加好友、发送消息的请求

三、客户端介绍

准备工作

MFC环境搭建:

Visual Studio 2019安装MFC(在VS已安装完成的情况下)_visual studio 安装mfc_小虎~的博客-CSDN博客

jsoncpp编译:

jsoncpp win下的编译与使用_jsoncpp windows_houxian1103的博客-CSDN博客

这里要注意的是:

我们服务端用的Jsoncpp版本是0.10.5,所以我们客户端应该也用该版本,当版本不一致时,会出现错误。

1、win-tcp封装

实现细节可以去看源码Git链接;

2、消息队列

3、MFC基于对话框编程(VS2019)

3.1注册界⾯ 及功能实现

void CRegisterDlg::OnBnClickedCommit(){
	// TODO:  在此添加控件通知处理程序代码
	/* 1.获取输入框的内容 */
	UpdateData(true);
	if (m_nickname_.IsEmpty() || m_school_.IsEmpty() || m_tel_.IsEmpty() || m_passwd_.IsEmpty()) {
		MessageBox(TEXT("please enter information"));
		return;
	}

	std::string nickname = CT2A(m_nickname_.GetString());
	std::string school = CT2A(m_school_.GetString());
	std::string telnum = CT2A(m_tel_.GetString());
	std::string passwd = CT2A(m_passwd_.GetString());

	/* 2. 组织ChatMsg数据 */
	ChatMsg cm;
	cm.msg_type_ = Register;
	cm.json_msg_["nickname"] = nickname.c_str();
	cm.json_msg_["school"] = school.c_str();
	cm.json_msg_["telnum"] = telnum.c_str();
	cm.json_msg_["passwd"] = passwd.c_str();

	/* 3.获取Tcp的实例化指针*/
	TcpSvr* ts = TcpSvr::GetInstance();

	/* 4.发送消息*/
	std::string msg;
	cm.GetMsg(&msg);
	ts->Send(msg);

	/* 5.获取消息队列的实例化指针*/
	MsgQueue* mq = MsgQueue::GetInstance();
	if (mq == NULL) {
		exit(1);
	}
	/* 6.按照消息类型 获取注册应答*/
	mq->Pop(Register_Resp, &msg);
	/*
		7.解析应答,判断应答结果
			注册成功==> 跳转到登录界面
			注册失败==> 清空输入框,并且提示失败原因
	*/
	cm.clear();
	cm.PraseMsg(-1, msg);
	if (cm.reply_status_ == REGISTER_FAILED) {
		MessageBox(TEXT("telnum repeat...,please chack"));
	}
	else {
		MessageBox(TEXT("register success..."));
	}
	/* */

	MessageBox(TEXT("OnBnClickedCommit"));
}

3.2登录界⾯ 及功能实现

void CchatDlg::OnBnClickedLogin()
{
	// TODO:  在此添加控件通知处理程序代码
	/* 1.获取输入框的内容 */
	UpdateData(true);
	if (m_tel_.IsEmpty()|| m_passwd_.IsEmpty()) {
		MessageBox(TEXT("输入的内容不能为空..."));
		return;
	}
	std::string telnum = CT2A(m_tel_.GetString());
	std::string passwd = CT2A(m_passwd_.GetString());

	/* 2. 组织ChatMsg数据 */
	/*
	{
		msg_type:Login
		json_msg_:{
					telnum:xx;
					passwd:xx
			}
	 }
	*/
	ChatMsg cm;
	cm.msg_type_ = Login;

	cm.json_msg_["telnum"] = telnum.c_str();
	cm.json_msg_["passwd"] = passwd.c_str();


	/* 3.获取Tcp的实例化指针*/
	TcpSvr* ts = TcpSvr::GetInstance();

	/* 4.发送消息*/
	std::string msg;
	cm.GetMsg(&msg);
	ts->Send(msg);


	/* 5.获取消息队列的实例化指针*/
	MsgQueue* mq = MsgQueue::GetInstance();
	if (mq == NULL) {
		exit(1);
	}
	/* 6.按照消息类型 获取注册应答*/
	mq->Pop(Login_Resp, &msg);

	/*
		7.解析应答,判断应答结果
			注册成功==> 跳转到登录界面
			注册失败==> 清空输入框,并且提示失败原因
	*/

	cm.clear();
	cm.PraseMsg(-1, msg);
	if (cm.reply_status_ == LOGIN_FAILED) {
		MessageBox(TEXT("请你检查 ,用户名或者密码是否输入错误"));
	}
	else {
		CDialog::OnCancel();
		CChatDlg ccd(cm.user_id_);
		ccd.DoModal();
	}

}

3.3 添加好友 及功能实现

UpdateData(true);
	if (m_fritel.IsEmpty()) {
		MessageBox(TEXT("输入内容不能为空..."));
		return;
	}
	std::string input = CT2A(m_fritel.GetString());
 
	ChatMsg cm;
	cm.msg_type_ = AddFriend;
	cm.user_id_ = userid_;
	cm.json_msg_["fri_telnum"] = input.c_str();
 
 
	/* 3. 获取TCP的实例化指针 */
	TcpSvr* ts = TcpSvr::GetInstance();
 
	/* 4. 发送消息 */
	std::string msg;
	cm.GetMsg(&msg);
	ts->Send(msg);
 
	CDialog::OnCancel();

3.4 聊天界⾯及功能实现

void CChatDlg::OnBnClickedSend()
{
	UpdateData(true);
	if (m_input_.IsEmpty()) {
		MessageBox(TEXT("输入的内容不能为空..."));
		return;
	}
	std::string input = CT2A(m_input_.GetString());

	ChatMsg cm;
	cm.msg_type_ = SendMsg;
	cm.user_id_ = user_id_;
	cm.json_msg_["msg"] = input.c_str();
	cm.json_msg_["recv_userid"] = recv_userid_;

	/* 3.获取Tcp的实例化指针*/
	TcpSvr* ts = TcpSvr::GetInstance();

	/* 4.发送消息*/
	std::string msg;
	cm.GetMsg(&msg);
	ts->Send(msg);

	/* 5.获取消息队列的实例化指针*/
	MsgQueue* mq = MsgQueue::GetInstance();
	if (mq == NULL) {
		exit(1);
	}

	/* 6.从队列当中获取“好友请求的应答”*/
	msg.clear();
	mq->Pop(SendMsg_Resp, &msg);

	/* 7.反序列化,获取状态及好友信息*/
	cm.clear();
	cm.PraseMsg(-1, msg);


	/* 5.更新对话框*/
	/* 6.记录在当前用户的history_msg当中*/
	for (int i = 0; i < fris_info_.size(); ++i) {
		if (fris_info_[i].user_id_ == recv_userid_) {
			std::string tmp;
			tmp += "我:";
			tmp += input.c_str();

			if (cm.reply_status_ == SENDMSG_FAILED) {
				tmp += "(send failed)";
			}
			else {
				tmp += "(send success)";
			}

			fris_info_[i].histroy_msg_.push_back(tmp);
			m_output_.InsertString(m_output_.GetCount(), tmp.c_str());
		}
	}
	/* 7.清空输入框*/
	m_input_.Empty();
	//
	m_input_edit_.SetWindowTextA(0);
}

源码Git链接:  Linux_project: linux小项目 (gitee.com)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
网络聊天系统的设计与实现是建立在客户端和服务器之间的通信基础上的。以下是一个简单的网络聊天系统的设计与实现。 首先,服务器端需要有一个监听端口来接收客户端的连接请求。一旦有客户端连接进来,服务器就会创建一个新的线程来处理与该客户端的通信。 在客户端,用户需要提供一个用户名来进行身份认证。用户首先通过与服务器建立的连接发送自己的用户名和密码给服务器。服务器通过数据库查询验证用户身份,并将验证结果返回给客户端。 一旦用户成功登录,客户端和服务器之间就可以进行实时的消息通信。客户端可以发送消息给服务器,服务器收到消息后将其转发给所有在线的用户。同时,服务器也会将其他用户发送的消息转发给当前用户。 为了保证系统的稳定性和安全性,可以在服务器端引入一些措施。例如,限制每个用户的连接数和消息发送速率,检测恶意行为且拉入黑名单等。 此外,网络聊天系统还可以设计一些附加功能,如创建群组、发送图片和文件、消息加密等。这些功能都可以通过与服务器的交互实现。 总结来说,网络聊天系统的设计与实现需要考虑客户端和服务器之间的通信、用户身份认证、实时消息通信、系统稳定性和安全性等方面。通过合理的设计和技术实现,可以创建出一个高效、稳定和安全的网络聊天系统

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值