Linux内核传输层UDP源码分析
一、用户数据包协议(UDP)
1.UDP数据报头
UDP 提供面向消息的不可靠传输,但没有拥塞控制功能。很多协议都使用 UDP,如用于 IP 网络传输音频和视频的实时传输协议 (Real-time Transport Protocol,RTP),此类型容许一定的数据包丢弃。UDP 报头长 8 字节,具体内核源码如下:
2.UDP初始化操作
定义对象udp_protocol(net_protocol对象)并使用方法inet_add_protocol()来添加它,具体源码如下:
1.
udp_protocol
结构体分析(协议注册)static struct net_protocol udp_protocol = {.early_demux = udp_v4_early_demux, // 早期解复用函数,处理初始阶段的数据包.early_demux_handler = udp_v4_early_demux, // 早期解复用处理器.handler = udp_rcv, // UDP 数据包接收处理函数,处理完整的 UDP 数据接收.err_handler = udp_err, // 错误处理函数,处理 UDP 传输中的错误.no_policy = 1, // 标记是否忽略策略检查.netns_ok = 1 // 标记是否支持网络命名空间 };
- 作用:这是内核中 UDP 协议的注册实现。通过定义
net_protocol
类型的udp_protocol
,向内核网络子系统注册 UDP 协议的处理函数。内核通过inet_add_protocol()
方法将其加入协议处理链,使内核能够识别和处理 UDP 数据包。2.
inet_init_net
初始化(端口范围等配置)
- 作用:初始化网络命名空间内的 IPv4 相关参数。例如设置本地端口分配范围(
ip_local_ports
),限制程序动态申请端口的范围;配置 ping 套接字的用户组权限(ping_group_range
),控制哪些用户组可创建 ping 套接字。
3.发送UDP数据包udp_sendmsg(...)
从UDP用户空间套接字中发送数据,可以使用系统调用send()、sendto()、sendmsg()和write()。这些系统调用最终都会由内核中的方法udp_sendmsg()来处理。以下是这个函数的流程图:
1. 函数参数
struct sock *sk
:指向套接字的指针,该套接字表示当前进行 UDP 数据发送操作所使用的套接字对象,其中包含了套接字的各种状态信息和配置。struct msghdr *msg
:指向msghdr
结构体的指针,这个结构体包含了要发送的数据以及目标地址等信息,例如数据缓冲区、目标地址结构体等。size_t len
:表示要发送的数据的长度。2. 函数功能
udp_sendmsg
函数是 Linux 内核中用于通过 UDP 套接字发送数据的核心函数。它会对传入的消息进行一系列的检查和处理,包括验证目标地址、处理控制消息、查找路由表等操作,最终将数据封装成 UDP 数据包并发送出去。如果在发送过程中遇到错误,会返回相应的错误码;如果发送成功,则返回实际发送的数据长度。3. 重要部分及流程讲解
3.1 基本参数检查和初始化
if (len > 0xFFFF)return -EMSGSIZE;if (msg->msg_flags & MSG_OOB)return -EOPNOTSUPP;getfrag = is_udplite ? udplite_getfrag : ip_generic_getfrag;
- 首先检查要发送的数据长度是否超过了 UDP 数据包的最大长度(
0xFFFF
),如果超过则返回EMSGSIZE
错误。- 接着检查消息标志中是否包含
MSG_OOB
(带外数据),由于 UDP 不支持带外数据,所以如果包含则返回EOPNOTSUPP
错误。- 根据套接字是否为 UDP-Lite 类型,选择不同的数据获取函数。
3.2 处理已 corked 的套接字
fl4 = &inet->cork.fl.u.ip4; if (up->pending) {lock_sock(sk);if (likely(up->pending)) {if (unlikely(up->pending != AF_INET)) {release_sock(sk);return -EINVAL;}goto do_append_data;}release_sock(sk); }
- 检查套接字是否已经处于 corked 状态(即有未发送的数据包)。如果是,则加锁并进一步检查状态是否正确,若不正确则返回
EINVAL
错误,若正确则跳转到do_append_data
标签处继续处理。3.3 获取并验证目标地址
if (usin) {if (msg->msg_namelen < sizeof(*usin))return -EINVAL;if (usin->sin_family != AF_INET) {if (usin->sin_family != AF_UNSPEC)return -EAFNOSUPPORT;}daddr = usin->sin_addr.s_addr;dport = usin->sin_port;if (dport == 0)return -EINVAL; } else {if (sk->sk_state != TCP_ESTABLISHED)return -EDESTADDRREQ;daddr = inet->inet_daddr;dport = inet->inet_dport;connected = 1; }
- 如果
msg
中包含目标地址信息,则检查地址长度和地址族是否正确,获取目标 IP 地址和端口号,并验证端口号是否有效。- 如果
msg
中不包含目标地址信息,则检查套接字是否已经连接,如果未连接则返回EDESTADDRREQ
错误,否则使用套接字中保存的目标地址和端口号,并标记为已连接。3.4 处理控制消息
if (msg->msg_controllen) {err = udp_cmsg_send(sk, msg, &ipc.gso_size);if (err > 0)err = ip_cmsg_send(sk, msg, &ipc, sk->sk_family == AF_INET6);if (unlikely(err < 0)) {kfree(ipc.opt);return err;}if (ipc.opt)free = 1;connected = 0; }
3.5 查找路由表
if (connected)rt = (struct rtable *)sk_dst_check(sk, 0);if (!rt) {struct net *net = sock_net(sk);__u8 flow_flags = inet_sk_flowi_flags(sk);fl4 = &fl4_stack;flowi4_init_output(fl4, ipc.oif, ipc.sockc.mark, tos,RT_SCOPE_UNIVERSE, sk->sk_protocol,flow_flags,faddr, saddr, dport, inet->inet_sport,sk->sk_uid);security_sk_classify_flow(sk, flowi4_to_flowi(fl4));rt = ip_route_output_flow(net, fl4, sk);if (IS_ERR(rt)) {err = PTR_ERR(rt);rt = NULL;if (err == -ENETUNREACH)IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES);goto out;}err = -EACCES;if ((rt->rt_flags & RTCF_BROADCAST) &&!sock_flag(sk, SOCK_BROADCAST))goto out;if (connected)sk_dst_set(sk, dst_clone(&rt->dst)); }
- 如果套接字已经连接,则检查缓存的路由信息是否可用。
- 如果没有可用的路由信息,则初始化
flowi4
结构体,进行安全分类,并调用ip_route_output_flow
函数查找路由表。如果查找失败,则根据错误码进行相应处理,如增加统计信息并跳转到out
标签处。- 如果路由表中包含广播标志,但套接字不允许广播,则返回
EACCES
错误。3.6 发送数据
if (!corkreq) {struct inet_cork cork;skb = ip_make_skb(sk, fl4, getfrag, msg, ulen,sizeof(struct udphdr), &ipc, &rt,&cork, msg->msg_flags);err = PTR_ERR(skb);if (!IS_ERR_OR_NULL(skb))err = udp_send_skb(skb, fl4, &cork);goto out; }lock_sock(sk); if (unlikely(up->pending)) {release_sock(sk);net_dbg_ratelimited("socket already corked\n");err = -EINVAL;goto out; } fl4 = &inet->cork.fl.u.ip4; fl4->daddr = daddr; fl4->saddr = saddr; fl4->fl4_dport = dport; fl4->fl4_sport = inet->inet_sport; up->pending = AF_INET;do_append_data: up->len += ulen; err = ip_append_data(sk, fl4, getfrag, msg, ulen,sizeof(struct udphdr), &ipc, &rt,corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags); if (err)udp_flush_pending_frames(sk); else if (!corkreq)err = udp_push_pending_frames(sk); else if (unlikely(skb_queue_empty(&sk->sk_write_queue)))up->pending = 0; release_sock(sk);
- 如果不需要 cork 操作(即
corkreq
为false
),则调用ip_make_skb
函数创建一个skb
(套接字缓冲区),并调用udp_send_skb
函数发送数据。- 如果需要 cork 操作,则加锁并检查套接字状态,更新相关信息,然后调用
ip_append_data
函数将数据追加到缓冲区中。如果追加过程中出现错误,则调用udp_flush_pending_frames
函数清空缓冲区;如果不需要 cork 操作,则调用udp_push_pending_frames
函数将缓冲区中的数据发送出去。3.7 清理资源并返回结果
out: ip_rt_put(rt); out_free: if (free)kfree(ipc.opt); if (!err)return len; if (err == -ENOBUFS || test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) {UDP_INC_STATS(sock_net(sk),UDP_MIB_SNDBUFERRORS, is_udplite); } return err;
如果
- 释放路由表资源和控制消息相关的内存。
- 如果没有错误发生,则返回实际发送的数据长度;如果出现错误,则根据错误码进行相应的统计更新并返回错误码。
msg
中包含控制消息,则调用udp_cmsg_send
和ip_cmsg_send
函数处理这些控制消息。如果处理过程中出现错误,则释放相关资源并返回错误码。
4.接收来自网络层的UDP数据包udp_rcv(...)
方法udp_rcv()是负责接收来自网络层的UDP数据包的主要处理程序,函数流程如下:
1. 函数参数和功能
函数参数
struct sk_buff *skb
:指向套接字缓冲区的指针,其中包含接收到的 UDP 数据包。struct udp_table *udptable
:指向 UDP 表的指针,该表用于存储 UDP 套接字的相关信息。int proto
:协议类型,通常为IPPROTO_UDP
。函数功能
udp_rcv
函数是一个简单的包装函数,它直接调用__udp4_lib_rcv
函数来处理接收到的 UDP 数据包。__udp4_lib_rcv
函数是处理 UDP 数据包接收的核心函数,它会对数据包进行一系列的验证和处理,包括检查数据包长度、校验和,查找对应的套接字,将数据包传递给相应的套接字进行处理,或者在找不到合适套接字时发送 ICMP 错误消息。2. 重要部分及流程讲解
2.1 基本参数检查和初始化
if (!pskb_may_pull(skb, sizeof(struct udphdr)))goto drop; /* No space for header. */uh = udp_hdr(skb); ulen = ntohs(uh->len); saddr = ip_hdr(skb)->saddr; daddr = ip_hdr(skb)->daddr;
- 首先检查套接字缓冲区中是否有足够的空间来存储 UDP 头部,如果没有则跳转到
drop
标签处丢弃数据包。- 提取 UDP 头部信息,包括 UDP 数据包长度
ulen
,源 IP 地址saddr
和目的 IP 地址daddr
。2.2 数据包长度验证
if (ulen > skb->len)goto short_packet;if (proto == IPPROTO_UDP) {/* UDP validates ulen. */if (ulen < sizeof(*uh) || pskb_trim_rcsum(skb, ulen))goto short_packet;uh = udp_hdr(skb); }
- 检查 UDP 头部中记录的数据包长度
ulen
是否超过了实际接收到的数据包长度skb->len
,如果超过则跳转到short_packet
标签处处理。- 如果协议类型为
IPPROTO_UDP
,还需要检查ulen
是否小于 UDP 头部长度,或者调用pskb_trim_rcsum
函数对数据包进行裁剪和校验和更新,如果出现问题则跳转到short_packet
标签处。2.3 校验和初始化
if (udp4_csum_init(skb, uh, proto))goto csum_error;
调用
udp4_csum_init
函数对 UDP 数据包的校验和进行初始化,如果初始化失败则跳转到csum_error
标签处处理。2.4 查找套接字并处理
sk = skb_steal_sock(skb); if (sk) {struct dst_entry *dst = skb_dst(skb);int ret;if (unlikely(sk->sk_rx_dst != dst))udp_sk_rx_dst_set(sk, dst);ret = udp_unicast_rcv_skb(sk, skb, uh);sock_put(sk);return ret; }
- 尝试从套接字缓冲区中获取关联的套接字
sk
。- 如果获取到了套接字,检查接收目标地址是否与套接字中的记录一致,如果不一致则更新。
- 调用
udp_unicast_rcv_skb
函数将数据包传递给该套接字进行处理,并返回处理结果。2.5 处理广播或多播数据包
if (rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))return __udp4_lib_mcast_deliver(net, skb, uh,saddr, daddr, udptable, proto);
如果路由表项中包含广播或多播标志,则调用
__udp4_lib_mcast_deliver
函数处理广播或多播数据包。2.6 再次查找套接字
sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable); if (sk)return udp_unicast_rcv_skb(sk, skb, uh);
如果之前没有找到关联的套接字,再次调用
__udp4_lib_lookup_skb
函数根据源端口和目的端口在 UDP 表中查找合适的套接字。如果找到则将数据包传递给该套接字进行处理并返回结果,这个过程通常包含以下步骤:
- 把接收到的数据添加到套接字的接收缓冲区。
- 若应用程序正在阻塞等待数据,就会唤醒该应用程序,让其从接收缓冲区读取数据。
- 若应用程序采用的是非阻塞模式,就会在后续的轮询或事件通知中得知有新数据到达。
2.7 策略检查和校验和检查
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))goto drop; nf_reset_ct(skb);/* No socket. Drop packet silently, if checksum is wrong */ if (udp_lib_checksum_complete(skb))goto csum_error;
- 重置连接跟踪信息。
- 调用
udp_lib_checksum_complete
函数检查 UDP 数据包的校验和,如果校验和错误则跳转到csum_error
标签处处理。2.8 发送 ICMP 错误消息
__UDP_INC_STATS(net, UDP_MIB_NOPORTS, proto == IPPROTO_UDPLITE); icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);/** Hmm. We got an UDP packet to a port to which we* don't wanna listen. Ignore it.*/ kfree_skb(skb); return 0;
如果没有找到合适的套接字,增加相应的统计信息,调用
icmp_send
函数发送 ICMP 目的不可达(端口不可达)消息,然后释放套接字缓冲区并返回 0。2.9 错误处理
short_packet:net_dbg_ratelimited("UDP%s: short packet: From %pI4:%u %d/%d to %pI4:%u\n",proto == IPPROTO_UDPLITE ? "Lite" : "",&saddr, ntohs(uh->source),ulen, skb->len,&daddr, ntohs(uh->dest));goto drop;csum_error:/** RFC1122: OK. Discards the bad packet silently (as far as* the network is concerned, anyway) as per 4.1.3.4 (MUST).*/net_dbg_ratelimited("UDP%s: bad checksum. From %pI4:%u to %pI4:%u ulen %d\n",proto == IPPROTO_UDPLITE ? "Lite" : "",&saddr, ntohs(uh->source), &daddr, ntohs(uh->dest),ulen);__UDP_INC_STATS(net, UDP_MIB_CSUMERRORS, proto == IPPROTO_UDPLITE); drop:__UDP_INC_STATS(net, UDP_MIB_INERRORS, proto == IPPROTO_UDPLITE);kfree_skb(skb);return 0;
调用
short_packet
标签处:记录数据包长度过短的调试信息,然后跳转到drop
标签处。csum_error
标签处:记录校验和错误的调试信息,增加校验和错误的统计信息,然后跳转到drop
标签处。drop
标签处:增加接收错误的统计信息,释放套接字缓冲区并返回 0。xfrm4_policy_check
函数进行安全策略检查,如果检查不通过则跳转到drop
标签处丢弃数据包。
5.UDP使用流程
1. 用户层调用(sendto)
- 用户通过 socket () 创建 UDP 套接字,调用 sendto () 发送数据报
- 参数包含目标 IP (47.95.193.211)、端口 (默认可能未指定,需填充)
- 内核进入 inet_sendmsg () 处理,分配 skb_buff(创建位置:传输层)
2. 传输层处理(UDP 层)
- 创建 sk_buff 数据结构(skb->dev = 网络设备指针)
- 填充 UDP 头(源端口 8888,目标端口待确认)
- 计算校验和(可选,由 net.core.udp_checksum 内核参数控制)
- skb->protocol = htons (ETH_P_IP),标识上层协议
3. 网络层处理(IP 层)
- 调用 ip_queue_xmit () 进行路由查找
- 路由表查询:
- 使用 FIB 表(fib_rules)查找最佳路由
- 确定输出设备(eth0/wlan0 等)
- 下一跳地址(可能是网关地址,若目标不在同一子网)
- 填充 IP 头:
- 源 IP (192.168.186.138),目标 IP (47.95.193.211)
- TTL、协议号 (17)、校验和
- 检查是否需要分片(根据 MTU)
4. 链路层处理
- 调用 dev_queue_xmit () 进入链路层
- ARP 表查询:
- 使用 neigh_table 结构查找 arp_cache
- 若下一跳是网关,查询网关 MAC
- 若目标在同一子网,直接查目标 MAC
- 若 ARP 缓存缺失,触发 ARP 请求
- 填充链路层头(以太网为例):
- 源 MAC(本地网卡 MAC)
- 目标 MAC(下一跳或目标 MAC)
- 类型字段 0x0800(IP 协议)
5. 物理层传输
- sk_buff 通过 net_device_ops->ndo_start_xmit () 发送
- 物理层将二进制数据转换为电信号 / 光信号传输
接收流程简要说明:
- 物理层接收信号并转为二进制数据
- 链路层剥离帧头,检查 CRC 校验
- IP 层校验和验证,路由表查找输入接口
- UDP 层校验和验证,端口号匹配
- 用户层通过 recvfrom () 接收数据
关键数据结构说明:
- sk_buff:贯穿各层,包含协议头、数据负载、设备指针等
- fib_rules:路由规则表,用于确定输出路径
- arp_cache:基于 neigh_table 的邻居缓存,存储 IP-MAC 映射
- net_device:网络设备结构体,包含发送 / 接收函数指针
相关文章:
Linux内核传输层UDP源码分析
一、用户数据包协议(UDP) 1.UDP数据报头 UDP 提供面向消息的不可靠传输,但没有拥塞控制功能。很多协议都使用 UDP,如用于 IP 网络传输音频和视频的实时传输协议 (Real-time Transport Protocol,RTP),此类型…...
GitHub 超火的开源终端工具——Warp
Warp 作为近年来 GitHub 上备受瞩目的开源终端工具,以其智能化、高性能和协作能力重新定义了命令行操作体验。以下从多个维度深入解析其核心特性、技术架构、用户评价及生态影响力: 一、背景与核心团队 Warp 由前 GitHub CTO Jason Warner 和 Google 前…...
【Java基础巩固系列】异常
业务背景 业务开发中,总会遇到代码出现异常的情况,不合理的异常处理或不处理异常除了影响业务功能和中断业务功能外,还会增加排查问题的难度。所以我们要学会正确的使用异常处理。合理的异常处理能减少很多潜在的问题,是提高代码…...
sass介绍
1、Sass简介 Sass 是一种 CSS 的预编译语言。它提供了 变量(variables)、嵌套(nested rules)、 混合(mixins)、 函数(functions)等功能,并且完全兼容 CSS 语法。Sass 能…...
第1章:云原生时代:容器技术的发展历程与核心价值
第1章:云原生时代:容器技术的发展历程与核心价值 作者:DogDog_Shuai 阅读时间:约15分钟 难度:入门级 目录 1. 引言2. 容器技术的发展历程3. 容器技术的核心价值4. 云原生时代的机遇与挑战5. 总结1. 引言...
软考程序员考试知识点汇总
软考程序员考试(初级资格)主要考察计算机基础理论、编程能力及软件开发相关知识。以下是核心知识点总结及备考建议: 一、计算机基础 数制与编码 二进制、八进制、十进制、十六进制转换原码、反码、补码表示(整数与浮点数…...
JVM OOM问题如何排查和解决
在 Java 开发中,JVM OOM(OutOfMemoryError)问题通常是指程序运行时,JVM 无法为对象分配足够的内存空间,导致发生内存溢出的错误。这个问题往往和内存的配置、内存泄漏、或者资源过度使用等因素有关。 1. OOM 错误类型…...
折叠树报表
折叠树报表中包含了三种信息: 1.树组织信息-可展开、收拢 2.节点的统计信息(汇总求和) 3.每个节点对应的数据信息 一、准备数据 mysql8 数据库中存在两张表 org和store表。 org表和部分数据如下,其中orgname是组织的名称,codepath是完整的组织代码,seq是每个节点的顺序,可…...
python 数据可视化matplotib库安装与使用
要使用 matplotlib 库进行数据可视化,首先你需要确保已经安装了该库。如果你还没有安装,可以通过 Python 的包管理器 pip 来安装它。在你的命令行工具中运行以下命令来安装 matplotlib: pip install matplotlib安装完成后,你就可以…...
Springdoc配置参数详解
文章目录 **1. 基础配置****API 文档路径-springdoc.api-docs.path****Swagger UI 路径-springdoc.swagger-ui.path****是否启用 API 文档-springdoc.api-docs.enabled****是否启用 Swagger UI-springdoc.swagger-ui.enabled** **2. 全局元信息-info****应用标题-springdoc.inf…...
抖音视频数据获取实战:从API调用到热门内容挖掘
在短视频流量为王的时代,掌握抖音热门视频数据已成为内容运营、竞品分析及营销决策的关键。本文将手把手教你通过抖音开放平台API获取视频详情数据,并提供完整的代码实现及商业化应用思路。 一、抖音API权限申请与核心接口 抖音API需企业资质认证&…...
【数学建模】灰色关联分析模型详解与应用
灰色关联分析模型详解与应用 文章目录 灰色关联分析模型详解与应用引言灰色系统理论简介灰色关联分析基本原理灰色关联分析计算步骤1. 确定分析序列2. 数据无量纲化处理3. 计算关联系数4. 计算关联度 灰色关联分析应用实例实例:某企业生产效率影响因素分析 灰色关联…...
Spring Boot 与 Couchbase 整合教程
精心整理了最新的面试资料和简历模板,有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 Spring Boot 与 Couchbase 整合教程 环境要求 JDK 8Spring Boot 2.7.xCouchbase Server 7.xMaven/Gradle 步骤 1:创建Spring Boot项目 使用 st…...
Oracle ASM Failgroup故障组
Oracle ASM Failgroup故障组 1. 故障组的核心作用2. 故障组的配置规则3. 故障组的设计最佳实践4. 故障组的实际示例场景1:普通冗余(2个故障组)场景2:高冗余(3个故障组,跨数据中心) 关键注意事项…...
深度学习框架PyTorch——从入门到精通(2)张量
又名:张亮的一生~~ 张量(Tensors)初始化张量张量的属性张量上的操作与NumPy桥接 张量(Tensors) 张量是一种专门的数据结构,类似Python中的数组或者矩阵。在Torch中,我们使用张量来编码模型的输…...
pytorch小记(十三):pytorch中`nn.ModuleList` 详解
pytorch小记(十三):pytorch中nn.ModuleList 详解 PyTorch 中的 nn.ModuleList 详解1. 什么是 nn.ModuleList?2. 为什么不直接使用普通的 Python 列表?3. nn.ModuleList 的基本用法示例:构建一个包含两层全连…...
C语言-动态内存管理
1.为什么要有动态内存分配 我们现如今已经掌握的内存开辟方式有 int main() {int a 0;int arr[30] { 0 };return 0; } 这两种方式,但是这种开辟空间的方式有两个特点: 1.空间开辟大小是固定的 2.数组在申明的时候,必须指定数组的长度&…...
深入解析MySQL数据库分库分表技术
友情提示:本文内容由银河易创(https://ai.eaigx.com)AI创作平台gpt-4-turbo模型生成,仅供参考。 随着互联网应用的快速发展,单一数据库在面对大规模数据时可能会遇到性能瓶颈。因此,数据库分库分表作为一种…...
【Embedded World 2025:边缘 AI、存储革新与 1X nm 工艺重塑嵌入式未来】
Embedded World 2025于3月11-13日在德国纽伦堡举办,作为全球嵌入式系统领域顶级盛会,汇聚超千家展商与3万专业观众,聚焦嵌入式智能、安全管理及行业解决方案。展会呈现边缘AI、低功耗MCU、5G RedCap、新型存储及车规级技术等前沿方向…...
【人工智能基础2】机器学习、深度学习总结
文章目录 一、人工智能关键技术二、机器学习基础1. 监督、无监督、半监督学习2. 损失函数:四种损失函数3. 泛化与交叉验证4. 过拟合与欠拟合5. 正则化6. 支持向量机 三、深度学习基础:深度神经网络1、概念与原理2、多层神经网络训练方法 一、人工智能关键…...
MySQL如何存储表情符号?
存储表情符号 默认mysql的字符集是utf8,排序规则为 utf8_general_ci INSERT INTO department (name) VALUES (😄)在存储表情的时候会报 1366 - Incorrect string value: \xF0\x9F\x98\x84 for column name at row 1, Time: 0.007000s 这时需要修改字符…...
Unity Shader 学习16:全局光照 概念理解
- 全局光照 直接光 间接光,在没有开启GI的情况下是不计算间接光的(如果放了光照探针 倒是可以模拟间接光 <光照探针只影响动态物体>); - 处理对象:静态物体(static) 、 非静态(动态)物体; - 计算方…...
Jobby、Quarkus 和 Spring Boot对比
Jobby、Quarkus 和 Spring Boot 是三种不同的 Java 框架,各自有不同的设计目标和适用场景。以下是对它们的详细对比: 1. 设计目标 框架设计目标Jobby轻量级的任务调度框架,专注于任务调度和执行。Quarkus面向云原生和 Kubernetes 的 Java 框…...
Dubbo 服务发现
总览 学习 Dubbo 的服务发现机制,可以从以下几方面入手: 注册中心的配置服务的注册客户端拉取服务列表服务列表的本地缓存服务提供者列表变更的监听机制服务发现的接口设计 注册中心的配置 Dubbo 通过解析用户配置决定使用的注册中心。比如用户配置了…...
Pytorch使用手册—自定义 C++ 和 CUDA 运算符(专题五十一)
你将学到什么 如何将用 C++/CUDA 编写的自定义运算符与 PyTorch 集成如何使用 torch.library.opcheck 测试自定义运算符先决条件 1. PyTorch 2.4 或更高版本 2. 对 C++ 和 CUDA 编程有基本了解 注意 本教程也适用于 AMD ROCm,无需额外修改。 PyTorch 提供了一个庞大的运算符库…...
Linux 实时同步服务实现(Rsync 结合 Inotify)
文章目录 1. 实时同步服务介绍2. Inotify 机制介绍3. Inotify-toolRsync 实时同步实践3.1 确认远程数据传输服务部署完成3.2 检查Linux系统是否支持Inotify实时监控3.3 安装inotify-tools3.4 命令测试3.5 重要监控事件汇总3.6 使用步骤 4. Sersync 工具使用(重点&am…...
用Java写斗地主前期工作的一些小想法
目前我们并不是要实现一个游戏,而是要对斗地主游戏做准备,主要是做牌+洗牌+发牌+给发的牌进行排序。在这个过程中我希望通过集中方式来实现: 1. 使用集合+方法+字符串的运用完成以上功能 2. 使用面向对象思想,对1做改进,主要是对其排序的改进,从而理解面向对象的真正意…...
鸿蒙数据持久化之首选项
场景介绍 用户首选项为应用提供Key-Value键值型的数据处理能力,支持应用持久化轻量级数据,并对其修改和查询。当用户希望有一个全局唯一存储的地方,可以采用用户首选项来进行存储。Preferences会将该数据缓存在内存中,当用户读取…...
将bin文件烧录到STM32
将bin文件烧录到STM32 CoFlash下载生成hex文件hex2bin使用下载bin到单片机 CoFlash下载 选择需要安装的目录 在Config中可以选择目标芯片的类型 我演示的是 stm32f103c8t6 最小系统板 Adapter:烧录器类型 Max Clock:下载速度 Por:接口类型&am…...
AtCoder Beginner Contest 397(ABCDE)
目录 A - Thermometer 翻译: 思路: 实现: B - Ticket Gate Log 翻译: 思路: 实现: C - Variety Split Easy 翻译: 思路: 实现: D - Cubes 翻译:…...
jasypt-spring-boot-starter项目如何使用jasypt加密密码
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; import org.jasypt.iv.RandomIvGenerator; import org.jasypt.salt.RandomSaltGenerator;/*** 加密密码的工具** author xxx* since 2025-03-17*/ public class JasyptTest {public static void main(String[] a…...
[AI速读]混合语言IP集成:挑战与高效解决方案
在现代SoC(系统级芯片)设计中,IP(知识产权模块)复用是提升开发效率的关键。然而,当设计涉及多种硬件描述语言(如SystemVerilog、VHDL、SystemC)时,如何高效集成不同语言的IP模块成为一大难题。本文将从实际设计场景出发,探讨混合语言IP集成的核心挑战,并介绍一套方法…...
SpringSecurity——如何获取当前登录用户的信息
目录 1. 直接注入 Principal 2. 直接注入 Authentication 3. 注入 UsernamePasswordAuthenticationToken 4. 通过 SecurityContextHolder 获取 5. 使用自定义工具方法 总结 如何获取更多的用户信息 自定义用户实体类 如何忽略某些字段(不返回前端ÿ…...
ospf动态路由
一、为什么使用动态路由 OSPF(open shortest path first开放最短路径优先)是内部网关协议(IGP)的一种,基于链路状态算法(LS)。 OSPF企业级路由协议(RFC2328 OSPFv2),核心重点协议 OSPF共三个版本,OSPFV1主要是实验室…...
在react当中利用IntersectionObserve实现下拉加载数据
目录 一、传统的下拉加载方案 二、存在问题 1.性能较差 2.不够精确 三、IntersectionObserve版本下拉加载 1、callback 2、options 四、IntersectionObserver实例 1、Intersection的优势 2、实现思路 3、代码实现 在进行前端开发的过程中,常常会碰到下拉…...
SpringBoot之如何集成SpringDoc最详细文档
文章目录 一、概念解释1、OpenAPI2、Swagger3、Springfox4、Springdoc5. 关系与区别 二、SpringDoc基本使用1、导包2、正常编写代码,不需要任何注解3、运行后访问下面的链接即可 三、SpringDoc进阶使用1、配置文档信息2、配置文档分组3、springdoc的配置参数**1. 基…...
Transaction rolled back because it has been marked as rollback-only问题解决
transaction rolled back because it has been marked as rollback-only 简略总结> 发生场景:try-catch多业务场景 发生原因:业务嵌套,事务管理混乱,外层业务与内层业务抛出异常节点与回滚节点不一致。 解决方式:修…...
单片机写的小液晶屏驱动+汉字滚屏
单片机写的小液晶屏驱动汉字滚屏 stm32f401freertos内置HZK16 单片机汉字滚屏...
SpringBoot整合LangChain4j操作AI大模型实战详解
一、引言 在当今这个人工智能飞速发展的时代,AI大模型已经逐渐渗透到我们生活的方方面面,为企业和开发者带来了前所未有的机遇。然而,如何高效地接入并利用这些AI大模型,成为了摆在许多开发者面前的一道难题。SpringBoot作为一款…...
深度解析 | Android 13 Launcher3分页指示器改造:横线变圆点实战指南
一、需求背景与技术挑战 在Android 13系统定制开发中,我们面临将Launcher3桌面从传统双层架构优化为现代单层布局的挑战。原生系统采用的分页横线指示器在视觉呈现上存在两点不足: 风格陈旧不符合Material You设计规范 空间占用较大影响屏幕利用率 通…...
【Qt】private槽函数可以被其他类中的信号连接
private槽函数可以被其他类中的信号连接。 即使 B 类的槽函数是 private 的,A 类通过信号连接 B 类的槽函数也没有问题。这是因为 Qt 的信号和槽机制是通过元对象系统(Meta-Object System)实现的,而不是直接调用函数。只要信号和…...
C语言每日一练——day_7
引言 针对初学者,每日练习几个题,快速上手C语言。第七天。(连续更新中) 采用在线OJ的形式 什么是在线OJ? 在线判题系统(英语:Online Judge,缩写OJ)是一种在编程竞赛中用…...
Python --**kwargs
在 Python 中,**kwargs 是一个特殊语法,用于在函数定义中接收任意数量的关键字参数(即键值对参数),并将这些参数以字典形式存储。它是 Python 中处理动态参数的强大工具,适用于需要灵活传递参数的场景。 1.…...
网络编程之客户端通过服务器与另外一个客户端交流
服务器使用select模型搭建,客户端1使用线程搭建,客户端2使用poll模型搭建, 使用时需要先运行服务器,具体编译可看我最后的图片 head.h头文件 #ifndef __HEAD_H_ #define __HEAD_H_ #include <stdio.h> #include <string…...
mybatis集合映射association与collection
官方文档:MyBatis的一对多关联关系 一、用途 一对一:association 一对多:collection 二、association 比较容易理解,可参考官方文档 三、collection <?xml version"1.0" encoding"UTF-8"?> &l…...
windows克隆项目找不到,修改git bash中存储的账号密码
git clone项目找不到,提示:remote: The project you were looking for could not be found. 有可能是地址错误、没有权限、账号密码错误 1.地址错误 从新检查git地址,确保地址是正确的。 2.没有权限 确保你有访问该项目的权限 3.账号密码…...
Linux中安装redis
Redis的安装包,从官方下载下来的是c语言的源码包,我们需要自己编译安装。具体操作步骤如下: 安装redis 上传redis资源包 安装C语言的编译环境 gcc yum install -y gcc-c 解压redis源码在当前目录 tar -zxvf redis-6.2.4.tar.gz 进入解压目录…...
Springboot项目打包成war包
1、首先创建一个springboot工程,然后我们改造启动类如: import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuil…...
Word:双栏排版操作步骤及注意事项
将word单栏文字排版为双栏是论文投稿前的重要步骤,也是非常容易出错的一步。但事实上排版的操作并不复杂,本篇博客旨在把操作过程及注意事项简单记录下来,便于日后浏览和查询。 【Step1】打开“显示/隐藏段落标记” (ctrl*&#…...
ArcGIS10. 8简介与安装,附下载地址
目录 ArcGIS10.8 1. 概述 2. 组成与功能 3. 10.8 特性 下载链接 安装步骤 1. 安装准备 2. 具体步骤 3.补丁 其他版本安装 ArcGIS10.8 1. 概述 ArcGIS 10.8 是由美国 Esri 公司精心研发的一款功能强大的地理信息系统(GIS)平台。其核心功能在于…...