当前位置: 首页 > news >正文

NebulaChat项目构建笔记

一、nlohmann::json 常见用法

1. 引入和别名

#include <nlohmann/json.hpp>
using json = nlohmann::json;

以后就可以直接写 json 这个类型。

2. 字符串 -> JSON 对象(parse)

在服务器里最常用的就是这句:

json req = json::parse(line);

含义:
把一段 JSON 格式的字符串 line 解析成一个 json 类型的对象。

典型场景:客户端发来的请求:

{"cmd":"echo","msg":"hi"}

服务器:

try {json req = json::parse(line);
} catch (const std::exception& e) {// 说明 line 不是合法 JSON,直接按“坏请求”处理
}

3. 访问字段:operator[]value()

假设有:

json req = json::parse(R"({"cmd":"echo","msg":"hi"})");

常用几种访问方式:

3.1 下标访问:operator[]
std::string cmd = req["cmd"];  // 如果没有 "cmd" 会抛异常

特点:

  • key 不存在、类型不匹配,可能抛异常

  • 适合你“确定一定有”的字段

3.2 安全访问:value(key, default)
std::string cmd = req.value("cmd", "");
std::string msg = req.value("msg", "");
int age        = req.value("age", 0);      // 不存在就用 0
bool close     = req.value("close", false);

规则:

  • "cmd" 存在 → 转成你给的默认值类型(这里是 std::string

  • "cmd" 不存在 → 直接返回第二个参数(默认值)

4. 构造 JSON 响应

现在在 processLine 里大概是这样写的:

json resp;
resp["ok"]   = true;
resp["data"] = msg;

也可以一口气用初始化列表:

json resp = {{"ok", true},{"data", msg}
};

数组也很简单:

json arr = json::array();
arr.push_back(1);
arr.push_back(2);
arr.push_back(3);
// 或者
json arr2 = {1, 2, 3};

5. JSON -> 字符串:dump()

std::string out = resp.dump();       // 一行压缩形式
std::string pretty = resp.dump(4);   // 带缩进的格式化输出(4 空格缩进)

在网络里用的一般是第一种:

return resp.dump() + "\n";

这样客户端拿到的是一行 JSON,方便你用按行协议拆包。

6. 错误处理(parse 出错)

现在的模板:

std::string Server::processLine(const std::string& line){json resp;try {json req = json::parse(line);// 正常业务逻辑……} catch (const std::exception& e) {resp["ok"]  = false;resp["err"] = "bad json";  // 或 resp["err"] = e.what();}return resp.dump() + "\n";
}

只要 parse 失败(不是合法 JSON),就会进 catch,就可以统一返回一个错误 JSON。

7. 小结一句话

最常用:

  1. json req = json::parse(line); 解析请求

  2. req.value("key", default) 读字段

  3. resp["key"] = value; resp.dump() 构造响应

二、std::map::find 返回值说明

以项目常见的这种为例:

std::map<int, std::unique_ptr<Connection>> conns_;

1. find 返回什么?

auto it = conns_.find(fd);

find 返回的是一个“迭代器”(iterator),可以理解成:

  • 指向 map 里某一个元素的“指针样的东西”

map 的元素类型是:

std::pair<const Key, T>

在例子里:

Key = int
T   = std::unique_ptr<Connection>*it 的类型是:std::pair<const int, std::unique_ptr<Connection>>

2. 怎么判断有没有找到?

auto it = conns_.find(fd);
if (it == conns_.end()) {// 没找到
} else {// 找到了
}

end() 表示“序列末尾的下一个位置”,也就是“找不到”的标准写法。

这是非常固定的模式:

auto it = m.find(key);
if (it != m.end()) {// 用 it
}

3. 找到后怎么用?

访问 keyvalue

int key_fd = it->first;                        // key
std::unique_ptr<Connection>& ptr = it->second; // valueConnection& conn = *ptr;                      // 引用
Connection* pconn = ptr.get();                // 原始指针

在项目中经常看到类似:

auto it = conns_.find(fd);
if (it == conns_.end()) return;
Connection& conn = *it->second;

理解为:

  1. it 是“指向某个键值对”的迭代器

  2. it->second 是那个 unique_ptr<Connection>

  3. *it->second 才是 Connection&

4. 结合你的场景:保存一个指针出来

写过:

std::unique_ptr<Connection>* p_holder = nullptr;
{std::lock_guard<std::mutex> lock(conns_mtx_);auto it = conns_.find(fd);if(it == conns_.end()) return;p_holder = &it->second;
}
Connection& conn = *p_holder->get();

这里 step by step:

  • conns_.find(fd) → 找到对应的迭代器 it

  • it->secondunique_ptr<Connection>

  • &it->second 就是“指向这个 unique_ptr 的指针”

  • p_holder->get() → 拿到 Connection*

  • *p_holder->get() → 变成 Connection&

这么写的目的是:
在解锁后还能继续访问 Connection 对象本身(但已经不动 map 的结构了)。

三、智能指针总结(unique_ptr / shared_ptr / weak_ptr)

1. 为什么要用智能指针

裸指针(T*)的问题:

  • 忘记 delete → 内存泄漏

  • new / delete 分散在各处,不好维护

  • 异常、提前 return 时很容易少写 delete

  • 多个地方持有同一指针时,很容易 double free

智能指针的核心思想:RAII(资源在对象生命周期结束时自动释放)。

2. std::unique_ptr:独占所有权

特点:

  • 一个对象只能有一个 unique_ptr 拥有它

  • 不能拷贝,只能移动

  • 适合“资源有唯一主人”的场景,比如一个连接 Connection 只由 Server

常用写法:

#include <memory>std::unique_ptr<Connection> conn = std::make_unique<Connection>();// 访问
conn->fd = 10;      // 像指针
(*conn).fd = 10;    // 像引用// 传递所有权(移动)
std::unique_ptr<Connection> other = std::move(conn);
// 此时 conn == nullptr,所有权给了 other// 释放资源
conn.reset();  // 显式释放
// 或者 conn 在作用域结束自动释放

容器里用:

std::map<int, std::unique_ptr<Connection>> conns;
conns.emplace(fd, std::make_unique<Connection>());

3. std::shared_ptr:共享所有权

特点:

  • 可以有多个 shared_ptr 指向同一个对象

  • 内部有引用计数,最后一个 shared_ptr 析构时才真正 delete 对象

  • use_count() 可以查看当前引用数

典型用法:

#include <memory>auto p1 = std::make_shared<User>("Alice");
{auto p2 = p1;  // 引用计数 +1
}                   // p2 析构,计数 -1
// 最后一个 shared_ptr 消失时才 delete User

缺点:

  • 有一点额外开销(引用计数管理)

  • 两个对象互相持有 shared_ptr 时会产生“循环引用”,导致内存泄漏(这时需要 weak_ptr)

一般用在:

  • 同一个对象需要被多个模块共享管理

  • 生命周期比较复杂,很难用一个“唯一主人”描述清楚的时候

4. std::weak_ptr:配合 shared_ptr 打破循环引用

特点:

  • 不增加引用计数

  • 只能观察 shared_ptr 管理的对象

  • 需要用 lock() 转成 shared_ptr 才能使用

例子:

std::shared_ptr<Node> a = std::make_shared<Node>();
std::shared_ptr<Node> b = std::make_shared<Node>();a->next = b;                 // shared_ptr
b->prev = a;                 // 如果也是 shared_ptr,会循环引用// 解决办法:prev 用 weak_ptr
struct Node {std::shared_ptr<Node> next;std::weak_ptr<Node> prev;  // 不增加引用计数
};

5. 结合你现在的 NebulaChat

你现在的关键结构:

std::map<int, std::unique_ptr<Connection>> conns_;

理解为:

  • conns_ 是“连接管理器”

  • 每个 Connection 只属于 Server 一个地方

  • map 中这个元素被 erase 或者整个 conns_ 析构时,对应连接自动 delete,不用你手动管理内存

结合 map.find:

auto it = conns_.find(fd);
if (it == conns_.end()) return;Connection& conn = *it->second;  // 从 unique_ptr 拿出真正的对象

这就是 map + unique_ptr + 智能指针三者一起工作的典型模式。

http://www.dtcms.com/a/609786.html

相关文章:

  • Socket:TCP/UDP通信详解
  • QT opencv实现高拍仪场景识别物体轮廓的案例详解
  • 广水住房和城乡建设部网站舟山网站建设开发
  • 深度学习打卡第R4周:LSTM-火灾温度预测
  • 最好的营销策划公司做seo网站优化价格
  • 通过Rust高性能异步网络服务器的实现看Rust语言的核心优势
  • 第36节:AI集成与3D场景中的智能NPC
  • 一个基于 LayUI + .NET 开源、轻量的医院住院管理系统
  • StarRocks 4.0:让 Apache Iceberg 数据真正 Query-Ready
  • 网站建设 自己的服务器爬虫python入门
  • android抽屉DrawerLayout在2025的沉浸式兼容
  • 美颜SDK性能优化实战:GPU加速与AI人脸美型的融合开发
  • AndroidStudio历史版本下载
  • Mac抹除重装卡在激活锁?两步快速解锁
  • Java语言是编译型还是解释型| 探究Java的运行机制与性能优化
  • 网站发语音功能如何做广州比较好的网站建设公司
  • 公司网站域名更改怎么做建设行业协会网站发展的建议
  • 【ZeroRange WebRTC】Kinesis Video Streams WebRTC Data Plane WebSocket API 深度解析
  • Docker核心概念、常用命令与实战指南
  • 交换机安全基线整改方式-华为S5700系列
  • Django 接口文档生成:Swagger 与 ReDoc 全面说明
  • Docker K8s VM 简介
  • FPGA教程系列-Vivado中读取ROM中数据
  • 网站怎么添加模块鹿寨建设局网站
  • 响应式外贸网站案例国外ps网站
  • springcloud feign远程调用请求参数对象变成linkhashmap处理
  • “耐达讯自动化Profibus总线光端机在化工变频泵控制系统中的应用与价值解析”
  • centos7.2安装cacti1.2.27
  • 将 vue3 项目打包后部署在 springboot 项目运行
  • 福州短视频seo网站建筑网站首页大图