【远程通信系统】服务端初始化

news2024/9/24 5:25:11

服务器架构:libevent + 线程池
在这里插入图片描述
数据库:MySQL
有两张表:chat_user和chat_group,分别保存用户信息和群信息

在这里插入图片描述

在这里插入图片描述
在线用户和群的保存:

struct User
{
        std::string name;//账号(用户名
        struct bufferevent* bev;//客户端对应的事件
};

//保存在线用户信息
std::list<User> *online_user;
//保存所有群的信息
std::map<std::string,std::list<std::string>> *group_info;

在这里插入图片描述

ChatServer的初始化

在这里插入图片描述

ChatServer::ChatServer()
{
        //初始化事件集合
        this->base =event_base_new();

        //初始化数据库对象
        db = new DataBase();

        //初始化数据库表(chat_user,chat_group)
        if(!db->database_init_table())
        {
                std::cout<<"init table failure"<<std::endl;
                exit(1);
        }

        //初始化数据结构对象
        info = new ChatInfo();

        //初始化群信息:把群信息从数据库里读出来,放入map
        server_update_group_info();
        
		//初始化线程池
        thread_num = 3;
        cur_thread = 0;
        pool = new ChatThread[thread_num];

        for(int i=0;i<thread_num;i++)
        {
                pool[i].start(info,db);
        }
}

初始化事件集合,放入监听事件

ChatServer::ChatServer()
{
        this->base =event_base_new();
}

ChatServer::listen()

下载libevent源码并查看:
在这里插入图片描述
进入/xx/usr/share/doc/libevent-dev/examples,可查看libevent使用示例。
在这里插入图片描述
查看hello-world.c
在这里插入图片描述
去头文件查看evconnlistener_new_bind的用法。
头文件都在/usr/include/中。
在这里插入图片描述
进入listener.h
在这里插入图片描述
在这里插入图片描述

struct evconnlistener *evconnlistener_new_bind(
    struct event_base *base,//事件集合
    evconnlistener_cb cb,//一旦有客户端连接,就会触发回调函数
    void *ptr,
    unsigned flags,
    int backlog,//监听队列里的容量
    const struct sockaddr *sa,
    int socklen
);

unsigned flags:说明里flags Any number of LEV_OPT_* flags
于是搜索LEV_OPT_,得如下结果
在这里插入图片描述
写代码:

//创建监听对象
void ChatServer::listen(const char* ip, int port)
{
        struct sockaddr_in server_info;
        memset(&server_info,0,sizeof(server_info));//清空
        server_info.sin_family = AF_INET;
        server_info.sin_addr.s_addr = inet_addr(ip);
        server_info.sin_port = htons(port);

        struct evconnlistener *listener=evconnlistener_new_bind(base,
                        listener_cb,this,LEV_OPT_CLOSE_ON_FREE,5,
                        (struct sockaddr*)&server_info,sizeof(server_info));
        if(listener == NULL){
                std::cout<<"evconnlistener_new_bind error"<<std::endl;
                exit(1);
        }
        //监听集合
        event_base_dispatch(base);//死循环,如果集合没有事件,退出
        //释放对象
        evconnlistener_free(listener);
        event_base_free(base);
}

查看回调函数listenner_cb的声明:
在这里插入图片描述
把光标放在这,按下shift+8(也就是*),然后next
在这里插入图片描述

listener_cb

在回调函数中打印客户端的ip和端口,方便调试。
关于listener_cb的this参数的说明

struct evconnlistener *listener=evconnlistener_new_bind(base,
       listener_cb,   this   ,LEV_OPT_CLOSE_ON_FREE,5,
       (struct sockaddr*)&server_info,sizeof(server_info));

因为listener_cb是个静态函数,而静态函数只能通过对象来调用普通成员函数,所以listener_cb不能直接调用server_alloc_enevt()。
所以传入this参数,通过this来调用。(因为参数是void*,所以记得先把this强转回来)

//回调函数,有客户端发起连接,会触发该函数
void ChatServer::listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
                struct sockaddr *c, int socklen, void *arg)
{
        struct sockaddr_in *client_info = (struct sockaddr_in*)c;
        std::cout<<"[connection]";
        std::cout<<" client ip : " <<inet_ntoa(client_info->sin_addr);
        std::cout<<" port : " << client_info->sin_port<<std::endl;

        //创建事件,放入线程池
        ser->server_alloc_event(fd);
}

初始化数据库对象

查找头文件
在这里插入图片描述
包含头文件要把include后面都写上

#include<mysql/mysql.h>
class DataBase
{
private:
        MYSQL *mysql;
        std::mutex _mutex;
public:
        DataBase();
        ~DataBase();
        bool database_connect();
        void database_disconnect();
        bool database_init_table();
}

构造函数不需要做什么。

DataBase::DataBase()
{
}

初始化数据库表

是否要一直打开数据库——取决于项目对数据库的使用频繁程度。
远程通信系统对数据库使用不频繁,所以不用时将数据库关闭。

连接数据库:

在这里插入图片描述
在命令行敲的所有命令都可以通过mysql_query()来执行
在这里插入图片描述
mysql_query()如果查询成功,返回0。如果出现错误,返回非0值。

建议先在在mysql中测试语句的可执行性,再写入代码:

mysql -u root -p登录mysql
在这里插入图片描述

set names utf8;

在这里插入图片描述
显示query 成功。

bool DataBase::database_connect()
{
        //初始化数据库句柄
        mysql = mysql_init(NULL);//分配堆内存

        //连接数据库
        mysql = mysql_real_connect(mysql, "localhost","root","root",
                        "chat_database",0,NULL,0);
        if(mysql==NULL)
        {
                std::cout<<"mysql_real_connect error"<<std::endl;
                return false;
        }

        //设置编码格式   (防止中文乱码)
        if(mysql_query(mysql, "set names utf8;")!=0)
        {
                std::cout<<"set names utf8 error"<<std::endl;
                return false;
        }

        return true;
}

断开数据库:

直接调用mysql_close()函数

void DataBase::database_disconnect()
{
        mysql_close(mysql);
}

初始化数据库表:

创建chat_group的sql语句:

create table if not exists chat_group(
groupname varchar(128),
groupowner varchar(128),
groupmember varchar(4096)
)charset utf8;

创建chat_user的sql语句

create table if not exists chat_user(
username varchar(128),
password varchar(128),
friendlist varchar(4096),
grouplist varchar(4096)
)charset utf8;

初始化数据库表:

bool DataBase::database_init_table()
{
        database_connect();

        const char* g="create table if not exists chat_group(groupname varchar(128),groupowner varchar(128),groupmember varchar(4096))charset utf8;";
        if(mysql_query(mysql,g)!=0)
        {
                return false;
        }

        const char* u="create table if not exists chat_user(username varchar(128),password varchar(128),friendlist varchar(4096),grouplist varchar(4096))charset utf8;";

        if(mysql_query(mysql,u)!=0)
        {
                return false;
        }

        database_disconnect();

        return true;
}

初始化数据结构对象

struct User
{
        std::string name;//账号(用户名
        struct bufferevent* bev;//客户端对应的事件
};

class ChatInfo
{
private:
        //保存在线用户信息
        std::list<User> *online_user;
        //保存所有群的信息
        std::map<std::string,std::list<std::string>> *group_info;
        //访问在线用户的锁
        std::mutex list_mutex;
        //访问群信息的锁
        std::mutex map_mutex;
public:
        ChatInfo();
        ~ChatInfo();
        void list_update_group(std::string* ,int);
        void list_print_group();
};

ChatInfo::ChatInfo()
{
        online_user = new std::list<User>;
        group_info = new std::map<std::string,std::list<std::string>>;
}

初始化群信息

server_update_group_info

逻辑:从数据库获取群信息,然后写入list。
所以获取群信息的函数是属于DataBase的,写入list的函数时属于ChatInfo的

void ChatServer::server_update_group_info()
{
        //连接数据库
        if(!db->database_connect())
        {
                exit(1);
        }

        std::string groupinfo[1024];//最多1024个群
        int num = db->database_get_group_info(groupinfo);
        std::cout<<"group num : "<<num<<std::endl;

        //断开数据库
        db->database_disconnect();

        info->list_update_group(groupinfo,num);

        //info->list_print_group();//测试用
}

database_get_group_info

在这里插入图片描述
在这里插入图片描述
mysql_store_result()用于从服务器获取结果集并将其存储在客户端中以供检索和处理,返回值是MYSQL_RES*
在这里插入图片描述
mysql_fetch_row()用于逐行获取查询结果集中的数据,返回值是MYSQL_ROW
在这里插入图片描述
MYSQL_ROW是个数组
在这里插入图片描述
用竖线|间隔每个数据。

int DataBase::database_get_group_info(std::string *g)
{
        if(mysql_query(mysql,"select * from chat_group;")!=0)
        {
                std::cout<<"select error"<<std::endl;
                return -1;
        }
        MYSQL_RES *res = mysql_store_result(mysql);
        if(res==NULL)
        {
                std::cout<<"store result error"<<std::endl;
                return -1;
        }
        MYSQL_ROW r;
        int idx=0;
        while(r = mysql_fetch_row(res))
        {
                g[idx] += r[0];
                g[idx] +='|';
                g[idx] += r[2];
                //std::cout<<g[idx]<<std::endl;
                idx++;
        }

        mysql_free_result(res);
        return idx;
}

list_update_group()

按database_get_group_info中的格式,查找竖线|,已得到每个数据。

void ChatInfo::list_update_group(std::string* g, int size)
{
        int idx=0,start =0;
        std::string groupname,membername;
        std::list<std::string> l;

        for(int i=0;i<size;i++)
        {
                idx = g[i].find('|');
                groupname = g[i].substr(0,idx);
                //std::cout<<groupname<<std::endl;

                start = idx +1;
                while(1)//idx查找竖线,找不到是-1
                {
                        idx = g[i].find('|',idx+1); //从idx开始查找
                        if(idx==-1)break;
                        membername = g[i].substr(start,idx-start);
                        l.push_back(membername);
                        start = idx +1;
                }

                membername = g[i].substr(start,idx - start);
                l.push_back(membername);

                this->group_info->insert(std::pair<std::string,std::list<std::string>>(groupname,l));

                l.clear();
        }
}            

初始化线程池

class ChatThread
{
private:
        std::thread *_thread;
        std::thread::id _id;
        struct event_base *base;
        ChatInfo *info;
        DataBase *db;
public:
        ChatThread();
        ~ChatThread();
        void start(ChatInfo *,DataBase *);
        void run();
        static void worker(ChatThread*);

};

ChatThread()

ChatThread::ChatThread()
{
        _thread = new std::thread(worker,this);
        _id = _thread->get_id();//get_id()时线程标准库里的

        base = event_base_new();
}

回调函数worker

因为静态成员函数worker只能访问静态成员变量,不能访问普通成员变量,却可以通过对象调用普通成员函数,所以再写一个普通成员函数run,同时在构造时传入this参数以调用run

void ChatThread::worker(ChatThread *t)
{
        t->run();
}

run()

因为event_base_dispatch()当集合中无事件时自动退出,所以随便放一个事件进集合。
比如放一个定时器事件,查找示例:
在这里插入图片描述
在这里插入图片描述
查看main函数中的使用示例

在这里插入图片描述

写代码:

void ChatThread::run()
{
        //集合中放入一个定时器事件
        struct event timeout;
        struct timeval tv;

        //将事件与集合绑定
        //base是构造函数初始化的base
        //EV_PERSIST表示定时器永远都有用
        event_assign(&timeout, base, -1, EV_PERSIST, timeout_cb,this);

        evutil_timerclear(&tv);
        tv.tv_sec=3;
        event_add(&timeout,&tv);

        std::cout<<"--- thread "<<_id<<" start working ---"<<std::endl;

        event_base_dispatch(base);//死循环,当集合中没有事件的时候退出

        event_base_free(base);

}
void ChatThread::timeout_cb(evutil_socket_t fd, short event, void *arg)
{
        ChatThread *t=(ChatThread *)arg;
        //std::cout<<"-- thread "<<t->thread_get_id()<<" is listening --"<<std::endl;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1439512.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

WordPress如何实现随机显示一句话经典语录?怎么添加到评论框中?

我们在一些WordPress网站的顶部或侧边栏或评论框中&#xff0c;经常看到会随机显示一句经典语录&#xff0c;他们是怎么实现的呢&#xff1f; 其实&#xff0c;boke112百科前面跟大家分享的『WordPress集成一言&#xff08;Hitokoto&#xff09;API经典语句功能』一文中就提供…

94.网游逆向分析与插件开发-游戏窗口化助手-地图数据获取的逆向分析与C++代码还原

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;升级经验数据获取的逆向分析 码云地址&#xff08;游戏窗口化助手 分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本号&#xff1a;c4351a5b346d8953a1a8e3ec…

Android:国际化弹出框

3.13 风格与主题、国际化 1、应用国际化 应用国际化&#xff0c;通过修改系统语言&#xff0c;应用显示语言跟着改变。 选择Locale,点击>>符号。 创建多个国家&#xff0c;地区strings.xml文件&#xff0c;有一个默认strings.xml文件&#xff0c;各个stirngs.xml中<…

以“防方视角”观社工钓鱼攻击

为方便您的阅读&#xff0c;可点击下方蓝色字体&#xff0c;进行跳转↓↓↓ 01 案例概述02 攻击路径03 防方思路 01 案例概述 这篇文章来自奇安信攻防社区“小艾”&#xff0c;记录的某师傅通过社工钓鱼诱导企业员工点击含有木马的文件&#xff0c;侵入系统取得了终端控制权。接…

中科大计网学习记录笔记(四):Internet 和 ISP | 分组延时、丢失和吞吐量

前言&#xff1a; 学习视频&#xff1a;中科大郑烇、杨坚全套《计算机网络&#xff08;自顶向下方法 第7版&#xff0c;James F.Kurose&#xff0c;Keith W.Ross&#xff09;》课程 该视频是B站非常著名的计网学习视频&#xff0c;但相信很多朋友和我一样在听完前面的部分发现信…

27. 云原生流量治理之kubesphere灰度发布

云原生专栏大纲 文章目录 灰度发布介绍灰度发布策略KubeSphere中恢复发布策略蓝绿部署金丝雀发布流量镜像 灰度发布实战部署自制应用金丝雀发布创建金丝雀发布任务测试金丝雀发布情况 蓝绿部署创建蓝绿部署测试蓝绿部署情况 流量镜像创建流量进行任务测试流量镜像情况 灰度发布…

Vue CLI学习笔记

在看任何开源库的源码之前&#xff0c;必须先了解它有哪些功能&#xff0c;这样才能针对性地分模块阅读源码。 Vue CLI 简介 Vue CLI是Vue.js的官方命令行工具&#xff0c;它是一个基于Vue.js进行快速开发的完整系统。 通过Vue CLI&#xff0c;开发者可以快速搭建和开发Vue.js项…

机器学习--K-近邻算法常见的几种距离算法详解

文章目录 距离度量1 欧式距离(Euclidean Distance)2 曼哈顿距离(Manhattan Distance)3 切比雪夫距离 (Chebyshev Distance)4 闵可夫斯基距离(Minkowski Distance)5 标准化欧氏距离 (Standardized EuclideanDistance)6 余弦距离(Cosine Distance)7 汉明距离(Hamming Distance)【…

使用 WPF + Chrome 内核实现高稳定性的在线客服系统复合应用程序

对于在线客服与营销系统&#xff0c;客服端指的是后台提供服务的客服或营销人员&#xff0c;他们使用客服程序在后台观察网站的被访情况&#xff0c;开展营销活动或提供客户服务。在本篇文章中&#xff0c;我将详细介绍如何通过 WPF Chrome 内核的方式实现复合客服端应用程序。…

前端-Vue項目初始化

大家好我是苏麟 , 今天聊聊前端依赖 Ant Design Vue 快速初始化项目 . Ant Design Vue官网 : 快速上手 - Ant Design Vue (antdv.com) 初始化项目 找到文档->快速上手 脚手架命令 : npm install -g vue/cli 找到一个文件夹(不要在中文路径) 下打开cmd窗口输入脚手架命令 成…

PyTorch 2.2大更新!集成FlashAttention-2,性能提升2倍

【新智元导读】新的一年&#xff0c;PyTorch也迎来了重大更新&#xff0c;PyTorch 2.2集成了FlashAttention-2和AOTInductor等新特性&#xff0c;计算性能翻倍。 新的一年&#xff0c;PyTorch也迎来了重大更新&#xff01; 继去年十月份的PyTorch大会发布了2.1版本之后&#…

查看NodeJs版本和查看NPM版本

Windows10 Dos命令下 查看NodeJs版本和查看NPM版本 NodeJs的命令是&#xff1a;node -v Npm的命令是&#xff1a;npm -v 下图&#xff1a; 记录下&#xff01;~

Docker Compose 构建 LNMP 环境:一站式 PHP 网站部署指南

Docker Compose 构建 LNMP 环境&#xff1a;一站式 PHP 网站部署指南 简介环境准备和安装安装 Docker安装 Docker Compose准备项目目录结构 编写 Docker Compose 文件基础结构配置 Nginx 服务配置 PHP 服务配置 MySQL 服务完整的 docker-compose.yml 示例 Nginx 容器配置创建 N…

Text2SQL研究-Chat2DB体验与剖析

文章目录 概要业务数据库配置Chat2DB安装设置原理剖析 小结 概要 近期笔者在做Text2SQL的研究&#xff0c;于是调研了下Chat2DB&#xff0c;基于车辆订单业务做了一些SQL生成验证&#xff0c;有了一点心得&#xff0c;和大家分享一下.&#xff1a; 业务数据库设置 基于车辆订…

【stomp实战】websocket原理解析与简单使用

一、WebSocket 原理 WebSocket是HTML5提供的一种浏览器与服务器进行全双工通讯的网络技术&#xff0c;属于应用层协议。它基于TCP传输协议&#xff0c;并复用HTTP的握手通道。浏览器和服务器只需要完成一次握手&#xff0c;两者之间就直接可以创建持久性的连接&#xff0c; 并…

【DDD】学习笔记-领域模型驱动设计

领域模型驱动设计自然是以提炼和转换业务需求中的领域知识为设计的起点。在提炼领域知识时&#xff0c;没有数据库的概念&#xff0c;亦没有服务的概念&#xff0c;一切围绕着业务需求而来。尤其是领域建模的分析阶段&#xff0c;应该只关注问题域&#xff0c;模型表达的是业务…

自用metadata.pegasus.txt bat生成脚本

下面代码保存为一个bat文件,放在存放rom的文件夹下运行 echo off setlocal enabledelayedexpansion :menu cls echo Please select a game ROM type: echo 1. FC echo 2. PSP echo. set /p choice"Enter your choice (1 or 2): " if "%choice%…

林浩然与杨凌芸的Java奇遇记:内部类的四重奏

林浩然与杨凌芸的Java奇遇记&#xff1a;内部类的四重奏 Lin Haoran and Yang Lingyun’s Java Adventure: A Quartet of Inner Classes 在一个充满代码香气的午后&#xff0c;程序员界的才子林浩然与机智灵动的女程序员杨凌芸&#xff0c;在Java的世界里展开了一场别开生面的“…

FPGA_vga显示

一 VGA 1.1 VGA VGA是视频图像阵列&#xff0c;是一种使用模拟信号进行视频传输的标准协议。 1.2 VGA接引脚定义 VGA分公母两种&#xff0c;RGB显示标准。 1.3 VGA显示器 VGA显示器采用图像扫描的方式进行图像显示&#xff0c;将构成图像的像素点&#xff0c;在行同步信号…

2024年阿里云服务器活动价格表

2024年2月阿里云服务器租用价格表更新&#xff0c;云服务器ECS经济型e实例2核2G、3M固定带宽99元一年、ECS u1实例2核4G、5M固定带宽、80G ESSD Entry盘优惠价格199元一年&#xff0c;轻量应用服务器2核2G3M带宽轻量服务器一年61元、2核4G4M带宽轻量服务器一年165元12个月、2核…