Clion开发STM32之W5500系列(DNS服务封装)

news2024/11/25 14:55:47

概述

  1. 在w5500基础库中进行封装,通过域名的方式获取实际的ip地址
  2. 用于动态获取ntp的ip地址

DNS封装

头文件

/*******************************************************************************
 Copyright (c) [scl]。保留所有权利。
 ******************************************************************************/
#ifndef F1XX_TEMPLATE_W5500_DNS_H
#define F1XX_TEMPLATE_W5500_DNS_H

#include "socket.h"

#define TYPE_A 1      /* Host address */
#define TYPE_NS 2      /* Name server */
#define TYPE_MD 3      /* Mail destination (obsolete) */
#define TYPE_MF 4      /* Mail forwarder (obsolete) */
#define TYPE_CNAME 5  /* Canonical name */
#define TYPE_SOA 6      /* Start of Authority */
#define TYPE_MB 7      /* Mailbox name (experimental) */
#define TYPE_MG 8      /* Mail group member (experimental) */
#define TYPE_MR 9      /* Mail rename name (experimental) */
#define TYPE_NULL 10  /* Null (experimental) */
#define TYPE_WKS 11      /* Well-known sockets */
#define TYPE_PTR 12      /* Pointer record */
#define TYPE_HINFO 13 /* Host information */
#define TYPE_MINFO 14 /* Mailbox information (experimental)*/
#define TYPE_MX 15      /* Mail exchanger */
#define TYPE_TXT 16      /* Text strings */
#define TYPE_ANY 255  /* Matches any type */
#define MAX_DNS_BUF_SIZE 256 /* maximum size of DNS buffer. */

/**
 * @memberof delay_ms_cb 毫秒延迟回调
 * @memberof dns_server_ip: dns 服务ip
 * @memberof dns_port: dns 端口,默认53
 */
struct dns_conf {
    void (*delay_ms_cb)(uint32_t ms);

    uint8_t dns_server_ip[4];
    uint16_t dns_port;
};

void dns_config_set(struct dns_conf *cnf);
/**
 *
 * @param s socket num
 * @param domain_name 服务域名
 * @param dst_ip [out] 保存解析的数据
 * @param wait_ms [in] 发送请求后等待时间
 * @return
 */
bool do_dns(SOCKET s, uint8_t *domain_name, uint8_t *dst_ip, uint32_t wait_ms);


#endif //F1XX_TEMPLATE_W5500_DNS_H

源文件

/*******************************************************************************
 Copyright (c) [scl]。保留所有权利。
 ******************************************************************************/
#include "w5500_dns.h"

#define DBG_ENABLE
#define DBG_SECTION_NAME "dns"
#define DBG_LEVEL DBG_INFO // DBG_LOG DBG_INFO DBG_WARNING DBG_ERROR

#include "sys_dbg.h"


/* Header for all domain messages */
struct dhdr {
    uint16_t id; /* Identification */
    uint8_t qr;  /* Query/Response */
    uint8_t opcode;
    uint8_t aa;     /* Authoratative answer */
    uint8_t tc;     /* Truncation */
    uint8_t rd;     /* Recursion desired */
    uint8_t ra;     /* Recursion available */
    uint8_t rcode; /* Response code */
    uint16_t qdcount; /* Question count */
    uint16_t ancount; /* Answer count */
    uint16_t nscount; /* Authority (name server) count */
    uint16_t arcount; /* Additional record count */
};

static uint8_t DNS_BUF[MAX_DNS_BUF_SIZE] = {0};
static struct dhdr dhp = {0}; /*定义一个结构体用来包含报文头信息*/
static uint16_t MSG_ID = 0x1122;
struct dns_conf *cnf_ptr = NULL;

static int dns_packet_build(uint16_t op, uint8_t *name, uint8_t *buf, uint16_t len);

static bool parse_DNS_Response(struct dhdr *pdhdr, uint8_t *pbuf, uint8_t *dst_result_ip);

static uint8_t *dns_question(uint8_t *msg, uint8_t *cp);

static uint8_t *dns_answer(uint8_t *msg, uint8_t *cp, uint8_t *dst_result_ip);

static int16_t parse_name(uint8_t *msg, uint8_t *compressed, uint16_t len);

void dns_config_set(struct dns_conf *cnf) {
    cnf_ptr = cnf;
}


/**
 * dns 解析
 * @param s
 * @param domain_name 域名
 * @return
 */
bool do_dns(SOCKET s, uint8_t *domain_name, uint8_t *dst_ip, uint32_t wait_ms) {
    if (cnf_ptr == NULL) {
        LOG_E("dns_config_set not set");
        goto _exit_false;
    }
    if (udp_client_init(s, cnf_ptr->dns_port)) {
        uint16_t len = dns_packet_build(0, domain_name, DNS_BUF, MAX_DNS_BUF_SIZE);
        sendto(s, DNS_BUF, len, cnf_ptr->dns_server_ip, cnf_ptr->dns_port); /*发送DNS请求报文给DNS服务器*/
        for (int i = 0; i < wait_ms / 2; ++i) {
            len = w5500_socket_rx_size_read(s);
            if (len > 0) break;
            cnf_ptr->delay_ms_cb(2);
        }
        if (len > 0) {
            if (len > MAX_DNS_BUF_SIZE) len = MAX_DNS_BUF_SIZE;
            recvfrom_simple(s, DNS_BUF, len);
            if (parse_DNS_Response(&dhp, DNS_BUF, dst_ip)) {
                close(s);/*关闭socket*/
                return true;/*返回DNS解析成功域名信息*/
            } else {
                LOG_W("parse_DNS_Response err");
            }
        } else {
            LOG_D("w5500_socket_rx_size_read not rec data");
        }
    } else {
        LOG_W("udp_client_init error");
    }
    _exit_false:
    return false;
}


static int dns_packet_build(uint16_t op, uint8_t *name, uint8_t *buf, uint16_t len) {
    uint8_t *cp = buf;
    uint8_t *cp1;
    uint8_t *dname;
    uint16_t p;
    uint16_t dlen;
    MSG_ID++;
    *(uint16_t *) &cp[0] = htons(MSG_ID);
    p = (op << 11) | 0x0100;
    *(uint16_t *) &cp[2] = htons(p);
    *(uint16_t *) &cp[4] = htons(1);
    *(uint16_t *) &cp[6] = htons(0);
    *(uint16_t *) &cp[8] = htons(0);
    *(uint16_t *) &cp[10] = htons(0);
    cp += sizeof(uint16_t) * 6;
    dname = name;
    dlen = strlen((char *) dname);
    for (;;) {
        /* Look for next dot */
        cp1 = (unsigned char *) strchr((char *) dname, '.');

        if (cp1) {
            /* More to come */
            len = cp1 - dname;
        } else {
            len = dlen; /* Last component */
        }
        *cp++ = len;   /* Write length of component */
        if (len == 0) break;
        strncpy((char *) cp, (char *) dname, len);/* Copy component up to (but not including) dot */
        cp += len;
        if (!cp1) {
            *cp++ = 0;                                                                                                                    /* Last one; write null and finish */
            break;
        }
        dname += len + 1;
        dlen -= len + 1;
    }
    *(uint16_t *) &cp[0] = htons(0x0001);    /* type */
    *(uint16_t *) &cp[2] = htons(0x0001);    /* class */
    cp += sizeof(uint16_t) * 2;
    return ((int) ((uint32_t) (cp) - (uint32_t) (buf)));

}


static bool parse_DNS_Response(struct dhdr *pdhdr, uint8_t *pbuf, uint8_t *dst_result_ip) {
    uint16_t tmp = 0, i = 0;
    uint8_t *msg = NULL, *cp = NULL;
    msg = pbuf;
    memset(pdhdr, 0, sizeof(struct dhdr));
    pdhdr->id = ntohs(*((uint16_t *) &msg[0]));
    tmp = ntohs(*((uint16_t *) &msg[2]));
    if (tmp & 0x8000)
        pdhdr->qr = 1;

    pdhdr->opcode = (tmp >> 11) & 0xf;

    if (tmp & 0x0400)
        pdhdr->aa = 1;
    if (tmp & 0x0200)
        pdhdr->tc = 1;
    if (tmp & 0x0100)
        pdhdr->rd = 1;
    if (tmp & 0x0080)
        pdhdr->ra = 1;

    pdhdr->rcode = tmp & 0xf;
    pdhdr->qdcount = ntohs(*((uint16_t *) &msg[4]));
    pdhdr->ancount = ntohs(*((uint16_t *) &msg[6]));
    pdhdr->nscount = ntohs(*((uint16_t *) &msg[8]));
    pdhdr->arcount = ntohs(*((uint16_t *) &msg[10]));

    /* Now parse the variable length sections */
    cp = &msg[12];
    /* Question section */
    for (i = 0; i < pdhdr->qdcount; i++) {
        cp = dns_question(msg, cp);
    }
    /* Answer section */
    for (i = 0; i < pdhdr->ancount; i++) {
        cp = dns_answer(msg, cp, dst_result_ip);
    }
    /* Name server (authority) section */
    for (i = 0; i < pdhdr->nscount; i++) { ;
    }
    /* Additional section */
    for (i = 0; i < pdhdr->arcount; i++) { ;
    }

    if (pdhdr->rcode == 0)
        return 1;
    else
        return 0;
}

/**
*@brief		 解析回复消息的问询记录
*@param		 msg - 指向回复信息的指针
           cp  - 指向问询记录的指针
*@return	 返回下一个记录指针
*/
uint8_t *dns_question(uint8_t *msg, uint8_t *cp) {
    int16_t len = parse_name(msg, cp, MAX_DNS_BUF_SIZE);
    if (len == -1)return 0;
    cp += len;
    cp += 2; /* type */
    cp += 2; /* class */

    return cp;
}

static uint8_t *dns_answer(uint8_t *msg, uint8_t *cp, uint8_t *dst_result_ip) {
    int16_t len, type;
    len = parse_name(msg, cp, /*name,*/ MAX_DNS_BUF_SIZE);
    if (len == -1)
        return 0;

    cp += len;
    type = ntohs(*((uint16_t *) &cp[0]));
    cp += 2; /* type */
    cp += 2; /* class */
    cp += 4; /* ttl */
    cp += 2; /* len */

    switch (type) {
        case TYPE_A:
            dst_result_ip[0] = *cp++;
            dst_result_ip[1] = *cp++;
            dst_result_ip[2] = *cp++;
            dst_result_ip[3] = *cp++;
            break;
        case TYPE_CNAME:
        case TYPE_MB:
        case TYPE_MG:
        case TYPE_MR:
        case TYPE_NS:
        case TYPE_PTR:
            /* These types all consist of a single domain name */
            /* convert it to ascii format */
            len = parse_name(msg, cp, MAX_DNS_BUF_SIZE);
            if (len == -1)
                return 0;
            cp += len;
            break;
        case TYPE_HINFO:
            len = *cp++;
            cp += len;
            len = *cp++;
            cp += len;
            break;
        case TYPE_MX:
            cp += 2;
            /* Get domain name of exchanger */
            len = parse_name(msg, cp, MAX_DNS_BUF_SIZE);
            if (len == -1)
                return 0;
            cp += len;
            break;

        case TYPE_SOA:
            /* Get domain name of name server */
            len = parse_name(msg, cp, MAX_DNS_BUF_SIZE);
            if (len == -1)
                return 0;
            cp += len;
            /* Get domain name of responsible person */
            len = parse_name(msg, cp, MAX_DNS_BUF_SIZE);
            if (len == -1)
                return 0;

            cp += len;

            cp += 4;
            cp += 4;
            cp += 4;
            cp += 4;
            cp += 4;
            break;

        case TYPE_TXT:
            /* Just stash */
            break;

        default:
            /* Ignore */
            break;
    }

    return cp;
}

static int16_t parse_name(uint8_t *msg, uint8_t *compressed, uint16_t len) {
    static int8_t name[MAX_DNS_BUF_SIZE];
    uint16_t slen = 0; /* Length of current segment */
    uint8_t *cp = NULL;
    int16_t clen = 0;     /* Total length of compressed name */
    int16_t indirect = 0; /* Set if indirection encountered */
    int16_t nseg = 0;     /* Total number of segments in name */
    int8_t *buf = NULL;
    buf = name;
    cp = compressed;
    for (;;) {
        slen = *cp++; /* Length of this segment */

        if (!indirect)
            clen++;

        if ((slen & 0xc0) == 0xc0) {
            if (!indirect)
                clen++;
            indirect = 1;
            /* Follow indirection */
            cp = &msg[((slen & 0x3f) << 8) + *cp];
            slen = *cp++;
        }

        if (slen == 0) /* zero length == all done */
            break;

        len -= slen + 1;

        if (len <= 0)
            return -1;

        if (!indirect)
            clen += slen;

        while (slen-- != 0)
            *buf++ = (int8_t) *cp++;
        *buf++ = '.';
        nseg++;
    }

    if (nseg == 0) /* Root name; represent as single dot */
    {
        *buf++ = '.';
        len--;
    }

    *buf++ = '\0';
    len--;

    return clen; /* Length of compressed message */
}

测试

配置文件

/*******************************************************************************
 *  Copyright (c) [scl]。保留所有权利。
 *     本文仅供个人学习和研究使用,禁止用于商业用途。
 ******************************************************************************/

#include "app_conf.h"
#include "w5500_config.h"

#if APP_CONFIG_W5500
#define DBG_ENABLE
#define DBG_SECTION_NAME "w5500"
#define DBG_LEVEL W5500_DBG_LEVEL

#include "sys_dbg.h"
#include "w5500_dns.h"
#include "w5500_ntp.h"

#define W5500_CS stm_port_define(B,12)
#define W5500_RST stm_port_define(C,7)

extern struct net_date_time gb_app_time;


static SPI_HandleTypeDef *w5500_spi = NULL;

static void send_and_rec_bytes(uint8_t *in_dat, uint8_t *out_data, uint16_t len) {
    while (HAL_SPI_GetState(w5500_spi) != HAL_SPI_STATE_READY);
    HAL_SPI_TransmitReceive(w5500_spi, in_dat, out_data, len, 1000);
    while (HAL_SPI_GetState(w5500_spi) != HAL_SPI_STATE_READY);
}

static void send_only(uint8_t *in_data, uint16_t len) {
    HAL_SPI_Transmit(w5500_spi, in_data, len, 1000);
}

static void W5500_RST_HIGH(void) { stm_pin_high(W5500_RST); }

static void W5500_RST_LOW(void) { stm_pin_low(W5500_RST); }

static void W5500_CS_LOW(void) { stm_pin_low(W5500_CS); }

static void W5500_CS_HIGH(void) { stm_pin_high(W5500_CS); }

static void W5500_Driver_MspInit(void) {
    stm32_pin_mode(W5500_CS, pin_mode_output);  /*CS*/
    stm32_pin_mode(W5500_RST, pin_mode_output); /*RST*/
    stm_pin_low(W5500_RST);
    stm_pin_low(W5500_CS);
    /*初始化SPI外设*/
    /*W5500 支持 SPI 模式 0 及模式 3..MOSI 和 MISO 信号无论是接收或发送,均遵从从最高标志位(MSB)到最低标志位(LSB)的传输序列。*/
    bsp_SpiHandleInit(w5500_spi, SPI_BAUDRATEPRESCALER_2, spi_mode_3);
}

module_w5500_t w5500_conf = {
        .base_conf={
                .socket_num = 4,
                .rx_size={4, 4, 4, 4},
                .tx_size={4, 4, 4, 4},
        },
        .net_conf={
                .ip={192, 168, 199, 12},
                .gw={192, 168, 199, 1},
                .sub={255, 255, 255, 0},
        },
        .driver={
                .cs_high = W5500_CS_HIGH,
                .cs_low = W5500_CS_LOW,
                .rst_high= W5500_RST_HIGH,
                .rst_low=W5500_RST_LOW,
                .delay = HAL_Delay,
                .send_and_rec_bytes = send_and_rec_bytes,
                .send_only =send_only
        },
        .api = {
                .msp_init=W5500_Driver_MspInit,
        }
};
uint8_t ntp_domain[] = {"ntp.ntsc.ac.cn"}; /*ntp域名*/
static struct dns_conf net_dns_cnf = { /*dns服务配置*/
        .dns_server_ip={114, 114, 114, 114},
        .dns_port = 53,
        .delay_ms_cb = HAL_Delay
};
static struct ntp_conf net_ntp_conf = {
//        .ntp_server={114, 118, 7, 163},
        .ntp_port = 123,
        .delay_ms_cb = HAL_Delay
};


static void w5500_pre_init(void) {
    /*一般做数据加载,此时系统时钟使用的是内部时钟,如需要使用系统时钟的外设不在此进行初始化*/
    w5500_spi = conv_spi_handle_ptr(handle_get_by_id(spi2_id));
    /*初始化资源*/
    module_w5500_init(&w5500_conf);
    uint32_t uid0 = HAL_GetUIDw0();
    uint32_t uid1 = HAL_GetUIDw1();
    uint32_t uid2 = HAL_GetUIDw2();
    uint8_t mac[6] = {0, uid0 >> 8, uid1, uid1 >> 8, uid2, uid2 >> 8};
    memcpy(w5500_conf.net_conf.mac, mac, sizeof(mac));

}

static void w5500_init(void) {

    w5500_conf.api.msp_init();/*初始化*/
    w5500_conf.net_conf_init();
    uint8_t ip[4];
    w5500_reg_ip_read(ip);
    LOG_D("w5500_reg_ip_read:%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
    w5500_reg_gw_read(ip);
    LOG_D("w5500_reg_gw_read:%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
}


static void w5500_after_init(void) {
    uint8_t try_cnt = 3;// 尝试次数
    SOCKET sn = 1;/*使用的socket*/
#if APP_CONFIG_W5500_DNS // 启用dns
    for (int i = 0; i < try_cnt; ++i) {
        dns_config_set(&net_dns_cnf);
        if (do_dns(sn, ntp_domain, net_ntp_conf.ntp_server, 500)) {
            LOG_D("dns parse result ip:%d.%d.%d.%d",
                  net_ntp_conf.ntp_server[0], net_ntp_conf.ntp_server[1],
                  net_ntp_conf.ntp_server[2], net_ntp_conf.ntp_server[3]
            );
            goto next_step; /*解析成功跳转下一步*/
        }
    }
    LOG_D("do dns try cnt over");
    return;
    next_step:

#endif

#if APP_CONFIG_W5500_NTP // 启用ntp

    ntp_config_set(&net_ntp_conf);
    for (int i = 0; i < try_cnt; ++i) {
        if (ntp_date_time_get(sn, 500, &gb_app_time)) {
            HAL_TIM_Base_Start_IT(handle_get_by_id(tim6_id));
            goto exit_ok;
        }
    }
    LOG_W("ntp_date_time_get time out");
    return;
    exit_ok:
    LOG_D("NTP TIME:%d-%02d-%02d %02d:%02d:%02d",
          gb_app_time.year, gb_app_time.month, gb_app_time.day,
          gb_app_time.hour, gb_app_time.min, gb_app_time.sec
    );
#else
    return;
#endif
}

app_init_export(w5500_net_conf, w5500_pre_init, w5500_init, w5500_after_init);
#endif





结果

在这里插入图片描述

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

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

相关文章

JVM对象在堆内存中是否如何分配?

1&#xff1a;指针碰撞&#xff1a;内存规整的情况下 2&#xff1a;空闲列表: 内存不规整的情况下 选择那种分配方式 是有 java堆是否规整而决定的。而java堆是否规整是否对应的垃圾回收器是否带有空间压缩整理的能力决定的。 因此当使用Serial,ParNew等带有压缩整理过程的收…

win10环境下,应用无法启动并被删除(无法完成操作,因为文件包含病毒或潜在的垃圾文件)

现象&#xff1a; 解决办法&#xff1a; 一、关闭所有自己安装的杀毒软件&#xff0c;如&#xff1a;360安全卫士&#xff0c;金山毒霸等 二、关闭win10本身的杀毒软件&#xff0c;步骤如下&#xff1a; 1、搜索栏输入“病毒和威胁防护” 2、进入以后&#xff0c;点击"病…

Pytorch迁移学习使用Resnet50进行模型训练预测猫狗二分类

目录 1.ResNet残差网络 1.1 ResNet定义 1.2 ResNet 几种网络配置 1.3 ResNet50网络结构 1.3.1 前几层卷积和池化 1.3.2 残差块&#xff1a;构建深度残差网络 1.3.3 ResNet主体&#xff1a;堆叠多个残差块 1.4 迁移学习猫狗二分类实战 1.4.1 迁移学习 1.4.2 模型训练 1.…

vue3基础+进阶(二、vue3常用组合式api基本使用)

目录 第二章、组合式API 2.1 入口&#xff1a;setup 2.1.1 setup选项的写法和执行时机 2.1.2 setup中写代码的特点 2.1.3 script setup语法糖 2.1.4 setup中this的指向 2.2 生成响应式数据&#xff1a;reactive和ref函数 2.2.1 reactive函数 2.2.2 ref函数 2.2.3 rea…

Cesium态势标绘专题-入口

本专题没有废话,只有代码,撸! 标绘主类MilitaryPlotting.ts /** 态势标绘主类* @Author: Wang jianLei* @Date: 2023-01-13 14:47:20* @Last Modified by: jianlei wang* @Last Modified time: 2023-05-31 09:55:34*/ import * as Creator from ./create/index; import Cre…

S32K324双核的核间通信使用示例

文章目录 前言修改ld文件核0的ld文件核1的ld文件 定义共享数据使用共享数据编译共享数据文件总结 前言 最近项目用S32K324开发&#xff0c;暂时只用了MCAL&#xff0c;没有Autosar上层的模块&#xff0c;最开始用官方给的demo工程双核可以正常跑起来&#xff0c;但实际开发时都…

使用nginx和ffmpeg搭建HTTP FLV流媒体服务器(摄像头RTSP视频流->RTMP->http-flv)

名词解释 RTSP &#xff08;Real-Time Streaming Protocol&#xff09; 是一种网络协议&#xff0c;用于控制实时流媒体的传输。它是一种应用层协议&#xff0c;通常用于在客户端和流媒体服务器之间建立和控制媒体流的传输。RTSP允许客户端向服务器发送请求&#xff0c;如…

数据分析工具与技术

数据分析工具与技术 数据分析技术 数据分析工具 备选方案分析 一种对已识别的可选方案进行评估的技术&#xff0c;用来决定选择哪种方案 或使用何种方法来执行项目工作。 其他风险参数评估 为了方便未来分析和行动&#xff0c;在对单个项目风险进行优先级排序时&#xff0…

GO内存模型(同步机制)

文章目录 概念1. 先行发生 编译器重排同步机制init函数协程的创建channelsync 包1. sync.mutex2. sync.rwmutex3. sync.once atomic 参考文献 概念 1. 先行发生 The happens before relation is defined as the transitive closure of the union of the sequenced before and …

超详细-Vivado配置Sublime+Sublime实现Verilog语法实时检查

目录 一、前言 二、准备工作 三、Vivado配置Sublime 3.1 Vivado配置Sublime 3.2 环境变量添加 3.3 环境变量验证 3.4 Vivado设置 3.5 配置验证 3.6 解决Vivado配置失败问题 四、Sublime配置 4.1 Sublime安装Package Control 4.2 Sublime安装Verilog插件 4.3 安装语…

centos7中MySQL备份还原策略

目录 一、直接拷贝数据库文件 1.1在shangke主机上停止服务并且打包压缩数据库文件 1.2 在shangke主机上把数据库文件传输到localhost主机上(ip为192.168.33.157) 1.3在localhost主机上停止服务&#xff0c;解压数据库文件 1.4 在localhost主机上开启服务 1.5 测试 二、m…

利用@Excel实现复杂表头导入

EasyPoi导入 <a-upload name"file" :showUploadList"false" :multiple"false" :headers"tokenHeader" :action"importExcelUrl"change"handleImportExcel"><a-button type"primary" icon&quo…

【软件测试】如何选择回归用例

目录 如何在原始用例集中挑选测试用例 具体实践 总结 本文讨论一下在回归测试活动中&#xff0c;如何选择测试用例集。 回归测试用例集包括基本测试用例集&#xff08;原始用例&#xff09;迭代新增测试用例集&#xff08;修复故障引入的用例和新增功能引入的用例集&#xf…

洛必达法则和分部积分的应用之计算数学期望EX--概率论浙大版填坑记

如下图所示&#xff0c;概率论与数理统计浙大第四版有如下例题&#xff1a; 简单说就是&#xff1a;已知两个相互独立工作电子装置寿命的概率密度函数&#xff0c;将二者串联成整机&#xff0c;求整机寿命的数学期望。 这个题目解答中的微积分部分可谓是相当的坑爹&#xff0c;…

【1++的C++初阶】之适配器

&#x1f44d;作者主页&#xff1a;进击的1 &#x1f929; 专栏链接&#xff1a;【1的C初阶】 文章目录 一&#xff0c;什么是适配器二&#xff0c;栈与队列模拟实现三&#xff0c;优先级队列四&#xff0c;reverse_iterator 一&#xff0c;什么是适配器 适配器作为STL的六大组…

【高阶数据结构】跳表

文章目录 一、什么是跳表二、跳表的效率如何保证&#xff1f;三、skiplist的实现四、skiplist跟平衡搜索树和哈希表的对比 一、什么是跳表 skiplist本质上也是一种查找结构&#xff0c;用于解决算法中的查找问题&#xff0c;跟平衡搜索树和哈希表的价值是 一样的&#xff0c;可…

Windows环境Docker安装

目录 安装Docker Desktop的步骤 Docker Desktop 更新WSL WSL 的手动安装步骤 Windows PowerShell 拉取&#xff08;Pull&#xff09;镜像 查看已下载的镜像 输出"Hello Docker!" Docker Desktop是Docker官方提供的用于Windows的图形化桌面应用程序&#xff0c…

区间预测 | MATLAB实现QRBiLSTM双向长短期记忆神经网络分位数回归多输入单输出区间预测

区间预测 | MATLAB实现QRBiLSTM双向长短期记忆神经网络分位数回归多输入单输出区间预测 目录 区间预测 | MATLAB实现QRBiLSTM双向长短期记忆神经网络分位数回归多输入单输出区间预测效果一览基本介绍模型描述程序设计参考资料 效果一览 基本介绍 区间预测 | MATLAB实现QRBiLSTM…

odoo16 用好计量单位中的激活功能

odoo16 用好计量单位中的激活功能 根据国内常用&#xff0c;把不常用的单位去除&#xff0c;删除不了&#xff0c;提示已用&#xff0c;其实不用删除&#xff0c;每个单位后有个激活功能&#xff0c;选一下就可以了&#xff0c;显示成整洁的界面了 第一次用时&#xff0c;小伙伴…

解决spring cloud 中使用spring security全局异常处理器失效

写auth认证模块实现忘记密码与注册功能时&#xff0c;用异常抛出&#xff0c;全局异常处理器无法捕获。 无法进行异常捕捉 解决方案&#xff1a;使用WebSecurityConfigurerAdapter.configure中http实现自定义异常&#xff1a; EnableWebSecurity EnableGlobalMethodSecurity(…