前言

先看看新增的目录与源文件:
在这里插入图片描述
之前的6个文件(ChatServer、ChatService、public.h、main.cpp)构建了一个高性能网络框架 + 业务分发解耦,但业务(login/reg)只是打印日志,没有真实功能。
新增的5个文件(db.h、db.cpp、user.hpp、usermodel.hpp、usermodel.cpp)加入了数据持久化层,实现了真正的用户注册(把用户名、密码存到 MySQL 数据库,并返回自增 id)。

整体架构现在是:
网络层 → 业务控制层 → 模型操作层 → 实体层 → 数据库连接层
好处:完全解耦,网络层完全不感知数据库,业务层只调用模型接口。
当前已实现:注册业务完整闭环(从客户端 JSON → 数据库插入 → 返回新 id)。登录业务后续会扩展。

逐个文件讲解

1. db.h —— 数据库连接封装头文件(底层 DAO)

目的作用:

封装 MySQL C API,提供简单安全的数据库操作接口。
屏蔽底层细节,让上层(UserModel)只需调用 connect/update/query 即可。

代码:

#ifndef DB_H
#define DB_H


#include<mysql/mysql.h>
#include<string>
using namespace std;

//数据库操作类
class MySQL
{
public:
    //初始化数据库连接
    MySQL();
    //释放数据库连接资源
    ~MySQL();
    //连接数据库
    bool connect();
    //更新操作
    bool update(string sql);
    //查询操作
    MYSQL_RES *query(string sql);
    //获取连接
    MYSQL* getConnection();
private:
    MYSQL *_conn;
};


#endif

单连接设计:每个 MySQL 对象持有一个连接(局部创建,用完析构关闭)。
日志:失败时用 muduo::LOG_INFO 记录。
字符集:connect 成功后执行 “set names gbk” 防中文乱码。

2. db.cpp —— 数据库连接实现

目的作用:
实现 db.h 中声明的方法,真正连接和执行 SQL。

实现细节:

硬编码配置:static string 定义主机、用户、密码、库名(本地开发方便)。
connect():调用 mysql_real_connect,成功后 set names gbk 并日志。
update():mysql_query 执行,非0失败 → 日志 + false。
query():同上,返回 mysql_use_result(流式读取,省内存)。
资源管理:析构自动 mysql_close。

当前局限:每次操作新建连接(非连接池),高并发下效率低,但简单易懂。

代码:

#include"db.h"
#include<muduo/base/Logging.h>

//数据库配置信息
static string server = "127.0.0.1";
static string user = "root";
static string password = "123456";
static string dbname = "chat";

//初始化数据库连接
MySQL::MySQL()
{
    _conn = mysql_init(nullptr);
}
//释放数据库连接资源
MySQL::~MySQL()
{
    if(_conn != nullptr) mysql_close(_conn);
}

//连接数据库
bool MySQL::connect()
{
    MYSQL *p = mysql_real_connect(_conn,server.c_str(),user.c_str(),password.c_str(),dbname.c_str(),3306,nullptr,0);
    if(p != nullptr)
    {
        //C和C++代码默认的编码字符是ASCII,如果不设置,从MySQL上拉下来的中文显示?
        mysql_query(_conn,"set names gbk");
        LOG_INFO<<"connect mysql success!";
    }
    else
    {
        LOG_INFO<<"connect mysql fail!";
    }
    return p;
}
//更新操作
bool MySQL::update(string sql)
{
    if(mysql_query(_conn,sql.c_str()))
    {
        LOG_INFO<<__FILE__<<":"<<__LINE__<<":"<<sql<<"更新失败!";
        return false;
    }
    return true;
}
//查询操作
MYSQL_RES* MySQL::query(string sql)
{
    if(mysql_query(_conn,sql.c_str()))
    {
        LOG_INFO<<__FILE__<<":"<<__LINE__<<":"<<sql<<"查询失败!";
        return nullptr;
    }
    return mysql_use_result(_conn);
}

//获取连接
MYSQL* MySQL::getConnection()
{
    return _conn;
}

3. user.hpp —— 用户实体类(ORM 实体)

目的作用:
对应数据库 user 表的一条记录,作为数据载体在业务层 ↔ 数据库层之间传递;纯 POJO(Plain Old C++ Object),只存数据 + getter/setter。
代码:

#ifndef USER_H
#define USER_H

#include<string>
using namespace std;

//匹配User表的ORM类
class User
{
public:
    User(int id =1,string name="",string pwd="",string state="offline")
    {
        this->id = id;
        this->name = name;
        this->password = pwd;
        this->state = state;
    }
    void setId(int id) {this->id = id;}
    void setName(string name){this->name = name;}
    void setPwd(string pwd) {this->password = pwd;}
    void setState(string state) {this->state = state;}

    int getId() {return this->id;}
    string getName(){return this->name;}
    string getPwd() {return this->password;}
    string getState() {return this->state;}
private:
    int id;
    string name;
    string password;
    string state;
};


#endif

为什么 id 默认 -1:区分“新用户”(未插入)和“已存在用户”。
state 默认 offline:新注册用户默认离线。

4. usermodel.hpp(usermodel.h) —— 用户表操作接口声明

目的作用:
定义 UserModel 类,封装对 user 表的 CRUD 操作;
当前只声明 insert(注册用),后续会扩展 query/update 等。

代码:

#ifndef USERMODEL_H
#define USERMODEL_H

#include"user.hpp"

//User表的数据操作类
class UserModel
{
public:
    //User表的增加方法
    bool insert(User &user);

};


#endif

职责单一,只操作 user 表,不碰其他。

5. usermodel.cpp —— 用户表插入实现

目的作用:实现注册时的数据库插入,把 User 对象持久化到表中,并写回自增 id

代码:

#include"usermodel.hpp"
#include"db.h"
#include<iostream>
using namespace std;

//User表的增加方法
bool UserModel::insert(User &user)
{
    //1 组装sql语句
    char sql[1024] = {0};
    sprintf(sql,"insert into User(name,password,state)values('%s','%s','%s')",
        user.getName().c_str(),user.getPwd().c_str(),user.getState().c_str());

    MySQL mysql;
    if(mysql.connect())
    {
        if(mysql.update(sql))
        {
            //获取插入成功的用户数据生成的主键id
            user.setId(mysql_insert_id(mysql.getConnection()));
            return true;
        }
    }

    return false;
}

SQL 拼接:简单但有注入风险(后面会优化的)。
写回 id:注册成功后客户端能立刻知道自己的 id。
局部 MySQL 对象:用完自动关闭连接。

文件的整体联系

1.最顶层:网络模块(只负责IO和初步分发)
文件:main.cpp → ChatServer.h / chatserver.cpp(依赖Muduo库)

main.cpp 是程序入口,只负责创建EventLoop、InetAddress、ChatServer对象,调用server.start()和loop.loop()。

ChatServer 是网络主类:

  • 构造函数注册Muduo的connectionCallback和messageCallback(绑定onConnection和onMessage)。
  • onMessage 是关键入口:收到客户端字节流 → retrieveAllAsString → json::parse → 取出js[“msgid”] → 调用ChatService单例的getHandler(msgid)获取处理器 → 执行处理器(把conn、js、time传进去)。

这一层与其他层的联系:
只依赖业务模块(ChatService单例)。
把所有收到的消息(已转JSON)完全交给业务模块处理。
通过传进来的conn对象,业务模块可以直接发送响应。
完全不依赖数据库层、模型层、实体层(甚至不知道它们存在)。

2.中间层:业务模块(调度中心,根据msgid决定走哪条业务)
文件:public.h + ChatService.h / chatservice.cpp

public.h 定义msgid常量(LOGIN_MSG=1、REG_MSG=2),供网络模块(解析js[“msgid”])和业务模块(_msgHandlerMap注册)共同使用,保证协议一致。

ChatService 是单例(instance()返回静态对象):

  • 构造函数注册_msgHandlerMap:LOGIN_MSG → login方法,REG_MSG → reg方法(用std::bind绑定)。
  • getHandler(msgid):查找map返回对应handler,如果不存在返回空操作+日志。
  • login/reg方法:当前实现是占位(打印日志),但设计上就是在这里调用下层模型操作。

这一层与其他层的联系:
依赖网络模块:handler签名接收conn(用于发送响应)、js(客户端数据)、time。
依赖public.h:用msgid常量注册和分发。
依赖模型操作层(UserModel):在reg/login方法内部会创建UserModel对象,调用insert(或其他后续query/update)。
依赖实体层(User):从js提取字段填充User对象,传给UserModel,之后从User取id或其他字段构造响应。
通过conn发送响应:最终结果回传给网络模块发给客户端。
不直接依赖数据库连接层(MySQL),只通过UserModel间接使用。

3.下层:模型操作层 + 实体层 + 数据库连接层(负责持久化)
文件:usermodel.hpp / usermodel.cpp → user.hpp → db.h / db.cpp

user.hpp 定义User实体类(id、name、password、state + getter/setter),纯数据容器。

usermodel.hpp / usermodel.cpp 定义并实现UserModel类:

  • 只提供insert(User &user)(引用传参)。
  • insert内部:读取User的name、password、state → 组装SQL → 创建MySQL对象 → connect + update执行 → mysql_insert_id写回User的id → 返回bool。

db.h / db.cpp 定义并实现MySQL类:

  • 提供connect、update、query、getConnection、析构关闭。
  • 硬编码数据库配置(127.0.0.1、root、123456、chat库)。

这一组内部联系:
UserModel → User:读取字段组SQL,写回id(引用修改)。
UserModel → MySQL:局部创建MySQL对象,调用connect/update/getConnection,函数结束MySQL析构关闭连接。
MySQL内部:db.h声明 + db.cpp实现所有底层API。
User:只被UserModel和业务模块使用,作为数据传递桥梁。

这一层与上层(业务模块)的联系:
只通过接口被业务模块调用(insert返回bool + 修改User)。
完全不依赖业务模块、网络模块(不知道msgid、conn、JSON等概念)。

完结,下一篇会对用户注册业务、用户登录业务等进行实现。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐