Linux 网络之netlink 简介

news2024/11/30 6:49:41

文章目录

  • 前言
  • 一、Netlink用户态应用的使用
    • 1.1 Netlink socket
    • 1.2 Netlink宏操作
  • 二、Netlink对应数据结构
    • 2.1 struct sockaddr_nl
    • 2.2 struct nlmsghdr
    • 2.3 struct msghdr
  • 三、用户层实例
  • 参考资料

前言

The Netlink socket family 是一个 Linux 内核接口,用于内核和用户空间进程之间以及不同用户空间进程之间的进程间通信 (IPC),其方式类似于 Unix domain sockets。 与 Unix domain sockets类似,但与 INET sockets不同,Netlink 通信不能跨越主机边界。Unix domain sockets使用文件系统名称空间,Netlink进程通常由进程标识符(pid)寻址。

Netlink 设计用于在内核空间和用户空间进程之间传输各种网络信息。 网络实用程序,例如 iproute2 工具包,使用 Netlink 从用户空间与 Linux 内核进行通信。 Netlink 为用户空间进程提供了一个标准的基于套接字的接口,以及一个供内核模块内部使用的内核端 API。 Netlink 使用 AF_NETLINKsocket family。

Netlink 主要是用来进行内核和用户空间进程之间的通信,用户空间进程之间的进程间通信 (IPC)一般不用 Netlink。

Netlink 支持双工通信,是一种异步通信机制,采用数据报信息(SOCK_DGRAM)格式传送数据。在内核与用户态应用之间传递的消息保存在socket缓存队列中,发送消息只是把消息保存在接收者的socket的接收队列,而不需要等待接收者收到消息。

该机制一个非常重要的用户是通用设备模型,使用netlink套接字将各种关于内核内部事务的状态信息传递到用户层,其中包括新设备的注册和移除、硬件层次上发生的特别事件等等。

一、Netlink用户态应用的使用

1.1 Netlink socket

以下命令查看netlink套接字相关信息:

man 7 netlink

Netlink是一个面向数据报的服务。用户态应用使用标准的 socket API 就可以使用 netlink 提供的强大功能,AF_NETLINK 系列提供多个协议子集。 每个接口都连接到不同的内核组件并具有不同的消息传递子集。 该子集由套接字调用中的 the protocol 字段引用:

/* Create a new socket of type TYPE in domain DOMAIN, using
   protocol PROTOCOL.  If PROTOCOL is zero, one is chosen automatically.
   Returns a file descriptor for the new socket, or -1 for errors.  */
extern int socket (int __domain, int __type, int __protocol) __THROW;

int socket(AF_NETLINK, SOCK_DGRAM or SOCK_RAW, protocol)

第一个参数必须是PF_NETLINK或者AF_NETLINK(这两者等价)。

对于第二个参数,由于缺乏标准,SOCK_DGRAM 和 SOCK_RAW 不能保证在给定的 Linux(或其他操作系统)版本中实现。 一些消息来源指出这两个选项都是合理的,Red Hat 推荐使用 SOCK_RAW 作为参数。 但是,iproute2 工具包中可以互换使用两者。

对于第三个参数,netlink提供“协议”来标示通信实体,在创建socket的时候,需要指定netlink的通信协议号。每个协议号代表一种“应用”,上层可以用内核已经定义的协议和内核进行通信,获得内核已经提供的信息。其中protocol的选项可以是:

#define NETLINK_ROUTE		0	/* Routing/device hook				*/
#define NETLINK_UNUSED		1	/* Unused number				*/
#define NETLINK_USERSOCK	2	/* Reserved for user mode socket protocols 	*/
#define NETLINK_FIREWALL	3	/* Unused number, formerly ip_queue		*/
#define NETLINK_SOCK_DIAG	4	/* socket monitoring				*/
#define NETLINK_NFLOG		5	/* netfilter/iptables ULOG */
#define NETLINK_XFRM		6	/* ipsec */
#define NETLINK_SELINUX		7	/* SELinux event notifications */
#define NETLINK_ISCSI		8	/* Open-iSCSI */
#define NETLINK_AUDIT		9	/* auditing */
#define NETLINK_FIB_LOOKUP	10	
#define NETLINK_CONNECTOR	11
#define NETLINK_NETFILTER	12	/* netfilter subsystem */
#define NETLINK_IP6_FW		13
#define NETLINK_DNRTMSG		14	/* DECnet routing messages */
#define NETLINK_KOBJECT_UEVENT	15	/* Kernel messages to userspace */
#define NETLINK_GENERIC		16
/* leave room for NETLINK_DM (DM Events) */
#define NETLINK_SCSITRANSPORT	18	/* SCSI Transports */
#define NETLINK_ECRYPTFS	19
#define NETLINK_RDMA		20
#define NETLINK_CRYPTO		21	/* Crypto layer */

#define NETLINK_INET_DIAG	NETLINK_SOCK_DIAG

#define MAX_LINKS 32

NETLINK_ROUTE:提供路由和链接信息, 此信息主要用于用户空间路由守护程序。
NETLINK_FIREWALL:为用户空间应用程序提供接收防火墙数据包的接口。
NETLINK_NFLOG:提供了一个用于Netfilter(内核模块)和iptables(用户空间工具包)之间通信的接口。
NETLINK_ARPD:提供从用户空间管理 ARP 表的接口。
NETLINK_AUDIT:为 Linux 内核版本 2.6.6 及更高版本中的审计子系统提供接口。
NETLINK_IP6_FW:提供一个接口将数据包从 netfilter 传输到用户空间。
NETLINK_XFRM:用于发送和接受有关IPSec的信息。
NETLINK_KOBJECT_UEVENT:提供内核广播 uevent 的接口,通常由 udev 使用,是内核通用模型向用户层发送信息所采用的协议(内核热插拔机制的基础)。
NETLINK_GENERIC:Netlink 协议的缺点之一是协议族的数量被限制为 32 (MAX_LINKS)。这是创建通用 Netlink 族的主要原因之一 – 为增加更多的 families 提供支持。它充当一个Netlink多路复用器,并与单个Netlink families NETLINK_GENERIC一起工作。通用Netlink协议基于Netlink协议并使用它的API

由此可见在Linux内核中,使用nertlink进行应用与内核通信的应用很多。

对于上述协议的用途,比如用来创建一个上层应用,通过使用NETLINK_ROUTE协议通信,可以获得内核的路由信息。当然我们也可以自定义通信协议,但不能使用已有的协议编号,也不能超过MAX_LINKS,因此我们我们自定义netlink通信协议编号可以 22 - 32

1.2 Netlink宏操作

Netlink消息由一个字节流和一个或多个nlmsghdr头以及相关的有效负载组成。只能使用标准的NLMSG_*宏访问字节流。

<linux/netlink.h> 定义了几个标准宏来访问或创建 netlink 数据报。 应该只使用这些宏来访问传入和传出 netlink 套接字的缓冲区。

#define NLMSG_ALIGNTO	4U
#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
#define NLMSG_HDRLEN	 ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)
#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
#define NLMSG_DATA(nlh)  ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
#define NLMSG_NEXT(nlh,len)	 ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
				  (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \
			   (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
			   (nlh)->nlmsg_len <= (len))
#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
NLMSG_ALIGN()
        Round the length of a netlink message up to align it properly.

 NLMSG_LENGTH()
        Given the payload length, len, this macro returns the aligned length to store in the nlmsg_len field of the nlmsghdr.

 NLMSG_SPACE()
        Return the number of bytes that a netlink message with payload of len would occupy.

 NLMSG_DATA()
        Return a pointer to the payload associated with the passed nlmsghdr.

 NLMSG_NEXT()
        Get the next nlmsghdr in a multipart message.  The caller must check if the current nlmsghdr didn't have the NLMSG_DONE set—this function doesn't return NULL on end.  The len  argument
        is an lvalue containing the remaining length of the message buffer.  This macro decrements it by the length of the message header.

 NLMSG_OK()
        Return true if the netlink message is not truncated and is in a form suitable for parsing.

 NLMSG_PAYLOAD()
        Return the length of the payload associated with the nlmsghdr.

二、Netlink对应数据结构

2.1 struct sockaddr_nl

struct sockaddr_nl是netlink套接字通信地址:

struct sockaddr_nl {
	__kernel_sa_family_t	nl_family;	/* AF_NETLINK	*/
	unsigned short			nl_pad;		/* zero		*/
	__u32					nl_pid;		/* port ID	unicast address */
    __u32					nl_groups;	/* multicast groups mask */
};

nl_pid 是 netlink 套接字的单播地址,一个约定的通信端口,用户态使用的时候需要用一个非0的数字,一般来说可以直接采用上层应用的进程ID(不用进程ID号码也没事,只要系统中不冲突的一个数字即可使用)。对于内核的地址,该值必须用0,也就是说,如果上层通过sendto向内核发送netlink消息,peer addr中nl_pid必须填写0。
nl_pid 标识的是 netlink 套接字,而不是进程。本质上,nl_pid就是netlink的通信地址。

通过 protocol 和 nl_pid 组成Netlink的通信方式。

编写netlink通信时,需要源struct sockaddr_nl结构体和目的地 struct sockaddr_nl结构体。

2.2 struct nlmsghdr

Netlink的报文由消息头和消息体构成,struct nlmsghdr即为消息头:

struct nlmsghdr {
	__u32		nlmsg_len;	/* Length of message including header */
	__u16		nlmsg_type;	/* Message content */
	__u16		nlmsg_flags;	/* Additional flags */
	__u32		nlmsg_seq;	/* Sequence number */
	__u32		nlmsg_pid;	/* Sending process port ID */
};

(1) nlmsg_len:整个消息的长度,按字节计算。包括了Netlink消息头本身。
(2) nlmsg_type:消息的类型,即是数据还是控制消息。
(3) nlmsg_flags:附加在消息上的额外说明信息。

在这里插入图片描述
其中的nlmsg_flags:

/* Flags values */

#define NLM_F_REQUEST		1	/* It is request message. 	*/
#define NLM_F_MULTI		2	/* Multipart message, terminated by NLMSG_DONE */
#define NLM_F_ACK		4	/* Reply with ack, with zero or error code */
#define NLM_F_ECHO		8	/* Echo this request 		*/
#define NLM_F_DUMP_INTR		16	/* Dump was inconsistent due to sequence change */

/* Modifiers to GET request */
#define NLM_F_ROOT	0x100	/* specify tree	root	*/
#define NLM_F_MATCH	0x200	/* return all matching	*/
#define NLM_F_ATOMIC	0x400	/* atomic GET		*/
#define NLM_F_DUMP	(NLM_F_ROOT|NLM_F_MATCH)

/* Modifiers to NEW request */
#define NLM_F_REPLACE	0x100	/* Override existing		*/
#define NLM_F_EXCL	0x200	/* Do not touch, if it exists	*/
#define NLM_F_CREATE	0x400	/* Create, if it does not exist	*/
#define NLM_F_APPEND	0x800	/* Add to end of list		*/

由于Netlink socketd的type是SOCK_DGRAM ,所以Netlink是一个不可靠的协议。它尽最大努力将消息传递到目的地,但当内存不足的情况或其他错误发生时,可能会丢弃消息。为了实现可靠的传输,发送方可以通过设置NLM_F_ACK标志向接收方请求确认。确认是一个 NLMSG_ERROR 数据包,错误字段设置为 0。应用程序必须为接收到的消息本身生成确认。内核尝试为每个失败的包发送一个NLMSG_ERROR消息。用户进程也应该遵循这个约定。

#define NLMSG_NOOP		0x1	/* Nothing.		*/
#define NLMSG_ERROR		0x2	/* Error		*/
#define NLMSG_DONE		0x3	/* End of a dump	*/
#define NLMSG_OVERRUN		0x4	/* Data lost		*/

#define NLMSG_MIN_TYPE		0x10	/* < 0x10: reserved control messages */

2.3 struct msghdr

用户层调用sendmsg和recvmsg是所需要的结构体:

/* Send a message described MESSAGE on socket FD.
   Returns the number of bytes sent, or -1 for errors.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern ssize_t sendmsg (int __fd, const struct msghdr *__message,
			int __flags);

/* Receive a message as described by MESSAGE from socket FD.
   Returns the number of bytes read or -1 for errors.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern ssize_t recvmsg (int __fd, struct msghdr *__message, int __flags);
/* Structure describing messages sent by
   `sendmsg' and received by `recvmsg'.  */
struct msghdr
  {
    void *msg_name;		/* Address to send to/receive from.  */
    socklen_t msg_namelen;	/* Length of address data.  */

    struct iovec *msg_iov;	/* Vector of data to send/receive into.  */
    size_t msg_iovlen;		/* Number of elements in the vector.  */

    void *msg_control;		/* Ancillary data (eg BSD filedesc passing). */
    size_t msg_controllen;	/* Ancillary data buffer length.
				   !! The type should be socklen_t but the
				   definition of the kernel is incompatible
				   with this.  */

    int msg_flags;		/* Flags on received message.  */
  };

其中 struct iovec :

/* Structure for scatter/gather I/O.  */
struct iovec
{
	void *iov_base;	/* Pointer to data.  */
	size_t iov_len;	/* Length of data.  */
};

iov_base: iov_base指向数据包缓冲区,即参数buff,iov_len是buff的长度。msghdr中允许一次传递多个buff,以数组的形式组织在 msg_iov中,msg_iovlen就记录数组的长度 (即有多少个buff)。
iov_base指向如下数据结构(包含了消息头 struct nlmsghdr ):
在这里插入图片描述

以上三种关系如下图所示:
在这里插入图片描述
图片来自于:linux用户空间与内核空间通信——Netlink通信机制

三、用户层实例

NETLINK_ROUTE:接收路由和链路更新,并可用于修改路由表(IPv4和IPv6)、IP地址、链路参数、邻居设置、排队规则、流分类和包分类。

/****
 *		General form of address family dependent message.
 ****/

struct rtgenmsg {
	unsigned char		rtgen_family;
};

/*****************************************************************
 *		Link layer specific messages.
 ****/

/* struct ifinfomsg
 * passes link level specific information, not dependent
 * on network protocol.
 */

struct ifinfomsg {
	unsigned char	ifi_family;
	unsigned char	__ifi_pad;
	unsigned short	ifi_type;		/* ARPHRD_* */
	int		ifi_index;		/* Link index	*/
	unsigned	ifi_flags;		/* IFF_* flags	*/
	unsigned	ifi_change;		/* IFF_* change mask */
};

/********************************************************************
/* 
   Generic structure for encapsulation of optional route information.
   It is reminiscent of sockaddr, but with sa_family replaced
   with attribute type.
 */

struct rtattr {
	unsigned short	rta_len;
	unsigned short	rta_type;
};

/* Macros to handle rtattributes */

#define RTA_ALIGNTO	4
#define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) )
#define RTA_OK(rta,len) ((len) >= (int)sizeof(struct rtattr) && \
			 (rta)->rta_len >= sizeof(struct rtattr) && \
			 (rta)->rta_len <= (len))
#define RTA_NEXT(rta,attrlen)	((attrlen) -= RTA_ALIGN((rta)->rta_len), \
				 (struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len)))
#define RTA_LENGTH(len)	(RTA_ALIGN(sizeof(struct rtattr)) + (len))
#define RTA_SPACE(len)	RTA_ALIGN(RTA_LENGTH(len))
#define RTA_DATA(rta)   ((void*)(((char*)(rta)) + RTA_LENGTH(0)))
#define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0))
enum {
	IFLA_UNSPEC,
	IFLA_ADDRESS,
	IFLA_BROADCAST,
	IFLA_IFNAME,
	IFLA_MTU,
	IFLA_LINK,
	IFLA_QDISC,
	IFLA_STATS,
	......
}

获取网络接口的名字:

/*
 *  Display all network interface names
 */
#include <stdio.h>            
#include <string.h>          
#include <unistd.h>           
#include <sys/socket.h>       
#include <arpa/inet.h>        
#include <linux/netlink.h>    
#include <linux/rtnetlink.h> 

#define BUFSIZE 8192

struct nl_req_s {
  struct nlmsghdr hdr;
  struct rtgenmsg gen;
};

void rtnl_print_link(struct nlmsghdr * h)
{
    struct ifinfomsg * iface;
    struct rtattr * attr;
    int len;

    iface = NLMSG_DATA(h);
    len = RTM_PAYLOAD(h);

    /* loop over all attributes for the NEWLINK message */
    for (attr = IFLA_RTA(iface); RTA_OK(attr, len); attr = RTA_NEXT(attr, len))
    {
        switch (attr->rta_type)
        {
        case IFLA_IFNAME:
            printf("Interface %d : %s\n", iface->ifi_index, (char *)RTA_DATA(attr));
            break;
        default:
            break;
        }
    }
}

int main(void)
{
    struct sockaddr_nl src_addr;
    int s, end=0, len;
    struct msghdr msg;
    struct nl_req_s req;
    struct iovec io;
    char buf[BUFSIZE];

    //build src_addr netlink address
    memset(&src_addr, 0, sizeof(src_addr));
    src_addr.nl_family = AF_NETLINK;
    src_addr.nl_groups = 0;

    //create a Netlink socket
    if ((s=socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0){
        perror("socket");
        return -1;
    }

    //build netlink message
    memset(&req, 0, sizeof(req));
    req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
    req.hdr.nlmsg_type = RTM_GETLINK;
    req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
    req.hdr.nlmsg_seq = 1;
    req.hdr.nlmsg_pid = getpid();
    req.gen.rtgen_family = AF_INET;

    memset(&io, 0, sizeof(io));
    io.iov_base = &req;
    io.iov_len = req.hdr.nlmsg_len;

    memset(&msg, 0, sizeof(msg));
    msg.msg_iov = &io;
    msg.msg_iovlen = 1;
    msg.msg_name = &src_addr;
    msg.msg_namelen = sizeof(src_addr);

    //send the message
    if (sendmsg(s, &msg, 0) < 0)
    {
        perror("sendmsg");
    }

    //parse reply
    while (!end){
        memset(buf, 0, BUFSIZE);
        msg.msg_iov->iov_base = buf;
        msg.msg_iov->iov_len = BUFSIZE;
        if ((len=recvmsg(s, &msg, 0)) < 0){
            perror("recvmsg");
        }

        for (struct nlmsghdr * msg_ptr = (struct nlmsghdr *)buf;
             NLMSG_OK(msg_ptr, len); msg_ptr = NLMSG_NEXT(msg_ptr, len)){
            switch (msg_ptr->nlmsg_type)
            {
            case NLMSG_DONE:
                end++;
                break;
            case RTM_NEWLINK:
                rtnl_print_link(msg_ptr);
                break;
            default:
                printf("Ignored msg: type=%d, len=%d\n", msg_ptr->nlmsg_type, msg_ptr->nlmsg_len);
                break;
            }
        }
    }

    close(s);
    return 0;
}
[root@localhost netlink]# ./a.out
Interface 1 : lo
Interface 2 : enp1s0
Interface 3 : virbr0
Interface 4 : virbr0-nic

参考资料

https://zhuanlan.zhihu.com/p/269141945
https://www.jianshu.com/p/073bcd9c3b08
https://gist.github.com/cl4u2/5204374

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

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

相关文章

网课查题接口

网课查题接口 本平台优点&#xff1a;免费查题接口搭建 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a;题库后台http://daili.jueguangzhe.cn/ 题库后台&am…

[前端基础] CSS3 篇

CSS3 使用了层叠样式表技术&#xff0c;可以对网页布局、字体、颜色、背景灯效果做出控制。CSS3 作为 CSS 的进阶版&#xff0c;拆分和增加了盒子模型、列表模块、语言模块 、背景边框 、文字特效 、多栏布局等等。CSS3 的改变有很多&#xff0c;增加了文字特效&#xff0c;丰富…

C#基础知识

文章目录简介隐式显式转换ifswitch循环随机数方法do whilestatic递归数组foreachvar推断类型,父类二维数组&#xff0c;交错数组当方法参数类型确定&#xff0c;个数不确定时数据类型类型分类快捷方式函数参数&#xff1a;引用、输出参数装箱&#xff0c;拆箱string储存地址枚举…

Netty入门——组件(Channel)二

目录一、channel的主要作用二、EventLoop处理io任务代码示例2.1、服务端代码示例2.2、客户端代码示例2.3、服务端和客户端查看控制台输出结果三、CloseFuture关闭问题代码示例3.1、服务端代码示例3.2、客户端代码示例3.3、服务端和客户端查看控制台输出结果3.4、CloseFuture关闭…

【C++ STL】-- deque与vector相比的优势与劣势

目录 deque容器 与stack相比deque的优缺点&#xff1a; deque的迭代器 deque的成员函数 deque容器 deque的相关文档 deque与vector十分的相识。vector是单向开口的连续线性空间&#xff08;单向扩容&#xff09;&#xff0c;deque则是一种双向开口的连续线性空间&#xff…

GIC/ITS代码分析(9)中断应用实例之IPI中断

PPI中断为外设私有中断&#xff0c;在ARM64上arch_timer为PPI中断。这里以arch_timer为例&#xff08;代码位置drivers/clocksource/arm_arch_timer.c&#xff09;&#xff0c;作应用实例讲解。 先对ARM64通用定时器作简要介绍。通用定时器为Arm core提供标准定时器。通用定时器…

VSCode下载安装与使用教程

目录1. 打开官网进行下载安装2. 下载3. 安装4. 使用4.1 python extensions4.2 建立 python 工作区4.3 运行python代码的 3 种方法方法一&#xff1a;点击按钮方法二&#xff1a;右键菜单方法三&#xff1a;交互式解释器5. debug参考资料VSCode是一款免费开源的现代化轻量级代码…

【C++修炼之路】8. string类详解

每一个不曾起舞的日子都是对生命的辜负 C之string类本节目标1. string类概览1.1 string的由来1.2 string函数列表2.string常用接口1. 初始化2. string::npos3. c_str()4. 获取长度&#xff08;length、size)5. 容量&#xff08;size、capacity)6. 插入&#xff08;insert)7. 替…

牛客网-《刷C语言百题》第三期

✅作者简介&#xff1a;嵌入式入坑者&#xff0c;与大家一起加油&#xff0c;希望文章能够帮助各位&#xff01;&#xff01;&#xff01;&#xff01; &#x1f4c3;个人主页&#xff1a;rivencode的个人主页 &#x1f525;系列专栏&#xff1a;《C语言入门必刷百题》 &#x…

【HTML5期末大作业】制作一个简单HTML我的班级网页(HTML+CSS+JS)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

微信小程序 |从零实现酷炫纸质翻页效果

&#x1f4cc;个人主页&#xff1a;个人主页 ​&#x1f9c0; 推荐专栏&#xff1a;小程序开发成神之路 --【这是一个为想要入门和进阶小程序开发专门开启的精品专栏&#xff01;从个人到商业的全套开发教程&#xff0c;实打实的干货分享&#xff0c;确定不来看看&#xff1f; …

保姆级透明背景动画导出 —— json格式(Mac系统)

日常能想到的小动画基本就是使用ps导出GIF动画/AE通过插件直接导出GIF&#xff0c;方便快捷&#xff1b;但最近工作中遇到了关于透明背景导出GIF动画的问题&#xff1a;导出的GIF动画有白边&#xff0c;有锯齿感。 网上找了一大堆教程&#xff0c;主要原因出在GIF格式本身的问…

黑马程序员Java实战项目--- ATM系统

项目介绍与功能演示 黑马银行ATM系统技术选型分析&#xff1a; 学习本项目&#xff0c;你将至少得到如下收获&#xff1a; 1、优秀的面向对象编程能力。 2、 清晰、缜密的业务、数据分析能力。 3、熟练使用程序流程技术来控制计算机完成自己的想法。 4、形成良好的编码习惯…

【面试题】移除数组删除重复项合并数组

1️⃣ 原地移除数组中所有的元素val 【OJ链接】 2️⃣ 删除排序数组中的重复项 【OJ链接】 3️⃣ 合并两个有序数组 【OJ链接】 1️⃣ 原地移除数组中所有的元素val 题目要求是要在数组中&#xff0c;把指定的数字全部去除&#xff0c;题目中也是强调了空间复杂度要求为O(1)&am…

项目经理要“谋定而后动,知止而有得,万事皆有法,不可乱也”

出自《大学》&#xff0c;“谋定而后动&#xff0c;知止而有得”的含义为谋划准确周到而后行动&#xff0c;知道目的地&#xff08;合适的时机收手&#xff09;才能够有所收获。 谋定而后动是告诉我们做任何事一定要进行谋划部署&#xff0c;做好准备&#xff0c;我们才能开始行…

数据结构作业:时间复杂度和二叉树

计算时间复杂度&#xff1a; int x0,i,j; 1 for(i1;i<n;i) n { for(j1;j<2*i;j) 2(n-1) { x; 2(n-1)*n } } 2(n-1)*n2n^2-2n 修改后的运行次数函数中&#xff0c;只保留最高阶项。 所以时间复杂度为 &#xff1a;…

老生常谈的商城系统(Asp.Net+uniapp)

真正的大师,永远都怀着一颗学徒的心&#xff01; 最近几天合肥是真热 这时候就应该宅在家里 吃着西瓜 啃着鸭脖 喝着啤酒 刷着剧 想想也太美好了吧 哈哈 我得醒醒 写完这篇推荐 吃吃喝喝去了 一、项目简介 今天推荐一款商城系统&#xff0c;虽然比较老生常谈了&#xff0…

【项目】若依框架如何实现批量导入,并解析出表中内容返回给前端? - poi依赖

文章目录实现效果&#xff1a;具体实现步骤&#xff1a;扩展实例内容&#xff1a;poi工具包ExcelHandlerAdapter接口ExcelUtil.java代码controllerserviceserviceImplmapper.javamapper.xmlTnProductProperty.java实现效果&#xff1a; 点击“导入excl”按钮 --> 弹出“文件…

MSDC 4.3 接口规范(27)

MSDC 4.3 接口规范&#xff08;27&#xff09;7.4.4 代码7.4.5 更新组呼业务7.4.5.1 接口函数7.4.5.2 先决条件7.4.5.3 说明7.4.5.4 调用流程7.4.6 其他信息通知7.4.6.1 组呼服务停止7.4.6.1.1 接口函数7.4.6.1.2 先决条件7.4.6.1.3 说明7.4.6.1.4 调用流程7.4.6.2 SAI 列表更新…

Nginx禁止文件下载防止服务器被恶意扫描

比如将网站数据库导出到站点根目录进行备份&#xff0c;很有可能也会被别人下载&#xff0c;从而导致数据丢失的风险。以下规则可以防止一些常规的文件被下载&#xff0c;可根据实际情况增减。我们可以通过以下俩种方法来防止服务器被恶意扫描&#xff0c;其中以彼之道&#xf…