Linux下,使用C语言开发一个简单的GATT Server,我的Ubuntu上跑的BlueZ版本是5.79,使用的GLib库版本是2.85.2,这里我直接使用GLib里的D‑Bus来实现与BlueZ通信。BlueZ 官方推荐通过 D-Bus 进行通信和控制,如果是要使用原始的hci接口来实现,可以参考BlueZ源码目录下的
tools/btgatt-server.c 文件。

我这里是使用的usb蓝牙,支持经典蓝牙和BLE(低功耗蓝牙)模式,我直接通过bluez工具来开启BLE功能和BLE广播,常用的一些控制命令如下,做下记录:

### 经典蓝牙 ###bluetoothctl                        # 运行 bluetoothctl
power on                            # 打开蓝牙适配器(开启蓝牙功能)
agent on                            # 启用配对助手(允许输入或确认配对码等操作)
scan on                             # 开始扫描附近的蓝牙设备
scan off                            # 停止扫描
pair 94:87:E0:8C:D5:94              # 与指定MAC地址的设备进行配对
connect 94:87:E0:8C:D5:94           # 连接到已配对的指定设备
trust 94:87:E0:8C:D5:94             # 设置信任该设备(以后自动连接无需确认)sudo systemctl restart bluetooth    # 重启蓝牙服务
remove 94:87:E0:8C:D5:94            # 清除旧配对信息### BLE ###通过 bluetoothctl 控制bluetoothctl power on          # 开启蓝牙适配器
bluetoothctl discoverable on   # 让本设备对其他设备可见
bluetoothctl pairable on       # 允许其他设备与本设备配对
bluetoothctl advertise on      # 开启BLE广播(让本设备可以被BLE客户端发现)通过 btmgmt 控制sudo btmgmt power on         # 开启蓝牙
sudo btmgmt le on            # 开启BLE(低功耗蓝牙)功能
sudo btmgmt advertising on   # 开启BLE广播
sudo btmgmt connectable on   # 允许连接
sudo btmgmt info             # 查看适配器支持和当前状态

源码如下 gatt-server.c:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <locale.h>#include "gio/gio.h"// ================== 蓝牙 GATT 应用的一些常量定义 ==================
#define BLUEZ_BUS_NAME "org.bluez"            // BlueZ 的 DBus 服务名
#define ADAPTER_PATH "/org/bluez/hci0"        // 默认蓝牙适配器路径(hci0)
#define APP_PATH "/org/example"               // 应用根路径
#define SERVICE_PATH APP_PATH "/service0"     // 自定义 GATT 服务路径
#define CHAR_READ_PATH SERVICE_PATH "/char0"  // 自定义 "只读" 特征路径
#define CHAR_WRITE_PATH SERVICE_PATH "/char1" // 自定义 "可写" 特征路径// UUID 定义(随便写的示例 UUID)
static const gchar *SERVICE_UUID = "12345678-1234-5678-1234-56789abcdef0";
static const gchar *CHAR_READ_UUID = "12345678-1234-5678-1234-56789abcdef1";
static const gchar *CHAR_WRITE_UUID = "12345678-1234-5678-1234-56789abcdef2";// 特征的访问权限标志
static const gchar *flags_read[] = {"read", NULL};
static const gchar *flags_write[] = {"write", NULL};// ================== Introspection XML 定义 ==================
// 这些 XML 定义了 DBus 接口(ObjectManager、Service、Characteristic)// ObjectManager 接口(用于告诉 BlueZ 我们有哪些对象)
static const gchar *object_manager_xml ="<node>""  <interface name='org.freedesktop.DBus.ObjectManager'>""    <method name='GetManagedObjects'>""      <arg name='objects' type='a{oa{sa{sv}}}' direction='out'/>""    </method>""  </interface>""</node>";// GATT Service 接口
static const gchar *service_xml ="<node>""  <interface name='org.bluez.GattService1'>""    <property name='UUID' type='s' access='read'/>""    <property name='Primary' type='b' access='read'/>""  </interface>""</node>";// GATT Characteristic 接口(包含读写方法)
static const gchar *char_xml ="<node>""  <interface name='org.bluez.GattCharacteristic1'>""    <property name='UUID' type='s' access='read'/>""    <property name='Service' type='o' access='read'/>""    <property name='Flags' type='as' access='read'/>""    <method name='ReadValue'>""      <arg name='options' type='a{sv}' direction='in'/>""      <arg name='value' type='ay' direction='out'/>""    </method>""    <method name='WriteValue'>""      <arg name='value' type='ay' direction='in'/>""      <arg name='options' type='a{sv}' direction='in'/>""    </method>""  </interface>""</node>";// ================== 全局只读特征的内容 ==================
#define READ_VALUE_MAX_LEN 256
static guint8 read_value_buffer[READ_VALUE_MAX_LEN] = "Hello"; // 初始内容
static gsize read_value_len = 5;                               // 当前长度
static pthread_mutex_t read_value_mutex = PTHREAD_MUTEX_INITIALIZER;// 一个线程,用来从终端输入更新 "只读特征" 的内容
void *stdin_input_thread(void *arg)
{char input[READ_VALUE_MAX_LEN];while (1){printf("请输入新的只读特征内容(按回车确认):\n");if (fgets(input, sizeof(input), stdin)){size_t len = strlen(input);if (len > 0 && input[len - 1] == '\n')input[len - 1] = '\0'; // 去掉换行pthread_mutex_lock(&read_value_mutex);read_value_len = strlen(input);if (read_value_len > READ_VALUE_MAX_LEN)read_value_len = READ_VALUE_MAX_LEN;memcpy(read_value_buffer, input, read_value_len);pthread_mutex_unlock(&read_value_mutex);printf("已更新只读特征内容:%s\n", input);}}return NULL;
}// ================== DBus 方法调用处理 ==================
static void on_method_call(GDBusConnection *conn,const gchar *sender,const gchar *object_path,const gchar *interface_name,const gchar *method_name,GVariant *parameters,GDBusMethodInvocation *invocation,gpointer user_data)
{// 打印调试信息g_print("\n*** METHOD CALL RECEIVED ***\n");g_print("Sender: %s\n", sender);g_print("Object Path: %s\n", object_path);g_print("Interface: %s\n", interface_name);g_print("Method: %s\n", method_name);g_print("*****************************\n");// ============= 处理 GetManagedObjects(告诉 BlueZ 我们有哪些对象)=============if (g_strcmp0(interface_name, "org.freedesktop.DBus.ObjectManager") == 0 &&g_strcmp0(method_name, "GetManagedObjects") == 0){g_print("*** HANDLING GetManagedObjects ***\n");GVariantBuilder objects_builder;g_variant_builder_init(&objects_builder, G_VARIANT_TYPE("a{oa{sa{sv}}}"));// 添加 GATT Service 对象{GVariantBuilder service_props;g_variant_builder_init(&service_props, G_VARIANT_TYPE("a{sv}"));g_variant_builder_add(&service_props, "{sv}", "UUID", g_variant_new_string(SERVICE_UUID));g_variant_builder_add(&service_props, "{sv}", "Primary", g_variant_new_boolean(TRUE));GVariantBuilder service_interfaces;g_variant_builder_init(&service_interfaces, G_VARIANT_TYPE("a{sa{sv}}"));g_variant_builder_add(&service_interfaces, "{s@a{sv}}", "org.bluez.GattService1",g_variant_builder_end(&service_props));g_variant_builder_add(&objects_builder, "{o@a{sa{sv}}}", SERVICE_PATH,g_variant_builder_end(&service_interfaces));}// 添加 "只读特征" 对象{GVariantBuilder char_read_props;g_variant_builder_init(&char_read_props, G_VARIANT_TYPE("a{sv}"));g_variant_builder_add(&char_read_props, "{sv}", "UUID", g_variant_new_string(CHAR_READ_UUID));g_variant_builder_add(&char_read_props, "{sv}", "Service", g_variant_new_object_path(SERVICE_PATH));g_variant_builder_add(&char_read_props, "{sv}", "Flags", g_variant_new_strv(flags_read, -1));GVariantBuilder char_read_interfaces;g_variant_builder_init(&char_read_interfaces, G_VARIANT_TYPE("a{sa{sv}}"));g_variant_builder_add(&char_read_interfaces, "{s@a{sv}}", "org.bluez.GattCharacteristic1",g_variant_builder_end(&char_read_props));g_variant_builder_add(&objects_builder, "{o@a{sa{sv}}}", CHAR_READ_PATH,g_variant_builder_end(&char_read_interfaces));}// 添加 "可写特征" 对象{GVariantBuilder char_write_props;g_variant_builder_init(&char_write_props, G_VARIANT_TYPE("a{sv}"));g_variant_builder_add(&char_write_props, "{sv}", "UUID", g_variant_new_string(CHAR_WRITE_UUID));g_variant_builder_add(&char_write_props, "{sv}", "Service", g_variant_new_object_path(SERVICE_PATH));g_variant_builder_add(&char_write_props, "{sv}", "Flags", g_variant_new_strv(flags_write, -1));GVariantBuilder char_write_interfaces;g_variant_builder_init(&char_write_interfaces, G_VARIANT_TYPE("a{sa{sv}}"));g_variant_builder_add(&char_write_interfaces, "{s@a{sv}}", "org.bluez.GattCharacteristic1",g_variant_builder_end(&char_write_props));g_variant_builder_add(&objects_builder, "{o@a{sa{sv}}}", CHAR_WRITE_PATH,g_variant_builder_end(&char_write_interfaces));}// 返回对象列表GVariant *result = g_variant_new("(@a{oa{sa{sv}}})", g_variant_builder_end(&objects_builder));g_dbus_method_invocation_return_value(invocation, result);g_print("*** GetManagedObjects completed ***\n");return;}// ============= 处理 ReadValue 请求(读取只读特征的值)=============if (g_strcmp0(interface_name, "org.bluez.GattCharacteristic1") == 0 &&g_strcmp0(method_name, "ReadValue") == 0){g_print("ReadValue called on %s\n", object_path);GVariant *value;pthread_mutex_lock(&read_value_mutex);value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,read_value_buffer, read_value_len, sizeof(guint8));pthread_mutex_unlock(&read_value_mutex);g_dbus_method_invocation_return_value(invocation, g_variant_new("(@ay)", value));return;}// ============= 处理 WriteValue 请求(写入数据到可写特征)=============if (g_strcmp0(interface_name, "org.bluez.GattCharacteristic1") == 0 &&g_strcmp0(method_name, "WriteValue") == 0){GVariant *value_variant;GVariant *options_variant;g_variant_get(parameters, "(@ay@a{sv})", &value_variant, &options_variant);gsize len;const guint8 *data = g_variant_get_fixed_array(value_variant, &len, sizeof(guint8));g_print("WriteValue called on %s, data: ", object_path);for (gsize i = 0; i < len; i++){g_print("%02X ", data[i]);}g_print(" string: %.*s\n", (int)len, (const char *)data);g_variant_unref(value_variant);g_variant_unref(options_variant);g_dbus_method_invocation_return_value(invocation, NULL);return;}// 未知方法g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,G_DBUS_ERROR_UNKNOWN_METHOD,"Unknown method: %s", method_name);
}// ================== 属性读取处理 ==================
static GVariant *on_get_property(GDBusConnection *conn,const gchar *sender,const gchar *object_path,const gchar *interface_name,const gchar *property_name,GError **error,gpointer user_data)
{g_print("Get property: %s.%s on %s\n", interface_name, property_name, object_path);// 处理 Service 属性if (g_strcmp0(interface_name, "org.bluez.GattService1") == 0){if (g_strcmp0(property_name, "UUID") == 0)return g_variant_new_string(SERVICE_UUID);if (g_strcmp0(property_name, "Primary") == 0)return g_variant_new_boolean(TRUE);}// 处理 Characteristic 属性if (g_strcmp0(interface_name, "org.bluez.GattCharacteristic1") == 0){if (g_strcmp0(object_path, CHAR_READ_PATH) == 0){if (g_strcmp0(property_name, "UUID") == 0)return g_variant_new_string(CHAR_READ_UUID);if (g_strcmp0(property_name, "Service") == 0)return g_variant_new_object_path(SERVICE_PATH);if (g_strcmp0(property_name, "Flags") == 0)return g_variant_new_strv(flags_read, -1);}if (g_strcmp0(object_path, CHAR_WRITE_PATH) == 0){if (g_strcmp0(property_name, "UUID") == 0)return g_variant_new_string(CHAR_WRITE_UUID);if (g_strcmp0(property_name, "Service") == 0)return g_variant_new_object_path(SERVICE_PATH);if (g_strcmp0(property_name, "Flags") == 0)return g_variant_new_strv(flags_write, -1);}}// 未知属性g_set_error(error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY,"Unknown property: %s", property_name);return NULL;
}// ================== 注册回调 ==================
static GMainLoop *main_loop = NULL;
static gboolean registration_success = FALSE;static void on_register_application_done(GObject *source_object,GAsyncResult *res,gpointer user_data)
{GError *error = NULL;GVariant *result = g_dbus_proxy_call_finish(G_DBUS_PROXY(source_object), res, &error);if (result){g_print("SUCCESS: GATT Application registered!\n");g_variant_unref(result);registration_success = TRUE;}else{g_printerr("RegisterApplication failed: %s\n", error->message);g_error_free(error);registration_success = FALSE;}
}// ================== 主函数 ==================
int main(int argc, char *argv[])
{GError *error = NULL;// 设置本地化环境,不然 g_print 输出中文时乱码setlocale(LC_ALL, "");g_print("Connecting to system bus...\n");// 连接到系统总线GDBusConnection *connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);if (!connection){g_printerr("Failed to connect to system bus: %s\n", error->message);return 1;}g_print("Parsing introspection XML...\n");GDBusNodeInfo *object_manager_info = g_dbus_node_info_new_for_xml(object_manager_xml, NULL);GDBusNodeInfo *service_info = g_dbus_node_info_new_for_xml(service_xml, NULL);GDBusNodeInfo *char_info = g_dbus_node_info_new_for_xml(char_xml, NULL);// 定义虚表(方法回调 + 属性回调)static const GDBusInterfaceVTable interface_vtable = {.method_call = on_method_call,.get_property = on_get_property,.set_property = NULL};// 注册 ObjectManagerg_print("Registering ObjectManager at %s\n", APP_PATH);guint reg_id1 = g_dbus_connection_register_object(connection, APP_PATH,object_manager_info->interfaces[0],&interface_vtable, NULL, NULL, &error);if (!reg_id1){g_printerr("Failed to register ObjectManager: %s\n", error->message);return 1;}// 注册 Serviceg_print("Registering Service at %s\n", SERVICE_PATH);guint reg_id2 = g_dbus_connection_register_object(connection, SERVICE_PATH,service_info->interfaces[0],&interface_vtable, NULL, NULL, &error);if (!reg_id2){g_printerr("Failed to register Service: %s\n", error->message);return 1;}// 注册 Read Characteristicg_print("Registering Read Characteristic at %s\n", CHAR_READ_PATH);guint reg_id3 = g_dbus_connection_register_object(connection, CHAR_READ_PATH,char_info->interfaces[0],&interface_vtable, NULL, NULL, &error);if (!reg_id3){g_printerr("Failed to register Read Characteristic: %s\n", error->message);return 1;}// 注册 Write Characteristicg_print("Registering Write Characteristic at %s\n", CHAR_WRITE_PATH);guint reg_id4 = g_dbus_connection_register_object(connection, CHAR_WRITE_PATH,char_info->interfaces[0],&interface_vtable, NULL, NULL, &error);if (!reg_id4){g_printerr("Failed to register Write Characteristic: %s\n", error->message);return 1;}// 创建 GATT Manager 的代理GDBusProxy *manager_proxy = g_dbus_proxy_new_sync(connection,G_DBUS_PROXY_FLAGS_NONE,NULL,BLUEZ_BUS_NAME,ADAPTER_PATH,"org.bluez.GattManager1",NULL,&error);if (!manager_proxy){g_printerr("Failed to create GattManager1 proxy: %s\n", error->message);return 1;}g_print("Attempting to register GATT application...\n");// 启动输入线程(用于动态修改只读特征内容)pthread_t tid;pthread_create(&tid, NULL, stdin_input_thread, NULL);// 创建主循环main_loop = g_main_loop_new(NULL, FALSE);// 异步调用 RegisterApplicationg_dbus_proxy_call(manager_proxy, "RegisterApplication",g_variant_new("(oa{sv})", APP_PATH, NULL),G_DBUS_CALL_FLAGS_NONE,-1,NULL,on_register_application_done,NULL);// 运行主循环(等待 DBus 事件)g_main_loop_run(main_loop);// 程序退出时的清理g_object_unref(manager_proxy);g_dbus_connection_unregister_object(connection, reg_id1);g_dbus_connection_unregister_object(connection, reg_id2);g_dbus_connection_unregister_object(connection, reg_id3);g_dbus_connection_unregister_object(connection, reg_id4);g_object_unref(connection);g_print("Program finished.\n");return registration_success ? 0 : 1;
}

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

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

相关文章

【Linux基础】Linux文件系统深度解析:EXT4与XFS技术详解与应用

目录 引言 1 Linux文件系统概述 1.1 文件系统的基本概念 1.2 日志文件系统的概念 2 EXT4文件系统详解 2.1 EXT4概述 2.2 EXT4的磁盘结构 2.3 EXT4的inode结构 2.4 EXT4的新特性 2.4.1 Extents 2.4.2 延迟分配 2.4.3 快速文件系统检查 2.5 EXT4的性能特点 3 XFS文…

埃文科技荣获2025年“数据要素×”大赛河南分赛二等奖

2025年8月19日&#xff0c;2025年“数据要素”大赛河南分赛决赛在郑州举行&#xff0c;本届河南分赛聚焦数据价值赋能。郑州埃文科技有限公司&#xff08;以下简称“埃文科技”&#xff09;凭借其前沿成果“IP地址高精度地理定位及应用场景划分数据集”&#xff0c;从500多支参…

链上迷局:区块链技术的法律暗礁与合规导航

高鹏律师首席数据官&#xff0c;数字经济团队创作AI辅助区块链&#xff0c;这个被誉为“信任机器”的技术&#xff0c;正以颠覆性的力量重塑数字经济的底层逻辑。从比特币的横空出世到NFT的全民狂欢&#xff0c;从DeFi的金融革命到DAO的组织重构&#xff0c;技术永不眠&#xf…

线性代数基础 | 基底 / 矩阵 / 行列式 / 秩 / 线性方程组

注&#xff1a;本文为 “线性代数基础 ” 相关合辑。 略作重排&#xff0c;未作全校。 如有内容异常&#xff0c;请看原文。 线性代数的本质&#xff08;1&#xff09;——基底、向量、线性变换、逆阵、行列式 野指针小李于 2020-08-13 16:34:45 发布 零、基底 在展开后续内…

GORM.io详细指南

GORM.io 详细指南 GORM&#xff08;全称 Go ORM&#xff09;是一个功能丰富的 ORM&#xff08;Object-Relational Mapping&#xff09;库&#xff0c;用于 Go 语言。它简化了数据库操作&#xff0c;将 SQL 查询映射到 Go 结构体&#xff0c;支持多种数据库后端。GORM 的设计理念…

【Flask】测试平台开发,应用管理模块实现-第十一篇

概述通过Element UI抽屉和表单校验&增改接口合并实现应用管理Drawer 抽屉之前产品修改和添加是使用Dialog组件实现的&#xff0c;但这个组件有时候并不满足我们的需求, 比如表单很长, 亦或是你需要临时展示一些文档, Drawer 是可以从侧面弹出的一个层&#xff0c;可以容纳更…

Elasticsearch 深分页限制与解决方案

最近在准备面试&#xff0c;正把平时积累的笔记、项目中遇到的问题与解决方案、对核心原理的理解&#xff0c;以及高频业务场景的应对策略系统梳理一遍&#xff0c;既能加深记忆&#xff0c;也能让知识体系更扎实&#xff0c;供大家参考&#xff0c;欢迎讨论。在项目中遇到一个…

基于偏最小二乘法PLS多输入单输出的回归预测【MATLAB】

基于偏最小二乘法&#xff08;PLS&#xff09;多输入单输出的回归预测【MATLAB】 在科学研究和工程实践中&#xff0c;我们常常需要根据多个相关变量来预测一个关键结果。例如&#xff0c;根据气温、湿度、风速等多个气象因素预测空气质量指数&#xff0c;或根据多种原材料成分…

SQL Server核心架构深度解析

SQL Server 的体系结构是一个复杂但设计精密的系统&#xff0c;主要可以分为四大核心组件&#xff0c;它们协同工作以管理数据库、处理查询、确保数据安全与一致性。以下是其体系结构的核心组成部分&#xff1a; 核心组件&#xff1a;协议层 (Protocol Layer) 作用&#xff1a;…

Django REST Framework Serializer 进阶教程

1. 序列化器概述 在 Django REST Framework&#xff08;DRF&#xff09;中&#xff0c;序列化器&#xff08;Serializer&#xff09;用于将复杂的数据类型&#xff08;如模型实例&#xff09;转换为 JSON 格式&#xff0c;以便于 API 返回给客户端。此外&#xff0c;序列化器还…

面试问题详解十四:Qt 多线程同步【QSemaphore】讲解

在多线程开发中&#xff0c;经常需要控制多个线程对共享资源的访问数量。例如限制同时下载文件的数量、控制数据库连接池的连接使用等等。这时候&#xff0c;Qt 提供的 QSemaphore&#xff08;信号量&#xff09;就非常派得上用场。一、什么是 QSemaphore&#xff1f; QSemapho…

Spark mapGroups 函数详解与多种用法示例

mapGroups 是 Spark 中一个强大的分组操作函数&#xff0c;它允许你对每个分组应用自定义逻辑并返回一个结果。以下是多个使用简单样例数据的具体用法示例。基础示例数据假设我们有一个简单的学生成绩数据集&#xff1a;// 创建示例DataFrame val studentScores Seq(("Ma…

【图论】Graphs.jl 图数据的读写与生成器

文章目录图数据的读写Graphs.loadgraphGraphs.loadgraphsGraphs.savegraph保存单个图保存图字典Graphs.loadlg_multGraphs.savelgGraphs.savelg_mult图的生成器1. 随机图模型1.1 Erdős–Rnyi 模型1.2 巴拉巴西-阿尔伯特模型 (无标度网络)1.3 小世界网络模型1.4 随机块模型 (SB…

Go指针全解析:从基础到实战

基本概念与定义指针的定义指针是一种特殊的变量类型&#xff0c;它存储的不是实际数据值&#xff0c;而是另一个变量在计算机内存中的地址。在底层实现上&#xff0c;指针本质上是保存内存位置的无符号整数&#xff0c;它直接指向内存中的特定位置&#xff0c;允许程序直接操作…

Oracle 查询有哪些用户 提示用户名密码无效

要查询 Oracle 数据库中的所有用户&#xff0c;可以使用以下 SQL 查询语句。这个查询将返回数据库中所有用户的列表。 [] SELECT username FROM all_users ORDER BY username;如果你有足够的权限&#xff08;通常是 DBA 权限&#xff09;&#xff0c;你也可以使用 dba_users 视…

小白成长之路-develops -jenkins部署lnmp平台

文章目录一、准备工作1.1两台虚拟机1.2配置文件1.3免密登录二、实战1.构建主item2.测试nginx,php,mysql2.1新建测试项目2.2与正式项目绑定构建后的操作2.3测试2.4导入discuz项目总结一、准备工作 1.1两台虚拟机 服务器&#xff1a;192.168.144.24 客户端&#xff1a;192.168.…

【HarmonyOS 6】仿AI唤起屏幕边缘流光特效

【HarmonyOS 6】仿AI唤起屏幕边缘流光特效 一、前言 最近在做 HarmonyOS 6.0 的适配&#xff0c;发现 Beta1版本里多了个很实用的视效功能——自带背景的双边流光。 之前做屏幕边缘流光特效的时候&#xff0c;要么得自己写渐变动画拼效果&#xff0c;要么就得套好几个组件叠层&…

跟做springboot尚品甄选项目

springbootvue3 【尚硅谷Java项目《尚品甄选》 SpringBootSpringCloud萌新学会企业级java项目】003.后台系统-搭建前端环境&#xff08;工程创建&#xff09;_哔哩哔哩_bilibili E:\project\AllProJect\Shangpin Selection\项目材料素材\课件\尚品甄选项目课件 前端套用框架…

【Linux】创建线程

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 文章目录 一、为什么需要线程&#xff1f; 创建线程 示例&#xff1a;计算斐波恩夕法 一、为什么需要线程&#xff1f; 在多核处理器的计算机上&#xff0c;线程可…

HTML应用指南:利用POST请求获取全国九号电动车体验店服务店位置信息

九号公司(Ninebot)作为全球领先的智能短途出行解决方案提供商,始终秉持“智慧移动,愉悦生活”的品牌理念,致力于为个人用户打造安全、智能、时尚的城市出行体验。依托“智能硬件 + 数字服务 + 线下触点”三位一体的战略布局,九号公司已建立起覆盖全国、辐射全球的销售与服…