1.dh.h

类定义概要

类名: MySQL

功能: 简化MySQL的连接、查询和更新操作,提供接口给上层应用使用。


成员变量

private:MYSQL *_conn;
  • _conn:指向MYSQL结构体的指针,用于代表数据库连接实例。由mysql_init()初始化,之后通过mysql_real_connect建立连接。

构造与析构函数

MySQL()

  • 作用: 初始化数据库连接对象,调用mysql_init(nullptr)
  • 功能: 为连接准备一个空的MYSQL实例。

~MySQL()

  • 作用: 释放数据库连接资源。
  • 调用: mysql_close(_conn),关闭连接,释放资源。

成员函数(公共接口)

bool connect()

  • 用途: 建立实际的数据库连接。
  • 实现细节:
    • 调用mysql_real_connect,连接到配置信息中的数据库(serveruserpassworddbname、端口3306)。
    • 连接成功后,设置字符集为gbkmysql_query(_conn, "set names gbk"))。
    • 连接成功则输出日志“connect mysql success!”。
    • 连接失败则输出详细的错误信息,包含错误原因和连接参数。
  • 返回值:
    • true:连接成功
    • false:连接失败

bool update(string sql)

  • 用途: 执行SQL更新操作(INSERT、UPDATE、DELETE等)。
  • 实现细节:
    • 调用mysql_query(_conn, sql.c_str())执行SQL。
    • 若失败,输出错误,返回false
    • 成功返回true
  • 示例: 插入新用户或修改数据。

MYSQL_RES *query(string sql)

  • 用途: 执行SQL查询语句(SELECT),返回结果集。
  • 实现细节:
    • 调用mysql_query(_conn, sql.c_str())执行查询。
    • 若失败,输出错误信息,返回nullptr
    • 若成功,调用mysql_use_result(_conn)获取结果集。
  • 返回值:
    • MYSQL_RES*:结果集指针,调用者需负责释放。

MYSQL* getConnection()

  • 用途: 提供底层连接指针,便于更多的低级操作或调试。
  • 返回: _conn指针。

其他细节

  • 连接信息存储:

    • server127.0.0.1
    • userroot
    • passwordSf523416&111
    • dbnamechat

    这些信息是静态变量(static string),方便在整个程序中使用。

  • 日志输出:

    • 使用 muduo/base/Logging.h 提供的日志宏 LOG_INFO 和 LOG_ERROR,方便调试和监控。

总结

这个 MySQL 类封装了:

  • 连接管理: 内部维护一个MYSQL*连接句柄。
  • 连接方法: connect(),连接数据库。
  • 数据操作:
    • update() 执行写操作(增删改)。
    • query() 执行读操作,返回结果集指针。
  • 资源管理: 在析构函数中释放连接。

它为上层应用提供了简洁、封装的数据库访问接口,并加入了错误信息输出和日志记录,非常适合在实际的C++服务器项目中使用。

2.FriendModel

让我们一起来详细分析这个FriendModel类,它主要负责维护和处理好友关系相关的操作。这个类封装了操作好友信息的两个核心功能:添加好友关系和查询好友列表。


类定义概述

#ifndef FRIENDMODEL_H
#define FRIENDMODEL_H#include "user.hpp"
#include <vector>
using namespace std;class FriendModel
{
public:// 添加好友关系void insert(int userid, int friendid);// 返回用户好友列表vector<User> query(int userid);
};#endif

成员方法概述

1. void insert(int userid, int friendid)
  • 功能: 在好友关系表中插入一条新的好友关系。
2. vector<User> query(int userid)
  • 功能: 查询某个用户的所有好友,返回一个User对象的向量(好友列表)。

方法详细解读

1. 添加好友关系:void insert(int userid, int friendid)

实现逻辑:

  • 构造SQL语句,格式为:
    insert into friend values(userid, friendid)
    
  • 创建MySQL对象,并连接数据库。
  • 如果连接成功,执行update()方法插入好友关系。

说明:

这个操作假设friend表有两列(例如:userid 和 friendid),代表一对好友关系。


2. 查询好友列表:vector<User> query(int userid)

实现逻辑:

  • 构造SQL语句,使用内部连接(inner join),查询用户的好友信息:

    select a.id, a.name, a.state
    from user a
    inner join friend b on b.friendid = a.id
    where b.userid = userid
    

    这条语句的作用:

    • user表中选出所有该用户的好友信息。
    • 通过friend关系表关联用户ID。
  • 创建一个vector<User>存放好友信息。

  • 建立数据库连接,执行查询:

    • 若成功,遍历MYSQL_RES结果集:

      • 利用mysql_fetch_row()读取每一行数据。

      • 创建一个User对象,设置其字段(idnamestate)。

      • User加入结果向量中。

    • 释放MYSQL_RES资源。

  • 返回好友列表vector<User>


类成员变量

// 目前该类没有显式成员变量,只有成员方法

备注:

  • 这个类设计中没有持久的成员变量,每次操作时都新建MySQL对象以连接数据库。这是一种简单实现,适合关系不频繁变动的场景。

其他细节

  • 数据库操作:

    • 插入关系时调用mysql.update(sql),没有事务控制,简单插入。
    • 查询好友信息时调用mysql.query(sql),处理结果集。
  • 安全性:

    • 使用sprintf拼接SQL语句,可能存在SQL注入风险;实际应用中建议用参数化查询。
  • 性能:

    • 每次操作都新建数据库连接,不复用连接,可能影响效率。

总结

这个FriendModel类提供了两个主要功能

  1. 添加好友关系insert()

    • 将两用户的好友关系存入数据库。
  2. 查询好友列表query()

    • 根据用户ID,查找所有好友信息,返回User对象的数组(好友列表)。

该设计结构简洁,直接,但在性能优化和安全性方面可以进一步增强(如连接池、参数化查询等)。

同时,它依赖于数据库表结构(friend表和user表)合理设计。

3.类比FriendModel类,还有usermodel, groupmodel,OfflinMegModel

这是数据操作模型层,负责在业务逻辑和数据存储之间传递信息。它知道如何把业务需求(如添加好友)转化为数据库操作,也懂得如何把数据库查询结果转化为业务可用的对象。

4.group.hpp,groupuser.hpp,user.hpp

 让我们详细分析这三个类(UserGroupUserGroup)的设计与作用,它们都是典型的 ORM(对象关系映射)类,用于映射数据库中的表结构到 C++ 对象中,方便开发中对数据的操作。

1. 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; }protected:int id;string name;string password;string state;
};

作用与设计:

  • 表示“用户”实体,将用户表中的字段(idnamepasswordstate)映射为类成员。
  • 提供了构造函数、getter、setter方法,方便操作。
  • 典型的ORM类,用于在程序中封装用户信息。

2. GroupUser 类(群组用户,扩展了User,添加角色信息)

复制代码

class GroupUser : public User
{
public:void setRole(string role) { this->role = role; }string getRole() { return this->role; }private:string role; // 用户在群组中的角色(如管理员、普通成员)
};

作用与设计:

  • 继承自User,意味着“群组内的用户”具有用户的所有基本属性(如ID、用户名、密码、状态)。
  • 增加role属性,表示用户在群组中的角色。
  • 设计理念:一个群组中的用户不仅仅算作普通用户,还需区分角色,因此在群组关系中,添加了角色字段。

总结:

  • GroupUser是一个“关系类”,表示某个用户在某个群组中的身份信息(不仅仅是用户,还包括角色属性)。
  • 这样设计方便在多对多关系中存储角色信息(如用户属于哪个群,角色怎样)。

3. Group 类(群组类)

class Group
{
public:Group(int id = -1, string name = "", string desc = ""){this->id = id;this->name = name;this->desc = desc;}void setId(int id) { this->id = id; }void setName(string name) { this->name = name; }void setDesc(string desc) { this->desc = desc; }int getId() { return this->id; }string getName() { return this->name; }string getDesc() { return this->desc; }vector<GroupUser> &getUsers() { return this->users; }private:int id;               // 群组IDstring name;          // 群组名称string desc;          // 群组描述vector<GroupUser> users; // 包含的成员列表(每个成员带角色信息)
};

作用与设计:

  • 表示“群组”实体,映射群组表。
  • 含有ID、名称、描述字段。
  • users 成员变量用来存储群组的所有成员(GroupUser对象),实现多对多关系(一个群组有多个用户,一用户可能在多个群组)。
  • 提供了基本的getter/setter方法。

关系与整体架构理解

  • User:代表整个系统的用户信息,映射用户表。
  • GroupUser:继承用户信息,并附加角色信息,代表某用户在特定群组中的角色(多对多关系中的中间表/关系实体)。
  • Group:代表群组实体,含其成员(GroupUser的集合)信息。

这套设计体现了面向对象方式对数据库关系的封装:

  • 通过Group类管理群组信息和成员列表;
  • 通过GroupUser类描述用户和角色之间的关系;
  • 通过User类描述用户基础信息。

总结:

  • 这三类共同组成了一个与“群组聊天”功能相关的对象模型架构。
  • 设计充分利用继承(GroupUser继承User)来复用代码,增强扩展性。
  • 使得程序能够以对象的方式操作数据库中的用户、群组与成员关系。

5.Redis 类

这份redis.hppredis.cpp实现了一个封装了Redis发布订阅(Pub/Sub)功能的类,方便在C++项目中使用Redis进行消息通信。下面我会详细介绍这个类中的成员变量和成员函数,帮助你理解它的设计思想和使用方法。


Redis 类成员和方法详解

1. 成员变量

复制代码

private:redisContext *_publish_context;   // 负责发布消息的上下文redisContext *_subcribe_context;  // 负责订阅消息的上下文function<void(int, string)> _notify_message_handler; // 消息接收时的回调函数
  • _publish_context:用来连接和操作Redis的发布(PUBLISH)命令,用于向频道发送消息。
  • _subcribe_context:用来连接和操作Redis的订阅(SUBSCRIBE)命令,用于监听和接收频道的消息。
  • _notify_message_handler:业务层定义的回调函数,用于通知业务层收到的消息。

2. 构造函数和析构函数

复制代码

Redis();  // 构造函数,初始化成员指针为空
~Redis(); // 析构函数,释放连接
  • 构造函数:初始化指针为nullptr
  • 析构函数:如果指针不为空,调用redisFree()释放资源,避免内存泄漏。

3. 核心功能成员方法

connect()

复制代码

bool connect();
  • 连接本地Redis服务器(默认地址为 127.0.0.1:6379)。
  • 分别创建两个连接:一个用于发布,一个用于订阅。
  • 连接成功后,启动一个独立线程调用observer_channel_message(),不断监听订阅通道中的消息,异步通知业务层。
publish()

复制代码

bool publish(int channel, string message);
  • 向指定频道发布消息。
  • 使用redisCommand()执行PUBLISH命令。
  • 如果成功,返回true,否则返回false
subscribe()

复制代码

bool subscribe(int channel);
  • 订阅指定频道。
  • 使用redisAppendCommand()redisBufferWrite()向Redis服务器发出SUBSCRIBE命令。
  • 注意:订阅操作是非阻塞的,只是发出订阅命令,接收消息由后台线程监听。
unsubscribe()

复制代码

bool unsubscribe(int channel);
  • 取消订阅某个频道。
  • 使用类似subscribe()的方式发出UNSUBSCRIBE命令。
observer_channel_message()

复制代码

void observer_channel_message();
  • 运行在后台线程中,持续调用redisGetReply()接收Redis服务器推送的消息。
  • 解析收到的消息数组(reply->element):
    • reply->element[2]->str:消息内容
    • reply->element[1]->str:频道编号
  • 调用_notify_message_handler()通知业务层收到新消息。
init_notify_handler()

复制代码

void init_notify_handler(function<void(int, string)> fn);
  • 初始化消息通知的回调函数。
  • 用户在业务层定义处理逻辑后,调用此函数注册。

其他细节说明

  • 连接管理:在connect()中创建两个redisContext,方便分别处理发布和订阅的连接,避免竞争和阻塞。
  • 多线程
    • 订阅消息的监听在observer_channel_message()中运行在单独的线程,保证异步处理。
  • 内存管理
    • 使用freeReplyObject()释放redisReply资源。
  • 命令执行
    • 发布:redisCommand()
    • 订阅/取消订阅:redisAppendCommand() + redisBufferWrite(),避免阻塞调用。
  • 错误处理
    • 连接失败和命令失败都输出错误信息,返回false

总结:

这个Redis类封装了Redis的Pub/Sub基本操作:

  • 连接connect()
  • 发布消息publish()
  • 订阅频道subscribe()
  • 取消订阅unsubscribe()
  • 异步监听消息:在后台线程中自动调用observer_channel_message(),收到消息后通过回调通知业务逻辑。
  • 回调机制:用户可以注册自己的消息处理函数。

它实现了异步订阅模型,能很好地应用在类似聊天服务器、实时通知等场景,非常实用。

6.ChatServer类

你的代码基于知名的 Muduo 网络库,实现了一个高效、易维护的聊天服务器主控类。下面按照结构–成员变量–成员方法详细分析:


一、ChatServer 类的结构作用

ChatServer 封装了实际的网络服务器逻辑,负责:

  • 初始化服务器、设置参数
  • 监听和管理客户端连接
  • 管理消息收发和业务处理
  • 调度事件循环(事件驱动)

二、成员变量讲解

private:TcpServer _server; // 组合muduo库,实现服务器功能的类对象EventLoop *_loop;  // 指向事件循环对象(相当于主线程的事件分发器)

1. TcpServer _server

  • Muduo 中封装的 TCP 服务器核心类。
  • 能自动监听端口,负责底层 epoll、多线程 I/O、连接管理等“苦力活”。
  • ChatServer 通过它实现与客户端的所有网络交互。

2. EventLoop *_loop

  • 事件循环指针。
  • 实际上是主 Reactor 的事件分发器,用于响应 I/O 事件。

三、成员方法详解

构造函数:ChatServer(EventLoop*, const InetAddress&, const string&)

ChatServer(EventLoop *loop, const InetAddress &listenAddr, const string &nameArg): _server(loop, listenAddr, nameArg), _loop(loop)
{// 注册链接回调_server.setConnectionCallback(std::bind(&ChatServer::onConnection, this, _1));// 注册消息回调_server.setMessageCallback(std::bind(&ChatServer::onMessage, this, _1, _2, _3));// 设置线程数量_server.setThreadNum(4);
}

作用总结与细节分析:

  • 初始化 _server,告诉 muduo 用哪台主机 IP/端口监听(listenAddr)、取什么名字(nameArg)、用哪个 event loop。
  • setConnectionCallback():绑定"有新连接/断开"时执行 onConnection 方法。
  • setMessageCallback():绑定"收到消息"时执行 onMessage 方法。
  • setThreadNum(4):用 4 个工作线程处理 I/O,提升吞吐量。

void start()

void ChatServer::start()
{_server.start();
}
  • 真正地启动服务器(让 Muduo 开始监听并接收客户端连接)。

void onConnection(const TcpConnectionPtr&)

void ChatServer::onConnection(const TcpConnectionPtr &conn)
{// 客户端断开链接if (!conn->connected()){ChatService::instance()->clientCloseException(conn);conn->shutdown();}
}

核心作用:

  • 这是连接状态变化时的回调(新连接或断开)。
  • 如果连接断开,则做以下处理:
    • 通知业务服务层进行账号、数据等“下线”处理(clientCloseException)。
    • 主动关闭 socket(conn->shutdown())。

void onMessage(const TcpConnectionPtr&, Buffer *, Timestamp)

void ChatServer::onMessage(const TcpConnectionPtr &conn, Buffer *buffer, Timestamp time)
{string buf = buffer->retrieveAllAsString();try {json js = json::parse(buf);// 检查必须字段if (!js.contains("msgid")) {throw json::exception("Missing 'msgid' field");}auto msgHandler = ChatService::instance()->getHandler(js["msgid"].get<int>());msgHandler(conn, js, time);}catch (const json::exception& e) {LOG_ERROR << "JSON parse error: " << e.what();if (conn->connected()) {json response;response["msgid"] = INVALID_MSG_ACK;response["errno"] = 400;response["errmsg"] = "Invalid JSON format: " + string(e.what());conn->send(response.dump());}}catch (const exception& e) {LOG_ERROR << "Message handling error: " << e.what();}
}

详细流程:

  1. 拿到完整缓冲区数据(收到的一次消息)。
  2. 尝试解析为 JSON 对象。
  3. 检查是否有"msgid"字段(决定了本消息应该怎么处理)。
  4. 调用 ChatService::instance()->getHandler(msgid) 找到对应业务处理函数,然后把消息传给它执行。
  5. 如果解析失败/消息结构有误,则:
    • 打日志报错
    • 给客户端返回错误提示

作用通俗理解:

“服务器收到客户发来的消息,把这消息当成快递包裹拆开看——快递单上写着包裹类型(msgid),然后根据类型分给对应员工(handler)处理。拆快递出错就写个反馈单还回去。”


四、补充说明

成员方法之间的关系

  • start 用于启动服务器。
  • onConnection/onMessage 并不是主动调用,而是事件驱动,在有连接/消息时被 Muduo 框架自动调用。
  • 事件响应机制和reactor思想一致。

和 ChatService 的协作

ChatServer 只管网络部分,具体业务如何处理交由 ChatService,“专人专职”,大大降低耦合。


五、总结

ChatServer 核心成员&方法概览:

名称简要作用
_serverMuduo 的 TCP 服务器,负责监听和通信。
_loop名字叫event loop,事件派发中心。
start()启动服务器。
onConnection()检测连接/断开,并在断开时交给业务进一步下线处理。
onMessage()收到客户端数据时解析json,根据业务类型分发给具体handler处理;出问题自动回错。
构造函数初始化服务器参数并注册各回调,每一步都让ChatServer做最擅长的事情。

这样,不仅代码结构清晰,而且便于扩展和维护,是高性能网络服务推荐的模式!

7.chatservice类

会把这个ChatService类中的所有成员变量和方法,逐个详细讲解,确保你理解每个部分的作用、设计思想以及它们的工作流程,而且会尽力用通俗易懂的语言,让你像听故事一样理解这个“聊天服务”系统。


一、类的整体架构和角色定位

ChatService是一个“聊天服务器”的核心业务类(管理者)。它采用单例模式(确保整个程序中只存在唯一的实例),负责处理所有的“用户登录、注册、点对点聊天、群聊、好友关系、群组操作”等业务逻辑。


二、成员变量(这个类内部存的“存储空间”和“工具箱”)

1. 单例实例

static ChatService *instance();
  • 作用:提供调用的入口,用于获取或创建唯一的ChatService实例(即单例设计模式)。
  • 通俗理解:就像超市只有一个“管理员”实例,这个方法帮你拿到它。

2. 消息处理映射表

unordered_map<int, MsgHandler> _msgHandlerMap;
  • 作用:把不同“消息ID”映射到对应“处理函数”。
  • 比如登录对应login()函数,注册对应reg()函数。
  • 通俗理解:就像菜单点菜表,点“牛排”就知道要做牛排一样。

3. 在线用户连接表

unordered_map<int, TcpConnectionPtr> _userConnMap;
  • 作用:存放已登录、在线用户的“用户ID”和“对应的TCP连接指针”。
  • 用途:当需要向某用户发消息时,先找这个表看用户是否在线,在线了就可以直接推送消息。
  • 通俗理解:像“电话簿”,存着每个谁在线、用哪个电话(连接)。

4. 连接互斥锁

mutex _connMutex;
  • 作用:在多线程环境下,保证对_userConnMap的操作(增删改查)是安全的。
  • 为何需要:因为可能有多个连接同时操作这个表,比如一个用户刚登录、登出,或服务器在同时处理多个请求。

5. 数据模型对象(操作数据库)

复制代码

UserModel _userModel;
OfflineMsgModel _offlineMsgModel;
FriendModel _friendModel;
GroupModel _groupModel;
  • 作用:封装了数据库操作的封装类(操作用户信息,离线消息,好友关系,群组信息)。
  • 例子
    • _userModel:查询或更新用户信息。
    • _offlineMsgModel:存储或读取离线消息。
    • _friendModel:管理好友关系(添加好友等)。
    • _groupModel:管理群聊信息。

6. Redis操作对象

复制代码

Redis _redis;
  • 作用:封装了与Redis(一个高速的数据库和消息队列系统)的交互,用于实现“跨进程”、“多服务器”的消息推送和订阅。

三、主要方法(业务逻辑执行的“技能”)

1. 获取单例实例

复制代码

static ChatService *instance();
  • 作用:返回全局唯一的ChatService对象,确保程序中只有一个“聊天服务”。
  • 实现:用“静态局部变量”,保证只创建一次。

2. 构造函数(注册消息-handler)

复制代码

ChatService::ChatService()
  • 作用
    • 初始化各种消息ID和对应的请求处理函数的映射关系。
    • 链接(连接)Redis,为后续消息订阅做准备。
注册消息响应(通过bind绑定对应成员函数)
  • login注册到LOGIN_MSG

  • register注册到REG_MSG

  • oneChat注册到ONE_CHAT_MSG

  • 还有好友添加、创建群、加入群、群聊的处理方法。

  • 通俗理解:像打电话时拨通不同号码调用不同小姐姐的服务。


3. 业务重置(重要)

复制代码

void reset();
  • 作用
    • 在服务器异常或重启时调用。
    • 将所有“已登录的用户”状态重置为“离线”状态(确保状态同步真实情况)。
  • 调用场景:系统崩溃后,恢复正常工作状态。

4. 获取特定“消息类型”对应的处理函数

复制代码

MsgHandler getHandler(int msgid);
  • 作用
    • 输入msgid,返回对应的处理函数(函数指针或lambda)。
    • 如果找不到,就返回一个“错误处理器”。
  • 设计亮点
    • 动态路由:前端发的不同消息会被路由到不同的处理函数去。

5. 各类具体业务的处理方法(详细讲解)

login()登陆流程
  • 输入:用户ID、密码。
  • 逻辑:-查询用户信息,验证密码。-用户已在线,提示重复登录,否则:-存用户名和连接关系到_userConnMap,方便推送。-在Redis订阅频道(方便跨服务通知)。-更新用户状态为在线。-返回用户信息(包括离线消息、好友列表、群组信息)。
  • 作用:实现实际“登陆”功能。
reg()注册
  • 输入:用户名、密码。
  • 逻辑:-校验格式、长度。-存入数据库。-返回成功或失败。
loginout()退出/注销
  • 作用:-从活跃连接中删除用户。-在Redis取消订阅某渠道(让其他服务器知道该用户已离线)。-更新数据库用户状态。
clientCloseException()
  • 作用:
    • 处理客户端异常断开连接(比如突然关闭窗口,没有正常退出)。
    • 类似loginout,清除连接,更新状态。
oneChat()点对点聊天
  • 输入:接收方toid,消息内容。
  • 机制:-第一步:找_userConnMap,看看对方是否在线(直接推)。-第二步:如不在线,则存离线消息。-第三步:如果对方在线但不在自己服务器(假设多节点),就用Redis发布消息,其他节点会收信息。
addFriend()添加好友
  • 输入:自己ID和好友ID。
  • 操作:存入好友关系表。
createGroup()
  • 输入:发起人ID、群组名、描述。
  • 操作:-在数据库中创建新群。-将发起人加入该群(权限设为“creator”)。
addGroup()加入群组
  • 输入:用户ID、群ID。
  • 操作:将用户加入群组。
groupChat()群聊
  • 输入:群ID、群成员消息。
  • 作用:
    • 中转消息:
      • 先找成员在不在线(在自己连接map里)。
      • 在线了推送。
      • 不在线存离线消息(存到数据库中)。

6. 处理Redis订阅消息

复制代码

void handleRedisSubscribeMessage(int, string);
  • 作用
    • 其他服务器发布同步消息后,调用此函数。
    • 找到对应的用户连接,如果在,推送消息。
    -否则,存离线消息。

四、重点总结(通俗版)

  • ChatService就像一个“指挥官”,协调所有聊天相关的操作。
  • 存放“用户和连接关系”,方便主动“发消息给用户”。
  • 有事注册“事件处理器”:不同的ID对应不同操作(登录、注册、聊天、群聊)。
  • 利用Redis实现多服务消息同步
  • 每个业务都是封装好的函数:登录、注册、点对点聊天等。
  • 整体设计高扩展性、线程安全、结构清晰。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电缆安全双保险:不止防盗,更能防触电的塔能智慧照明守护方案

城市照明、地下车库以及园区路灯所涉及的电缆安全问题&#xff0c;向来都是运维管理方面颇为棘手的难题。在传统的运维管理模式之下&#xff0c;电缆一旦被盗&#xff0c;那么所造成的影响可不小&#xff0c;一方面会带来直接的经济损失&#xff0c;另一方面还极有可能因为线路…

Leetcode刷题营第二十九,三十题:二叉树的中序以及后序遍历

94.二叉树的中序遍历 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,3,2]示例 2&#xff1a; 输入&#xff1a;root [] 输出&#xff1a;[]示例 3&#xff1a; 输入&#x…

Rabbitmq Direct Exchange(直连交换机)可以保证消费不被重复消费吗,可以多个消费者,但是需要保证同一个消息,不会被投递给多个消费者

在 RabbitMQ 中&#xff0c;默认情况下&#xff0c;不能保证消息不被重复消费&#xff0c;但可以通过 队列绑定方式 消费者竞争机制 来确保 同一消息只被一个消费者处理。以下是几种可行的方案&#xff1a;方案 1&#xff1a;单队列 竞争消费者模式&#xff08;默认行为&…

常用的OTP语音芯片有哪些?

唯创知音在 OTP 语音芯片有着26年的历史&#xff0c;有着丰富的技术积累与产品迭代历程。1999 年&#xff0c;唯创知音在广州成立&#xff0c;彼时便开始在电子领域积极探索。2000 年&#xff0c;公司敏锐捕捉到语音芯片行业的发展潜力&#xff0c;正式进军该领域。经过数年技术…

分布式光伏发电系统中的“四可”指的是什么?

在分布式光伏电站规模爆发式增长的今天&#xff0c;“看不见、管不住、调不动”的难题却成为行业痛点。如何让散布各处的光伏电站真正成为稳定高效的“绿色能量站”&#xff1f;2025年《分布式光伏发电开发建设管理办法》大型工商业项目&#xff08;≥6MW&#xff09;明确要求具…

健康管理系统新趋势:AI + 物联网如何重塑健康管理

一、传统健康管理的痛点与变革之必然长久以来&#xff0c;我们熟悉的健康管理方式存在明显局限&#xff1a;数据孤岛严重&#xff1a;体检报告在抽屉里沉睡&#xff0c;健身手环数据仅存于手机&#xff0c;不同医疗机构信息互不相通&#xff0c;个人健康信息犹如碎片散落各处。…

git基本操作【GIT-2】

git基本操作初始化一个仓库&#xff08;repository&#xff09;、开始或停止跟踪&#xff08;track&#xff09;文件、暂存&#xff08;stage&#xff09;或提交&#xff08;commit&#xff09;更改如何配置 Git 来忽略指定的文件和文件模式、如何迅速而简单地撤销错误操作、如…

【数据准备】——深度学习.全连接神经网络

目录 1 数据加载器 1.1 构建数据类 1.1.1 Dataset类 1.1.2 TensorDataset类 1.2 数据加载器 2 数据加载案例 2.1 加载csv数据集 2.2 加载图片数据集 2.3 加载官方数据集 2.4 pytorch实现线性回归 1 数据加载器 分数据集和加载器2个步骤~ 1.1 构建数据类 1.1.1 Dat…

健康生活,从细节开始

健康生活&#xff0c;从细节开始在当今快节奏的生活中&#xff0c;健康逐渐成为人们关注的焦点。拥有健康的身体&#xff0c;才能更好地享受生活、追求梦想。那么&#xff0c;如何才能拥有健康呢&#xff1f;这就需要我们从生活中的点滴细节入手&#xff0c;培养良好的生活习惯…

javax.servlet.http.HttpServletResponse;API导入报错解决方案

javax.servlet.http.HttpServletResponse;API导入报错解决方案与Postman上传下载文件验证 1. 主要错误&#xff1a;缺少 Servlet API 依赖 错误信息显示 javax.servlet.http 包不存在。这是因为你的项目缺少 Servlet API 依赖。 解决方案&#xff1a; 如果你使用的是 Maven&…

reids依赖删除,但springboot仍然需要redis参数才能启动

背景&#xff1a;项目需要删除redis。我删除完项目所有配置redis的依赖&#xff0c;启动报错。[2025-07-17 15:08:37:561] [DEBUG] [restartedMain] DEBUG _.s.w.s.H.Mappings - [detectHandlerMethods,295] [] - o.s.b.a.w.s.e.BasicErrorController:{ [/error]}: error(HttpS…

【前端】CSS类命名规范指南

在 CSS 中&#xff0c;合理且规范的 class 命名格式对项目的可维护性和协作效率至关重要。以下是主流的 class 命名规范和方法论&#xff1a;一、核心命名原则语义化命名&#xff1a;描述功能而非样式 ✅ .search-form&#xff08;描述功能&#xff09;❌ .red-text&#xff08…

C++网络编程 4.UDP套接字(socket)编程示例程序

以下是基于UDP协议的完整客户端和服务器代码。UDP与TCP的核心区别在于无连接特性&#xff0c;因此代码结构会更简单&#xff08;无需监听和接受连接&#xff09;。 UDP服务器代码&#xff08;udp_server.cpp&#xff09; #include <iostream> #include <sys/socket.h&…

King’s LIMS:实验室数字化转型的智能高效之选

实验室数字化转型不仅是技术升级&#xff0c;更是管理理念和工作方式的革新。LIMS系统作为这一转型的核心工具&#xff0c;能够将分散的实验数据转化为可分析、可复用的资产&#xff0c;为科研决策提供支持&#xff1b;规范检测流程&#xff0c;减少人为干预&#xff0c;确保结…

【力扣 中等 C】97. 交错字符串

目录 题目 解法一 题目 待添加 解法一 bool isInterleave(char* s1, char* s2, char* s3) {const int len1 strlen(s1);const int len2 strlen(s2);const int len3 strlen(s3);if (len1 len2 ! len3) {return false;}if (len1 < len2) {return isInterleave(s2, s1,…

Class9简洁实现

Class9简洁实现 %matplotlib inline import torch from torch import nn from d2l import torch as d2l# 初始化训练样本、测试样本、样本特征维度和批量大小 n_train,n_test,num_inputs,batch_size 20,100,200,5 # 设置真实权重和偏置 true_w,true_b torch.ones((num_inputs…

ELK日志分析,涉及logstash、elasticsearch、kibana等多方面应用,必看!

目录 ELK日志分析 1、下载lrzsc 2、下载源包 3、解压文件,下载elasticsearch、kibana、 logstash 4、配置elasticsearch 5、配种域名解析 6、配置kibana 7、配置logstash 8、进行测试 ELK日志分析 1、下载lrzsc [rootlocalhost ~]# hostnamectl set-hostname elk ##…

终极剖析HashMap:数据结构、哈希冲突与解决方案全解

文章目录 引言 一、HashMap底层数据结构&#xff1a;三维存储架构 1. 核心存储层&#xff08;硬件优化设计&#xff09; 2. 内存布局对比 二、哈希冲突的本质与数学原理 1. 冲突产生的必然性 2. 冲突概率公式 三、哈希冲突解决方案全景图 1. 链地址法&#xff08;Hash…

1.1.5 模块与包——AI教你学Django

1.1.5 模块与包&#xff08;Django 基础学习细节&#xff09; 模块和包是 Python 项目组织和代码复用的基础。Django 项目本质上就是由多个模块和包组成。理解和灵活运用模块与包机制&#xff0c;是写好大型项目的关键。 一、import、from-import、as 的用法 1. import 用于导入…

UE5 相机后处理材质与动态参数修改

一、完整实现流程1. 创建后处理材质材质设置&#xff1a;在材质编辑器中&#xff0c;将材质域(Material Domain)设为后处理(Post Process)设置混合位置(Blendable Location)&#xff08;如After Tonemapping&#xff09;创建标量/向量参数&#xff08;如Intensity, ColorTint&a…