c++全栈聊天项目
1. 简介
本项目为c++全栈聊天项目实战,包括PC端QT界面编程,asio异步服务器设计,beast网络库搭建http网关,nodejs搭建验证服务,各服务间用grpc通信,server和client用asio通信等,也包括用户信息的录入等,实现跨平台设计,先设计windows的server,之后再考虑移植到linux中,较为全面的展示c++在实际项目中的应用。
1.在QT创建一个主界面:
新建—>Application—>Qt Widgets Application—>项目名称为lxxchat—>类名为MainWindow—>基类为QMainWindow
2.创建一个登录的对话框:
新建—>Qt—>Qt设计师界面类—>选择界面模板:Dialog without Buttons—>类名为:LoginDialog
3.创建一个资源文件:
新建—>Qt—>Qt Resource File—>名称为rc
4.创建一个注册的对话框
新建—>Qt—>Qt设计师界面类—>选择界面模板:Dialog without Buttons—>类名为:RegisterDialog
5.创建一个全局的文件
新建—>c++—>c++ Header File—>名称为global.h
新建—>c++—>c++ Source File—>名称为global.cpp
6.单例类的创建
新建—>c++—>c++ Header File—>名称为singleton.h
7.创建一个Qt的Http发送的管理者类
新建—>c++—>c++class—>类名为HttpMgr
8.定时按钮类
新建—>c++,c++clase—>类名为TimerBtn,基类为Custom
创建好后,令其继承QPushButton按钮类
将注册界面的获取按钮提升为TimerBtn,当点击该按钮后,就会触发TimerBtn重写的一些功能。
9.创建可点击的标签类ClickedLabel,用于密码和确认密码的隐藏和显示
新建—>c++,c++clase—>类名为ClickedLabel,基类为Custom(创建好后,继承基类QLabel)
将注册页面的两个label标签提升为ClickedLabel类
将登录界面的忘记密码按钮提升为ClickedLabel类
将ChatPage界面里面的emo_lb和file_lb两个label提升为可点击的ClickedLabel类
10.创建应该TCP管理者类
新建—>c++,c++clase—>类名为TcpMgr,基类为Custom(先不写,默认的)
11.创建聊天对话框
新建—>Qt,Qt设计师界面类—>界面模板为Dialog without Buttons—>类名为ChatDialog
12.创建可点击的按钮类
新建—>c++,c++clase—>类名为ClickedBtn,基类为Custom(创建好后,继承基类QPushButton)
将聊天界面里面的add_btn按钮提升为ClickedBtn类
将ChatPage界面的receive_btn和send_btn提升为ClickedBtn类
13.创建自定义的输入框类
新建—>c++,c++clase—>类名为CustomizeEdit,基类为Custom(创建好后,继承基类QLineEdit)
将聊天界面里面的search_edit输入框提升为CustomizeEdit类
14.创建自定义的listwidget类
新建—>c++,c++clase—>类名为ChatUserList,基类为Custom(创建好后,继承基类QListWidget)
将聊天界面里面的char_user_list框提升为ChatUserList类
15.创建用户窗口
新建—>Qt,Qt设计师界面类—>界面模板为widget—>类名为ChatUserWid
创建好好继承ListItemBase类(自己写的)
16.创建控制item的基类ListItemBase
新建—>c++,c++clase—>类名为ListItemBase,基类为Custom(创建好后,继承基类QWidget)
17.创建加载内容的对话框
新建—>Qt,Qt设计师界面类—>界面模板为Dialog without Buttons—>类名为LoadingDlg
18.创建聊天页类ChatPage
新建—>Qt,Qt设计师界面类—>界面模板为widget—>类名为ChatPage
将聊天界面中QstackedWidget模块里面的chat_page提升为ChatPage类
19.创建聊天气泡
新建—>c++,c++clase—>类名为ChatView,基类为Custom(创建好后,继承基类QWidget)
2. 网关服务器GateServer
网关服务器主要应答客户端基本的连接请求,包括根据服务器负载情况选择合适服务器给客户端登录、注册、获取验证服务等,接收http请求并应答。
1.绑定和监听连接(服务端)
利用visual studio创建一个空项目,项目名字为GateServer,然后按照之前的方法配置boost库和jsoncpp配置好后,我们添加一个新的类,名字叫CServer。添加成功后生成的CServer.h和CServer.cpp也会自动加入到项目中。
在GateServer
项目中的逻辑:先是CServer启动,其启动后,就会监听连接。当对端有连接请求来之后,就交给HttpConnection
类去管理,在这个类里面,它会先监听读事件,当对端有数据发来之后会触发读回调函数,在这个函数里面会处理读的请求HandleReq()
,并且启动超时检测CheckDeadline()
(检测发送是否超时)。其中在处理读请求HandleReq()
函数中,会调用底层的LogicSystem
逻辑层去处理请求,同时这里会把请求的url
和HttpConnection
对象的智能指针作为参数传过去。而在LogicSystem逻辑层类的HandleGet
函数中,就是在map容器里面去找对应的url,如果之前没有注册过这个url,就会返回false,底层就会返回404错误;如果注册过,就会调用对应的回调(处理器)。
对应写回包就是写好响应头,然后准备对端请求的数据发送过去即可,如果在规定时间内写完发送给对端,在触发的写回调中就会把定时器取消,否则定时器会进行检测,超时的话它就会把socket强制关闭。
2.在Qt中添加客户端的配置文件config.ini
在QT的pro文件中需要添加如下程序:这段程序主要用于在Windows平台的调试模式(debug
配置)下,将配置文件(如config.ini
)从工程目录拷贝到输出目录。可以这样理解,你的Qt程序需要使用config.ini
配置文件来读取网络设置、数据库连接信息等。当你在调试时,程序从debug
目录运行,而这个目录默认情况下并不包含config.ini
。该脚本自动将配置文件从项目的根目录拷贝到debug
目录,确保程序运行时可以正确找到配置文件。
1 | win32:CONFIG(debug, debug | release) |
3.创建一个获取验证码的grpc客户端VerifyGrpcClient:
先定义一个message.proto文件,这是一种用于定义结构化数据的序列化协议,常用于远程过程调用(RPC)系统或者消息格式的定义。代码定义了一个服务和两个消息,用于实现一个获取验证码的接口。
1 | syntax = "proto3"; //定义了Protobuf的语法版本 |
接下来就是在message.proto所在文件夹的powershell上执行如下命令,利用grpc编译后生成的proc.exe来生成proto的grpc的头文件和源文件。即会生成message.grpc.pb.cc和message.grpc.pb.h文件,这两个文件里面保存了grpc通信的接口。
1 | C:\cppsoft\grpc\visualpro\third_party\protobuf\Debug\protoc.exe -I="." --grpc_out="." --plugin=protoc-gen-grpc="C:\cppsoft\grpc\visualpro\Debug\grpc_cpp_plugin.exe" "message.proto" |
对于通信的接口所使用的参数需要通过以下命令来生成,即会得到message.pb.cc和message.pb.h文件,这两个文件保存了通信使用的参数。
1 | C:\cppsoft\grpc\visualpro\third_party\protobuf\Debug\protoc.exe --cpp_out=. "message.proto" |
4.添加一个查询状态的grpc客户端StatusGrpcClient
3. 认证服务
认证服务要给邮箱发送验证码,所以用nodejs较为合适,nodejs是一门IO效率很高而且生态完善的语言,用到发送邮件的库也方便。
1.初始化node.js项目的一个配置文件:在VarifyServer文件夹下打开PowerShell终端执行npm init
,然后一路点击回车即可。
2.安装grpc-js包,也可以安装grpc,grpc是C++版本,grpc-js是js版本,C++版本停止维护了。所以用grpc-js版本。
在VarifyServer文件夹的PowerShell终端下执行npm install @grpc/grpc-js
。
3.安装proto-loader用来动态解析proto文件,在PowerShell终端下继续执行npm install @grpc/proto-loader
。
4.安装email处理的库,在PowerShell终端下继续执行npm install nodemailer
。
5.启动程序:npm run serve
81.68.86.146
流程:
用Qt编写了一个client,它会把请求(获取验证码)给到visual Studio编写的服务端GateServer,而GateServer会调用grpc,把请求投递给验证服务Varify,验证服务就会调用邮箱服务,该邮箱是各个平台提供的邮箱接口API,调用该API,发送到指定的邮箱里。如果发送成功了,邮箱接口API还会把成功的请求告诉验证服务Varify,验证服务也就会通过grpc服务把这个发送成功的请求回复给GateServer。当然不管成功还是失败,GateServer得到结果后都会发送给Qt那边的客户端。
4. 设置验证码过期
验证码是要设置过期的,可以用redis管理过期的验证码自动删除,key为邮箱,value为验证码,过期时间为3min。
4.1 redis服务搭建
1.在Redis-x64-5.0.14.1文件夹下的redis.windows.conf文件中处理如下:
修改端口:
1 | port 6380 |
添加requirepass
1 | # requirepass foobared |
2.启动redis服务器(在Redis-x64-5.0.14.1目录下):.\redis-server.exe .\redis.windows.conf
3.启动客户端并输入密码:
4.widows编译和配置redis(很麻烦,省略)
4.2 VerifyServer增加redis
4.3 mysql
1.在C:\cppsoft\mysql\mysql\bin目录下打开cmd输入以下命令:
1 | //安装mysql,安装完成后Mysql会有一个随机密码 |
得到如下图,随机密码要记住,以后我们改密码会用到
2.在C:\cppsoft\mysql\mysql\bin目录下以管理员身份打开cmd输入以下命令:
1 | //安装mysql服务并启动 |
3.修改mysql密码
首先是在本机启动mysql服务:电脑搜索服务,找到mysql,启动它。
然后在C:\cppsoft\mysql\mysql\bin目录下打开终端,执行命令:.\mysql -uroot -p
,进入后先填写原始密码,上面保留那个
然后执行改命令,进行修改密码:ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';
4.配置环境变量
新建系统变量:
变量名:MYSQL_HOME
变量值:自己的msql目录(C:\cppsoft\mysql)
修改系统的path变量:点击编辑path,进去后添加 %MYSQL_HOME%\bin
5.状态服务器StatusServer
6.ChatServer类
长连接的流程:客户端想要登录,首先需要将登录请求发给GataServer服务器,GataServer就去StatusServer服务器上去查询,如果校验没有任何问题的话,它就会分配一个ip和token,给到GataServer服务器,GataServer就会把这个消息给到客户端。客户端就会利用这个ip和token来登录,ChatServer服务器会验证这个ip和token(去StatusServer服务器上查询),以及该用户的一些信息,如果都没有问题,它就会让其登录,返回一个rsp回包,这样客户端就和ChatServer服务器建立了连接,后续客户端需要发的信息内容,就可以发给ChatServer服务器。
7. usermgr类
8.流程
1.主窗口mainwindow
(创建登录界面对象)展示登录界面logindialog
。 mainwindow —–> logindialog
2.登录界面logindialog
点击注册按钮,发出信号switchRegister,主窗口mainwindow
接收。 loginwindow —–> mainwindow
3.主窗口mainwindow
接收信号switchRegister,执行槽函数SlotSwitchReg(创建注册界面对象),展示注册界面registerdialog
。mainwindow —–> registerdialog
4.注册界面registerdialog点击获取按钮(检查邮箱格式),发送请求获取验证码(调用http管理者httpmgr
的发送请求接口)。
5.注册界面registerdialog
点击确定按钮执行槽函数on_sure_btn_clicked,检查输入框的内容,没有问题就发送请求注册用户(调用http管理者httpmgr
的发送请求接口)。
6.管理者httpmgr
执行完异步发送请求后(向网关服务器GateServer发送请求),等待回复,收到回复后,无论成功与否,都发出信号sig_http_finish(附带请求模块),由自己接收处理。 httpmgr —–> httpmgr
7.管理者httpmgr
收到回复成功信号后,执行槽函数slot_http_finish,根据不同请求,都发出信号sig_login_mod_finish(附带请求模块)
8.注册界面registerdialog
根据接收的信号sig_login_mod_finish,会通过不同的id执行相应的函数对象(注册界面初始化时就注册进去的),比如说有获取验证码的id、注册用户的id。