ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

chat集群聊天室项目 代码+讲解(二):业务模块

2021-09-12 16:59:14  阅读:181  来源: 互联网

标签:聊天室 chat msgid js ChatService 模块 id response conn


在这里插入图片描述

文章目录

项目简单架构图

在这里插入图片描述

类图不急。。。

代码

闲话不多说,直接上代码。

我精简化了一下,业务代码基本千篇一律的,没什么好看的。
redis那一块也先拿掉了,后面升级再说。

#pragma

#include<muduo/net/TcpConnection.h>
#include<unordered_map>
#include<functional>
#include<mutex>

#include "json.hpp"

using json = nlohmann::json;
using namespace std;
using namespace muduo;
using namespace muduo::net;


//处理消息的事件回调方法类型
using MsgHandler = std::function<void(const TcpConnectionPtr &conn,json &js,Timestamp time)>;

//聊天服务器业务
class ChatService{
public:
    //单例模式
    static ChatService* instance();
    
    void login(const TcpConnectionPtr &conn,json &js,Timestamp time);

    //获取消息对应的处理器
    MsgHandler getHandle(int msgid);

    //处理客户端异常退出
    void clientCloseException(const TcpConnectionPtr &conn);

    //处理服务端异常退出
    void reset();
private:

    ChatService();

    //存储消息id和对应的处理方法
    unordered_map<int,MsgHandler> _msgHanderMap;

    //存储在线用户连接
    unordered_map<int,TcpConnectionPtr> _userConnMap;
    
    //数据操作类的对象
    UserModel _usermodel;

    //定义互斥锁
    mutex _connMutex;
};
#include"chatservice.hpp"
#include"public.hpp"
#include<string>
#include<vector>
#include<map>
#include<muduo/base/Logging.h>

using namespace std;
using namespace muduo;

ChatService* ChatService::instance(){
    static ChatService service;

    return &service;
}
    
//注册消息以及对应的回调操作
ChatService::ChatService(){
    _msgHanderMap.insert({LOGIN_TYPE,std::bind(&ChatService::login,this,_1,_2,_3)});
    _msgHanderMap.insert({REG_TYPE,std::bind(&ChatService::reg,this,_1,_2,_3)});
    ···
}

//获取存储消息id和对应的处理方法
MsgHandler ChatService::getHandle(int msgid){

    //日志记录
    auto it = _msgHanderMap.find(msgid);
    if(it == _msgHanderMap.end()){
        //返回一个lambda表达式,返回一个默认的空处理器,防止业务挂掉,后可做平滑升级处理        
        return [=](const TcpConnectionPtr &conn,json &js,Timestamp time){
            LOG_ERROR<<"msgid:"<<msgid<<"can not find handle!";
        };
    }
    else{
        return _msgHanderMap[msgid];
    }
}

void ChatService::login(const TcpConnectionPtr &conn,json &js,Timestamp time){
    int id = js["id"].get<int>();
    string pwd = js["password"];

    User user = _usermodel.query(id);
    if (user.getID() == id && user.getpassword() == pwd)
    {
        if (user.getstate() == "online")
        {
            // 该用户已经登录,不允许重复登录
            json response;
            response["msgid"] = LOGIN_MSG_ACK;
            response["errno"] = 2;
            response["errmsg"] = "this account is using, input another!";

            conn->send(response.dump());
        }
        else
        {   
            //添加作用域,限制锁的粒度
            {
                lock_guard<mutex> lock(_connMutex);
          
                //记录用户连接
                _userConnMap.insert({id,conn});
            }
           
            // 登录成功,更新用户状态信息 state offline=>online
            user.setstate("online");
            _usermodel.updateState(user);       //    !!!     Single stepping until exit from function _IO_default_xsputn,

            json response;
            response["msgid"] = LOGIN_MSG_ACK;
            response["errno"] = 0;
            response["id"] = user.getID();
            response["name"] = user.getname();
            
            //查询用户是否有离线消息
            vector<string> vecofflinemsg = _offlineMsgmodel.query(id);
            if(!vecofflinemsg.empty()){
                response["offlinemsg"] = vecofflinemsg;

                //清空离线消息
                _offlineMsgmodel.remove(id);
            }

            vector<User> uservec = _friendmodel.query(id);
            if(!uservec.empty()){
                vector<string> vecfriend;
                for(User &user:uservec){
                    json js;
                    js["id"] = user.getID();
                    js["name"] = user.getname();
                    js["state"] = user.getstate();

                    vecfriend.push_back(js.dump());
                }
                response["friends"] = vecfriend;
            }

            conn->send(response.dump());
        }
    }
    else
    {
        // 该用户不存在,用户存在但是密码错误,登录失败
        json response;
        response["msgid"] = LOGIN_MSG_ACK;
        response["errno"] = 1;
        response["errmsg"] = "id or password is invalid!";
        conn->send(response.dump());
    }
}

void ChatService::reset(){
    //把所有online状态的用户转为offline
    _usermodel.resetstate();
}

讲解

为什么要设置单例

难道单例就只能拿来保证对象的单一性吗?
如果是为了保证对象的单一性,那取对象的时候就应该上个锁了,甚至是像“懒汉”那样上两个锁了。

在网络模块儿中,是这么写的:


void ChatServer::onMessage(const TcpConnectionPtr &conn, Buffer *buff, Timestamp time){

···
//通过msgid获取业务回调,进行网络模块和任务模块之间的解耦合
auto msgHandler = ChatService::instance()->getHandle(js["msgid"].get<int>());

//回调消息绑定好的事件处理器,执行相应的业务处理
msgHandler(conn,js,time);

···

就取个任务的事情,任务取完就甩手给channel去办事儿了。
难道每次我来取个任务还要 new 一下吗?

那为什么不在ChatServer里面放一个Chatservice chatservice_ 对象呢?
那为什么就非要加上这么一层耦合呢?


MsgHandler 的设计

 //通过msgid获取业务回调,进行网络模块和任务模块之间的解耦合
 auto msgHandler = ChatService::instance()->getHandle(js["msgid"].get<int>());
    
 //回调消息绑定好的事件处理器,执行相应的业务处理
msgHandler(conn,js,time);

-------------------

_msgHanderMap.insert({LOGIN_TYPE,std::bind(&ChatService::login,this,_1,_2,_3)});
_msgHanderMap.insert({REG_TYPE,std::bind(&ChatService::reg,this,_1,_2,_3)});

--------------------

//获取存储消息id和对应的处理方法
MsgHandler ChatService::getHandle(int msgid)
{
    auto it = _msgHanderMap.find(msgid);
    if(it == _msgHanderMap.end()){
        //返回一个lambda表达式,返回一个默认的空处理器,防止业务挂掉,后可做平滑升级处理        
        return [=](const TcpConnectionPtr &conn,json &js,Timestamp time){
            LOG_ERROR<<"msgid:"<<msgid<<"can not find handle!";
        };
    }
    else{
        return _msgHanderMap[msgid];
    }
}

把两个文件结合起来看,这样处理难道不妙吗?


业务中为什么不直接对接数据库?

在放出来的登录业务中,也可以看到业务层并没有直接对接数据库的权利。

走一层数据库映射不麻烦吗?

业务层还没有知道数据库设计的权力。给它数据就够了,数据哪里来的不用它管了。

我们希望业务层看到的都是对象,了解一下 ORM框架。简单了解可以看一下这篇

MVC的代码写过,ORM的代码也写过,两者之间的差距还是能感受到的。


标签:聊天室,chat,msgid,js,ChatService,模块,id,response,conn
来源: https://blog.csdn.net/qq_43762191/article/details/120251675

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有