【Redis】Redis C++使用

一、Redis的自定义网络协议

1.1 为什么可以编写出一个自定义的Redis客户端

       为什么我们可以编写出一个自定义的Redis客户端?因为Redis公开了自己的自定义协议。而对于一些其他软件的客户端,我们无法编写出一个自定义的Redis客户端,因为他们没有公开自己的自定义协议,但是我们可以通过一些抓包/逆向的手段猜测其应用层协议是什么样子的!!

       在网络通信过程中,会用到很多的“协议”,比如数据链路层的以太网协议,网络层的IP协议,传输层的TCP/UDP协议,应用层的协议更多!!虽然业界中有很多成熟的应用层协议:HTTP等~~但是此处更多的时候,都会“自定义”应用层协议,Redis此处的应用层协议就是自定义的协议(传输层还是基于TCP)~~

1.2 RESP protocol spec

RESP 协议的优点:

  1. 简单好实现
  2. 快速进行解析
  3. 肉眼可读

传输层这里基于TCP,但是又和TCP没有强耦合。

请求和响应之间的通信模型是一问一答的形式~~(客户端给服务器发送一个请求,服务器返回一个响应)

       我们需要根据上述规则进行字符串的编写,然后将这个字符串写入到 tcp socket 中。因此,redis客户端服务器要做的工作是:

  1. 按照上述格式,构造出字符串,往 socket 中写入
  2. 从 socket 中读取字符串,按照上述格式解析

二、安装 redis-plus-plus

       由于 redis-plus-plus 依赖了 hiredis(C语言版本的redis客户端库),所以我们要先下载 hiredis 库(可以直接使用包管理器来安装)。

       但是 redis-plus-plus 本体,只能编译安装了。如果是编译安装,使用 Ubuntu 比 使用 centos 简单很多。redis-plus-plus 本身功能比较简陋,比较原始,写起来也比较麻烦,实际开发中很少会手写 makefile。通过程序来生成 makefile,cmake就是一个生成 makefile 的工具。

先来看一下如何使用 CMake 编译程序:

  1. 创建一个 build 目录是习惯做法,并非是必须,目的是为了让编译生成的临时文件都放到build下面,避免污染源代码目录~
  2. cmake .. 这个操作是生成 makefile,此处的 .. 指向的是刚才 CMakeLists.txt 文件所在的目录~~
  3. make 进行编译
  4. make install 把刚才的库拷贝到系统目录

三、进行 ping 命令验证

#include <sw/redis++/redis++.h>

1.包含 redis-plus-plus 的头文件,如果我们不知道将这个库下载到哪里,我们可以使用 find 命令进行查找,命令如下所示:

find /XXX(路径) -name XXX 

2.创建了一个 Redis 对象

sw::redis::Redis redis("tcp://127.0.0.1:6379);

3.进行 ping 命令

4.使用 Makefile 编译程序

编译程序的时候,需要引入一些库文件(需要知道这些库文件的目录):

  1. redis++ 自己的静态库
  2. hiredis 的静态库
  3. 线程库

四、Redis 的通用命令的使用

一览整个Redis通用命令的使用:

get/set
exists
del
keys
expire/ttl
type

4.1 get/set

       在 C++ 中,std::string 是可以修改,既能读,也能写。但是 StringView 是只读的(不能修改),针对只读操作,做很多的优化工作,效率比 std::string 更高。在 C++17 标准库中,也提供了一个 std::string_view。这里是为了兼容 C++11,14,17,所以自己封装了一个类型。StringView 中的各个操作和string类似,只不过只是包含了一些只读方法

       在 Java 中的 String 就是类似于 StringView 只读的,Java 中要想使用可修改的字符串,要使用 StringBuilder 或者 StringBuffer。


对于 get 来说,有可能获取不到元素,这时应该返回什么类型呢??

       如果直接使用 std::string 来表示,不方便来表现这个 nil(无效值),如果使用 std::string* 来表示,是可以使用 nullptr 表示无效的,但是返回指针又涉及到内存归谁管~~

       因此,作者就自己封装了一个类型,此处的 Optional 可以表示 “非法值” 或者 “无效值”。在 Boost 中,很早就引入了 optional 类型,C++14版本中,就正式归纳标准库了。

在使用 Optional 类型的时候,有可能出现以下这个错误:

       此处不需要给这个 Optional 类型搞一个 << 重载,只需要把 Optional 里面包含的元素取出来即可~~

void test1(sw::redis::Redis& redis) {std::cout << "get 和 set 的使用" << std::endl;// 清空一下数据库, 避免之前残留的数据有干扰. redis.flushall();// 使用 set 设置 keyredis.set("key1", "111");redis.set("key2", "222");redis.set("key3", "333");// 使用 get 获取到 key 对应的 valueauto value1 = redis.get("key1");// optional 可以隐式转成 bool 类型, 可以直接在 if 中判定. 如果是无效元素, 就是返回 falseif (value1) {std::cout << "value1=" << value1.value() << std::endl;}auto value2 = redis.get("key2");if (value2) {std::cout << "value2=" << value2.value() << std::endl;}auto value3 = redis.get("key3");if (value3) {std::cout << "value3=" << value3.value() << std::endl;}auto value4 = redis.get("key4");if (value4) {std::cout << "value4=" << value4.value() << std::endl;}
}

4.2 exists

void test2(sw::redis::Redis& redis) {std::cout << "exists" << std::endl;redis.flushall();redis.set("key", "111");redis.set("key3", "111");auto ret = redis.exists("key");std::cout << ret << std::endl;ret = redis.exists("key2");std::cout << ret << std::endl;ret = redis.exists({"key", "key2", "key3"});std::cout << ret << std::endl;
}

       对于 exists 命令来说,我们可以一次性查看多个键值,我们可以使用初始化列表传参,代码如下:

redis.exists({"key", "key2", "key3"});

4.3 del

void test3(sw::redis::Redis& redis) {std::cout << "del" << std::endl;// 清除库非常必要的! redis.flushall();redis.set("key", "111");redis.set("key2", "111");// redis.del("key");auto ret = redis.del({"key", "key2", "key3"});std::cout << ret << std::endl;ret = redis.exists({"key", "key2"});std::cout << ret << std::endl;
}

4.4 keys

       keys 命令不可以随便使用,否则会影响其他命令的执行,因为 Redis 是单线程。keys 的返回值有多个。其返回值类型为:

       这是插入迭代器,插入迭代器的本质是一种“输出迭代器”,通常,一个输出迭代器主要表示一个位置。插入迭代器,则是“位置” + “动作”。 插入迭代器总共有三种类型:

       这里直接使用容器作为参数,keys内部直接操作容器,进行插入不是更好吗,为什么要通过迭代器呢??

       因为可以解耦合。 

void test4(sw::redis::Redis& redis) {std::cout << "keys" << std::endl;redis.flushall();redis.set("key", "111");redis.set("key2", "222");redis.set("key3", "333");redis.set("key4", "444");redis.set("key5", "555");redis.set("key6", "666");// keys 的第二个参数, 是一个 "插入迭代器". 咱们需要先准备好一个保存结果的容器. // 接下来再创建一个插入迭代器指向容器的位置. 就可以把 keys 获取到的结果依次通过刚才的插入迭代器插入到容器的指定位置中了. vector<string> result;auto it = std::back_inserter(result);redis.keys("*", it);printContainer(result);
}

4.5 expire/ttl

void test5(sw::redis::Redis& redis) {using namespace std::chrono_literals;std::cout << "expire and ttl" << std::endl;redis.flushall();redis.set("key", "111");// 10s => std::chrono::seconds(10)redis.expire("key", 10s);std::this_thread::sleep_for(3s);auto time = redis.ttl("key");std::cout << time << std::endl;
}

       在使用睡眠函数的时候,由于不同系统之间的单位不同,我们更好的选择是使用线程库中的睡眠函数:sleep_for。

       Linux的sleep和Windows的Sleep,都属于系统函数,是和系统相关的,同样的功能,在不同系统中可能是完全不同的函数~

4.6 type

void test6(sw::redis::Redis& redis) {std::cout << "type" << std::endl;redis.flushall();redis.set("key", "111");string result = redis.type("key");std::cout << "key: " << result << std::endl;redis.lpush("key2", "111");result = redis.type("key2");std::cout << "key2: " << result << std::endl;redis.hset("key3", "aaa", "111");result = redis.type("key3");std::cout << "key3: " << result << std::endl;redis.sadd("key4", "aaa");result = redis.type("key4");std::cout << "key4: " << result << std::endl;redis.zadd("key5", "吕布", 99);result = redis.type("key5");std::cout << "key5: " << result << std::endl;
}

五、string类型的操作

5.1 get/set

void test1(Redis& redis) {std::cout << "get 和 set" << std::endl;redis.flushall();redis.set("key", "111");auto value = redis.get("key");if (value) {std::cout << "value: " << value.value() << std::endl;}redis.set("key", "222");value = redis.get("key");if (value) {std::cout << "value: " << value.value() << std::endl;}
}

5.2 set带有超时时间

void test2(Redis& redis) {std::cout << "set 带有超时时间" << std::endl;redis.flushall();redis.set("key", "111", 10s);std::this_thread::sleep_for(3s);long long time = redis.ttl("key");std::cout << "time: " << time << std::endl;
}

5.3 set NX/XX

void test3(Redis& redis) {std::cout << "set NX 和 XX" << std::endl;redis.flushall();redis.set("key", "111");// set 的重载版本中, 没有单独提供 NX 和 XX 的版本, 必须搭配过期时间的版本来使用. redis.set("key", "222", 0s, sw::redis::UpdateType::EXIST);auto value = redis.get("key");if (value) {std::cout << "value: " << value.value() << std::endl;} else {std::cout << "key 不存在!" << std::endl;}
}

5.4 mset

void test4(Redis& redis) {std::cout << "mset" << std::endl;redis.flushall();// 第一种写法, 使用初始化列表描述多个键值对// redis.mset({ std::make_pair("key1", "111"), std::make_pair("key2", "222"), std::make_pair("key3", "333") });// 第二种写法, 可以把多个键值对提前组织到容器中. 以迭代器的形式告诉 msetvector<std::pair<string, string>> keys = {{"key1", "111"},{"key2", "222"},{"key3", "333"}};redis.mset(keys.begin(), keys.end());auto value = redis.get("key1");if (value) {std::cout << "value: " << value.value() << std::endl;}value = redis.get("key2");if (value) {std::cout << "value: " << value.value() << std::endl;}value = redis.get("key3");if (value) {std::cout << "value: " << value.value() << std::endl;}
}

5.5 mget

void test5(Redis& redis) {std::cout << "mget" << std::endl;redis.flushall();vector<std::pair<string, string>> keys = {{"key1", "111"},{"key2", "222"},{"key3", "333"}};redis.mset(keys.begin(), keys.end());vector<sw::redis::OptionalString> result;auto it = std::back_inserter(result);redis.mget({"key1", "key2", "key3", "key4"}, it);printContainerOptional(result);
}

5.6 getrange/setrange

void test6(Redis& redis) {std::cout << "getrange 和 setrange" << std::endl;redis.flushall();redis.set("key", "abcdefghijk");string result = redis.getrange("key", 2, 5);std::cout << "result: " << result << std::endl;redis.setrange("key", 2, "xyz");auto value = redis.get("key");std::cout << "value: " << value.value() << std::endl;
}

5.7 incr/decr

       incr 和 decr 得到的是 long long 类型(使用这个更多一些),get 得到的是 OptionalString 类型,需要手动转成数字~C++中把字符串转成数字,也有很多种方法。

void test7(Redis& redis) {std::cout << "incr 和 decr" << std::endl;redis.flushall();redis.set("key", "100");long long result = redis.incr("key");std::cout << "result: " << result << std::endl;auto value = redis.get("key");std::cout << "value: " << value.value() << std::endl;result = redis.decr("key");std::cout << "result: " << result << std::endl;value = redis.get("key");std::cout << "value: " << value.value() << std::endl;
}

六、list类型的操作

6.1 lpush/prange

void test1(Redis& redis) {std::cout << "lpush 和 lrange" << std::endl;redis.flushall();// 插入单个元素redis.lpush("key", "111");// 插入一组元素, 基于初始化列表redis.lpush("key", {"222", "333", "444"});// 插入一组元素, 基于迭代器vector<string> values = {"555", "666", "777"};redis.lpush("key", values.begin(), values.end());// lrange 获取到列表中的元素vector<string> results;auto it = std::back_inserter(results);redis.lrange("key", 0, -1, it);printContainer(results);
}

6.2 rpush

void test2(Redis& redis) {std::cout << "rpush" << std::endl;redis.flushall();// 插入单个元素redis.rpush("key", "111");// 插入多个元素, 基于初始化列表redis.rpush("key", {"222", "333", "444"});// 插入多个元素, 基于容器vector<string> values = {"555", "666", "777"};redis.rpush("key", values.begin(), values.end());// 使用 lrange 获取元素vector<string> results;auto it = std::back_inserter(results);redis.lrange("key", 0, -1, it);printContainer(results);
}

6.3 lpop/rpop

void test3(Redis& redis) {std::cout << "lpop 和 rpop" << std::endl;redis.flushall();// 构造一个 listredis.rpush("key", {"1", "2", "3", "4"});auto result = redis.lpop("key");if (result) {std::cout << "lpop: " << result.value() << std::endl;}result = redis.rpop("key");if (result) {std::cout << "rpop: " << result.value() << std::endl;}
}

6.4 blpop

void test4(Redis& redis) {using namespace std::chrono_literals;std::cout << "blpop" << std::endl;redis.flushall();auto result = redis.blpop({"key", "key2", "key3"}, 10s);if (result) {std::cout << "key:" << result->first << std::endl;std::cout << "elem:" << result->second << std::endl;} else {std::cout << "result 无效!" << std::endl;}
}

6.5 llen

void test5(Redis& redis) {std::cout << "llen" << std::endl;redis.flushall();redis.lpush("key", {"111", "222", "333", "444"});long long len = redis.llen("key");std::cout << "len: " << len << std::endl;
}

对于 redis-plus-plus 这个库来说,接口风格的设计是非常统一的。

  • 当一个函数参数需要传递多个值的时候,往往都是支持初始化列表或者一对迭代器的方式来进行实现的
  • 当一个函数的返回值需要表示多个数据的时候,也往往会借助插入迭代器来实现往一个容器中添加元素的效果
  • 当某些场景涉及到无效值的时候,往往会搭配 std::optional 来使用

七、set类型的操作

7.1 sadd/smembers

void test1(Redis& redis) {std::cout << "sadd 和 smembers" << std::endl;redis.flushall();// 一次添加一个元素redis.sadd("key", "111");// 一次添加多个元素(使用初始化列表)redis.sadd("key", {"222", "333", "444"});// 一次添加多个元素(使用迭代器)set<string> elems = {"555", "666", "777"};redis.sadd("key", elems.begin(), elems.end());// 获取到上述元素// 此处用来保存 smembers 的结果, 使用 set 可能更合适. vector<string> result;// auto it = std::back_inserter(result);// 由于此处 set 里的元素顺序是固定的. 指定一个 result.end() 或者 result.begin() 或者其他位置的迭代器, 都无所谓~~auto it = std::inserter(result, result.end());redis.smembers("key", it);printContainer(result);
}

7.2 sismember

void test2(Redis& redis) {std::cout << "sismember" << std::endl;redis.flushall();redis.sadd("key", {"111", "222", "333", "444"});bool result = redis.sismember("key", "555");std::cout << "result: " << result << std::endl;
}

7.3 scrad

void test3(Redis& redis) {std::cout << "scard" << std::endl;redis.flushall();redis.sadd("key", {"111", "222", "333"});long long result = redis.scard("key");std::cout << "result: " << result << std::endl;
}

7.4 spop

void test4(Redis& redis) {std::cout << "spop" << std::endl;redis.flushall();redis.sadd("key", {"111", "222", "333", "444"});auto result = redis.spop("key");if (result) {std::cout << "result: " << result.value() << std::endl;} else {std::cout << "result 无效!" << std::endl;}
}

7.5 sinter

void test5(Redis& redis) {std::cout << "sinter" << std::endl;redis.flushall();redis.sadd("key1", {"111", "222", "333"});redis.sadd("key2", {"111", "222", "444"});set<string> result;auto it = std::inserter(result, result.end());redis.sinter({"key1", "key2"}, it);printContainer(result);
}

7.6 sinterstore

void test6(Redis& redis) {std::cout << "sinterstore" << std::endl;redis.flushall();redis.sadd("key1", {"111", "222", "333"});redis.sadd("key2", {"111", "222", "444"});long long len = redis.sinterstore("key3", {"key1", "key2"});std::cout << "len: " << len << std::endl;set<string> result;auto it = std::inserter(result, result.end());redis.smembers("key3", it);printContainer(result);
}

八、hash类型的操作

8.1 hset/hget

void test1(Redis& redis) {std::cout << "hset 和 hget" << std::endl;redis.flushall();redis.hset("key", "f1", "111");redis.hset("key", std::make_pair("f2", "222"));// hset 能够一次性插入多个 field-value 对!!redis.hset("key", {std::make_pair("f3", "333"),std::make_pair("f4", "444")});vector<std::pair<string, string>> fields = {std::make_pair("f5", "555"),std::make_pair("f6", "666")};redis.hset("key", fields.begin(), fields.end());auto result = redis.hget("key", "f3");if (result) {std::cout << "result: " << result.value() << std::endl;} else {std::cout << "result 无效!" << std::endl;}
}

8.2 hexists

void test2(Redis& redis) {std::cout << "hexits" << std::endl;redis.flushall();redis.hset("key", "f1", "111");redis.hset("key", "f2", "222");redis.hset("key", "f3", "333");bool result = redis.hexists("key", "f4");std::cout << "result: " << result << std::endl;
}

8.3 hdel

void test3(Redis& redis) {std::cout << "hdel" << std::endl;redis.flushall();redis.hset("key", "f1", "111");redis.hset("key", "f2", "222");redis.hset("key", "f3", "333");long long result = redis.hdel("key", "f1");std::cout << "result: " << result << std::endl;result = redis.hdel("key", {"f2", "f3"});std::cout << "result: " << result << std::endl;long long len = redis.hlen("key");std::cout << "len: " << len << std::endl;
}

8.4 hkeys/hvals

void test4(Redis& redis) {std::cout << "hkeys 和 hvals" << std::endl;redis.flushall();redis.hset("key", "f1", "111");redis.hset("key", "f2", "222");redis.hset("key", "f3", "333");vector<string> fields;auto itFields = std::back_inserter(fields);redis.hkeys("key", itFields);printContainer(fields);vector<string> values;auto itValues = std::back_inserter(values);redis.hvals("key", itValues);printContainer(values);
}

8.5 hmget/hmset

void test5(Redis& redis) {std::cout << "hmget 和 hmset" << std::endl;redis.flushall();redis.hmset("key", {std::make_pair("f1", "111"),std::make_pair("f2", "222"),std::make_pair("f3", "333")});vector<std::pair<string, string>> pairs = {std::make_pair("f4", "444"),std::make_pair("f5", "555"),std::make_pair("f6", "666")};redis.hmset("key", pairs.begin(), pairs.end());vector<string> values;auto it = std::back_inserter(values);redis.hmget("key", {"f1", "f2", "f3"}, it);printContainer(values);
}

九、zset类型的操作

void test1(Redis& redis) {std::cout << "zadd 和 zrange" << std::endl;redis.flushall();redis.zadd("key", "吕布", 99);redis.zadd("key", {std::make_pair("赵云", 98),std::make_pair("典韦", 97)});vector<std::pair<string, double>> members = {std::make_pair("关羽", 95),std::make_pair("张飞", 93)};redis.zadd("key", members.begin(), members.end());// zrange 支持两种主要的风格:// 1. 只查询 member, 不带 score// 2. 查询 member 同时带 score// 关键就是看插入迭代器指向的容器的类型. // 指向的容器只是包含一个 string, 就是只查询 member// 指向的容器包含的是一个 pair, 里面有 string 和 double, 就是查询 member 同时带有 scorevector<string> memberResults;auto it = std::back_inserter(memberResults);redis.zrange("key", 0, -1, it);printContainer(memberResults);vector<std::pair<string, double>> membersWithScore;auto it2 = std::back_inserter(membersWithScore);redis.zrange("key", 0, -1, it2);printContainerPair(membersWithScore);
}void test2(Redis& redis) {std::cout << "zcard" << std::endl;redis.flushall();redis.zadd("key", "zhangsan", 90);redis.zadd("key", "lisi", 91);redis.zadd("key", "wangwu", 92);redis.zadd("key", "zhaoliu", 93);long long result = redis.zcard("key");std::cout << "result: " << result << std::endl;
}void test3(Redis& redis) {std::cout << "zrem" << std::endl;redis.flushall();redis.zadd("key", "zhangsan", 90);redis.zadd("key", "lisi", 91);redis.zadd("key", "wangwu", 92);redis.zadd("key", "zhaoliu", 93);redis.zrem("key", "zhangsan");long long result = redis.zcard("key");std::cout << "result: " << result << std::endl;
}void test4(Redis& redis) {std::cout << "zscore" << std::endl;redis.flushall();redis.zadd("key", "zhangsan", 90);redis.zadd("key", "lisi", 91);redis.zadd("key", "wangwu", 92);redis.zadd("key", "zhaoliu", 93);auto score = redis.zscore("key", "zhangsan");if (score) {std::cout << "score: " << score.value() << std::endl;} else {std::cout << "score 无效" << std::endl;}
}void test5(Redis& redis) {std::cout << "zrank" << std::endl;redis.flushall();redis.zadd("key", "zhangsan", 90);redis.zadd("key", "lisi", 91);redis.zadd("key", "wangwu", 92);redis.zadd("key", "zhaoliu", 93);auto rank = redis.zrank("key", "zhaoliu");if (rank) {std::cout << "rank: " << rank.value() << std::endl;} else {std::cout << "rank 无效" << std::endl;}
}

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

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

相关文章

【软考系统架构设计师】软件工程知识点

1、 软件开发生命周期 软件定义时期&#xff1a;包括可行性研究和详细需求分析过程&#xff0c;任务是确定软件开发工程必须完成的总目标&#xff0c;具体分为问题定义、可行性研究、需求分析等 软件开发时期&#xff1a;软件的设计与实现&#xff0c;分为概要设计、详细设计、…

DeepSeek 与开源:肥沃土壤孕育 AI 硕果

当国产 AI DeepSeek 以其低成本推理和多模态能力在全球范围内引起轰动时&#xff0c;人们惊叹于中国技术的迅猛发展&#xff0c;却很少有人深究这一成就背后的根基。答案其实早已写在中国开源生态二十多年的发展历程中。 从倪光南院士提出“以开源打破技术垄断”的理念&#x…

职坐标:智慧城市未来发展的核心驱动力

内容概要 智慧城市的演进正以颠覆性创新重构人类生存空间&#xff0c;其发展脉络由物联网、人工智能与云计算三大技术支柱交织而成。这些技术不仅推动城市治理从经验决策转向数据驱动模式&#xff0c;更通过实时感知与智能分析&#xff0c;实现交通、能源等领域的精准调控。以…

vue复习46~90

1.小兔鲜 所有都折叠 ctrl k,ctrl0 所有都展开 ctrl k,ctrlj当前结构渲染5次 <BaseBrandItem v-for"item in 5" :key"item"><BaseBrandItem>2.scoped样式冲突 结构&#xff1a;只能有一个根元素样式&#xff1a;全局样式(默认)&#xff1…

PHP 用 workman 即时通讯,做个简版QQ

1. workman是什么 &#xff0c;一般应用在那些地方 workerman是一个高性能的PHP socket 服务器框架&#xff0c;workerman基于PHP多进程以及libevent事件轮询库&#xff0c;PHP开发者只要实现一两个接口&#xff0c;便可以开发出自己的网络应用&#xff0c;例如Rpc服务、聊天室…

【WORD】批量将doc转为docx

具体步骤进行&#xff1a; 打开Word文档&#xff0c;按下AltF11快捷键&#xff0c;打开VBA编辑器。在VBA编辑器中&#xff0c;左侧的“项目资源管理器”窗口会显示当前打开的Word文档相关项目。找到您要添加代码的文档项目&#xff08;通常以文档名称命名&#xff09;&#xf…

【免费】【实测有用】5KPlayer Windows 电脑作为 MacBook 无线扩展屏

总结&#xff1a;使用 5KPlayer 将 Windows 电脑作为 MacBook 无线扩展屏 准备工作 设备要求&#xff1a; MacBook 和 Windows 电脑需连接到同一 Wi-Fi 网络。【这里有雷&#xff1a;eduroam不会成功&#xff0c;家里的WIFI成功了&#xff0c;需要确认校园网是否可行。】确保…

华为华三模拟器解决兼容问题Win11 24H2 现在使用ENSP的问题解决了

一、Win11 24H2 现在使用ENSP的问题解决了 这个Win11 的 24H2不能使用ENSP的问题已经困扰我们很久了,在之前的文章中,我们也有说明这个问题 之前ENSP肯定启动会报错40 当时还建议大家先不要更新到win11的24H2版本,现在终于迎来了更新,不用再担心了,包括早就升级了24H2版…

嵌入式WebRTC轻量化SDK压缩至500K-800K ,为嵌入式设备节省Flash资源

一、SDK轻量化的核心技术实现 1、WebRTC库裁剪与模块化设计 EasyRTC针对嵌入式设备的资源限制&#xff0c;对原生WebRTC库进行深度裁剪&#xff0c;仅保留核心通信功能&#xff08;如信令管理、编解码、网络传输等&#xff09;&#xff0c;移除冗余组件&#xff08;如部分调试…

Maya云渲染工作流,提升渲染速度

在三维动画与影视特效领域&#xff0c;Autodesk Maya作为行业标杆工具&#xff0c;承载着从角色建模到复杂特效渲染的全流程创作。然而&#xff0c;本地硬件性能不足、渲染周期漫长、跨团队协作效率低等痛点始终困扰着创作者。渲染101云渲染以弹性算力资源、智能化工作流与全方…

git怎么使远程分支回退到指定的节点处

git使远程分支回退到指定的节点 引言场景描述步骤 引言 最近提交代码的时候&#xff0c;总将分支合并错&#xff0c;原本要合到A分支&#xff0c;结果合并到了B分支&#xff0c;这样就导致b分支需要回退到我没有合并之前的节点处。 本文记录下怎么将远程分支回退到指定的节点。…

全网通emotn ui桌面免费吗?如何开机自启动

在智能设备的使用中&#xff0c;一款优秀的桌面系统能带来截然不同的体验。全网通Emotn UI桌面便是其中的佼佼者&#xff0c;它以完全免费的特性与卓越性能&#xff0c;成为众多用户的心头好。 其简洁美观的界面设计如同为设备换上"清新外衣"&#xff0c;常用功能一…

通过微信APPID获取小程序名称

进入微信公众平台&#xff0c;登录自己的小程序后台管理端&#xff0c;在“账号设置”中找到“第三方设置” 在“第三方设置”页面中&#xff0c;将页面拉到最下面&#xff0c;即可通过appid获取到这个小程序的名称信息

2025年第十六届蓝桥杯省赛JavaB组真题回顾

第16届蓝桥杯省赛已经结束了&#xff0c;第一次参加也是坐牢了4个小时&#xff0c;现在还是来总结一下吧&#xff08;先声明以下的解法&#xff0c;大家可以当作一种思路来看&#xff0c;解法不一定是正解&#xff0c;只是给大家提供一种能够正常想到的思路吧&#xff09; 试题…

深入剖析 Axios 的 POST 请求:何时使用 qs 处理数据

在前端开发中&#xff0c;Axios 是一个广泛使用的用于发送 HTTP 请求的库&#xff0c;特别是在处理 POST 请求时&#xff0c;数据的处理方式会直接影响到请求能否正确被后端接收和处理。其中&#xff0c;使用 qs 库对数据进行处理是一个常见的操作点&#xff0c;本文将深入探讨…

通过websocket给服务端发送订单催单提醒消息

controller层 GetMapping("/reminder/{id}")public Result Remainder(PathVariable("id") Long id){orderService.remainder(id);return Result.success();} 实现类 Overridepublic void remainder(Long id) {Orders ordersDB orderMapper.getById(id);…

ros_note02

note02 节点 ROS2中每一个节点只负责一个单独的模块化功能 如&#xff1a;一个节点负责控制车轮转动&#xff0c;一个节点负责从激光雷达获取数据&#xff0c;一个节点负责定位 通信方式&#xff1a; 话题&#xff1a;topic服务&#xff1a;services动作&#xff1a;Actio…

使用治疗前MR图像预测脑膜瘤Ki-67的多模态深度学习模型

大家好&#xff0c;我是带我去滑雪&#xff01; 脑膜瘤是一种常见的脑部肿瘤&#xff0c;Ki-67作为肿瘤细胞增殖的标志物&#xff0c;对于评估肿瘤的生物学行为、预后以及治疗方案的制定具有至关重要的作用。然而&#xff0c;传统的Ki-67检测依赖于组织学切片和免疫组化染色等方…

【大模型系列篇】深度研究智能体技术演进:从DeepResearch到DeepResearcher,如何重构AI研究范式

DeepResearch 的概念与功能最早由 Google 在 Gemini 系列产品中推出&#xff0c;用于自动化生成结构化研究报告&#xff0c;近期底层依赖模型Gemini升级到了2.5 Pro。而我们常规认知的DeepResearch是由OpenAI推出的一款由优化版的 o3 模型驱动专注于深度研究和分析的AI智能体产…

PostgreSQL 如何查看端口号

PostgreSQL 如何查看端口号 PostgreSQL大多数情况下&#xff0c;默认端口是5432&#xff0c;但某些环境中可能配置为其它端口。 一 基本查询方法 1.1 psql 命令行工具查询 -- 方法1&#xff1a;查看当前连接信息&#xff08;包含端口&#xff09; \conninfo-- 方法2&#x…