C:mbedtls库实现https双向认证连接示例_七侠镇莫尛貝大侠20241122

目的:使用C mbedtls库实现https(RSA证书)双向认证连接。

开发环境:windows 11, VS2022,mbedtls-3.6.2

私钥格式:p1/p8

#include "mbedtls/net_sockets.h"
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/debug.h"
#include "mbedtls/error.h"
#include "mbedtls/certs.h"#include <mbedtls/pk.h>
#include <mbedtls/pem.h>
#include <mbedtls/base64.h>// 这个函数假设 SSL 握手已经成功完成
void print_server_san(mbedtls_ssl_context* ssl) {const mbedtls_x509_crt* cert = mbedtls_ssl_get_peer_cert(ssl);if (cert == NULL) {printf("No server certificate found.\n");return;}// 确保 `subject_alt_names` 是正确的类型const mbedtls_x509_sequence* san = &cert->subject_alt_names;while (san != NULL) {const mbedtls_asn1_buf* entry = &san->buf;unsigned char tag = entry->tag;// 检查是否为 IP 地址 (context-specific tag 7)if ((tag & MBEDTLS_ASN1_TAG_CLASS_MASK) == MBEDTLS_ASN1_CONTEXT_SPECIFIC &&(tag & MBEDTLS_ASN1_TAG_VALUE_MASK) == 7) {// IP 地址以二进制形式存储if (entry->len == 4) { // IPv4 地址printf("Server certificate SAN (IP): %u.%u.%u.%u\n",entry->p[0], entry->p[1], entry->p[2], entry->p[3]);}else if (entry->len == 16) { // IPv6 地址printf("Server certificate SAN (IPv6): ");for (int i = 0; i < 16; i++) {printf("%02x", entry->p[i]);if (i % 2 == 1 && i < 15) {printf(":");}}printf("\n");}}san = san->next;}
}void print_mbedtls_error(int ret) {char error_buf[100];mbedtls_strerror(ret, error_buf, sizeof(error_buf));fprintf(stderr, "Error: %s\n", error_buf);
}int print_prikey_b64() {int ret;mbedtls_pk_context pkey;unsigned char* key_buffer = NULL;size_t key_len = 0;unsigned char* base64_buffer = NULL;size_t base64_len = 0;mbedtls_pk_init(&pkey);// 读取私钥文件ret = mbedtls_pk_parse_keyfile(&pkey, "res/yax/client_p8.key", NULL);if (ret != 0) {print_mbedtls_error(ret);goto cleanup;}// 获取私钥的 DER 格式所需的缓冲区长度key_len = mbedtls_pk_write_key_der(&pkey, NULL, 0);if (key_len <= 0) {print_mbedtls_error(key_len);goto cleanup;}printf("Key length required for DER format: %zu bytes\n", key_len);// 分配比所需长度更大的缓冲区来确保足够的空间key_buffer = (unsigned char*)malloc(key_len);if (!key_buffer) {fprintf(stderr, "Memory allocation failed for key_buffer with size %zu bytes\n", key_len);ret = -1;goto cleanup;}// 将私钥写入 DER 格式缓冲区,注意返回值是实际写入的字节数ret = mbedtls_pk_write_key_der(&pkey, key_buffer, key_len);if (ret < 0) {print_mbedtls_error(ret);goto cleanup;}// 计算 Base64 编码所需的缓冲区长度key_len = ret;  // 实际写入的字节数ret = mbedtls_base64_encode(NULL, 0, &base64_len, key_buffer + (key_len - ret), key_len);if (ret != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) {print_mbedtls_error(ret);goto cleanup;}// 分配缓冲区以存储 Base64 编码的数据base64_buffer = (unsigned char*)malloc(base64_len);if (!base64_buffer) {fprintf(stderr, "Memory allocation failed for base64_buffer\n");ret = -1;goto cleanup;}// 将 DER 格式的私钥转换为 Base64 编码ret = mbedtls_base64_encode(base64_buffer, base64_len, &base64_len, key_buffer + (key_len - ret), key_len);if (ret != 0) {print_mbedtls_error(ret);goto cleanup;}// 打印 Base64 编码的私钥printf("Base64 Encoded Private Key:\n%s\n", base64_buffer);cleanup:mbedtls_pk_free(&pkey);if (key_buffer) {free(key_buffer);}if (base64_buffer) {free(base64_buffer);}//Key length required for DER format: 18446744073709551508 bytes//Memory allocation failed for key_buffer with size 18446744073709551508 bytesreturn ret != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}int testHttps_p8() {printf("Https功能演示start:\n");int ret;mbedtls_net_context server_fd;mbedtls_ssl_context ssl;mbedtls_ssl_config conf;mbedtls_x509_crt cacert, clicert;mbedtls_pk_context pkey;mbedtls_entropy_context entropy;mbedtls_ctr_drbg_context ctr_drbg;const char* pers = "ssl_client1";// Initialize structuresmbedtls_net_init(&server_fd);mbedtls_ssl_init(&ssl);mbedtls_ssl_config_init(&conf);mbedtls_x509_crt_init(&cacert);mbedtls_x509_crt_init(&clicert);mbedtls_pk_init(&pkey);mbedtls_ctr_drbg_init(&ctr_drbg);mbedtls_entropy_init(&entropy);// 设置 RNG//ret = mbedtls_ctr_drbg_seed(&(hc->tls.ctr_drbg), mbedtls_entropy_func, &hc->tls.entropy, NULL, 0);//ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char*)pers, strlen(pers));ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0);if (ret != 0){return ret;}// Load certificates//mbedtls_x509_crt_parse_file(&clicert, "res/yax/client_https_with_chain.cer");  //含有客户端证书链mbedtls_x509_crt_parse_file(&clicert, "res/yax/client_https.cer");  //不含有证书链mbedtls_x509_crt_parse_file(&cacert, "res/yax/ca.cer");   //所有客户端和服务端所有证书链//mbedtls_x509_crt_parse_file(&cacert, "res/yax/root.cer");   //只提供一个服务器证书的root证书即可mbedtls_pk_parse_keyfile(&pkey, "res/yax/client_p8.key", NULL);    //p1或p8格式都可以,但必须包含头尾//mbedtls_pk_parse_keyfile(&pkey, "res/yax2/client_p8.key", NULL);mbedtls_ssl_conf_authmode(&ssl, MBEDTLS_SSL_VERIFY_REQUIRED);  //设置认证模式为 MBEDTLS_SSL_VERIFY_OPTIONAL 或 MBEDTLS_SSL_VERIFY_NONE 来调试是否由于证书验证失败引起的问题//但是,这仅用于调试,生产环境应该始终使用 MBEDTLS_SSL_VERIFY_REQUIRED。          // Seed the random number generator//mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char*)pers, strlen(pers));// Setup SSL configurationmbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);mbedtls_ssl_conf_dbg(&conf, my_debug, stdout);mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL);mbedtls_ssl_conf_own_cert(&conf, &clicert, &pkey);//mbedtls_ssl_set_hostname(&ssl, "4.3.2.1");   //服务器证书alt name dns = ip而不是域名时,不要调用这个//mbedtls_ssl_set_hostname(&ssl, NULL); //忽略主机名验证//mbedtls_ssl_conf_verify(&conf, 0, NULL);  // 设置自定义的证书验证回调// Connect to servermbedtls_net_connect(&server_fd, "4.3.2.1", "443", MBEDTLS_NET_PROTO_TCP);    //rsa https 测试服务端//mbedtls_net_connect(&server_fd, "4.3.2.1", "443", MBEDTLS_NET_PROTO_TCP); //RSA 国密sm2 双算法证书https 不适用。// Setup SSL contextmbedtls_ssl_setup(&ssl, &conf);mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL);// Perform SSL handshakewhile ((ret = mbedtls_ssl_handshake(&ssl)) != 0) {if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {char error_buf[100];mbedtls_strerror(ret, error_buf, 100);printf("Handshake failed: %s\n", error_buf);return 1;}}//打印服务器证书的sanprint_server_san(&ssl);//print_prikey_b64();// Communicate with the server// ...// Send data to serverconst char* msg = "Hello, Server!";mbedtls_ssl_write(&ssl, (const unsigned char*)msg, strlen(msg));// Read responsechar buffer[1024];do {memset(buffer, 0, sizeof(buffer));ret = mbedtls_ssl_read(&ssl, (unsigned char*)buffer, sizeof(buffer) - 1);if (ret > 0) {printf("Received %d bytes:\n%s\n", ret, buffer);}else if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {break;}} while (ret > 0);// Clean upmbedtls_ssl_close_notify(&ssl);mbedtls_net_free(&server_fd);mbedtls_x509_crt_free(&cacert);mbedtls_x509_crt_free(&clicert);mbedtls_pk_free(&pkey);mbedtls_ssl_free(&ssl);mbedtls_ssl_config_free(&conf);mbedtls_ctr_drbg_free(&ctr_drbg);mbedtls_entropy_free(&entropy);printf("Https功能演示End:\n");return EXIT_SUCCESS;
}//在证书base64编码前后添加头尾
char* cert_add_ht(const char* filename) {FILE* file = fopen(filename, "r");if (!file) {perror("无法打开文件");return NULL;}// 获取文件大小fseek(file, 0, SEEK_END);long filesize = ftell(file);fseek(file, 0, SEEK_SET);// 分配内存读取文件内容char* base64_data = (char*)malloc(filesize + 1);if (!base64_data) {perror("内存分配失败");fclose(file);return NULL;}fread(base64_data, 1, filesize, file);base64_data[filesize] = '\0';  // 确保字符串结束fclose(file);const char* pem_header = "-----BEGIN CERTIFICATE-----\n";const char* pem_footer = "\n-----END CERTIFICATE-----\n";// 计算 PEM 格式总长度size_t pem_size = strlen(pem_header) + strlen(base64_data) + strlen(pem_footer) + 1;// 分配内存以存储 PEM 格式数据char* pem_cert = (char*)malloc(pem_size);if (!pem_cert) {perror("内存分配失败");free(base64_data);return NULL;}// 生成 PEM 格式数据snprintf(pem_cert, pem_size, "%s%s%s", pem_header, base64_data, pem_footer);free(base64_data);return pem_cert;
}char* pkcs1_key_add_ht(const char* filename) {FILE* file = fopen(filename, "r");if (!file) {perror("无法打开文件");return NULL;}// 获取文件大小fseek(file, 0, SEEK_END);long filesize = ftell(file);fseek(file, 0, SEEK_SET);// 分配内存读取文件内容char* base64_data = (char*)malloc(filesize + 1);if (!base64_data) {perror("内存分配失败");fclose(file);return NULL;}fread(base64_data, 1, filesize, file);base64_data[filesize] = '\0';  // 确保字符串结束fclose(file);const char* pem_header = "-----BEGIN RSA PRIVATE KEY-----\n";const char* pem_footer = "\n-----END RSA PRIVATE KEY-----\n";// 计算 PEM 格式总长度size_t pem_size = strlen(pem_header) + strlen(base64_data) + strlen(pem_footer) + 1;// 分配内存以存储 PEM 格式数据char* pem_cert = (char*)malloc(pem_size);if (!pem_cert) {perror("内存分配失败");free(base64_data);return NULL;}// 生成 PEM 格式数据snprintf(pem_cert, pem_size, "%s%s%s", pem_header, base64_data, pem_footer);free(base64_data);return pem_cert;
}int testHttps() {printf("Https功能演示start:\n");int ret;mbedtls_net_context server_fd;mbedtls_ssl_context ssl;mbedtls_ssl_config conf;mbedtls_x509_crt cacert, clicert;mbedtls_pk_context pkey;mbedtls_entropy_context entropy;mbedtls_ctr_drbg_context ctr_drbg;const char* pers = "ssl_client1";// Initialize structuresmbedtls_net_init(&server_fd);mbedtls_ssl_init(&ssl);mbedtls_ssl_config_init(&conf);mbedtls_x509_crt_init(&cacert);mbedtls_x509_crt_init(&clicert);mbedtls_pk_init(&pkey);mbedtls_ctr_drbg_init(&ctr_drbg);mbedtls_entropy_init(&entropy);// 设置 RNG//ret = mbedtls_ctr_drbg_seed(&(hc->tls.ctr_drbg), mbedtls_entropy_func, &hc->tls.entropy, NULL, 0);//ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char*)pers, strlen(pers));ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0);if (ret != 0){return ret;}const char* file_cert_b64 = "res/yax2/client_https_noht.cer";// 读取证书文件(不包含头尾)并转换为 PEM 格式(包含头尾)char* pem_cert = cert_add_ht(file_cert_b64);// 获取 PEM 证书的大小size_t pem_size = strlen(pem_cert) + 1;  // 包括终止符mbedtls_x509_crt_parse(&clicert, (const unsigned char*)pem_cert, pem_size);free(pem_cert);const char* file_key_p1_b64 = "res/yax2/client_p1_noht.key";// 读取证书文件(不包含头尾)并转换为 PEM 格式(包含头尾)char* pem_key = pkcs1_key_add_ht(file_key_p1_b64);// 获取 PEM 证书的大小size_t key_size = strlen(pem_key) + 1;  // 包括终止符mbedtls_pk_parse_key(&pkey, (const unsigned char*)pem_key, key_size,NULL,0);free(pem_key);mbedtls_x509_crt_parse_file(&cacert, "res/yax/ca.cer");   //所有客户端和服务端所有证书链// Load certificates//mbedtls_x509_crt_parse_file(&clicert, "res/yax/client_https_with_chain.cer");  //含有客户端证书链//mbedtls_x509_crt_parse_file(&clicert, "res/yax2/client_https_noht.cer");  //不含有证书链//mbedtls_x509_crt_parse_file(&cacert, "res/yax/ca.cer");   //所有客户端和服务端所有证书链//mbedtls_x509_crt_parse_file(&cacert, "res/yax/root.cer");   //只提供一个服务器证书的root证书即可//mbedtls_pk_parse_keyfile(&pkey, "res/yax2/client_p1.key", NULL);    //p1或p8格式都可以,但必须包含头尾//mbedtls_pk_parse_keyfile(&pkey, "res/yax2/client_p8.key", NULL);mbedtls_ssl_conf_authmode(&ssl, MBEDTLS_SSL_VERIFY_REQUIRED);  //设置认证模式为 MBEDTLS_SSL_VERIFY_OPTIONAL 或 MBEDTLS_SSL_VERIFY_NONE 来调试是否由于证书验证失败引起的问题//但是,这仅用于调试,生产环境应该始终使用 MBEDTLS_SSL_VERIFY_REQUIRED。          
// Seed the random number generator
//mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char*)pers, strlen(pers));// Setup SSL configurationmbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);mbedtls_ssl_conf_dbg(&conf, my_debug, stdout);mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL);mbedtls_ssl_conf_own_cert(&conf, &clicert, &pkey);//mbedtls_ssl_set_hostname(&ssl, "1.2.3.4");   //服务器证书alt name dns = ip而不是域名时,不要调用这个//mbedtls_ssl_set_hostname(&ssl, NULL); //忽略主机名验证//mbedtls_ssl_conf_verify(&conf, 0, NULL);  // 设置自定义的证书验证回调// Connect to servermbedtls_net_connect(&server_fd, "2.3.4.5", "443", MBEDTLS_NET_PROTO_TCP);    //rsa https 测试服务端//mbedtls_net_connect(&server_fd, "2.3.4.5", "443", MBEDTLS_NET_PROTO_TCP); //RSA 国密sm2 双算法证书https 不适用。// Setup SSL contextmbedtls_ssl_setup(&ssl, &conf);mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL);// Perform SSL handshakewhile ((ret = mbedtls_ssl_handshake(&ssl)) != 0) {if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {char error_buf[100];mbedtls_strerror(ret, error_buf, 100);printf("Handshake failed: %s\n", error_buf);return 1;}}//打印服务器证书的sanprint_server_san(&ssl);//print_prikey_b64();// Communicate with the server// ...// Send data to serverconst char* msg = "Hello, Server!";mbedtls_ssl_write(&ssl, (const unsigned char*)msg, strlen(msg));// Read responsechar buffer[1024];do {memset(buffer, 0, sizeof(buffer));ret = mbedtls_ssl_read(&ssl, (unsigned char*)buffer, sizeof(buffer) - 1);if (ret > 0) {printf("Received %d bytes:\n%s\n", ret, buffer);}else if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {break;}} while (ret > 0);// Clean upmbedtls_ssl_close_notify(&ssl);mbedtls_net_free(&server_fd);mbedtls_x509_crt_free(&cacert);mbedtls_x509_crt_free(&clicert);mbedtls_pk_free(&pkey);mbedtls_ssl_free(&ssl);mbedtls_ssl_config_free(&conf);mbedtls_ctr_drbg_free(&ctr_drbg);mbedtls_entropy_free(&entropy);printf("Https功能演示End:\n");return EXIT_SUCCESS;
}

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

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

相关文章

染色质重塑与心衰中的细胞间通讯机制:解读一篇Nature力作

染色质重塑与心衰中的细胞间通讯机制&#xff1a;解读一篇Nature力作 一、文献的主要行文思路与观点 在这篇发表在 Nature 的文章中&#xff0c;作者聚焦于心脏衰竭中的慢性炎症与纤维化问题&#xff0c;试图揭示免疫细胞与成纤维细胞之间的通讯机制。研究围绕以下几个核心问题…

WordPress添加类似说说、微博的时间轴微语页面

这个版本的WordPress可以直接使用&#xff0c;CSS样式可以完美兼容。效果如图 使用方法&#xff1a; 一、后台配置 新建微语功能 将下面的代码复制粘贴到主题的functions.php函数文件中&#xff0c;为WordPress添加微语功能。添加完成后&#xff0c;可以在WordPress后台菜单…

解决IDEA报包不存在,但实际存在的问题

前言 最近在把一个亿老项目交割给同事&#xff0c;同事在导入项目运行时遇到IDEA报包不存在&#xff0c;但实际存在的问题&#xff0c;最终通过以下方式解决 现象 在IDEA里启动运行项目&#xff0c;报某个类有问题&#xff0c;引入的包不存在。 点击这个引入的包&#xff0c;可…

云原生之k8s服务管理

文章目录 服务管理Service服务原理ClusterIP服务 对外发布应用服务类型NodePort服务Ingress安装配置Ingress规则 Dashboard概述 认证和授权ServiceAccount用户概述创建ServiceAccount 权限管理角色与授权 服务管理 Service 服务原理 容器化带来的问题 自动调度&#xff1a;…

RocketMQ: 集群部署注意事项

概述 RocketMQ 是一款分布式、队列模型的消息中间件&#xff0c;具有以下特点&#xff1a; 能够保证严格的消息顺序提供丰富的消息拉取模式高效的订阅者水平扩展能力实时的消息订阅机制亿级消息堆积能力 选用理由&#xff1a; 强调集群无单点&#xff0c;可扩展&#xff0c;任…

【Unity How】Unity中如何实现物体的匀速往返移动

直接上代码 using UnityEngine;public class CubeBouncePingPong : MonoBehaviour {[Header("移动参数")][Tooltip("移动速度")]public float moveSpeed 2f; // 控制移动的速度[Tooltip("最大移动距离")]public float maxDistance 5f; // 最大…

ECharts柱状图-带圆角的堆积柱状图,附视频讲解与代码下载

引言&#xff1a; 在数据可视化的世界里&#xff0c;ECharts凭借其丰富的图表类型和强大的配置能力&#xff0c;成为了众多开发者的首选。今天&#xff0c;我将带大家一起实现一个柱状图图表&#xff0c;通过该图表我们可以直观地展示和分析数据。此外&#xff0c;我还将提供…

element-plus的组件数据配置化封装 - table

目录 一、封装的table、table-column组件以及相关ts类型的定义 1、ATable组件的封装 - index.ts 2、ATableColumn组件的封装 - ATableColumn.ts 3、ATable、ATableColumn类型 - interface.ts 二、ATable、ATableColumn组件的使用 三、相关属性、方法的使用以及相关说明 1. C…

《数字图像处理基础》学习06-图像几何变换之最邻近插值法缩小图像

目录 一&#xff0c;概念 二&#xff0c;题目 三&#xff0c;matlab实现 对图像进行几何变换时&#xff0c;都是对数字图像进行处理。由于在matlab中使用imread函数读取的图像通常已经是数字图像&#xff0c;因此不需要进行额外的采样和量化等操作&#xff0c;就可以将图像…

TabNet 模型示例

代码功能 加载数据&#xff1a;从 UCI Adult Census 数据集中读取样本&#xff0c;进行清洗和编码。 特征处理&#xff1a;对分类特征进行标签编码&#xff0c;对数值特征进行标准化。 模型训练&#xff1a;使用 TabNet 模型对数据进行分类训练&#xff0c;采用早停机制提高效…

一次封装,解放双手:Requests如何实现0入侵请求与响应的智能加解密

引言 之前写了 Requests 自动重试的文章&#xff0c;突然想到&#xff0c;之前还用到过 Requests 自动加解密请求的逻辑&#xff0c;分享一下。之前在做逆向的时候&#xff0c;发现一般医院的小程序请求会这么玩&#xff0c;请求数据可能加密也可能不加密&#xff0c;但是返回…

锂电池学习笔记(一) 初识锂电池

前言 锂电池近几年一直都是很热门的产品&#xff0c;充放电管理更是学问蛮多&#xff0c;工作生活中难免会碰到&#xff0c;所以说学习锂电池是工程师的必备知识储备&#xff0c;今天学习锂电池的基本知识&#xff0c;分类&#xff0c;优缺点&#xff0c;循序渐进 学习参考 【…

《Vue零基础入门教程》第四课: 应用实例

往期内容 《Vue零基础入门教程》第一课&#xff1a;Vue简介 《Vue零基础入门教程》第二课&#xff1a;搭建开发环境 《Vue零基础入门教程》第三课&#xff1a;起步案例 参考官方文档 https://cn.vuejs.org/api/application#create-app 示例 const {createApp} Vue// 通…

介绍一下strncmp(c基础)

strncmp是strcmp的进阶版 链接介绍一下strcmp(c基础)-CSDN博客 作用 比较两个字符串的前n位 格式 #include <string.h> strncmp (arr1,arr2,n); 工作原理&#xff1a;strcmp函数按照ACII&#xff08;字符编码顺序&#xff09;比较两个字符串。它从两个字符串的第一…

Lucene(2):Springboot整合全文检索引擎TermInSetQuery应用实例附源码

前言 本章代码已分享至Gitee: https://gitee.com/lengcz/springbootlucene01 接上文。Lucene(1):Springboot整合全文检索引擎Lucene常规入门附源码 如何在指定范围内查询。从lucene 7 开始&#xff0c;filter 被弃用&#xff0c;导致无法进行调节过滤。 TermInSetQuery 指定…

【电路笔记 TMS320F28335DSP】时钟+看门狗+相关寄存器(功能模块使能、时钟频率配置、看门狗配置)

时钟源和主时钟&#xff08;SYSCLKOUT&#xff09; 外部晶振&#xff1a;通常使用外部晶振&#xff08;如 20 MHz&#xff09;作为主要时钟源。内部振荡器&#xff1a;还可以选择内部振荡器&#xff08;INTOSC1 和 INTOSC2&#xff09;&#xff0c;适合无需高精度外部时钟的应…

java 并发编程 (1)java中如何实现并发编程

目录 1. 继承 Thread 类 2. 实现 Runnable 接口 3. 使用 FutureTask 4. 使用 Executor 框架 5. 具体案例 1. 继承 Thread 类 概述&#xff1a;通过继承 Thread 类并重写其 run() 方法来创建一个新的线程。 步骤&#xff1a; 创建一个继承 Thread 类的子类。重…

巧用观测云可用性监测(云拨测)

前言 做为系统运维或者开发&#xff0c;很多时候我们需要能够实时感知我们所运维的系统和服务的情况&#xff0c;比如以下的场景&#xff1a; 系统上线前测试&#xff1a;包括功能完整性检查&#xff0c;确保页面元素&#xff08;如图像、视频、脚本等&#xff09;都能够正常…

python oa服务器巡检报告脚本的重构和修改(适应数盾OTP)有空再去改

Two-Step Vertification required&#xff1a; Please enter the mobile app OTPverification code: 01.因为巡检的服务器要双因子认证登录&#xff0c;也就是登录堡垒机时还要输入验证码。这对我的巡检查服务器的工作带来了不便。它的机制是每一次登录&#xff0c;算一次会话…

Unreal从入门到精通之如何绘制用于VR的3DUI交互的手柄射线

文章目录 前言实现方式MenuLaser实现步骤1.Laser和Cursor2.移植函数3.启动逻辑4.检测射线和UI的碰撞5.激活手柄射线6.更新手柄射线位置7.隐藏手柄射线8.添加手柄的Trigger监听完整节点如下:效果图前言 之前我写过一篇文章《Unreal5从入门到精通之如何在VR中使用3DUI》,其中讲…