当前位置: 首页 > news >正文

LWIP之一:使用STM32CubeMX搭建基于FreeRTOS的LWIP工程并分析协议栈初始化过程

工程搭建及LWIP协议栈初始化过程

  • 一、使用STM32CubeMX快速生成工程
  • 二、修改测试
  • 三、LWIP协议栈初始化过程分析
    • 3.1 tcpip_init()
      • 3.1.1 lwip_init()
        • 3.1.1.1 sys_init()
        • 3.1.1.2 mem_init()
        • 3.1.1.3 memp_init()
        • 3.1.1.4 netif_init()
        • 3.1.1.5 udp_init()
        • 3.1.1.6 tcp_init()
        • 3.1.1.7 igmp_init()
        • 3.1.1.8 dns_init()
        • 3.1.1.9 ppp_init()
        • 3.1.1.10 sys_timeouts_init()
      • 3.1.2 sys_mbox_new(&tcpip_mbox, TCPIP_MBOX_SIZE)
      • 3.1.3 sys_mutex_new(&lock_tcpip_core)
      • 3.1.4 tcpip_thread
    • 3.2 netif_add()
    • 3.3 netif_set_default()和netif_set_up()函数
    • 3.4 netif_set_link_callback()和netif_set_status_callback()函数
    • 3.5 ethernet_link_thread线程
    • 3.6 dhcp_start()

一、使用STM32CubeMX快速生成工程

首先使用STM32CubeMX配置好FreeRTOS之后,使能LWIP:
在这里插入图片描述
根据PHY芯片类型设置平台(我使用的是LAN8720A,选择8742就可以):
在这里插入图片描述
根据需要选择是否使用DHCP和需要支持的协议类型:
在这里插入图片描述
根据需要选择要支持的回调函数:
在这里插入图片描述
生成工程。

二、修改测试

根据需要添加PHY芯片的复位操作,比如LAN8720A硬件复位需要将 RESETn 引脚拉低至少 100μs。
在这里插入图片描述

修改网卡状态回调函数:

static void netif_status_updated(struct netif *netif)
{printf("\r\nenter netif_status_updated func\r\n");if (netif_is_up(netif)){printf("link available\r\n");printf("IP:%hhu.%hhu.%hhu.%hhu\r\n",ip4_addr1_val(netif->ip_addr),ip4_addr2_val(netif->ip_addr),ip4_addr3_val(netif->ip_addr),ip4_addr4_val(netif->ip_addr));printf("NM:%hhu.%hhu.%hhu.%hhu\r\n",ip4_addr1_val(netif->netmask),ip4_addr2_val(netif->netmask),ip4_addr3_val(netif->netmask),ip4_addr4_val(netif->netmask));printf("GW:%hhu.%hhu.%hhu.%hhu\r\n",ip4_addr1_val(netif->gw),ip4_addr2_val(netif->gw),ip4_addr3_val(netif->gw),ip4_addr4_val(netif->gw));}else{printf("link unavailable\r\n");}
}

上电后,通过DHCP获取到IP地址之后,会调用netif_status_updated(),输出如下:
在这里插入图片描述
断开或重新连接网线也会调用此函数:
在这里插入图片描述
这个时候,就可以Ping通板子了:
在这里插入图片描述

三、LWIP协议栈初始化过程分析

使用STM32CubeMX生成的工程中调用MX_LWIP_Init()函数初始化LWIP协议栈。
在这里插入图片描述

void MX_LWIP_Init(void)
{/* Initialize the LwIP stack with RTOS */tcpip_init( NULL, NULL );/* IP addresses initialization with DHCP (IPv4) */ipaddr.addr = 0;netmask.addr = 0;gw.addr = 0;/* add the network interface (IPv4/IPv6) with RTOS */netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &tcpip_input);/* Registers the default network interface */netif_set_default(&gnetif);/* We must always bring the network interface up connection or not... */netif_set_up(&gnetif);/* Set the link callback function, this function is called on change of link status*/netif_set_link_callback(&gnetif, ethernet_link_status_updated);/* Create the Ethernet link handler thread */
/* USER CODE BEGIN H7_OS_THREAD_DEF_CREATE_CMSIS_RTOS_V1 */osThreadDef(EthLink, ethernet_link_thread, osPriorityBelowNormal, 0, configMINIMAL_STACK_SIZE *2);osThreadCreate (osThread(EthLink), &gnetif);
/* USER CODE END H7_OS_THREAD_DEF_CREATE_CMSIS_RTOS_V1 *//* Start DHCP negotiation for a network interface (IPv4) */dhcp_start(&gnetif);/* USER CODE BEGIN 3 */netif_set_status_callback(&gnetif, netif_status_updated);
/* USER CODE END 3 */
}

3.1 tcpip_init()

tcpip_init()函数用于初始化LWIP协议栈。
在这里插入图片描述

void
tcpip_init(tcpip_init_done_fn initfunc, void *arg)
{lwip_init();tcpip_init_done = initfunc;tcpip_init_done_arg = arg;if (sys_mbox_new(&tcpip_mbox, TCPIP_MBOX_SIZE) != ERR_OK) {LWIP_ASSERT("failed to create tcpip_thread mbox", 0);}
#if LWIP_TCPIP_CORE_LOCKINGif (sys_mutex_new(&lock_tcpip_core) != ERR_OK) {LWIP_ASSERT("failed to create lock_tcpip_core", 0);}
#endif /* LWIP_TCPIP_CORE_LOCKING */sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
}

lock_tcpip_core互斥锁主要目的是确保在多任务环境下,对 TCP/IP 协议栈核心部分(如数据收发、连接管理等关键操作)进行互斥访问。使用LOCK_TCPIP_CORE()和UNLOCK_TCPIP_CORE()两个宏配合使用。
Freertos多核SMP模式下Lwip出现共享资源违规访问问题

3.1.1 lwip_init()

初始化LWIP的所有功能模块。
在这里插入图片描述

3.1.1.1 sys_init()

在sys_init()函数中,创建了一个LWIP系统的互斥信号量。

void sys_init(void)
{
#if (osCMSIS < 0x20000U)lwip_sys_mutex = osMutexCreate(osMutex(lwip_sys_mutex));
#elselwip_sys_mutex = osMutexNew(NULL);
#endif
}
3.1.1.2 mem_init()

初始化内存堆。

3.1.1.3 memp_init()

初始化内存池。

3.1.1.4 netif_init()

初始化环回接口。
当LWIP_HAVE_LOOPIF被定义时,LWIP 协议栈将启用环回接口的相关功能。环回接口允许本地设备将数据发送到自己,就好像数据是通过网络从其他设备发送过来的一样。这对于测试网络协议栈的功能非常有用。例如,在开发基于 LWIP 的网络应用程序时,可以使用环回接口来测试 TCP 或 UDP 协议的连接建立、数据发送和接收等功能,而不需要实际连接到外部网络。环回接口可以用于在服务器端接收和处理来自本地客户端(其实就是自己)的数据请求。以一个简单的 HTTP 服务器为例,通过环回接口,可以在同一设备上测试服务器是否能够正确地接收请求、解析请求并返回响应,就像真正有外部客户端在访问服务器一样。
LWIP_HAVE_LOOPIF有助于验证 LWIP 协议栈内部的数据流转机制。当数据通过环回接口发送时,它会在 LWIP 协议栈内部经过一系列的协议层处理,如 IP 层的路由、TCP 层的连接管理等。通过观察和分析数据在环回接口通信过程中的处理情况,可以检查协议栈各个协议层之间的协同工作是否正常,是否存在数据丢失或处理错误等问题。

void
netif_init(void)
{
#if LWIP_HAVE_LOOPIF
#if LWIP_IPV4
#define LOOPIF_ADDRINIT &loop_ipaddr, &loop_netmask, &loop_gw,// 定义用于IPv4的环回接口的IP地址、子网掩码、网关地址的变量ip4_addr_t loop_ipaddr, loop_netmask, loop_gw;// 将环回接口的网关地址设置为127.0.0.1IP4_ADDR(&loop_gw, 127, 0, 0, 1);// 将环回接口的IP地址设置为127.0.0.1IP4_ADDR(&loop_ipaddr, 127, 0, 0, 1);// 将环回接口的子网掩码设置为255.0.0.0IP4_ADDR(&loop_netmask, 255, 0, 0, 0);
#else /* LWIP_IPV4 */
#define LOOPIF_ADDRINIT
#endif /* LWIP_IPV4 */
#if NO_SYS// 添加网卡,用于处理接收到的数据等操作,使用ip_input作为输入函数来处理接收到的数据等情况netif_add(&loop_netif, LOOPIF_ADDRINIT NULL, netif_loopif_init, ip_input);
#else  /* NO_SYS */// 如果不是无操作系统的情况,添加环回网络接口时,使用tcpip_input作为输入函数来处理接收到的数据等情况netif_add(&loop_netif, LOOPIF_ADDRINIT NULL, netif_loopif_init, tcpip_input);
#endif /* NO_SYS */#if LWIP_IPV6// 设置环回网络接口(loop_netif)的IPv6地址,这里将主机部分设置为0x00000001ULIP_ADDR6_HOST(loop_netif.ip6_addr, 0, 0, 0, 0x00000001UL);// 将环回网络接口的第一个IPv6地址状态设置为有效(IP6_ADDR_VALID),表明该地址可用loop_netif.ip6_addr_state[0] = IP6_ADDR_VALID;
#endif /* LWIP_IPV6 */// 将环回网络接口(loop_netif)的链路状态设置为“已连接(up)”netif_set_link_up(&loop_netif);// 将环回网络接口(loop_netif)设置为“启用(up)”状态,使其可以正常进行网络通信等操作netif_set_up(&loop_netif);
#endif /* LWIP_HAVE_LOOPIF */
}
3.1.1.5 udp_init()

在udp_init()函数中生成一个本地的随机UDP端口(动态端口或私有端口是49152(0xC000)到65535(0xFFFF)之间的端口)。

动态端口在网络通信中的作用:

  1. 灵活分配端口资源
    在网络通信中,服务器通常需要为不同的客户端连接提供服务。如果每个服务都使用固定端口,端口资源很快就会耗尽。动态端口可以根据需要灵活分配。
  2. 支持并发连接和多服务通信
    许多网络应用需要支持多个并发连接。以即时通讯软件为例,一个用户可能同时与多个好友进行聊天、语音通话或视频通话。动态端口允许软件为每个并发的通信任务分配一个端口,使得这些任务可以同时进行而不会相互干扰。
  3. 适应动态网络环境和应用需求
    网络环境是复杂多变的,应用程序的功能和通信需求也可能随时改变。动态端口能够很好地适应这种变化。例如,在移动网络环境中,移动设备的网络连接可能会频繁切换(如从 Wi - Fi 切换到移动数据),应用程序可以使用动态端口根据新的网络条件快速调整通信方式。而且,当应用程序需要临时增加新的通信功能(如软件更新过程中的数据下载),动态端口可以及时分配,满足应用程序的通信需求。

私有端口在网络通信中的作用:

  1. 内部网络通信安全保障
    私有端口主要用于内部网络(如企业内部局域网、家庭网络等)中的通信。在这些网络中,使用私有端口可以增加通信的安全性。
  2. 避免与公共网络服务冲突
    由于私有端口范围是预定义的,并且不用于公共网络服务,所以可以避免与互联网上广泛使用的公共服务端口(如 Web 服务的 80 端口、FTP 服务的 21 端口等)发生冲突。
  3. 实现自定义网络应用和服务
    私有端口为用户在内部网络中自定义网络应用和服务提供了便利。例如,在一个物联网智能家居环境中,用户可以使用私有端口来设置智能设备之间的通信方式。
void
udp_init(void)
{
#ifdef LWIP_RANDudp_port = UDP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND());
#endif /* LWIP_RAND */
}
3.1.1.6 tcp_init()

在tcp_init()函数中生成一个本地的随机TCP端口(动态端口或私有端口是49152(0xC000)到65535(0xFFFF)之间的端口)。

void
tcp_init(void)
{
#ifdef LWIP_RANDtcp_port = TCP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND());
#endif /* LWIP_RAND */
}
3.1.1.7 igmp_init()

在igmp_init()函数中设置了两个组播地址:allsystems(224.0.0.1)和allrouters(224.0.0.2)。
在 IP 网络中,“allsystems” 通常是指本地网络中的所有主机系统。从广播地址的角度理解,它类似于有限广播地址(255.255.255.255)的概念。这个地址用于将数据包发送给本地网络中的每一个设备。例如,在一个小型的办公室局域网中,如果一台设备想要向局域网内的所有其他设备发送一个通知消息,就可以使用与 “allsystems” 相关的广播方式。
LWIP 协议栈支持向 “allsystems” 发送数据包的功能。这可能涉及到应用程序使用 UDP(User Datagram Protocol)或其他合适的协议来发送广播消息。当应用程序调用 LWIP 的相关 API 发送广播消息到 “allsystems” 时,LWIP 会根据网络接口的配置和当前的网络状态,将数据包以广播的形式发送出去。例如,在实现一个简单的本地网络设备发现协议时,设备可以通过向 “allsystems” 广播自己的存在信息,让其他设备能够接收到这个消息并做出响应。

“allrouters” 主要是指网络中的所有路由器。在 IP 网络中,路由器在转发数据包方面起着关键的作用。向 “allrouters” 发送消息通常用于网络配置、路由更新等场景。例如,在动态路由协议(如 RIP - Router Information Protocol)的操作中,路由器可能会向 “allrouters” 发送路由更新信息,以便其他路由器能够获取最新的路由信息并更新自己的路由表。
在 LWIP 协议栈中,如果涉及到网络层的路由相关功能,向 “allrouters” 发送消息的功能可能会被启用。不过,这通常需要更复杂的网络设置和应用场景。例如,在一个支持动态路由的嵌入式网络设备中,当设备检测到网络拓扑结构发生变化时,它可能会通过 LWIP 协议栈向 “allrouters” 发送路由更新消息,以确保整个网络的路由信息能够及时更新。同时,LWIP 也需要根据网络接口的类型(如以太网接口、Wi - Fi 接口等)和网络的实际情况(如是否为多子网环境等)来正确地处理向 “allrouters” 发送消息的操作。

void
igmp_init(void)
{LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n"));IP4_ADDR(&allsystems, 224, 0, 0, 1);IP4_ADDR(&allrouters, 224, 0, 0, 2);
}
3.1.1.8 dns_init()

初始化DNS模块,创建一个UDP协议控制块。
DNS 模块与 LWIP 中的其他网络模块紧密协作。它和 UDP(User Datagram Protocol)模块密切相关,因为 DNS 查询和响应消息通常是通过 UDP 协议传输的(在某些情况下也可以使用 TCP 协议)。当应用程序发起一个 DNS 请求时,DNS 模块会构建一个 UDP 数据包,将请求发送到 DNS 服务器。
同时,DNS 模块也和 IP(Internet Protocol)模块协同。它需要使用 IP 模块来确定数据包的源和目的 IP 地址,以及处理数据包在网络中的路由。在收到 DNS 服务器的响应后,IP 模块将数据包传递给 DNS 模块进行解析,提取出域名对应的 IP 地址并返回给应用程序。

3.1.1.9 ppp_init()

通过不同的宏如PPPOS_SUPPORT、PPPOE_SUPPORT、PPPOL2TP_SUPPORT 等确认是否支持 PPP over Serial(基于串口的 PPP 协议)、PPPoE(以太网上的 PPP 协议)、PPP over L2TP(基于第二层隧道协议的 PPP 协议)等不同的 PPP 衍生协议或功能,并定义相应功能的内存池。

3.1.1.10 sys_timeouts_init()

LWIP 内部维护了定时器相关的数据结构。通常包括定时器的间隔时间(以毫秒为单位)、定时器的状态(是否正在运行、是否已过期等)以及一个指向回调函数的指针。这个回调函数是超时处理的核心,当定时器过期时,就会调用这个函数来执行特定的超时操作。
在 LWIP(Lightweight IP)协议栈中,超时处理是确保网络通信可靠性和稳定性的关键机制。例如,在 TCP(Transmission Control Protocol)协议中,需要通过超时处理来判断数据是否丢失并进行重传。当发送端发送数据后,如果在规定的时间内没有收到接收端的确认(ACK)消息,就会触发超时重传机制,以保证数据的可靠传输。
对于动态主机配置协议(DHCP)而言,超时处理用于管理 IP 地址的租约时间。当租约时间快要到期时,设备需要向 DHCP 服务器请求续租,否则 IP 地址可能会失效。这涉及到一个超时定时器,用于跟踪租约剩余时间,确保设备能够及时更新租约,维持网络连接的正常状态。

3.1.2 sys_mbox_new(&tcpip_mbox, TCPIP_MBOX_SIZE)

创建消息邮箱,由于Freertos中没有邮箱,所以是通过消息队列实现的。

err_t sys_mbox_new(sys_mbox_t *mbox, int size)
{
#if (osCMSIS < 0x20000U)osMessageQDef(QUEUE, size, void *);*mbox = osMessageCreate(osMessageQ(QUEUE), NULL);
#else*mbox = osMessageQueueNew(size, sizeof(void *), NULL);
#endif
#if SYS_STATS++lwip_stats.sys.mbox.used;if(lwip_stats.sys.mbox.max < lwip_stats.sys.mbox.used){lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used;}
#endif /* SYS_STATS */if(*mbox == NULL)return ERR_MEM;return ERR_OK;
}osMessageQId osMessageCreate (const osMessageQDef_t *queue_def, osThreadId thread_id)
{(void) thread_id;#if( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )if ((queue_def->buffer != NULL) && (queue_def->controlblock != NULL)) {return xQueueCreateStatic(queue_def->queue_sz, queue_def->item_sz, queue_def->buffer, queue_def->controlblock);}else {return xQueueCreate(queue_def->queue_sz, queue_def->item_sz);}
#elif ( configSUPPORT_STATIC_ALLOCATION == 1 )return xQueueCreateStatic(queue_def->queue_sz, queue_def->item_sz, queue_def->buffer, queue_def->controlblock);
#else  return xQueueCreate(queue_def->queue_sz, queue_def->item_sz);
#endif
}

3.1.3 sys_mutex_new(&lock_tcpip_core)

创建一个互斥信号量,用于保护内核线程,在 LWIP(Lightweight IP)协议栈中,由于可能存在多个任务或者中断同时访问 TCP/IP 核心部分(如协议栈的关键数据结构、正在处理的网络连接等),为了避免数据不一致和并发访问导致的错误,需要一种机制来进行访问控制。

3.1.4 tcpip_thread

创建LWIP的内核线程,内核线程等待邮箱中的消息,如果收到消息,就调用tcpip_thread_handle_msg()函数处理。

3.2 netif_add()

过调用 netif_add 函数向 LWIP 协议栈添加一个网络接口,对于使用DHCP的工程,默认网卡的IP,网关和子网掩码均设置为0,使用ethernetif_init函数设置底层的网口(最终调用low_level_init()函数。在low_level_init()函数中,除了设置硬件相关,还会创建两个信号量,用于控制网口发送和接收;同时创建ethernetif_input()任务,该任务在获取到接收信号量之后调用low_level_inpu()函数从底层收包),使用tcpip_input作为输入函数来处理接收到的数据。

netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &tcpip_input);

3.3 netif_set_default()和netif_set_up()函数

将刚添加的网络接口 gnetif 设置为默认网络接口,将网络接口 gnetif 的状态设置为 “启用(up)”。这一步操作使得网络接口具备了进行网络通信的基本条件,不管当前是否已经成功建立网络连接(比如是否已经通过 DHCP 获取到 IP 地址等),先将接口在协议栈层面设置为可工作状态,后续可以基于这个状态继续完成网络配置和实际通信的相关操作。

3.4 netif_set_link_callback()和netif_set_status_callback()函数

在 LWIP(Lightweight IP)协议栈中,netif_set_link_callback函数主要用于设置一个回调函数,这个回调函数会在网络接口(netif)的链路状态发生变化(up/down)时被调用。例如,当以太网接口的网线被插拔,或者 Wi - Fi 接口的连接状态从已连接变为断开连接时,链路状态发生了改变,此时就会触发这个回调函数。STM32CubeMX默认设置会打开这个回调函数。

netif_set_status_callback函数用于设置一个回调函数,当网络接口的综合状态发生变化时会被调用。这里的 “综合状态” 比单纯的链路状态更广泛,可能包括网络接口的配置状态变化、接口的启用 / 禁用状态变化等多种情况。例如,当网络接口的 IP 地址通过 DHCP(Dynamic Host Configuration Protocol)重新配置后,或者网络接口被手动禁用(通过系统命令或者配置文件),这个回调函数就会被触发。STM32CubeMX中要设置LWIP_NETIF_STATUS_CALLBACK为ENABLE。

3.5 ethernet_link_thread线程

这个线程主要用于处理以太网链路相关的事务,比如实时监测链路状态、执行链路维护操作等。默认100ms轮询。

3.6 dhcp_start()

调用 dhcp_start 函数针对网络接口 gnetif 启动动态主机配置协议(DHCP)的协商过程。由于前面已经将 IP 地址等相关信息初始化为 0,通过这个函数启动后,网络接口会向网络中的 DHCP 服务器发送请求,尝试获取合适的 IP 地址、子网掩码、网关地址等网络配置信息,以完成自身的网络配置,使得设备能够在网络中正常通信。

相关文章:

LWIP之一:使用STM32CubeMX搭建基于FreeRTOS的LWIP工程并分析协议栈初始化过程

工程搭建及LWIP协议栈初始化过程 一、使用STM32CubeMX快速生成工程二、修改测试三、LWIP协议栈初始化过程分析3.1 tcpip_init()3.1.1 lwip_init()3.1.1.1 sys_init()3.1.1.2 mem_init()3.1.1.3 memp_init()3.1.1.4 netif_init()3.1.1.5 udp_init()3.1.1.6 tcp_init()3.1.1.7 ig…...

个性化电影推荐系统|Java|SSM|JSP|

【技术栈】 1⃣️&#xff1a;架构: B/S、MVC 2⃣️&#xff1a;系统环境&#xff1a;Windowsh/Mac 3⃣️&#xff1a;开发环境&#xff1a;IDEA、JDK1.8、Maven、Mysql5.7 4⃣️&#xff1a;技术栈&#xff1a;Java、Mysql、SSM、Mybatis-Plus、JSP、jquery,html 5⃣️数据库可…...

UE5AI感知组件

官方解释&#xff1a; AI感知系统为Pawn提供了一种从环境中接收数据的方式&#xff0c;例如噪音的来源、AI是否遭到破坏、或AI是否看到了什么。 AI感知组件&#xff08;AIPerception Component&#xff09;是用于实现游戏中的非玩家角色&#xff08;NPC&#xff09;对环境和其…...

每日一学——日志管理工具(ELK Stack)

5.1 ELK Stack 5.1.1 Elasticsearch索引机制 嘿&#xff0c;小伙伴们&#xff01;今天我们要聊聊ELK Stack——一套由Elasticsearch、Logstash和Kibana组成的强大日志管理工具集。通过这套工具&#xff0c;我们可以轻松地收集、存储、搜索和可视化日志数据。首先&#xff0c;…...

“智能筛查新助手:AI智能筛查分析软件系统如何改变我们的生活

嘿&#xff0c;朋友们&#xff01;今天咱们来聊聊一个特别厉害的工具——AI智能筛查分析软件系统。想象一下&#xff0c;如果你有一个超级聪明的小助手&#xff0c;不仅能帮你快速找出问题的关键所在&#xff0c;还能提供精准的解决方案&#xff0c;是不是感觉工作和生活都变得…...

DeepSeek v3为何爆火?如何用其集成Milvus搭建RAG?

最近&#xff0c;DeepSeek v3&#xff08;一个MoE模型&#xff0c;拥有671B参数&#xff0c;其中37B参数被激活&#xff09;模型全球爆火。 作为一款能与Claude 3.5 Sonnet&#xff0c;GPT-4o等模型匹敌的开源模型DeepSeek v3不仅将其算法开源&#xff0c;还放出一份扎实的技术…...

linux-centos-安装miniconda3

参考&#xff1a; 最新保姆级Linux下安装与使用conda&#xff1a;从下载配置到使用全流程_linux conda-CSDN博客 https://blog.csdn.net/qq_51566832/article/details/144113661 Linux上删除Anaconda或Miniconda的步骤_linux 删除anaconda-CSDN博客 https://blog.csdn.net/m0_…...

html+css+js网页设计 美食 好厨艺西餐美食企业网站模板6个页面

htmlcssjs网页设计 美食 好厨艺西餐美食企业网站模板6个页面 网页作品代码简单&#xff0c;可使用任意HTML辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 …...

QT-窗口嵌入外部exe

窗口类&#xff1a; #pragma once #include <QApplication> #include <QWidget> #include <QVBoxLayout> #include <QProcess> #include <QTimer> #include <QDebug> #include <Windows.h> #include <QWindow> #include <…...

C#中使用系统默认应用程序打开文件

有时您可能希望程序使用默认应用程序打开文件。 例如&#xff0c;您可能希望显示 PDF 文件、网页或互联网上的 URL。 System.Diagnostics.Process类的Start方法启动系统与文件关联的应用程序。 例如&#xff0c;如果文件扩展名为.txt&#xff0c;则系统会在 NotePad、WordPa…...

如何在 Ubuntu 22.04 上配置 Logrotate 高级教程

简介 本教程将教你如何在 Ubuntu 22.04 上进行 Logrotate 的高级配置。 日志管理对于维护系统性能和确保你的日志不会占用太多磁盘空间至关重要。在 Ubuntu 上&#xff0c;logrotate 是一个强大的工具&#xff0c;它可以通过轮转、压缩和删除旧日志来自动管理日志文件。在本教…...

java项目之校园管理系统的设计与实现(源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的校园管理系统的设计与实现。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; springboot校园…...

关于 webservice 日志中 源IP是node IP的问题,是否能解决换成 真实的客户端IP呢

本篇目录 1. 问题背景2. 部署gitlab 17.52.1 添加repo源2.2 添加repo源 下载17.5.0的charts包2.3 修改values文件2.3.1 hosts修改如下2.3.2 appConfig修改如下2.3.3 gitlab下的sidekiq配置2.3.4 certmanager修改如下2.3.5 nginx-ingress修改如下2.3.6 <可选> prometheus修…...

Serializable接口

最近写项目的时候&#xff0c;发现有一些类要实现Serializable接口&#xff0c;一开始只是粗略的知道实现了Serializable接口&#xff0c;这个类的对象可以被序列化&#xff0c;但我比较轴&#xff0c;想知道这个接口到底有什么作用。 我点开这个接口发现什么方法都没有实现&am…...

如何操作github,gitee,gitcode三个git平台建立镜像仓库机制,这样便于维护项目只需要维护一个平台仓库地址的即可-优雅草央千澈

如何操作github&#xff0c;gitee&#xff0c;gitcode三个git平台建立镜像仓库机制&#xff0c;这样便于维护项目只需要维护一个平台仓库地址的即可-优雅草央千澈 问题背景 由于我司最早期19年使用的是gitee&#xff0c;因此大部分仓库都在gitee有几百个库的代码&#xff0c;…...

【HDU】1089 A+B for Input-Output Practice (I)

1089 AB for Input-Output Practice (I):以EOF结尾的输入 Problem Description Your task is to Calculate a b. Too easy?! Of course! I specially designed the problem for acm beginners. You must have found that some problems have the same titles with this one,…...

lua库介绍:数据处理与操作工具库 - leo

leo库简介 leo 模块的创作初衷旨在简化数据处理的复杂流程&#xff0c;提高代码的可读性和执行效率&#xff0c;希望leo 模块都能为你提供一系列便捷的工具函数&#xff0c;涵盖因子编码、多维数组创建、数据框构建、列表管理以及管道操作等功能。 要使用 Leo 模块&#xff0c;…...

逆向入门(1)C篇-正儿巴经的第1个实验

接触了这么久&#xff0c;第一次不是使用CV大法编写程序&#xff0c;认认真真的重头开始学习&#xff0c;记录一下找调试的感觉。 第一段代码&#xff0c;重温原码&#xff0c;反码和补码。 #include "stdafx.h"int main(int argc, char* argv[]) {char x -7;print…...

vue数据请求通用方案:axios的options都有哪些值

Axios 是一个基于 promise 的 HTTP 库&#xff0c;可以用在浏览器和 Node.js 中。 在使用 Axios 发送请求时&#xff0c;可以通过传递一个配置对象来指定请求的各种选项。 以下是一些常用的 Axios 配置选项及其说明&#xff1a; 1.url: &#xff08;必需&#xff09;请求的 …...

使用R语言绘制标准的中国地图和世界地图

在日常的学习和生活中&#xff0c;有时我们常常需要制作带有国界线的地图。这个时候绘制标准的国家地图就显得很重要。目前国家标准地图服务系统向全社会公布的标准中国地图数据&#xff0c;是最权威的地图数据。 今天介绍的R包“ggmapcn”&#xff0c;就是基于最新公布的地图…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

【kafka】Golang实现分布式Masscan任务调度系统

要求&#xff1a; 输出两个程序&#xff0c;一个命令行程序&#xff08;命令行参数用flag&#xff09;和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽&#xff0c;然后将消息推送到kafka里面。 服务端程序&#xff1a; 从kafka消费者接收…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具

作者&#xff1a;来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗&#xff1f;了解下一期 Elasticsearch Engineer 培训的时间吧&#xff01; Elasticsearch 拥有众多新功能&#xff0c;助你为自己…...

在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:

在 HarmonyOS 应用开发中&#xff0c;手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力&#xff0c;既支持点击、长按、拖拽等基础单一手势的精细控制&#xff0c;也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档&#xff0c…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...