NetLink内核套接字案例分析

一、基础知识

        Netlink 是 Linux 系统中一种内核与用户空间通信的高效机制,而 Netlink 消息是这种通信的核心载体。它允许用户态程序(如网络配置工具、监控工具)与内核子系统(如网络协议栈、设备驱动)交换数据,例如获取网络接口信息、配置路由表、接收内核事件通知等。


Netlink 消息的组成

一个完整的 Netlink 消息由两部分构成:

  1. 消息头(struct nlmsghdr
    定义消息的元信息,例如消息类型、长度、序列号等。

    struct nlmsghdr {__u32 nlmsg_len;    // 消息总长度(头部 + 数据)__u16 nlmsg_type;   // 消息类型(如请求、响应、错误)__u16 nlmsg_flags;  // 标志位(如请求标志、多部分消息标志)__u32 nlmsg_seq;    // 序列号(用于匹配请求和响应)__u32 nlmsg_pid;    // 发送方端口ID(通常为进程ID)
    };
  2. 消息体(Payload)
    具体的数据内容,格式由消息类型决定。例如:

    • 路由消息:struct rtgenmsg(指定地址族)

    • 接口信息:struct ifinfomsg(接口索引、状态等)

    • 属性列表:动态附加的属性(如接口名称、MAC地址等)。


Netlink 消息的作用

Netlink 消息的核心功能是双向通信

1. 用户空间 → 内核

用户程序通过发送 Netlink 消息向内核发起操作请求。例如:

  • 查询信息RTM_GETLINK(获取网络接口列表)、RTM_GETROUTE(获取路由表)。

  • 配置内核RTM_NEWLINK(创建新接口)、RTM_SETLINK(修改接口属性)。

2. 内核 → 用户空间

内核通过 Netlink 消息主动通知用户程序事件。例如:

  • 接口状态变化:网络接口启用/禁用。

  • 新设备插入:USB 设备连接、Wi-Fi 网络扫描结果。

  • 路由表更新:路由条目添加或删除。


为什么用 Netlink?

与其他内核通信方式相比,Netlink 的优势在于:

机制特点适用场景
Netlink双向、异步、支持多播、结构化数据、可扩展动态配置和实时事件通知
Sysfs通过文件系统操作(/sys),读写简单但效率低静态配置(如设置参数)
Procfs通过文件系统(/proc),主要用于状态查询读取系统信息(如进程状态)
ioctl通过设备文件操作,接口不统一,扩展性差设备驱动特定操作
Netlink 的独特优势
  1. 结构化数据
    消息通过二进制格式传递,避免了文本解析(如 procfs/sysfs)的开销。

  2. 异步通信
    支持非阻塞通信,用户程序无需等待内核响应。

  3. 多播支持
    内核可以向多个用户进程广播事件(如接口状态变化)。

  4. 可扩展性
    通过消息类型(nlmsg_type)和属性(struct rtattr)灵活扩展功能。


Netlink 消息的工作流程

以获取网络接口列表为例:

  1. 用户程序构造请求消息

    • 设置 nlmsghdrnlmsg_type = RTM_GETLINKnlmsg_flags = NLM_F_DUMP

    • 设置 rtgenmsgrtgen_family = AF_UNSPEC(获取所有接口)。

  2. 发送消息到内核
    通过 sendmsg 系统调用发送 Netlink 消息。

  3. 内核处理请求
    路由子系统解析消息,收集所有网络接口信息,封装为多个 Netlink 消息(可能分片)。

  4. 用户程序接收响应
    通过 recvmsg 读取消息,解析 nlmsghdr 和消息体,提取接口名称、状态等数据。


典型应用场景

  1. 网络配置工具
    iproute2 工具集(如 ip linkip route)底层使用 Netlink 配置网络。

  1. 设备监控
    监听内核事件,如接口状态变化、新设备连接。

  2. 防火墙和策略路由
    配置 netfilter(iptables/nftables)规则或复杂路由策略。

  3. 容器网络
    容器运行时(如 Docker)通过 Netlink 管理虚拟网络设备。

 

1.nlinterfaces.c

// 程序功能:应用Netlink套接字从Linux内核打印输出所有网络接口名称
#include <bits/types/struct_iovec.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>  //Netlink协议相关定义
#include <linux/rtnetlink.h> // 路由相关的Netlink消息定义#define BUFSIZE 10240//定义一个自定义结构体ln_request_s,它包含一个 Netlink 消息头nlmsghdr和一个路由通用消息结构体rtgenmsg
struct In_request_s{//Netlink消息头struct nlmsghdr hdr;//路由消息通用结构,指定地址族struct rtgenmsg gen;
};//功能:解析并打印网络接口信息
void rtnl_print_link(struct nlmsghdr *h){//struct ifinfomsg *iface:指向 Netlink 消息中包含的网络接口信息结构体struct ifinfomsg *iface;//struct rtattr *attr:指向路由属性结构体struct  rtattr *attr;int len = 0;//获取 Netlink 消息中实际的数据部分,计算方式:消息头地址 + 头部大小iface = NLMSG_DATA(h);//获取 Netlink 消息中有效负载的长度len = RTM_PAYLOAD(h);//遍历路由属性for(attr = IFLA_RTA(iface);RTA_OK(attr,len);attr = RTA_NEXT(attr,len)){switch (attr->rta_type){//如果属性是接口名称就打印case IFLA_IFNAME:printf("接口名称%d : %s\n", iface->ifi_index, (char *)RTA_DATA(attr));break;default:break;}}
}int main(int argc,char *argv[]){//Netlink地址结构,用于绑定套接字struct sockaddr_nl nkernel;//消息头结构,用于 sendmsg 和 recvmsgstruct msghdr msg;//分散、聚集I/O结构,用于消息传输,主要跟readv、writev等缓冲区合并有关,用于一次I/O操作处理多个缓冲区struct iovec io;//自定义请求结构struct In_request_s req;//s为套接字描述符,end为循环结束标志int s = -1, end = 0, ret;//接收缓冲区char buf[BUFSIZE];//初始化Netlink地址结构memset(&nkernel,0,sizeof(nkernel));nkernel.nl_family = AF_NETLINK;    //这里与内核通信所以不使用AF_INETnkernel.nl_groups = 0; // 不加入任何组播组//创建套接字/*AF_NETLINK: 使用 Netlink 协议族。SOCK_RAW: 原始套接字类型,允许直接操作 Netlink 消息。NETLINK_ROUTE: 路由子系统,用于获取网络接口和路由信息。*/if ((s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0){printf("创建Netlink套接字失败.\n");exit(EXIT_FAILURE);}//构造Netlink请求消息memset(&req, 0, sizeof(req));//#define NLMSG_LENGTH(len) ((len) + NLMSG_ALIGN(sizeof(struct nlmsghdr)))//nlmsg_len: 消息总长度(头部 + rtgenmsg 结构体),通过 NLMSG_LENGTH 计算对齐后的长度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;//发送方进程 IDreq.hdr.nlmsg_pid = getpid();//指定地址族为 IPv4(可改为 AF_UNSPEC 获取所有接口)req.gen.rtgen_family = AF_INET;//设置I/O向量和消息头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;          // 指向 I/O 向量msg.msg_iovlen = 1;         // 向量数量为 1msg.msg_name = &nkernel;    // 目标地址(内核)msg.msg_namelen = sizeof(nkernel);//发送请求消息if ((ret = sendmsg(s, &msg, 0)) < 0) {perror("发送消息失败");close(s);exit(EXIT_FAILURE);}//接收并解析内核响应,,当接收到 NLMSG_DONE 消息时,end 会被置为 1,从而退出循环while(!end){//定义一个指向 nlmsghdr 结构体的指针 msg_ptr,用于遍历接收到的 Netlink 消息struct nlmsghdr *msg_ptr;//用于记录还未处理的消息长度int remaining_len;memset(buf, 0, BUFSIZE);io.iov_base = buf;io.iov_len = BUFSIZE;if ((ret = recvmsg(s, &msg, 0)) < 0) {if (errno == EINTR) continue;  // 处理中断perror("接收消息失败");close(s);exit(EXIT_FAILURE);}// 处理消息分片(NLMSG_TRUNC标志)if (msg.msg_flags & MSG_TRUNC) {fprintf(stderr, "警告:消息被截断,考虑增大缓冲区\n");}//将 msg_ptr 指针指向接收缓冲区 buf 的起始位置,将其视为第一个 Netlink 消息的头部msg_ptr = (struct nlmsghdr *)buf;//将 remaining_len 初始化为接收到的消息总长度 retremaining_len = ret;for (; NLMSG_OK(msg_ptr, remaining_len);   //NLMSG_OK(msg_ptr, remaining_len):这是一个宏,用于检查 msg_ptr 指向的 Netlink 消息是否有效,即消息长度是否足够且未超出剩余未处理的消息长度msg_ptr = NLMSG_NEXT(msg_ptr, remaining_len)) {  //将 msg_ptr 指针移动到下一个 Netlink 消息的头部,并更新 remaining_len 的值//内核在回复单播请求时,会将 nlmsg_pid 设置为用户进程的 PID(即 self_pid)if (msg_ptr->nlmsg_pid != getpid()) {fprintf(stderr, "收到非本进程的消息,已忽略 (PID: %u)\n", msg_ptr->nlmsg_pid);continue;}   switch (msg_ptr->nlmsg_type) {case NLMSG_ERROR: {  //如果消息类型为 NLMSG_ERROR,表示内核返回了错误信息struct nlmsgerr *err = NLMSG_DATA(msg_ptr);  //使用 NLMSG_DATA 宏获取消息中的错误信息结构体 nlmsgerr 的指针if (err->error != 0) {fprintf(stderr, "内核返回错误: %s\n", strerror(-err->error));close(s);exit(EXIT_FAILURE);}break;}case NLMSG_DONE: //如果消息类型为 NLMSG_DONE,表示内核已经发送完所有请求的信息,将 end 标志置为 1,退出循环end = 1;break;case RTM_NEWLINK: //如果消息类型为 RTM_NEWLINK,表示接收到了新的网络接口信息。调用 rtnl_print_link 函数处理该消息,打印网络接口的相关信息rtnl_print_link(msg_ptr);break;default:   //如果消息类型不是上述几种情况,输出忽略消息的信息,包含消息类型和消息长度printf("忽略消息:type=%d, len=%d\n",msg_ptr->nlmsg_type, msg_ptr->nlmsg_len);break;}}// 处理未对齐的剩余数据if (remaining_len > 0) {fprintf(stderr, "剩余%d字节未处理数据\n", remaining_len);}}close(s);  // 确保关闭套接字return 0;
}

编译运行:

二、ipaddress.c

// 显示IPv4,应用Netlink套接字从Linux内核中获取所有网络接口的IP地址
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <errno.h>#define BUFFERSIZE 10240// 定义一个自定义结构体 netlink_reqest_s,它包含一个 Netlink 消息头 nlmsghdr 和一个路由通用消息结构体 rtgenmsg
struct netlink_reqest_s {// Netlink 消息头struct nlmsghdr hdr;// 路由消息通用结构,指定地址族struct rtgenmsg gen;
};// 功能:解析并打印网络接口的 IP 地址信息
void rtnetlink_disp_address(struct nlmsghdr *h) {// struct ifaddrmsg *addr:指向 Netlink 消息中包含的网络地址信息结构体struct ifaddrmsg *addr;// struct rtattr *attr:指向路由属性结构体struct rtattr *attr;// 用于记录 Netlink 消息中有效负载的长度int len;// 获取 Netlink 消息中实际的数据部分,计算方式:消息头地址 + 头部大小addr = NLMSG_DATA(h);// 获取 Netlink 消息中有效负载的长度len = RTM_PAYLOAD(h);/* 循环输出 Netlink 所有属性消息:网络接口名称及 IP 地址 */for (attr = IFA_RTA(addr); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {switch (attr->rta_type) {// 如果属性是接口名称就打印case IFA_LABEL:printf("网络接口名称 : %s\n", (char *)RTA_DATA(attr));break;// 如果属性是本地 IP 地址就打印case IFA_LOCAL: {// 获取 IP 地址的二进制表示int ip = *(int *)RTA_DATA(attr);// 用于存储 IP 地址的四个字节unsigned char bytes[4];// 提取 IP 地址的四个字节bytes[0] = ip & 0xFF;bytes[1] = (ip >> 8) & 0xFF;bytes[2] = (ip >> 16) & 0xFF;bytes[3] = (ip >> 24) & 0xFF;// 打印网络 IP 地址printf("网络 IP 地址为 : %d.%d.%d.%d\n\n", bytes[0], bytes[1], bytes[2], bytes[3]);break;}default:break;}}
}int main(void) {// Netlink 地址结构,用于绑定套接字struct sockaddr_nl kerl;// 套接字描述符int s;// 循环结束标志int end = 0;// 接收到的消息长度int len;// 消息头结构,用于 sendmsg 和 recvmsgstruct msghdr msg;// 自定义请求结构struct netlink_reqest_s req;// 分散、聚集 I/O 结构,用于消息传输struct iovec io;// 接收缓冲区char buffer[BUFFERSIZE];// 初始化 Netlink 地址结构memset(&kerl, 0, sizeof(kerl));// 使用 Netlink 协议族kerl.nl_family = AF_NETLINK;// 不加入任何组播组kerl.nl_groups = 0;// 创建套接字/*AF_NETLINK: 使用 Netlink 协议族。SOCK_RAW: 原始套接字类型,允许直接操作 Netlink 消息。NETLINK_ROUTE: 路由子系统,用于获取网络接口和路由信息。*/if ((s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {perror("创建 Netlink 套接字失败");exit(EXIT_FAILURE);}// 构造 Netlink 请求消息memset(&req, 0, sizeof(req));// 消息总长度(头部 + rtgenmsg 结构体),通过 NLMSG_LENGTH 计算对齐后的长度req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));// 请求获取网络接口地址信息req.hdr.nlmsg_type = RTM_GETADDR;// 标志为请求消息,并要求返回所有条目req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;// 序列号,用于匹配请求和响应req.hdr.nlmsg_seq = 1;// 发送方进程 IDreq.hdr.nlmsg_pid = getpid();// 指定地址族为 IPv4req.gen.rtgen_family = AF_INET;// 设置 I/O 向量和消息头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;          // 指向 I/O 向量msg.msg_iovlen = 1;         // 向量数量为 1msg.msg_name = &kerl;       // 目标地址(内核)msg.msg_namelen = sizeof(kerl);// 发送请求消息if (sendmsg(s, &msg, 0) < 0) {perror("发送消息失败");close(s);exit(EXIT_FAILURE);}// 接收并解析内核响应,当接收到 NLMSG_DONE 消息时,end 会被置为 1,从而退出循环while (!end) {// 定义一个指向 nlmsghdr 结构体的指针 msg_ptr,用于遍历接收到的 Netlink 消息struct nlmsghdr *msg_ptr;// 用于记录还未处理的消息长度int remaining_len;// 清空接收缓冲区memset(buffer, 0, BUFFERSIZE);io.iov_base = buffer;io.iov_len = BUFFERSIZE;// 接收消息if ((len = recvmsg(s, &msg, 0)) < 0) {if (errno == EINTR) continue;  // 处理中断perror("接收消息失败");close(s);exit(EXIT_FAILURE);}// 处理消息分片(NLMSG_TRUNC 标志)if (msg.msg_flags & MSG_TRUNC) {fprintf(stderr, "警告:消息被截断,考虑增大缓冲区\n");}// 将 msg_ptr 指针指向接收缓冲区 buffer 的起始位置,将其视为第一个 Netlink 消息的头部msg_ptr = (struct nlmsghdr *)buffer;// 将 remaining_len 初始化为接收到的消息总长度 lenremaining_len = len;for (; NLMSG_OK(msg_ptr, remaining_len);  // NLMSG_OK(msg_ptr, remaining_len):这是一个宏,用于检查 msg_ptr 指向的 Netlink 消息是否有效,即消息长度是否足够且未超出剩余未处理的消息长度msg_ptr = NLMSG_NEXT(msg_ptr, remaining_len)) {  // 将 msg_ptr 指针移动到下一个 Netlink 消息的头部,并更新 remaining_len 的值// 内核在回复单播请求时,会将 nlmsg_pid 设置为用户进程的 PID(即 self_pid)if (msg_ptr->nlmsg_pid != getpid()) {fprintf(stderr, "收到非本进程的消息,已忽略 (PID: %u)\n", msg_ptr->nlmsg_pid);continue;}switch (msg_ptr->nlmsg_type) {// 如果消息类型为 NLMSG_ERROR,表示内核返回了错误信息case NLMSG_ERROR: {// 使用 NLMSG_DATA 宏获取消息中的错误信息结构体 nlmsgerr 的指针struct nlmsgerr *err = NLMSG_DATA(msg_ptr);if (err->error != 0) {fprintf(stderr, "内核返回错误: %s\n", strerror(-err->error));close(s);exit(EXIT_FAILURE);}break;}// 如果消息类型为 NLMSG_DONE,表示内核已经发送完所有请求的信息,将 end 标志置为 1,退出循环case NLMSG_DONE:end = 1;break;// 如果消息类型为 RTM_NEWADDR,表示接收到了新的网络接口地址信息。调用 rtnetlink_disp_address 函数处理该消息,打印网络接口的相关信息case RTM_NEWADDR:rtnetlink_disp_address(msg_ptr);break;// 如果消息类型不是上述几种情况,输出忽略消息的信息,包含消息类型和消息长度default:printf("忽略消息:type=%d, len=%d\n", msg_ptr->nlmsg_type, msg_ptr->nlmsg_len);break;}}// 处理未对齐的剩余数据if (remaining_len > 0) {fprintf(stderr, "剩余 %d 字节未处理数据\n", remaining_len);}}// 确保关闭套接字close(s);return 0;
}    

编译运行:

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

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

相关文章

批量压缩与优化 Excel 文档,减少 Excel 文档大小

当我们在 Excel 文档中插入图片资源的时候&#xff0c;如果我们插入的是原图&#xff0c;可能会导致 Excel 变得非常的大。这非常不利于我们传输或者共享。那么当我们的 Excel 文件非常大的时候&#xff0c;我们就需要对文档做一些压缩或者优化的处理。那有没有什么方法可以实现…

基于深度学习的多模态人脸情绪识别研究与实现(视频+图像+语音)

这是一个结合图像和音频的情绪识别系统&#xff0c;从架构、数据准备、模型实现、训练等。包括数据收集、预处理、模型训练、融合方法、部署优化等全流程。确定完整系统的组成部分&#xff1a;数据收集与处理、模型设计与训练、多模态融合、系统集成、部署优化、用户界面等。详…

保姆级离线TiDB V8+解释

以前学习的时候还是3版本&#xff0c;如今已经是8版本了 https://cn.pingcap.com/product-community/?_gl1ujh2l9_gcl_auMTI3MTI3NTM3NC4xNzM5MjU3ODE2_gaMTYwNzE2NTI4OC4xNzMzOTA1MjUz_ga_3JVXJ41175MTc0MTk1NTc1OC4xMS4xLjE3NDE5NTU3NjIuNTYuMC41NDk4MTMxNTM._ga_CPG2VW1Y4…

spark实验2

一.实验题目 实验所需要求&#xff1a; centos7虚拟机 pyspark spark python3 hadoop分布式 统计历届春晚的节目数目 统计各个类型节目的数量&#xff0c;显示前10名 统计相声类节目历年的数目。 查询每个演员在春晚上表演节目的数量。 统计每年各类节目的数量&#xff0…

学习文章:Spring Boot 中如何使用 `@Async` 实现异步处理

文章目录 学习文章&#xff1a;Spring Boot 中如何使用 Async 实现异步处理 一、什么是 Async&#xff1f;优点&#xff1a; 二、Spring Boot 中启用 Async1. 启用异步支持2. 配置线程池&#xff08;可选&#xff09;3. 使用 Async 注解4. 调用异步方法 三、Async 的进阶用法1.…

Manus:成为AI Agent领域的标杆

一、引言 官网&#xff1a;Manus 随着人工智能技术的飞速发展&#xff0c;AI Agent&#xff08;智能体&#xff09;作为人工智能领域的重要分支&#xff0c;正逐渐从概念走向现实&#xff0c;并在各行各业展现出巨大的应用潜力。在众多AI Agent产品中&#xff0c;Manus以其独…

Git Fast-forward 合并详解:原理、场景与最佳实践

在使用 Git 进行团队协作时&#xff0c;我们经常需要合并分支。合并方式有很多种&#xff0c;其中 Fast-forward&#xff08;快速合并&#xff09; 是一种最简单且无冲突的合并方式。本文将详细介绍 Fast-forward 的原理、适用场景、常见问题及最佳实践。 一、Fast-forward 合并…

命令行重启Ubuntu软件

我是用Todesk远程桌面&#xff0c;如果卡死的时候&#xff0c;只能通过ssh连接命令行。于是&#xff0c;就有了如标题所示的需求。 首先&#xff0c;我们看一下todesk在系统里叫什么名字&#xff1a; systemctl list-unit-files | grep -i todesk看到发现是"todeskd.serv…

算法每日一练 (11)

&#x1f4a2;欢迎来到张胤尘的技术站 &#x1f4a5;技术如江河&#xff0c;汇聚众志成。代码似星辰&#xff0c;照亮行征程。开源精神长&#xff0c;传承永不忘。携手共前行&#xff0c;未来更辉煌&#x1f4a5; 文章目录 算法每日一练 (11)全排列题目描述解题思路解题代码c/c…

《Spring日志整合与注入技术:从入门到精通》

1.Spring与日志框架的整合 1.Spring与日志框架进行整合&#xff0c;日志框架就可以在控制台中&#xff0c;输出Spring框架运行过程中的一些重要的信息。 好处&#xff1a;方便了解Spring框架的运行过程&#xff0c;利于程序的调试。 Spring如何整合日志框架 Spring5.x整合log4j…

《SQL性能优化指南:新手如何写出高效的数据库查询

新手程序员如何用三个月成为SQL高手&#xff1f;万字自学指南带你弯道超车 在数据为王的时代&#xff0c;掌握SQL已成为职场新人的必修课。你可能不知道&#xff0c;仅用三个月系统学习&#xff0c;一个零基础的小白就能完成从数据库萌新到SQL达人的蜕变。去年刚毕业的小王就是…

【Unity】在项目中使用VisualScripting

1. 在packagemanager添加插件 2. 在设置中进行初始化。 Edit > Project Settings > Visual Scripting Initialize Visual Scripting You must select Initialize Visual Scripting the first time you use Visual Scripting in a project. Initialize Visual Scripting …

JConsole 在 Linux 上的使用

JConsole 在 Linux 上的使用指南 1. 启动 JConsole 远程监控 Linux 服务器上的 JVM 进程 1.1 修改 JMX 配置&#xff0c;允许远程访问 在 Linux 服务器 启动 Java 应用时&#xff0c;需要加上 -Djava.rmi.server.hostname<服务器IP>&#xff0c;完整的启动参数如下&am…

个人记录,Unity资源解压和管理插件

就是经典的两个AssetStudio 和 Ripper 没有什么干货&#xff0c;就是记录一下&#xff0c;内容没有很详细 AssetStudio 说错了&#xff0c;AssetStudio比较出名&#xff08;曾经&#xff09;&#xff0c;但好像堕落了 是&#xff0c;AssetBundlExtractor 这个工具有个好处就…

编译skia

1.准备工具 (1)vs2019,到微软官方下载下载 Visual Studio Tools - 免费安装 Windows、Mac、Linux (2)ninja,下载地址:Releases ninja-build/ninja GitHub (3)gn,下载地址:https://chrome-infra-packages.appspot.com/p/gn/gn/windows-amd64 (4)skia,下载地址:git …

vue 知识点整理

1.data为什么是一个函数而不是对象 维度对象形式函数形式数据隔离性所有实例共享同一对象&#xff0c;导致数据污染每个实例拥有独立数据副本复用安全性不适用于可复用组件支持组件安全复用语言机制引用传递引发副作用函数返回值实现作用域隔离&#xff08;闭包&#xff09;框…

DeepSeek-Open WebUI部署

1.DeepSeek部署-Win版本 2.DeepSeek部署-Linux版本 3.DeepSeek部署-一键部署(Linux版本) 4.DeepSeek部署-进阶版本(LinuxGPU) 5.DeepSeek部署-基于vLLM部署 前面部署了vLLM版本以后&#xff0c;访问它比较麻烦。如何才能更好的实现访问呢&#xff0c;这个就是我们今天要讲的…

(vue)elementUi中el-upload上传附件之后 点击附件可下载

(vue)elementUi中el-upload上传附件之后 点击附件可下载 handlePreview(file) {console.log(file)const fileUrl https://.../zzy/ file.urlconst a document.createElement(a)a.href fileUrla.download file.namea.style.display none// a.setAttribute(download, file.…

你认为 Java 的优势是什么?

你认为 Java 的优势是什么? 回答重点 我觉得可以从跨平台、垃圾回收、生态、面向对象四个方面来阐述。 跨平台 首先 Java 是跨平台的,不同平台执行的机器码是不一样的,而 Java 因为加了一层中间层 JVM,所以可以做到一次编写多平台(如 Windows、Linux、macOS)运行,即…

SpringBoot——Maven篇

Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的工具。它具有许多特性&#xff0c;其中一些重要的特性包括&#xff1a; 1. 自动配置&#xff1a;Spring Boot 提供了自动配置的机制&#xff0c;可以根据应用程序的依赖和环境自动配置应用程序的各种组件&#xff…