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 bytes

    return 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 structures
    mbedtls_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 configuration
    mbedtls_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 server
    mbedtls_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 context
    mbedtls_ssl_setup(&ssl, &conf);
    mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL);

    // Perform SSL handshake
    while ((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;
        }
    }

    //打印服务器证书的san
    print_server_san(&ssl);
    //print_prikey_b64();

    // Communicate with the server
    // ...
    // Send data to server
    const char* msg = "Hello, Server!";
    mbedtls_ssl_write(&ssl, (const unsigned char*)msg, strlen(msg));

    // Read response
    char 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 up
    mbedtls_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 structures
    mbedtls_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 configuration
    mbedtls_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 server
    mbedtls_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 context
    mbedtls_ssl_setup(&ssl, &conf);
    mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL);

    // Perform SSL handshake
    while ((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;
        }
    }

    //打印服务器证书的san
    print_server_san(&ssl);
    //print_prikey_b64();

    // Communicate with the server
    // ...
    // Send data to server
    const char* msg = "Hello, Server!";
    mbedtls_ssl_write(&ssl, (const unsigned char*)msg, strlen(msg));

    // Read response
    char 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 up
    mbedtls_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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VUE 的前置知识

一、JavaScript----导图导出 1. JS 提供的导入导出机制&#xff0c;可以实现按需导入 1.1 在html页面中可以把JS文件通过 <script src"showMessage.js"></script> 全部导入 1.2 通过在JS文件中写export关键字导出通过 <script src"showMessage…

---Arrays类

一 java 1.Arrays类 1.1 toString&#xff08;&#xff09; 1.2 arrays.sort( )-----sort排序 1&#xff09;直接调用sort&#xff08;&#xff09; Arrays.sort() 方法的默认排序顺序是 从小到大&#xff08;升序&#xff09;。 2&#xff09;定制排序【具体使用时 调整正负…

【H2O2|全栈】JS进阶知识(六)ES6(2)

目录 前言 开篇语 准备工作 Set和Map 基本概念 Set 相互转化 常见属性和API 数组去重 并集、交集和差集 Map 转化 常见的属性和API Set和Map的区别 This的指向 function函数 箭头函数 修改this 使用方式 三种方式的异同 案例 更改this指向为obj 求数组数…

Node基本使用

1. 创建自定义全局指令 1.1 新建一个空的文件夹, 创建一个cli.js文件 1.2 在cli.js写入内容 /usr/bin/env就是让系统使用node来执行你的脚本文件。 #! /usr/bin/env node1.3 执行终端指令 // 在文件夹 node-project 的终端下执行指令npm init执行完后package.json结构如下,…

Python编程技巧:多变量赋值的优雅艺术

在Python编程的世界里&#xff0c;有许多令人惊叹的语法特性&#xff0c;而多变量赋值就像是一颗闪耀的明珠&#xff0c;它不仅让代码更优雅&#xff0c;还能提升程序的执行效率。今天我们就深入探讨这个看似简单却蕴含深意的编程技巧。 基础认识 传统的变量赋值方式&#xff…

Transformer架构笔记

Attention is All You Need. 3.Model Architecture 3.1 整体架构如图 3.2 Encoder与Decoder Encoder&#xff1a;由 N 6 N6 N6个相同的Block/Layer堆叠而成。每个Block有两个子层sub-layer&#xff1a;多头注意力和MLP&#xff08;FFN&#xff0c;前馈神经网络&#xff09;&…

网络安全简单入门与扫描

网络安全简单入门 内容大纲 策略制定安全工具其他 1、安全策略 1.1、安全三要素 要全面地认识一个安全问题,我们有很多种办法,但首先要理解安全问题的组成属性。前人通过无数实践,最后将安全的属性总结为安全三要素,简称CIA。 安全三要素是安全的基本组成元素,分别是机密性…

在WPF程序中实现PropertyGrid功能

使用C#开发过Windows Forms的都知道&#xff0c;在Windows Forms程序中&#xff0c;有一个PropertyGrid控件&#xff0c;可以用于显示对象的属性&#xff0c;在WPF中并没有默认提供此功能的控件&#xff0c;今天以一个简单的小例子&#xff0c;简述在WPF中借助WinForm的Propert…

大模型时代的具身智能系列专题(十四)

冯晨团队 冯晨是纽约大学的副教授。他对通过多学科使用启发研究实现机器人主动和协作感知和学习感兴趣&#xff0c;这些研究源自建筑、制造和运输领域。在纽约大学之前&#xff0c;冯晨是马萨诸塞州剑桥市三菱电机研究实验室 (MERL) 计算机视觉小组的研究科学家&#xff0c;专…

力扣-Hot100-栈【算法学习day.40】

前言 ###我做这类文档一个重要的目的还是给正在学习的大家提供方向&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;&#xff09;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非常非常高滴&am…

RT_Thread内核源码分析(三)——线程

目录 1. 线程结构 2. 线程创建 2.1 静态线程创建 2.2 动态线程创建 2.3 源码分析 2.4 线程内存结构 3. 线程状态 3.1 线程状态分类 3.2 就绪状态和运行态 3.3 阻塞/挂起状态 3.3.1 阻塞工况 3.4 关闭状态 3.4.1 线程关闭接口 3.4.2 静态线程关闭 3.4.3 动态线程关…

043 商品详情

文章目录 详情页数据表结构voSkuItemVo.javaSkuItemSaleAttrVo.javaAttrValueAndSkuIdVo.javaSpuAttrGroupVo.javaGroupAttrParamVo.java pom.xmlSkuSaleAttrValueDao.xmlSkuSaleAttrValueDao.javaAttrGroupDao.xmlAttrGroupServiceImpl.javaSkuInfoServiceImpl.javaSkuSaleAtt…

硬件知识 cadence16.6 原理图输出为pdf 网络名下划线偏移 (ORCAD)

1. cadence原理图输出为PDF网络名下划线偏移 生这种情况的原因 1. 设计的原理图图纸大小比正常的 A4图纸大。 2. 打印为PDF 的时候&#xff0c;打印机的设置有问题。 2.cadence原理图输出为 PDF网络名下划线偏移的情况 可以看到上图&#xff0c;网络名往上漂移。 3. 解决办法 …

Spring-boot3.4最新版整合swagger和Mybatis-plus

好家伙,今天终于开始用spring-boot3开始写项目了&#xff0c;以后要彻底告别1.x和2.x了&#xff0c;同样的jdk也来到了最低17的要求了,废话不多说直接开始 这是官方文档的要求jdk最低是17 maven最低是3.6 一. 构建工程,这一步就不需要给大家解释了吧 二. 整合Knife4j 1.大于 …

从零开始:如何使用第三方视频美颜SDK开发实时直播美颜平台

开发一个具有实时美颜功能的直播平台&#xff0c;能够显著提高用户体验和内容质量。而利用第三方视频美颜SDK可以大大简化开发过程&#xff0c;加快产品上市速度。本篇文章&#xff0c;小编将从零开始&#xff0c;详细讲解如何使用第三方视频美颜SDK开发一个实时直播美颜平台。…

ROS入门学习ONE

ros入门玩法1&#xff1a;控制小龟龟 终端1输入 sudo apt install ros-noetic-rqt-steering 新建终端2&#xff08;快捷键CtrlAltT)&#xff0c;打开控制台 roscore //启动ros系统 回到原终端 rosrun rosrun rqt_robot_steering rqt_robot_steering 新建终端3&#xff0c;…

shell脚本(二)

声明&#xff01; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&#…

简单理解下基于 Redisson 库的分布式锁机制

目录 简单理解下基于 Redisson 库的分布式锁机制代码流程&#xff1a;方法的调用&#xff1a;具体锁的实现&#xff1a;riderBalance 方法&#xff1a;tryLock 方法&#xff08;重载&#xff09;&#xff1a;tryLock 方法&#xff08;核心实现&#xff09;&#xff1a; 简单理解…

小鹏汽车智慧材料数据库系统项目总成数据同步

1、定时任务处理 2、提供了接口 小鹏方面提供的推送的数据表结构&#xff1a; 这几个表总数为100多万&#xff0c;经过条件筛选过滤后大概2万多条数据 小鹏的人给的示例图&#xff1a; 界面&#xff1a; SQL: -- 查询车型 select bmm.md_material_id, bmm.material_num, bm…

LeetCode 3244.新增道路查询后的最短距离 II:贪心(跃迁合并)-9行py(O(n))

【LetMeFly】3244.新增道路查询后的最短距离 II&#xff1a;贪心&#xff08;跃迁合并&#xff09;-9行py&#xff08;O(n)&#xff09; 力扣题目链接&#xff1a;https://leetcode.cn/problems/shortest-distance-after-road-addition-queries-ii/ 给你一个整数 n 和一个二维…