基于muduo与nginx实现的网络集群聊天服务器系统

01 Vscode远程连接Linux

主机Ping通虚拟机的前提:

1.NAT模式下,需要对应网络的设置和外面的设置一样 IP、网关、子网掩码

Linux: ubuntu18.04

02 Json

安装Json第三方库 thirdparty

进行序列化和反序列化测试 testjson

msg_type:2   //消息类型 (登录消息、注册消息、添加好友、添加群聊、好友聊天、群组聊天)
from:xxx     //来自哪里
to:xxx       //发送到哪里
msg:xxx      //消息内容

通过网络TCP发送的都是字节流,所以对于这样的消息结构,要进行数据的序列化(转成json字节流发送给网络)和反序列化(远端接收到字节流,上报给应用,应用把json字节流反序列化成这样的消息结构)

xml节点的元素和标签特别多,浪费空间,现在protobuf和json多。

json数据序列化:

通过json.dump()函数将json数据对象转换为字符串 ,通过网络TCP发送

#include "json.hpp"
using json = nlohmann::json;

#include 
#include 
#include 
#include 
using namespace std;

//json序列化示例1
string func1()
{
    json js;//定义一个json类型的对象//添加数组
    js["msg_type"] = 2;
    js["from"] = "linzeyu";
    js["to"] = "zhang san";
    js["msg"] = "hello, are you ok now?";
 
    string sendBuf = js.dump();//输出的意思 
    //cout<
    return sendBuf;
}

//json序列化示例2
string func2()
{
    json js;
    //添加数组
    js["id"] = {1, 2, 3, 4, 5};
    //添加key-value
    js["name"] = "zhang san";
    //添加对象
    js["msg"]["zhang san"] = "hello world";
    js["msg"]["liu shuo"] = "hello china";
    //上面等同于下面这句一次性添加数组对象
    js["msg"] = {{"zhang san", "hello world"}, {"liu shuo", "hello china"}};
    //cout << js << endl;
    return js.dump();
}

//json序列化示例代码3
string func3()
{
    json js;

    //直接序列化一个vector容器
    vector<int> vec;
    vec.push_back(1);
    vec.push_back(2);
    vec.push_back(5);

    js["list"] = vec;

    //直接序列化一个map容器
    map<int, string> m;
    m.insert({1, "黄山"});
    m.insert({2, "华山"});
    m.insert({3, "泰山"});

    js["path"] = m;

    string sendBuf = js.dump(); // json数据对象 =》序列化 json字符串
    //cout<
    return sendBuf;
}

int main()
{
    //func1();
    //unc2();
    func3();
    return 0;
}

结果:

img

底层链式哈希表 无序

json数据反序列化:

通过parse函数将网络上接收的字符串(字节流)转为json数据对象甚至容器

int main()
{
    string recvBuf = func1();
    //数据的反序列化   json字符串 =》反序列化 数据对象(看作容器,方便访问)
    
    json jsbuf = json::parse(recvBuf);
    cout<<jsbuf["msg_type"]<<endl;
    cout<<jsbuf["from"]<<endl;
    cout<<jsbuf["to"]<<endl;
    cout<<jsbuf["msg"]<<endl;

    return 0;
}

结果:

在这里插入图片描述

总结:

基于muduo与nginx实现的网络集群聊天服务器系统_第1张图片

采用json的原因

(1)采用C++11标准编写的

(2)可以和STL容器相互转化

03 muduo

安装boost库

安装muduo库

网络服务器运行测试 testmuduo

muduo网络库的底层就是epoll加linux的pthread线程库。

/*
muduo网络库给用户提供了两个主要的类
TcpServer: 用于编写服务器程序的
TcpClient: 用于编写客户端程序的

epoll + 线程池
好处:能够把网络I/O的代码和业务代码区分开
业务代码就是:用户的连接和断开,用户的可读写事件
我们只需要关注业务代码,什么时候发生和如何监听这些事情的发生由muduo库去做 
*/
#include 
#include //事件循环 
#include 
#include //绑定器 
#include 
using namespace std;
using namespace muduo;
using namespace muduo::net;
using namespace placeholders;
//muduo的名字空间作用域
 
/*
基于muduo网络库开发服务器程序
步骤如下: 
1.组合TcpServer对象
2.创建EventLoop事件循环对象的指针
3.明确TcpServer构造函数需要什么参数,输出ChatServer的构造函数
4.在当前服务器类的构造函数当中,注册处理连接的回调函数和处理读写时间的回调函数
5.设置合适的服务端线程数量,muduo库会自己分配I/O线程和worker线程
*/
class ChatServer//TCPServer 
{
public:
    ChatServer(EventLoop *loop,               //事件循环
               const InetAddress &listenAddr, //muduo封装好的,绑定IP+Port
               const string &nameArg)//给TCPserver一个名字 
        : _server(loop, listenAddr, nameArg), _loop(loop)//没有默认构造哦 
    {
        //给服务器注册用户连接的创建和断开的回调,回调就是对端的相应事件发生了告诉网络库 ,然后网络库告诉我 ,我在回调函数开发业务 
        _server.setConnectionCallback(std::bind(&ChatServer::onConnection, this, _1));//绑定this对象到这个方法中,_1是参数占位符 

        //给服务器注册用户读写事件的回调
        _server.setMessageCallback(std::bind(&ChatServer::onMessage, this, _1, _2, _3));//绑定this对象到这个方法中 

        //设置服务器端的线程数量 1个I/O线程(监听新用户的连接事件), 3个worker线程
        //不设置的话,就1个线程而已,要处理连接又要处理业务 
        _server.setThreadNum(4);//设置4个线程,1个I/O线程,3个worker线程 
    }

    void start()//开启事件循环 
    {
        _server.start();
    }

private:
    //专门处理:用户的连接创建和断开 epoll listenfd accept
    //如果有新用户的连接或者断开,muduo库就会调用这个函数 
    void onConnection(const TcpConnectionPtr &conn)
    {
        if (conn->connected())//连接 , peerAddress()对端的地址 localAddress() 本地的地址 
        {
            cout << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << " state:online" << endl;
        }
        else//断开 
        {
            cout << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << " state:offline" << endl;
            conn->shutdown();//相当于这些close(fd)
            //_loop->quit();
        }
    }

    //专门处理:用户的读写事件,muduo库去调用这个函数 
    void onMessage(const TcpConnectionPtr &conn, //连接,通过这个连接可以读写数据 
                   Buffer *buffer,               //缓冲区,提高数据收发的性能 
                   Timestamp time)               //接收到数据的时间信息
    {
        string buf = buffer->retrieveAllAsString();//收到的数据放到这个字符串中 
        cout << "recv data:" << buf << " time:" << time.toFormattedString() << endl;
        conn->send(buf);//返回 ,收到什么发送什么 
    }

    TcpServer _server;//第一步 
    EventLoop *_loop; //第二步相当于 epoll 事件循环的指针,有事件发生,loop上报 
};

int main()
{
    EventLoop loop;//相当于像是创建了epoll
    InetAddress addr("127.0.0.1", 6000);//IP地址,端口号 
    ChatServer server(&loop, addr, "ChatServer");

    server.start();//listenfd通过 epoll_ctl 添加到 epoll 
    loop.loop();//相当于epoll_wait,以阻塞方式等待新用户连接,已连接用户的读写事件等
}

基于muduo与nginx实现的网络集群聊天服务器系统_第2张图片

04 Cmake

CMakeLists 创建及基本语法

一级 chatserver

二级 src

三级 server client

05 mysql

mysql安装

表设计

提供了一个model类,专门对数据库代码进程封装,数据层和业务层分开

06 项目工程创建

bin: 存放可执行文件

build:存放cmake生成的编译文件

include:存放头文件(.h)

src:存放源码(.cpp)

test:存放测试示例

thirdparty:存放第三方库(json)

autobuild.sh:shell脚本 一键运行

CMakeLists:cmake编译

README:工程简介

07 code

1.网络模块代码 chatserver

2.业务模块代码 chatservice

3.网络模块和业务模块降级耦合

4.网络模块 分发业务事件回调操作

5.mysql数据库代码封装

6.model数据层代码框架

7.用户注册业务

8.用户登录业务

9.线程安全 互斥锁

08 实现功能

客户端注册

解析json字符串,得到了name 和 pwd, 产生对应的id号

然后set一下,把id 和 pwd 传入到mysql中,

注册完成以后的话需要返回注册成功的消息

客户端登陆

解析json字符串,得到id和pwd

在mysql中使用query查询id号

判断密码是否正确,正确的话,在mysql中把这个id号由offline改为online状态

添加好友

好友聊天

msgid 消息id

id 本机id号

from 名字

to 对方id号

msg 聊天消息内容

首先判断对方是否在线,在线需要找到对应用户的connection

不在线,把消息存到历史消息中,等待对方上线发送给对方

创建群组

管理者和普通人员

多表联合查询

多了一个role角色

group name

group id

querygroup 查询群组信息

添加群组

addgroup函数

群组聊天

querygroupusers

userid groupid

image-20220705132206503

离线消息存储

数据库中专门有一个offline messqge存储离线消息

id

message

登录成功后检查用户是否有离线消息,有的话载入model类

09 nginx配置tcp负载均衡

基于muduo与nginx实现的网络集群聊天服务器系统_第3张图片

10 redis跨服务器通信

基于muduo与nginx实现的网络集群聊天服务器系统_第4张图片

基于muduo与nginx实现的网络集群聊天服务器系统_第5张图片

服务器间的通信成为问题,服务器之间直连会造成socket资源负载过重,引入redis的MQ消息队列处理服务器之间的通信请求

MQ消息队列

11 shell脚本输出

12 git代码托管

13 常见问题

数据明文传输的安全问题

历史消息存储问题

基于muduo与nginx实现的网络集群聊天服务器系统_第6张图片

redis功能不稳定还有哪些实现方式

你可能感兴趣的:(#,后端分布式系统架构设计,服务器)