集群聊天服务器

集群聊天服务器


项目地址:Focuspresent/ChatServer (github.com)

环境搭建(基于Unbuntu20.04)#

boost库安装#

sudo apt install libboost-all-dev

mysql开发库安装#

sudo apt install libmysqlclient-dev

muduo库安装#

先需要安装cmake以及boost

sudo apt install cmake

下载安装包,并解压#

wget https://github.com/chenshuo/muduo/archive/refs/tags/v2.0.2.zip
mv v2.0.2.zip muduo-2.0.2.zip
unzip muduo-2.0.2.zip

修改CMakeLists.txt#

cd muduo-2.0.2
vim CMakeLists.txt
  • 找到23行的-Werror注释掉解释
  • 找到13行的option(MUDUO_BUILD_EXAMPLES "Build Muduo examples" ON)注释掉,不需要测试样例

编译,安装#

# 当前在muduo-2.0.2 目录下
./build.sh
./build.sh install
cd ..

# 执行后
➜  Downloads ls
build  muduo-2.0.2  muduo-2.0.2.zip

之后,需要进入安装后的目录(位于muduo-2.0.2同一级),将头文件和库文件放到系统头文件路径和系统库文件路径,否则,使用时需要通过编译选项(-I,-L)告知编译器

# 拷贝头文件
cd build/release-install-cpp11/include
sudo mv muduo/ /usr/include

# 拷贝库文件
cd ../lib
sudo mv * /usr/local/lib

测试#

#include <muduo/net/TcpServer.h>
#include <muduo/base/Logging.h>
#include <boost/bind.hpp>
#include <muduo/net/EventLoop.h>

// 使用muduo开发回显服务器
class EchoServer
{
 public:
  EchoServer(muduo::net::EventLoop* loop,
             const muduo::net::InetAddress& listenAddr);

  void start(); 

 private:
  void onConnection(const muduo::net::TcpConnectionPtr& conn);

  void onMessage(const muduo::net::TcpConnectionPtr& conn,
                 muduo::net::Buffer* buf,
                 muduo::Timestamp time);

  muduo::net::TcpServer server_;
};

EchoServer::EchoServer(muduo::net::EventLoop* loop,
                       const muduo::net::InetAddress& listenAddr)
  : server_(loop, listenAddr, "EchoServer")
{
  server_.setConnectionCallback(
      boost::bind(&EchoServer::onConnection, this, _1));
  server_.setMessageCallback(
      boost::bind(&EchoServer::onMessage, this, _1, _2, _3));
}

void EchoServer::start()
{
  server_.start();
}

void EchoServer::onConnection(const muduo::net::TcpConnectionPtr& conn)
{
  LOG_INFO << "EchoServer - " << conn->peerAddress().toIpPort() << " -> "
           << conn->localAddress().toIpPort() << " is "
           << (conn->connected() ? "UP" : "DOWN");
}

void EchoServer::onMessage(const muduo::net::TcpConnectionPtr& conn,
                           muduo::net::Buffer* buf,
                           muduo::Timestamp time)
{
  // 接收到所有的消息,然后回显
  muduo::string msg(buf->retrieveAllAsString());
  LOG_INFO << conn->name() << " echo " << msg.size() << " bytes, "
           << "data received at " << time.toString();
  conn->send(msg);
}


int main()
{
  LOG_INFO << "pid = " << getpid();
  muduo::net::EventLoop loop;
  muduo::net::InetAddress listenAddr(8888);
  EchoServer server(&loop, listenAddr);
  server.start();
  loop.loop();
}

编译(muduo_net在muduo_base前面)

g++ main.cpp -lmuduo_net -lmuduo_base -lpthread -std=c++11

运行

./a.out

另启一个终端,并向端口发送数据

echo "hello world" | nc localhost 8888

显示的数据

20240815 14:23:48.762392Z  3639 INFO  pid = 3639 - main.cpp:61
20240815 14:24:01.374976Z  3639 INFO  TcpServer::newConnection [EchoServer] - new connection [EchoServer-0.0.0.0:8888#1] from 127.0.0.1:34428 - TcpServer.cc:80
20240815 14:24:01.375027Z  3639 INFO  EchoServer - 127.0.0.1:34428 -> 127.0.0.1:8888 is UP - main.cpp:42
20240815 14:24:01.375066Z  3639 INFO  EchoServer-0.0.0.0:8888#1 echo 12 bytes, data received at 1723731841.375035 - main.cpp:53
20240815 14:24:10.038320Z  3639 INFO  EchoServer - 127.0.0.1:34428 -> 127.0.0.1:8888 is DOWN - main.cpp:42
20240815 14:24:10.038368Z  3639 INFO  TcpServer::removeConnectionInLoop [EchoServer] - connection EchoServer-0.0.0.0:8888#1 - TcpServer.cc:109

nginx 配置tcp负载均衡#

源码安装#

  1. 安装
➜  Downloads mkdir nginx
➜  Downloads cd nginx 
➜  nginx wget https://nginx.org/download/nginx-1.26.2.tar.gz 
➜  nginx tar -zvxf nginx-1.26.2.tar.gz
# 需要什么依赖,就去装
➜  nginx-1.26.2 ./configure --with-stream 
➜  nginx-1.26.2 make 
➜  nginx-1.26.2 sudo make install
➜  nginx-1.26.2 cd /usr/local/nginx 
➜  nginx cd conf
# 修改配置文件
➜  conf sudo vim nginx.conf 
  1. 部分配置文件
stream {
    upstream MyServer {
      server 127.0.0.1:9001 weight=1;
    	server 127.0.0.1:9002 weight=1;
    }
    
    server {
    	listen 9000;
    	proxy_pass MyServer;
    	tcp_nodelay on;
    }
}

nginx需要的命令#

# 启动
sudo /usr/local/nginx/sbin/nginx
# 停止
sudo /usr/local/nginx/sbin/nginx -s stop
# 平滑加载(重新读取配置文件)
sudo /usr/local/nginx/sbin/nginx -s reload

redis#

redis环境#

sudo apt install redis-server

redis开发库#

➜  Downloads git clone https://github.com/redis/hiredis.git
➜  Downloads cd hiredis 
➜  hiredis git:(master) make
➜  hiredis git:(master) sudo make install
# 如果之后hiredis链接失败
sudo ldconfig /usr/local/lib

数据库设计#

数据库名(chat)

  1. 用户表(users)
列名 类型 描述 约束
id int 用户id 主键,自增
username varchar(32) 用户名 唯一,非空
password varchar(32) 用户密码 非空
state enum('offline','online') 是否在线 默认是'offline'
  1. 好友表(friends)
列名 类型 描述 约束
userid int 用户id 非空,联合主键
friendid int 好友id 非空,联合主键
state enum('pass','unverify') 这条好友消息的状态 默认是'unverify'
  1. 离线消息表(offlinemessages)
列名 类型 描述 约束
userid int 用户id 非空,普通索引
offlinemessage varchar(512) 离线的消息 非空
  1. 群组表(groups)
列名 类型 描述 约束
id int 群组id 主键,自增
groupname varchar(64) 群组名 唯一,非空
groupdesc varchar(128) 群组描述
  1. 群组用户表(groupusers)
列名 类型 描述 约束
groupid int 群组id 非空,联合主键
userid int 用户id 非空,联合主键
role enum('owner','admin','normal') 用户在群组中的角色 默认是'normal'

项目架构#

├── bin
├── include
│   ├── client
│   └── server
│       ├── db
│       └── model
├── lib
├── src
│   ├── client
│   └── server
│       ├── db
│       └── model
├── test
└── thirdparty
    └── jsoncpp

服务端开发#

网络层#

基于muduo开发的网络类,可以根据事件的发生,调用先前注册好的回调函数,尤其是连接以及处理消息,处理消息可以调用业务类先前注册好的回调函数,根据消息类型进行处理,使用的是json类型

业务层#

业务类,主要负责业务层回调函数的实现

登录#

凭借先前注册的得到的账号以及账号密码,向服务端发起登录请求,处理后,会返回错误码以及相关信息

注册#

用户输入账户名(唯一)以及账号密码(非空),向服务端发起注册请求,处理后,会返回账号,需要记住账号

点对点聊天#

该业务没有好友限制,只需要两个客户的账号就行,假如接受端不在线,接受端的信息会被存储到离线消息中,等待接受端上线后,存储在数据库中的离线消息会被清空

添加好友#

  1. 添加好友请求

一个客户向另一个客户发起添加好友请求,需要对方的账号即可,然后由服务端处理,转发给对方,等待对方同意

  1. 添加好友验证

接受到好友请求的客户,可以选择通过或者不通过,通过后,两方的好友列表(服务端)都会新增对方的信息,不通过则没有

群组聊天#

  1. 创建群组

需要由客户端填写群组名(唯一)和群组描述,向服务端发起请求,成功后会返回群组号

  1. 添加群组

只需填写群组号即可,向服务端发起请求,不需要其他人的验证

  1. 群组聊天

向某个群组号填写发送的信息,向服务端发起请求,由服务端来转发除发出者之外的所有群员,离线的群员,消息会存储在离线消息表

刷新#

由客户端发起,向服务端更新与当前客户相关的信息,比如,好友状态以及群组状态等

客户端开发#

多线程程序,主线程负责读取用户的输入,封装成json数据,并向服务器发出请求,子线程负责读取服务端的响应,以及解析json数据,并把相应的数据打印在界面上

  1. 启动后
*****************************
        login: 1
        register: 2
        exit: 3
*****************************
  1. 登录后
******主界面******
******命令列表******
groupchat:群组聊天groupchat:groupid:msg
creategroup:创建群组creategroup:groupname:groupdesc
addgroup:添加群组addgroup:groupid
verifyfriend:验证好友请求verifyfriend:to:[y/n]
addfriend:发送添加好友请求addfriend:to
logout:退出登录
show:显示当前登录的用户信息
refresh:刷新信息refresh
chat:点对点聊天chat:to:msg
help:显示支持的命令

遇到的问题#

vscode的cmake插件使用#

  • 描述

本来想使用vscode的cmake插件的智能提示,发现在CMakelists.txt下没有智能提示,并且侧边栏右击后,无法展示cmake插件的具体信息

  • 解决方法

没有智能提示,是没有配置vscode的配置下的cmake路径,配置.vscode/settings.json文件中的cmake.cmakePath即可
无法显示插件,是因为我之前使用了Makefile Tools插件,将其禁用即可

mysql开发库的使用#

  • 描述

在测试查询不存在的账号或者群组时,服务端异常退出

  • 解决方法

这是我之前代码的缺陷之处,只对MYSQL_RES*进行了成功判断,然后使用mysql_fetch_row()读取返回结果,但其实,成功了也有可能是空行,所以使用mysql_fetch_row()读取出来的MYSQL_ROW也要进行空指针判断,不是空指针,才能读取到数据

参考文章#

  1. C++ muduo网络库知识分享01 - Linux平台下muduo网络库源码编译安装
  2. 施磊老师相关视频
  3. Mysql教程
  4. Nginx安装教程
  5. Redis教程
posted @   忆长思  阅读(46)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示
主题色彩