06_W5500_DHCP

news2024/11/23 15:12:36

1.DHCP协议介绍:

        DHCP(Dynamic Host Configuration Protocol)是一种用于自动分配IP地址和其他网络配置信息的协议。它允许网络中的设备(如计算机、手机、打印机等)在连接到网络时自动获取IP地址、子网掩码、默认网关、DNS服务器等信息,而无需手动配置。

DHCP工作原理是通过DHCP服务器向网络中的设备提供IP地址和其他配置信息。当设备连接到网络时,它会发送一个DHCP请求,请求一个可用的IP地址和其他配置信息。DHCP服务器收到请求后,会分配一个可用的IP地址,并将其他配置信息一并发送给设备,使设备能够顺利地加入到网络中。

DHCP的优点是简化了网络管理,减少了手动配置的工作量,同时也提高了网络的灵活性和可扩展性。通过DHCP,管理员可以更轻松地管理网络中的设备,并且可以更快速地进行网络扩展和变更。

2.DHCP报文

DHCP报文种类:

DHCP一共有8中报文,各种类型报文的基本功能如下:

报文类型说明
Discover(0x01)    DHCP客户端在请求IP地址时并不知道DHCP服务器的位置,因此DHCP客户端会在本地网络内以广播方式发送Discover请求报文,以发现网络中的DHCP服务器。所有收到Discover报文的DHCP服务器都会发送应答报文,DHCP客户端据此可以知道网络中存在的DHCP服务器的位置。
Offer(0x02)DHCP服务器收到Discover报文后,就会在所配置的地址池中查找一个合适的IP地址,加上相应的租约期限和其他配置信息(如网关、DNS服务器等),构造一个Offer报文,发送给DHCP客户端,告知用户本服务器可以为其提供IP地址。但这个报文只是告诉DHCP客户端可以提供IP地址,最终还需要客户端通过ARP来检测该IP地址是否重复。
Request(0x03) DHCP客户端可能会收到很多Offer请求报文,所以必须在这些应答中选择一个。通常是选择第一个Offer应答报文的服务器作为自己的目标服务器,并向该服务器发送一个广播的Request请求报文,通告选择的服务器,希望获得所分配的IP地址。另外,DHCP客户端在成功获取IP地址后,在地址使用租期达到50%时,会向DHCP服务器发送单播Request请求报文请求续延租约,如果没有收到ACK报文,在租期达到87.5%时,会再次发送广播的Request请求报文以请求续延租约。
Decline(0x04)DHCP客户端收到DHCP服务器ACK应答报文后,通过地址冲突检测发现服务器分配的地址冲突或者由于其他原因导致不能使用,则会向DHCP服务器发送Decline请求报文,通知服务器所分配的IP地址不可用,以期获得新的IP地址。
ACK(0x05) DHCP服务器收到Request请求报文后,根据Request报文中携带的用户MAC来查找有没有相应的租约记录,如果有则发送ACK应答报文,通知用户可以使用分配的IP地址。
NAK(0x06如果DHCP服务器收到Request请求报文后,没有发现有相应的租约记录或者由于某些原因无法正常分配IP地址,则向DHCP客户端发送NAK应答报文,通知用户无法分配合适的IP地址。
Release(0x07) 当DHCP客户端不再需要使用分配IP地址时(一般出现在客户端关机、下线等状况)就会主动向DHCP服务器发送RELEASE请求报文,告知服务器用户不再需要分配IP地址,请求DHCP服务器释放对应的IP地址。
Inform(0x08) DHCP客户端如果需要从DHCP服务器端获取更为详细的配置信息,则向DHCP服务器发送Inform请求报文;DHCP服务器在收到该报文后,将根据租约进行查找到相应的配置信息后,向DHCP客户端发送ACK应答报文。目前基本上不用了。
DHCP报文格式:

字段长度说明
op1byte是报文的操作类型,分为请求报文和响应报文,1为请求报文;2为响应报文。具体的报文类型在option字段中标识。
htype1byte硬件地址的长度,以太网的硬件地址长度为6bytes。
hlen1byte硬件地址的长度,以太网的硬件地址长度为6bytes。
hops1byte表示当前dhcp报文经过的DHCP中继的数目,每经过一个DHCP中继这个字段就加1.
xid4bytes由client端产生的随机数,用于匹配请求和应答报文,就是匹配应答报文是对哪个请求报文做出应答。
secs2bytes客户端进入IP地址申请进程的时间或者更新IP地址进程的时间;由客户端软件根据情况设定。目前没有使用,固定为0。
flags2bytes是标志字段,16比特中只使用了最高位比特(即最左边的比特),这个个比特是广播响应标识位,用来标识DHCP服务器发出的响应报文是广播还是单播,0是单播,1是广播。其余的比特位保留不用,都为0.
ciaddr4bytes是客户端的IP地址,可以是client自己的IP地址,也可以是server分配给client的IP地址。
yiaddr4bytes(Your IP Address),是server分配给client的IP地址。
siaddr4bytes是client端获取IP地址等信息的server端的地址。
giaddr4bytes是client发出请求报文后经过的第一个中继的IP地址。
chaddr16bytes是client端的硬件地址,在client发出报文时会把自己网卡的硬件地址写进这个字段。
sname64bytes服务器主机名,是client端获取IP地址等信息的服务器名称。
file128bytes是client的启动配置文件名,是服务器为client指定的启动配置文件名及路径信息,由服务器填写。
options可变是可选变长的选项字段,这个字段包含了终端的初始配置信息和网络配置信息,包括报文类型,有效租期,DNS服务器的IP地址等配置信息。

2.DHCP流程:

DHCP的流程分为四个阶段:

分别是:

1.发现阶段:DHCP客户端向DHCP服务端发送DHCP_DISCOVER报文。

2.提供阶段:DHCP服务端收到客户端DHCP_DISCOVER报文后,向发送DHCP客户端发送DHCP_OFFER报文,该报文中包含尚未分配的地址信息。

3.选择阶段:DHCP客户端选择IP地址,然后广播发送DHCP_REQUEST包,宣告使用它挑选的DHCP服务器地址并正式请求DHCP服务器分配地址

4.确认阶段:当DHCP服务器收到DHCP客户端的DHCP_REQUEST包后,便向客户端发送包含它所提供的IP地址及其他配置信息的DHCPACK确认包。

3.代码分析:

首先初始化单片机的外设:时钟、spi、uart、24c02、配置w5500参数等。

然后调用函数:init_dhcp_client函数对DHCP进行初始化,主要设置ip、mac等信息,把dhcp_state状态设置为STATE_DHCP_READY

void init_dhcp_client(void)
{
    uint8 txsize[MAX_SOCK_NUM] = {2, 2, 2, 2, 2, 2, 2, 2};
    uint8 rxsize[MAX_SOCK_NUM] = {2, 2, 2, 2, 2, 2, 2, 2};
    //uint8 ip_broadcast[4]={255,};
    uint8 ip_0[4] = {0,};
    DHCP_XID = 0x12345678;
    memset(OLD_SIP, 0, sizeof(OLD_SIP));
    memset(DHCP_SIP, 0, sizeof(DHCP_SIP));
    memset(DHCP_REAL_SIP, 0, sizeof(GET_SN_MASK));

    iinchip_init();

    setSHAR(ConfigMsg.mac);
    setSUBR(ip_0);
    setGAR(ip_0);
    setSIPR(ip_0);
    printf("MAC=%02x:%02x:%02x:%02x:%02x:%02x\r\n", DHCP_CHADDR[0], DHCP_CHADDR[1], DHCP_CHADDR[2], DHCP_CHADDR[3], DHCP_CHADDR[4], DHCP_CHADDR[5]);
    sysinit(txsize, rxsize);
    //clear ip setted flag

    dhcp_state = STATE_DHCP_READY;
    #ifdef _DHCP_DEBUG
    printf("init_dhcp_client:%u\r\n", SOCK_DHCP);
    #endif
}

进入while循环执行DHCP_run()函数

uint8_t DHCP_run(void)
{
    uint8_t  type;
    uint8_t  ret;

    if(dhcp_state == STATE_DHCP_STOP) return DHCP_STOPPED;

    if(getSn_SR(SOCK_DHCP) != SOCK_UDP)
        socket(SOCK_DHCP, Sn_MR_UDP, DHCP_CLIENT_PORT, 0x00);

    ret = DHCP_RUNNING;
    type = parseDHCPMSG();

    switch ( dhcp_state )
    {
        case STATE_DHCP_READY :
            DHCP_allocated_ip[0] = 0;
            DHCP_allocated_ip[1] = 0;
            DHCP_allocated_ip[2] = 0;
            DHCP_allocated_ip[3] = 0;

            send_DHCP_DISCOVER();
            dhcp_time = 0;
            dhcp_state = STATE_DHCP_DISCOVER;
            break;

        case STATE_DHCP_DISCOVER :
            if (type == DHCP_OFFER)
            {
                #ifdef _DHCP_DEBUG_
                printf("> Receive DHCP_OFFER\r\n");
                #endif
                DHCP_allocated_ip[0] = pDHCPMSG->yiaddr[0];
                DHCP_allocated_ip[1] = pDHCPMSG->yiaddr[1];
                DHCP_allocated_ip[2] = pDHCPMSG->yiaddr[2];
                DHCP_allocated_ip[3] = pDHCPMSG->yiaddr[3];

                send_DHCP_REQUEST();
                dhcp_time = 0;
                dhcp_state = STATE_DHCP_REQUEST;
            }
            else
                ret = check_DHCP_timeout();

            break;

        case STATE_DHCP_REQUEST :
            if (type == DHCP_ACK)
            {
                #ifdef _DHCP_DEBUG_
                printf("> Receive DHCP_ACK\r\n");
                #endif

                if (check_DHCP_leasedIP())
                {
                    // Network info assignment from DHCP
                    printf("ip:%d.%d.%d.%d\r\n", DHCP_allocated_ip[0], DHCP_allocated_ip[1], DHCP_allocated_ip[2], DHCP_allocated_ip[3]);
                    printf("sn:%d.%d.%d.%d\r\n", DHCP_allocated_sn[0], DHCP_allocated_sn[1], DHCP_allocated_sn[2], DHCP_allocated_sn[3]);
                    printf("gw:%d.%d.%d.%d\r\n", DHCP_allocated_gw[0], DHCP_allocated_gw[1], DHCP_allocated_gw[2], DHCP_allocated_gw[3]);
                    dhcp_ip_assign();
                    reset_DHCP_timeout();
                    dhcp_state = STATE_DHCP_LEASED;
                }
                else
                {
                    // IP address conflict occurred
                    reset_DHCP_timeout();
                    dhcp_ip_conflict();
                    dhcp_state = STATE_DHCP_READY;
                }
            }
            else if (type == DHCP_NAK)
            {
                #ifdef _DHCP_DEBUG_
                printf("> Receive DHCP_NACK\r\n");
                #endif
                reset_DHCP_timeout();
                dhcp_state = STATE_DHCP_DISCOVER;
            }
            else
                ret = check_DHCP_timeout();

            break;

        case STATE_DHCP_LEASED :
				
            ret = DHCP_IP_LEASED;

            if ((dhcp_lease_time != DEFAULT_LEASETIME) && ((dhcp_lease_time / 2) < dhcp_time))
            {

                #ifdef _DHCP_DEBUG_
                printf("> Maintains the IP address \r\n");
                #endif
                type = 0;
                OLD_allocated_ip[0] = DHCP_allocated_ip[0];
                OLD_allocated_ip[1] = DHCP_allocated_ip[1];
                OLD_allocated_ip[2] = DHCP_allocated_ip[2];
                OLD_allocated_ip[3] = DHCP_allocated_ip[3];

                DHCP_XID++;
                send_DHCP_REQUEST();
                reset_DHCP_timeout();
                dhcp_state = STATE_DHCP_REREQUEST;
            }

            break;

        case STATE_DHCP_REREQUEST :
            ret = DHCP_IP_LEASED;

            if (type == DHCP_ACK)
            {
                dhcp_retry_count = 0;

                if (OLD_allocated_ip[0] != DHCP_allocated_ip[0] ||
                        OLD_allocated_ip[1] != DHCP_allocated_ip[1] ||
                        OLD_allocated_ip[2] != DHCP_allocated_ip[2] ||
                        OLD_allocated_ip[3] != DHCP_allocated_ip[3])
                {
                    ret = DHCP_IP_CHANGED;
                    dhcp_ip_update();
                    #ifdef _DHCP_DEBUG_
                    printf(">IP changed.\r\n");
                    #endif
                }

                #ifdef _DHCP_DEBUG_
                else printf(">IP is continued.\r\n");

                #endif
                reset_DHCP_timeout();
                dhcp_state = STATE_DHCP_LEASED;
            }
            else if (type == DHCP_NAK)
            {
                #ifdef _DHCP_DEBUG_
                printf("> Receive DHCP_NACK, Failed to maintain ip\r\n");
                #endif
                reset_DHCP_timeout();
                dhcp_state = STATE_DHCP_DISCOVER;
            }
            else ret = check_DHCP_timeout();

            break;

        default :
            break;
    }

    return ret;
}

分析DHCP_run函数:

1.  打开socket,调用parseDHCPMSG()函数,接受数据并解析,将收到的数据进行保存。

int8_t parseDHCPMSG(void)//20180625
{
    uint8_t svr_addr[6];
    uint16_t  svr_port;
    uint16_t len;

    uint8_t * p;
    uint8_t * e;
    uint8_t type;
    uint8_t opt_len;

    if((len = getSn_RX_RSR(SOCK_DHCP)) > 0)
    {
        len = recvfrom(SOCK_DHCP, (uint8_t *)pDHCPMSG, len, svr_addr, &svr_port);
        #ifdef _DHCP_DEBUG_
        printf("DHCP message : %d.%d.%d.%d(%d) %d received. \r\n", svr_addr[0], svr_addr[1], svr_addr[2], svr_addr[3], svr_port, len);
        #endif
    }
    else return 0;

    if (svr_port == DHCP_SERVER_PORT)
    {
        // compare mac address
        if ( (pDHCPMSG->chaddr[0] != DHCP_CHADDR[0]) || (pDHCPMSG->chaddr[1] != DHCP_CHADDR[1]) ||
                (pDHCPMSG->chaddr[2] != DHCP_CHADDR[2]) || (pDHCPMSG->chaddr[3] != DHCP_CHADDR[3]) ||
                (pDHCPMSG->chaddr[4] != DHCP_CHADDR[4]) || (pDHCPMSG->chaddr[5] != DHCP_CHADDR[5])   )
            return 0;

        type = 0;
        p = (uint8_t *)(&pDHCPMSG->op);//2
        p = p + 240;      // 240 = sizeof(RIP_MSG) + MAGIC_COOKIE size in RIP_MSG.opt - sizeof(RIP_MSG.opt)
        e = p + (len - 240);

        while ( p < e )
        {
            switch ( *p )
            {
                case endOption :
                    p = e;   // for break while(p < e)
                    break;

                case padOption :
                    p++;
                    break;

                case dhcpMessageType :
                    p++;
                    p++;
                    type = *p++;
                    break;

                case subnetMask :
                    p++;
                    p++;
                    DHCP_allocated_sn[0] = *p++;
                    DHCP_allocated_sn[1] = *p++;
                    DHCP_allocated_sn[2] = *p++;
                    DHCP_allocated_sn[3] = *p++;
                    break;

                case routersOnSubnet :
                    p++;
                    opt_len = *p++;
                    DHCP_allocated_gw[0] = *p++;
                    DHCP_allocated_gw[1] = *p++;
                    DHCP_allocated_gw[2] = *p++;
                    DHCP_allocated_gw[3] = *p++;
                    p = p + (opt_len - 4);
                    break;

                case dns :
                    p++;
                    opt_len = *p++;
                    DHCP_allocated_dns[0] = *p++;
                    DHCP_allocated_dns[1] = *p++;
                    DHCP_allocated_dns[2] = *p++;
                    DHCP_allocated_dns[3] = *p++;
                    p = p + (opt_len - 4);
                    break;

                case dhcpIPaddrLeaseTime :
                    p++;
                    opt_len = *p++;
                    dhcp_lease_time  = *p++;
                    dhcp_lease_time  = (dhcp_lease_time << 8) + *p++;
                    dhcp_lease_time  = (dhcp_lease_time << 8) + *p++;
                    dhcp_lease_time  = (dhcp_lease_time << 8) + *p++;
                    #ifdef _DHCP_DEBUG_
                    //               dhcp_lease_time = 10;
                    //				printf("dhcp_lease_time:%d\r\n",(int)dhcp_lease_time);
                    #endif
                    break;

                case dhcpServerIdentifier :
                    p++;
                    opt_len = *p++;
                    DHCP_SIP[0] = *p++;
                    DHCP_SIP[1] = *p++;
                    DHCP_SIP[2] = *p++;
                    DHCP_SIP[3] = *p++;
                    break;

                default :
                    p++;
                    opt_len = *p++;
                    p += opt_len;
                    break;
            } // switch
        } // while
    } // if

    return	type;
}

2.然后进入switch状态机,首先dhcp_state的值为STATE_DHCP_READY,执行一下代码:

进入发现阶段:DHCP客户端向DHCP服务端发送DHCP_DISCOVER报文,然后把dhcp_state的值设为STATE_DHCP_DISCOVER。

 case STATE_DHCP_READY :
            DHCP_allocated_ip[0] = 0;
            DHCP_allocated_ip[1] = 0;
            DHCP_allocated_ip[2] = 0;
            DHCP_allocated_ip[3] = 0;

            send_DHCP_DISCOVER();
            dhcp_time = 0;
            dhcp_state = STATE_DHCP_DISCOVER;
            break;

3.然后退出switch,再次调用parseDHCPMSG()函数去接受DHCP服务端发来的数据并解析,再次进入switch状态机中,执行STATE_DHCP_DISCOVER代码,如果数据包的类型是DHCP_OFFER

就执行以下代码:DHCP客户端选择IP地址,然后广播发送DHCP_REQUEST包,宣告使用它挑选的DHCP服务器地址并正式请求DHCP服务器分配地址。

 case STATE_DHCP_DISCOVER :
            if (type == DHCP_OFFER)
            {
                #ifdef _DHCP_DEBUG_
                printf("> Receive DHCP_OFFER\r\n");
                #endif
                DHCP_allocated_ip[0] = pDHCPMSG->yiaddr[0];
                DHCP_allocated_ip[1] = pDHCPMSG->yiaddr[1];
                DHCP_allocated_ip[2] = pDHCPMSG->yiaddr[2];
                DHCP_allocated_ip[3] = pDHCPMSG->yiaddr[3];

                send_DHCP_REQUEST();
                dhcp_time = 0;
                dhcp_state = STATE_DHCP_REQUEST;
            }

 4.如果正确接收到DHCP服务端的DHAP_ACK数据包,则DHCP请求就成功了。执行一下代码:

若是DHAP_ACK数据包,则调用check_DHCP_leasedIP()函数检查IP是否有效,然后我们去设置w5500的IP等信息,把dhcp_state状态设为STATE_DHCP_LEASED,若是DHCP_NAK数据包,调用reset_DHCP_timeout()把超时时间复位, 然后将dhcp_state值改为STATE_DHCP_DISCOVER重新进入发现阶段。

 case STATE_DHCP_REQUEST :
            if (type == DHCP_ACK)
            {
                #ifdef _DHCP_DEBUG_
                printf("> Receive DHCP_ACK\r\n");
                #endif

                if (check_DHCP_leasedIP())
                {
                    // Network info assignment from DHCP
                    printf("ip:%d.%d.%d.%d\r\n", DHCP_allocated_ip[0], DHCP_allocated_ip[1], DHCP_allocated_ip[2], DHCP_allocated_ip[3]);
                    printf("sn:%d.%d.%d.%d\r\n", DHCP_allocated_sn[0], DHCP_allocated_sn[1], DHCP_allocated_sn[2], DHCP_allocated_sn[3]);
                    printf("gw:%d.%d.%d.%d\r\n", DHCP_allocated_gw[0], DHCP_allocated_gw[1], DHCP_allocated_gw[2], DHCP_allocated_gw[3]);
                    dhcp_ip_assign();
                    reset_DHCP_timeout();
                    dhcp_state = STATE_DHCP_LEASED;
                }
                else
                {
                    // IP address conflict occurred
                    reset_DHCP_timeout();
                    dhcp_ip_conflict();
                    dhcp_state = STATE_DHCP_READY;
                }
            }
            else if (type == DHCP_NAK)
            {
                #ifdef _DHCP_DEBUG_
                printf("> Receive DHCP_NACK\r\n");
                #endif
                reset_DHCP_timeout();
                dhcp_state = STATE_DHCP_DISCOVER;
            }
            else
                ret = check_DHCP_timeout();

            break;

05_W5500_UDP通信 <--------- 上一篇                                                                                                下一篇----------> 07——W5500

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

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

相关文章

数据入表 | 详解数据资产会计核算与企业应对

从2015年《促进大数据发展行动纲要》到2022年《数据20条》到2023年8月份出台了《企业数据资源相关会计处理暂行规定》&#xff0c;可见国家层面对数据的重视和探索如何进一步挖掘数据价值&#xff0c;发挥数据的应用潜力。一石激起千层浪&#xff0c;面对如此重要的规定&#x…

C++1114新标准——统一初始化(Uniform Initialization)、Initializer_list(初始化列表)、explicit

系列文章目录 C11&14新标准——Variadic templates&#xff08;数量不定的模板参数&#xff09; C11&14新标准——Uniform Initialization&#xff08;统一初始化&#xff09;、Initializer_list&#xff08;初始化列表&#xff09;、explicit 文章目录 系列文章目录1…

python+pytest接口自动化(12)-自动化用例编写思路 (使用pytest编写一个测试脚本)

经过之前的学习铺垫&#xff0c;我们尝试着利用pytest框架编写一条接口自动化测试用例&#xff0c;来厘清接口自动化用例编写的思路。 我们在百度搜索天气查询&#xff0c;会出现如下图所示结果&#xff1a; 接下来&#xff0c;我们以该天气查询接口为例&#xff0c;编写接口测…

【Java 基础】32 定时调度

文章目录 Timer 类创建 Timer注意事项 ScheduledExecutorService 接口创建 ScheduledExecutorService注意事项 选择合适的定时调度方式Timer 的适用场景ScheduledExecutorService 的适用场景 总结 在软件开发中&#xff0c;定时任务是一种常见的需求&#xff0c;用于周期性地执…

了解振弦采集仪:工程质量控制的得力助手

了解振弦采集仪&#xff1a;工程质量控制的得力助手 振弦采集仪是一种专门用于工程质量控制的仪器设备&#xff0c;它可以帮助工程师监测和评估结构物的振动性能。它的工作原理是通过将传感器固定在结构物上的振弦上&#xff0c;然后测量振弦的振动频率、振动幅度等参数&#…

GPTs prompts灵感库:创意无限,专业级创作指南,打造吸睛之作的秘诀

GPTs prompts灵感库&#xff1a;创意无限&#xff0c;专业级创作指南&#xff0c;打造吸睛之作的秘诀 优质prompt展示 1.1 极简翻译 中英文转换 你是一个极简翻译工具&#xff0c;请在对话中遵循以下规则&#xff1a; - Prohibit repeating or paraphrasing any user instru…

mysql更新某个字段=这个字段+字符串

当我们像c#中用拼接执行sql语句时&#xff0c;如下&#xff1a; UPDATE abpusers set UserNameqyUserName where UserNameqy-wh 会出现以下错误&#xff1a; 在mysql中通过concat函数来实现 UPDATE abpusers set UserNameCONCAT(qy_,UserName) where UserNameqy-wh

Unity中实现ShaderToy卡通火(总结篇)

文章目录 前言一、把卡通火修改为后处理效果1、在Shader属性面板定义属性接收帧缓存纹理2、在片元着色器对其纹理采样后&#xff0c;与卡通火相加输出请添加图片描述 二、我们自定义卡通火1、修改 _CUTOFF 使卡通火显示在屏幕两侧2、使火附近屏幕偏红色 前言 在之前的文章中&a…

PP材料粘接ABS材料使用UV胶的好处?

跟随着现阶段材料的不断发展更迭&#xff0c;PP材料应用越来越广&#xff0c;生产效率要求越来越高&#xff0c;为了加快生产&#xff0c;提高效率&#xff0c;PP材料的粘接上使用UV胶粘接PP&#xff08;聚丙烯&#xff09;和ABS&#xff08;丙烯腈-丁二烯-苯乙烯共聚物&#x…

AI日报:人工智能与新材料的发现

文章目录 总览人工智能正在革命性地发现新的或更强的材料&#xff0c;这将改变制造业。更坚韧的合金问题研究解决方案 新材料人工智能存在的挑战方法探索 日本的研究人员正在使用人工智能制造更强的金属合金或发现新材料&#xff0c;并彻底改变制造过程 总览 日本的研究人员开…

解决使用pnpm安装时Sharp模块报错的方法

在使用pnpm进行项目依赖安装的过程中&#xff0c;有时候会遇到Sharp模块报错的情况。Sharp是一个用于处理图像的Node.js模块&#xff0c;但它的安装可能会因为各种原因而失败&#xff0c;导致项目无法正常启动。本文将介绍这个问题的方法。 问题描述 解决方法 在命令行分别输…

【Zerotier】自建PLANET服务器内网地址连接

之前已经完成了【Zerotier】通过docker自建PLANET服务器&#xff0c;但是遇到一个问题&#xff0c;因为各种原因&#xff0c;内网里面的ZeroTier Client无法通过PLANET服务器的公网地址连接&#xff0c;愁怀了这下。在经过多方测试验证后&#xff0c;可以采取重新生成一个PLANE…

MYSQL练题笔记-子查询-电影评分

一、题目相关内容 1&#xff09;相关的表 2&#xff09;题目 3&#xff09;帮助理解题目的示例&#xff0c;提供返回结果的格式 二、自己初步的理解 1.字典序是指从前到后比较两个字符串大小的方法。 首先比较第1个字符&#xff0c;如果不同则第1个字符较小的字符串更小&…

Linux install manual 1Panel

前言 1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。1Panel 的功能和优势包括: 快速建站:深度集成 Wordpress 和 Halo,域名绑定、SSL 证书配置等一键搞定;高效管理:通过 Web 端轻松管理 Linux 服务器,包括主机监控、文件管理、数据库管理、容器管理等;安全可…

CentOS 7.x操作系统的ECS云服务器上搭建WordPress网站

WordPress是使用PHP语言开发的博客平台&#xff0c;在支持PHP和MySQL数据库的服务器上&#xff0c;您可以用WordPress架设自己的网站&#xff0c;也可以用作内容管理系统&#xff08;CMS&#xff09;。本教程介绍如何在CentOS 7.x操作系统的ECS实例上搭建WordPress网站。 背景…

指针浅谈(四)

在指针浅谈(三)中http://t.csdnimg.cn/wYgJG我们知道了数组名是什么&#xff0c;任何用指针访问数组&#xff0c;一维数组传参的本质是什么&#xff0c;这一次我们来学习二级指针&#xff0c;指针数组&#xff0c;以及如何用指针数组模拟二维数组。 1.二级指针 指针变量也是变…

数据爬虫:获取申万一级行业数据

目录 1. 获取访问接口 2. 链接网址 3. 链接名单 免责声明&#xff1a;本文由作者参考相关资料&#xff0c;并结合自身实践和思考独立完成&#xff0c;对全文内容的准确性、完整性或可靠性不作任何保证。同时&#xff0c;文中提及的数据仅作为举例使用&#xff0c;不构成推荐…

1-4、JDK目录结构

语雀原文链接 文章目录 1、目录结构2、JDK中rt.jar、tools.jar和dt.jar作用3、bin目录部分说明&#xff08;基本工具&#xff09; 1、目录结构 bin目录&#xff1a;包含一些用于开发Java程序的工具&#xff0c;例如&#xff1a;编译工具(javac.exe)、运行工具 (java.exe) 、打…

[Python进阶] 消息框、弹窗:批处理消息框\msg

5.20 消息框、弹窗&#xff1a;批处理消息框\msg 批处理中的MSG命令是一个用于向用户发送消息的命令。它可以用来在批处理脚本中向指定的用户发送消息。 命令格式为MSG {username | sessionname | sessionid | filename | *}[/SERVER:servername] [/TIME:seconds] [/V] [/W] […

网络安全这条路,如何打怪升级干掉大Boss?

企业对网络安全的重视是挂在嘴上还是落实在行动中&#xff1f;网络安全人员岗位设置是否合理而有效&#xff1f;网络安全从业者最需要什么样的技能培训&#xff1f;网络安全从业者的职业发展路径应该如何规划&#xff1f;一份“网络安全从业人员现状调查”报告&#xff0c;解你…