Netty - 从Nginx 四层(TCP/UDP)流量中获取客户端真实/网络出口IP
文章目录
- 一、背景与原理
- 1.1 问题场景
- 网络架构影响分析
- 1.1 客户端与Nginx之间存在的NAT/VPN
- 1.2 Nginx与RPC服务之间的NAT
- 1.2 技术原理
- 二、环境配置验证
- 2.1 Nginx配置
- 2.2 版本要求
- 三、Netty服务端实现
- 3.1 Pipeline配置(核心代码)
- 3.2 协议处理器实现
- 3.3 业务处理器调用示例
- 四、关键实现细节
- 4.1 解码顺序保障
- 4.2 地址类型处理
- 4.3 安全性增强
- 五、异常处理方案
- 六、 HAProxyMessageDecoder
- 七、总结
一、背景与原理
1.1 问题场景
在TCP四层代理场景下,Nginx作为反向代理将客户端请求转发至Netty实现的RPC服务。由于经过代理转发,RPC服务默认只能获取到Nginx服务器的IP地址。需要通过Proxy Protocol协议传递客户端真实IP。
网络架构影响分析
1.1 客户端与Nginx之间存在的NAT/VPN
场景 | 可获取IP类型 | 技术原理 |
---|---|---|
企业级NAT网关 | 仅NAT出口公网IP | NAT设备替换源IP地址 |
家用路由器NAT | 路由器WAN口IP | 私有地址转换为公网IP |
全局VPN接入 | VPN服务器出口IP | 流量封装后隧道传输 |
结论:Proxy Protocol只能传递Nginx直接看到的IP(即NAT/VPN出口地址)
1.2 Nginx与RPC服务之间的NAT
场景 | 影响程度 | 解决方案 |
---|---|---|
普通NAT转发 | 无影响 | 保持现有Proxy Protocol配置 |
复杂SDN网络 | 需验证 | 确保TCP连接透传代理协议头 |
1.2 技术原理
- Proxy Protocol:由HAProxy提出的传输层协议扩展,在建立TCP连接时发送包含源地址信息的头部
- Nginx配置:
proxy_protocol on
指令启用协议支持 - Netty解码:通过
HAProxyMessageDecoder
解析协议头
二、环境配置验证
2.1 Nginx配置
仅用于演示,仅展示核心配置。
# 定义后端服务器组
upstream rpc_backend {server 10.0.0.1:12345;server 10.0.0.2:12345;
}# 配置TCP代理
stream {server {listen 12345;proxy_pass rpc_backend;proxy_protocol on;}
}
在Nginx的stream模块中,设置一个TCP代理服务器,监听12345端口,将所有到达该端口的连接通过proxy protocol转发到rpc_backend定的后端服务器组。这样后端服务器可以获取到原始客户端的IP地址等信息,前提是后端服务支持proxy protocol。
proxy_protocol on
- 启用PROXY协议(版本1或2),在转发流量时,将客户端的原始信息(如源IP、端口)附加到数据包头部。
- 作用:使后端服务器能获取客户端真实IP,而非Nginx代理的IP。
- 要求:后端服务必须支持并配置解析PROXY协议。
2.2 版本要求
- Nginx ≥ 1.9.0 我这里是1.27.1
- Netty ≥ 4.1.x 我这里是 4.1.109
三、Netty服务端实现
3.1 Pipeline配置(核心代码)
public class RpcServerInitializer extends ChannelInitializer<SocketChannel> {@Overrideprotected void initChannel(SocketChannel ch) {ChannelPipeline pipeline = ch.pipeline();// 添加PROXY协议解码器pipeline.addLast(new HAProxyMessageDecoder());// 自定义协议处理器pipeline.addLast(new ProxyProtocolHandler());pipeline.addLast(new RpcMessageDecoder());pipeline.addLast(new RpcMessageHandler());}
}
3.2 协议处理器实现
提取IP
public class ProxyProtocolHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {if (msg instanceof HAProxyMessage) {HAProxyMessage proxyMessage = (HAProxyMessage) msg;// 提取真实客户端地址String clientIp = proxyMessage.sourceAddress();int clientPort = proxyMessage.sourcePort();// 存储到Channel属性中ctx.channel().attr(CLIENT_IP_ATTRIBUTE).set(clientIp);// 释放资源并移除当前消息ReferenceCountUtil.release(msg);return;}// 非PROXY协议消息继续传递ctx.fireChannelRead(msg);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {log.error("Proxy protocol parsing failed", cause);ctx.close();}
}
3.3 业务处理器调用示例
使用IP
public class RpcMessageHandler extends SimpleChannelInboundHandler<RpcRequest> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, RpcRequest request) {String clientIp = ctx.channel().attr(CLIENT_IP_ATTRIBUTE).get();log.info("Received request from {}: {}", clientIp, request);// 业务处理逻辑...}
}
四、关键实现细节
4.1 解码顺序保障
- HAProxyMessageDecoder必须作为第一个入站处理器
- 需要处理完PROXY协议头后立即移除解码器(自动完成)
我抓了个包,如下
右键 Proxyv1 追踪流
4.2 地址类型处理
// 支持IPv4/IPv6地址类型判断
if (proxyMessage.proxyProtocol().addressType() == HAProxyAddressType.IPV4) {// IPv4处理逻辑
} else if (proxyMessage.proxyProtocol().addressType() == HAProxyAddressType.IPV6) {// IPv6处理逻辑
}
4.3 安全性增强
// 限制允许的代理服务器IP(可选)
List<String> allowedProxies = Arrays.asList("10.0.0.0/8", "192.168.0.0/16");
if (!isAllowedProxy(proxyMessage.destinationAddress())) {ctx.close();return;
}
五、异常处理方案
异常场景 | 处理方案 |
---|---|
无效PROXY头 | 记录日志并关闭连接 |
协议版本不匹配 | 返回错误响应码 |
地址格式错误 | 使用默认地址并告警 |
六、 HAProxyMessageDecoder
/** Copyright 2014 The Netty Project** The Netty Project licenses this file to you under the Apache License,* version 2.0 (the "License"); you may not use this file except in compliance* with the License. You may obtain a copy of the License at:** https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the* License for the specific language governing permissions and limitations* under the License.*/
package io.netty.handler.codec.haproxy;import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.ProtocolDetectionResult;
import io.netty.util.CharsetUtil;import java.util.List;import static io.netty.handler.codec.haproxy.HAProxyConstants.*;/*** Decodes an HAProxy proxy protocol header** @see <a href="https://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt">Proxy Protocol Specification</a>*/
public class HAProxyMessageDecoder extends ByteToMessageDecoder {/*** Maximum possible length of a v1 proxy protocol header per spec*/private static final int V1_MAX_LENGTH = 108;/*** Maximum possible length of a v2 proxy protocol header (fixed 16 bytes + max unsigned short)*/private static final int V2_MAX_LENGTH = 16 + 65535;/*** Minimum possible length of a fully functioning v2 proxy protocol header (fixed 16 bytes + v2 address info space)*/private static final int V2_MIN_LENGTH = 16 + 216;/*** Maximum possible length for v2 additional TLV data (max unsigned short - max v2 address info space)*/private static final int V2_MAX_TLV = 65535 - 216;/*** Binary header prefix length*/private static final int BINARY_PREFIX_LENGTH = BINARY_PREFIX.length;/*** {@link ProtocolDetectionResult} for {@link HAProxyProtocolVersion#V1}.*/private static final ProtocolDetectionResult<HAProxyProtocolVersion> DETECTION_RESULT_V1 =ProtocolDetectionResult.detected(HAProxyProtocolVersion.V1);/*** {@link ProtocolDetectionResult} for {@link HAProxyProtocolVersion#V2}.*/private static final ProtocolDetectionResult<HAProxyProtocolVersion> DETECTION_RESULT_V2 =ProtocolDetectionResult.detected(HAProxyProtocolVersion.V2);/*** Used to extract a header frame out of the {@link ByteBuf} and return it.*/private HeaderExtractor headerExtractor;/*** {@code true} if we're discarding input because we're already over maxLength*/private boolean discarding;/*** Number of discarded bytes*/private int discardedBytes;/*** Whether or not to throw an exception as soon as we exceed maxLength.*/private final boolean failFast;/*** {@code true} if we're finished decoding the proxy protocol header*/private boolean finished;/*** Protocol specification version*/private int version = -1;/*** The latest v2 spec (2014/05/18) allows for additional data to be sent in the proxy protocol header beyond the* address information block so now we need a configurable max header size*/private final int v2MaxHeaderSize;/*** Creates a new decoder with no additional data (TLV) restrictions, and should throw an exception as soon as* we exceed maxLength.*/public HAProxyMessageDecoder() {this(true);}/*** Creates a new decoder with no additional data (TLV) restrictions, whether or not to throw an exception as soon* as we exceed maxLength.** @param failFast Whether or not to throw an exception as soon as we exceed maxLength*/public HAProxyMessageDecoder(boolean failFast) {v2MaxHeaderSize = V2_MAX_LENGTH;this.failFast = failFast;}/*** Creates a new decoder with restricted additional data (TLV) size, and should throw an exception as soon as* we exceed maxLength.* <p>* <b>Note:</b> limiting TLV size only affects processing of v2, binary headers. Also, as allowed by the 1.5 spec* TLV data is currently ignored. For maximum performance it would be best to configure your upstream proxy host to* <b>NOT</b> send TLV data and instantiate with a max TLV size of {@code 0}.* </p>** @param maxTlvSize maximum number of bytes allowed for additional data (Type-Length-Value vectors) in a v2 header*/public HAProxyMessageDecoder(int maxTlvSize) {this(maxTlvSize, true);}/*** Creates a new decoder with restricted additional data (TLV) size, whether or not to throw an exception as soon* as we exceed maxLength.** @param maxTlvSize maximum number of bytes allowed for additional data (Type-Length-Value vectors) in a v2 header* @param failFast Whether or not to throw an exception as soon as we exceed maxLength*/public HAProxyMessageDecoder(int maxTlvSize, boolean failFast) {if (maxTlvSize < 1) {v2MaxHeaderSize = V2_MIN_LENGTH;} else if (maxTlvSize > V2_MAX_TLV) {v2MaxHeaderSize = V2_MAX_LENGTH;} else {int calcMax = maxTlvSize + V2_MIN_LENGTH;if (calcMax > V2_MAX_LENGTH) { // lgtm[java/constant-comparison]v2MaxHeaderSize = V2_MAX_LENGTH;} else {v2MaxHeaderSize = calcMax;}}this.failFast = failFast;}/*** Returns the proxy protocol specification version in the buffer if the version is found.* Returns -1 if no version was found in the buffer.*/private static int findVersion(final ByteBuf buffer) {final int n = buffer.readableBytes();// per spec, the version number is found in the 13th byteif (n < 13) {return -1;}int idx = buffer.readerIndex();// 主要修改这里if (match(TEXT_PREFIX, buffer, idx)) {return 1;}if (match(BINARY_PREFIX, buffer, idx)) {return buffer.getByte(idx + BINARY_PREFIX_LENGTH);}return -1;
// return match(BINARY_PREFIX, buffer, idx) ? buffer.getByte(idx + BINARY_PREFIX_LENGTH) : 1;}/*** Returns the index in the buffer of the end of header if found.* Returns -1 if no end of header was found in the buffer.*/private static int findEndOfHeader(final ByteBuf buffer) {final int n = buffer.readableBytes();// per spec, the 15th and 16th bytes contain the address length in bytesif (n < 16) {return -1;}int offset = buffer.readerIndex() + 14;// the total header length will be a fixed 16 byte sequence + the dynamic address information blockint totalHeaderBytes = 16 + buffer.getUnsignedShort(offset);// ensure we actually have the full header availableif (n >= totalHeaderBytes) {return totalHeaderBytes;} else {return -1;}}/*** Returns the index in the buffer of the end of line found.* Returns -1 if no end of line was found in the buffer.*/private static int findEndOfLine(final ByteBuf buffer) {final int n = buffer.writerIndex();for (int i = buffer.readerIndex(); i < n; i++) {final byte b = buffer.getByte(i);if (b == '\r' && i < n - 1 && buffer.getByte(i + 1) == '\n') {return i; // \r\n}}return -1; // Not found.}@Overridepublic boolean isSingleDecode() {// ByteToMessageDecoder uses this method to optionally break out of the decoding loop after each unit of work.// Since we only ever want to decode a single header we always return true to save a bit of work here.return true;}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {super.channelRead(ctx, msg);if (finished) {ctx.pipeline().remove(this);}}@Overrideprotected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {// determine the specification versionif (version == -1) {if ((version = findVersion(in)) == -1) {// 头部finished = true;return;}}ByteBuf decoded;if (version == 1) {decoded = decodeLine(ctx, in);} else {decoded = decodeStruct(ctx, in);}if (decoded != null) {finished = true;try {if (version == 1) {out.add(HAProxyMessage.decodeHeader(decoded.toString(CharsetUtil.US_ASCII)));} else {out.add(HAProxyMessage.decodeHeader(decoded));}} catch (HAProxyProtocolException e) {fail(ctx, null, e);}}}/*** Create a frame out of the {@link ByteBuf} and return it.** @param ctx the {@link ChannelHandlerContext} which this {@link HAProxyMessageDecoder} belongs to* @param buffer the {@link ByteBuf} from which to read data* @return frame the {@link ByteBuf} which represent the frame or {@code null} if no frame could* be created*/private ByteBuf decodeStruct(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {if (headerExtractor == null) {headerExtractor = new StructHeaderExtractor(v2MaxHeaderSize);}return headerExtractor.extract(ctx, buffer);}/*** Create a frame out of the {@link ByteBuf} and return it.** @param ctx the {@link ChannelHandlerContext} which this {@link HAProxyMessageDecoder} belongs to* @param buffer the {@link ByteBuf} from which to read data* @return frame the {@link ByteBuf} which represent the frame or {@code null} if no frame could* be created*/private ByteBuf decodeLine(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {if (headerExtractor == null) {headerExtractor = new LineHeaderExtractor(V1_MAX_LENGTH);}return headerExtractor.extract(ctx, buffer);}private void failOverLimit(final ChannelHandlerContext ctx, int length) {failOverLimit(ctx, String.valueOf(length));}private void failOverLimit(final ChannelHandlerContext ctx, String length) {int maxLength = version == 1 ? V1_MAX_LENGTH : v2MaxHeaderSize;fail(ctx, "header length (" + length + ") exceeds the allowed maximum (" + maxLength + ')', null);}private void fail(final ChannelHandlerContext ctx, String errMsg, Exception e) {finished = true;ctx.close(); // drop connection immediately per specHAProxyProtocolException ppex;if (errMsg != null && e != null) {ppex = new HAProxyProtocolException(errMsg, e);} else if (errMsg != null) {ppex = new HAProxyProtocolException(errMsg);} else if (e != null) {ppex = new HAProxyProtocolException(e);} else {ppex = new HAProxyProtocolException();}throw ppex;}/*** Returns the {@link ProtocolDetectionResult} for the given {@link ByteBuf}.*/public static ProtocolDetectionResult<HAProxyProtocolVersion> detectProtocol(ByteBuf buffer) {if (buffer.readableBytes() < 12) {return ProtocolDetectionResult.needsMoreData();}int idx = buffer.readerIndex();if (match(BINARY_PREFIX, buffer, idx)) {return DETECTION_RESULT_V2;}if (match(TEXT_PREFIX, buffer, idx)) {return DETECTION_RESULT_V1;}return ProtocolDetectionResult.invalid();}private static boolean match(byte[] prefix, ByteBuf buffer, int idx) {for (int i = 0; i < prefix.length; i++) {final byte b = buffer.getByte(idx + i);if (b != prefix[i]) {return false;}}return true;}/*** HeaderExtractor create a header frame out of the {@link ByteBuf}.*/private abstract class HeaderExtractor {/** Header max size */private final int maxHeaderSize;protected HeaderExtractor(int maxHeaderSize) {this.maxHeaderSize = maxHeaderSize;}/*** Create a frame out of the {@link ByteBuf} and return it.** @param ctx the {@link ChannelHandlerContext} which this {@link HAProxyMessageDecoder} belongs to* @param buffer the {@link ByteBuf} from which to read data* @return frame the {@link ByteBuf} which represent the frame or {@code null} if no frame could* be created* @throws Exception if exceed maxLength*/public ByteBuf extract(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {final int eoh = findEndOfHeader(buffer);if (!discarding) {if (eoh >= 0) {final int length = eoh - buffer.readerIndex();if (length > maxHeaderSize) {buffer.readerIndex(eoh + delimiterLength(buffer, eoh));failOverLimit(ctx, length);return null;}ByteBuf frame = buffer.readSlice(length);buffer.skipBytes(delimiterLength(buffer, eoh));return frame;} else {final int length = buffer.readableBytes();if (length > maxHeaderSize) {discardedBytes = length;buffer.skipBytes(length);discarding = true;if (failFast) {failOverLimit(ctx, "over " + discardedBytes);}}return null;}} else {if (eoh >= 0) {final int length = discardedBytes + eoh - buffer.readerIndex();buffer.readerIndex(eoh + delimiterLength(buffer, eoh));discardedBytes = 0;discarding = false;if (!failFast) {failOverLimit(ctx, "over " + length);}} else {discardedBytes += buffer.readableBytes();buffer.skipBytes(buffer.readableBytes());}return null;}}/*** Find the end of the header from the given {@link ByteBuf},the end may be a CRLF, or the length given by the* header.** @param buffer the buffer to be searched* @return {@code -1} if can not find the end, otherwise return the buffer index of end*/protected abstract int findEndOfHeader(ByteBuf buffer);/*** Get the length of the header delimiter.** @param buffer the buffer where delimiter is located* @param eoh index of delimiter* @return length of the delimiter*/protected abstract int delimiterLength(ByteBuf buffer, int eoh);}private final class LineHeaderExtractor extends HeaderExtractor {LineHeaderExtractor(int maxHeaderSize) {super(maxHeaderSize);}@Overrideprotected int findEndOfHeader(ByteBuf buffer) {return findEndOfLine(buffer);}@Overrideprotected int delimiterLength(ByteBuf buffer, int eoh) {return buffer.getByte(eoh) == '\r' ? 2 : 1;}}private final class StructHeaderExtractor extends HeaderExtractor {StructHeaderExtractor(int maxHeaderSize) {super(maxHeaderSize);}@Overrideprotected int findEndOfHeader(ByteBuf buffer) {return HAProxyMessageDecoder.findEndOfHeader(buffer);}@Overrideprotected int delimiterLength(ByteBuf buffer, int eoh) {return 0;}}
}
七、总结
在存在NAT或VPN的网络架构中,通过Proxy Protocol获取客户端真实IP的能力受限于网络设备的位置。
若NAT/VPN位于客户端与Nginx之间(如企业VPN或家庭路由),Proxy Protocol仅能传递经过NAT转换或VPN隧道出口的IP(如公网IP或VPN分配地址),无法穿透获取终端设备的内网真实IP。若需突破此限制,可采取混合方案:客户端主动上报IP(需改造客户端代码)并配合网络设备改造(如VPN网关记录原始IP、专用隧道协议)。但需注意隐私合规风险,避免采集敏感信息。
对于常规业务场景,Proxy Protocol结合Nginx配置已能满足“获取客户端侧网络出口IP”的需求,但若涉及终端设备溯源,需结合应用层协议与网络基础设施深度协同实现。
相关文章:
Netty - 从Nginx 四层(TCP/UDP)流量中获取客户端真实/网络出口IP
文章目录 一、背景与原理1.1 问题场景网络架构影响分析1.1 客户端与Nginx之间存在的NAT/VPN1.2 Nginx与RPC服务之间的NAT 1.2 技术原理 二、环境配置验证2.1 Nginx配置2.2 版本要求 三、Netty服务端实现3.1 Pipeline配置(核心代码)3.2 协议处理器实现3.3…...
基于Spring AI开发本地Jenkins MCP Server服务
前言 首先介绍下MCP是什么? MCP是由开发了 Claude 模型的 Anthropic 公司2024年12月提出并开源的一项开放标准,全称:Model Context Protocol,它是一个开放协议,它使 LLM 应用与外部数据源和工具之间的无缝集成成为可能…...
记录一次TDSQL事务太大拆过binlog阈值报错
记录一次TDSQL事务太大拆过binlog阈值报错处理过程 1、排查任何类型数据库故障的第一步, 同步实例信息、报错内容、报错时间段、当前是否恢复、如何感知到数据库问题的、对应用有什么影响、系统允许的时间窗口。 2、明确报错内容为单次写入binlog量超过阈值 3、登陆…...
1688商品详情接口:深度解析与应用实践
在电商领域,1688作为中国领先的B2B平台,拥有海量的商品信息。对于开发者、商家和数据分析师来说,获取1688商品的详细信息是实现数据分析、竞品研究、自动化管理和精准营销的重要手段。本文将详细介绍1688商品详情接口的使用方法、技术细节以及…...
机试题——村落基站建设
题目描述 假设村落以二叉树的形状分布,我们需要选择在哪些村落建设基站。如果某个村落建设了基站,那么它和它相邻的村落(包括本节点、父节点和子节点)也会有信号覆盖。目标是计算出最少需要建设的基站数。 输入描述 输入为一个…...
2025年高压电工考试真题分享
以下是一些高压电工考试题: 单选题 1、高压架空线路的档距一般为( )。 A. 20 - 30m B. 30 - 50m C. 50 - 80m D. 80 - 100m 答案:B。解析:高压架空线路档距一般在 30 - 50m,这样的档距能较好地保证线…...
T-SQL 语言基础:逆透视转换
概念 -- 把数据从列的转台转为行的状态-- 涉及查询数据的透视状态,将来自单个记录中多个列的值扩展为单个列中具有相同值的多个记录-- 也就是把透视表中的每个源行潜在地转换为多个行示例表继续使用上一篇博客,TempDB dbo.Orders 表。 T-SQL 语言基础&a…...
循环神经网络 - 给网络增加记忆能力
为了处理时序数据并利用其历史信息, 我们需要让网络具有短期记忆能力。而前馈网络是一种静态网络,不具备这种记忆能力。在正式学习循环神经网络之前,我们先来了解一下给网络增加短期记忆能力的三种方法。 一、延时神经网络 延时神经网络&am…...
Docker Desktop 界面功能介绍
Docker Desktop 界面功能介绍 左侧导航栏 Containers(容器): 用于管理容器,包括查看运行中或已停止的容器,检查容器状态、日志,执行容器内命令,启动、停止、删除容器等操作。Images(镜像): 管理本地 Docker 镜像,可查看镜像列表、从 Docker Hub 拉取新镜像、删除镜…...
R001-区块链
1.区块链概念 英文名:blockchain 或block chain what: 是一种块链式存储、不可篡改、安全可信的去中心化分布式账本. 特点:它结合了分布式存储、点对点传输、共识机制、密码学等技术,通过不断增长的数据块链(Blocks)记…...
无人机进行航空数据收集对于分析道路状况非常有用-使用无人机勘测高速公路而不阻碍交通-
无人机进行航空数据收集对于分析道路状况非常有用-使用无人机勘测高速公路而不阻碍交通- 瑞士拥有1,400 多公里长的高速公路网络。这些公路将人和货物从山谷高原运送到阿尔卑斯山的最高山口。维护这些高速公路使国家得以顺利运转。高速公路维护的重要性显而易见,但在…...
StarRocks 证书SRCA和SRCP
目录 引子 SRCA认证 SRCP认证 认证流程 引子 StarRocks介绍:极速全场景 MPP数据库starrocks介绍-CSDN博客 StarRocks中文社区推出了StarRocks 培训与认证,学习并通过考试后,可以得到StarRocks证书。对starrocks感兴趣或需要使用starrocks的…...
String类的模拟实现
我们在使用STL库的时候,不仅需要掌握如何使用,我们还需要了解一些底层的模拟实现。 1:需要模拟实现的string类函数 #pragma once #include<iostream> #include<assert.h> #include<utility> using namespace std;namespa…...
VMware Windows Tools 存在认证绕过漏洞(CVE-2025-22230)
漏洞概述 博通公司(Broadcom)近日修复了 VMware Windows Tools 中存在的一个高危认证绕过漏洞,该漏洞编号为 CVE-2025-22230(CVSS 评分为 9.8)。VMware Windows Tools 是一套实用程序套件,可提升运行在 VM…...
【计算机网络ICMP协议详解】
文章目录 一、前言二、ICMP协议概述2.1 ICMP 与 IP 协议的关系2.2 ICMP 的作用 三、ICMP报文格式3.1 字段解释: 四、常见ICMP类型与代码五、ICMP协议工作原理与示例5.1 ping 命令5.2 traceroute 命令 六、ICMP与网络安全6.1 ICMP的安全隐患6.2 防御措施 七、ICMP协议…...
WPF 自定义路由事件
WPF 路由事件的基础 什么是路由事件? 路由事件是一种特殊的事件机制,允许事件在可视化树中传播。它支持三种路由策略: 冒泡(Bubbling):事件从源元素向上传播到根元素。隧道(Tunneling…...
从零开始跑通3DGS教程:(一)数据(采集)
写在前面 本文内容 本文所属《从零开始跑通3DGS教程》系列文章; 本文介绍数据准备或者采集准备方式 平台/环境 linux, nvidia GPU, docker 转载请注明出处: https://blog.csdn.net/qq_41102371/article/details/146533367 目录 写在前面系列文章公开数据自己的数…...
DATEDIFF 函数
DATEDIFF 函数概述 DATEDIFF 函数用于计算两个日期之间的差值。 不同的数据库系统对 DATEDIFF 函数的实现和语法可能略有不同,但基本原理是相同的。 通用语法 DATEDIFF(datepart, startdate, enddate)datepart: 指定要计算的日期部分。 例如,day、wee…...
Java 大视界 -- 基于 Java 的大数据隐私计算在医疗影像数据共享中的实践探索(158)
💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...
在Windows下VSCodeSSH远程登录到Ubuntu
Window用VSCode通过SSH远程登录Ubuntu SSH 服务开启Windows远程登录 SSH 服务开启 首先要确保 Ubuntu 的 SSH 服务开启了,开启 Ubuntu 的 SSH 服务以后我们就可以在 Windwos 下使用终端软件登陆到 Ubuntu 开启 SSH sudo apt-get install openssh-serverWindows远…...
MATLAB 控制系统设计与仿真 - 31
二次型最优控制 考虑到系统如果以状态空间方程的形式给出,其性能指标为: 其中F,Q,R是有设计者事先选定。线性二次最优控制问题简称LQ(Linear Quadractic)问题,就是寻找一个控制,使得系统沿着由指定初态出发的相应轨迹,其性能指标J取得最小值。 LQ问题分…...
基于SpringBoot + Vue 的考勤管理系统
系统环境 开发工具:IntelliJ IDEAJava 版本:JDK 1.8数据库:MySQL项目构建工具:Maven 项目主要技术 后端技术 Spring Boot:简化 Spring 应用开发流程,实现快速搭建和部署。MyBatis:用于实现数…...
JavaScript 中Object.assign()和展开运算符在对象合并时的区别,各自的适用场景是什么?
JavaScript 中Object.assign()和展开运算符在对象合并时的区别,各自的适用场景是什么? 在 JavaScript 里,咱们常常要把多个对象合并成一个新对象。Object.assign()和展开运算符(...)就像两个得力小助手,能…...
鸿蒙北向应用开发:deveco 5.0 kit化文件相关2
鸿蒙北向应用开发:deveco 5.0 kit化文件相关 在kit化时,有时候会出现这样一种场景即你想把已有的d.ts导出换个名字,这样从名字上更贴合你的kit聚合 什么意思呢?比如现在有 ohos.hilog.d.ts 导出了hilog,现在你想kit化hilog,使得hilog导出名字为usrhilog,这样用户在使用你的k…...
北斗导航 | 改进伪距残差矢量的接收机自主完好性监测算法原理,公式,应用,RAIM算法研究综述,matlab代码
改进伪距残差矢量的接收机自主完好性监测算法研究 摘要 接收机自主完好性监测(RAIM)是保障全球卫星导航系统(GNSS)可靠性的核心技术。本文针对传统伪距残差矢量法在微小故障检测和多故障隔离中的不足,提出一种融合加权奇偶空间与动态阈值调整的改进算法。通过理论推导验证…...
Postman 如何发送 Post 请求上传文件? 全面指南
写一个后端接口,肯定离不开后续的调试,所以我使用了 Postman 来进行上传图片接口的调试,调试步骤也很简单: 第一步:填写请求 URL第二步:选择请求类型第三步:选择发送文件第四步:点击…...
Python 装饰模式
在软件开发中,随着系统的复杂性增加,需求的变化往往会导致代码的频繁修改。为了提高代码的灵活性和可维护性,设计模式应运而生。其中,装饰模式(Decorator Pattern)是一种非常实用的结构型设计模式ÿ…...
JVM 面经
1、什么是 JVM? JVM 就是 Java 虚拟机,它是 Java 实现跨平台的基石。程序运行之前,需要先通过编译器将 Java 源代码文件编译成 Java 字节码文件;程序运行时,JVM 会对字节码文件进行逐行解释,翻译成机器码指令&#x…...
java对pdf文件分页拆分
文章目录 pdf文件拆分指定分页大小 pdf文件拆分 导入依赖 <dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>2.0.32</version></dependency>2. 大文件拆分public static boolean …...
【PGCCC】PostgreSQL Certified Master 个人专访 | 第二期 何雄
由PGCCC发起的“PostgreSQL Certified Master个人专访”栏目,旨在挖掘PCM们对数据库行业的深度洞察,分享他们对行业发展的思考和个人感悟,对广大PGer们具有实际借鉴意义。 1.请简单介绍一下自己,您的爱好、您的事业。 大家好,我…...
什么是具身智能
具身智能(Embodied Intelligence)是人工智能与机器人学交叉的前沿领域,强调智能体通过身体与环境的动态交互实现自主学习和进化,其核心在于将感知、行动与认知深度融合。通俗地讲,就是机器人或者智能系统在物理环境中…...
Android开发EmojiCompat 初始化
Android开发EmojiCompat 初始化 报错信息: ensure spannable:java.lang.IllegalStateException: EmojiCompat is not initialized 在Application上写上下面代码即可: EmojiCompat.Config config new BundledEmojiCompatConfig(this);EmojiCompat.in…...
k近邻算法K-Nearest Neighbors(KNN)
算法核心 KNN算法的核心思想是“近朱者赤,近墨者黑”。对于一个待分类或预测的样本点,它会查找训练集中与其距离最近的K个样本点(即“最近邻”)。然后根据这K个最近邻的标签信息来对当前样本进行分类或回归。 在分类任务中&#…...
TextGrad:案例
原文:Yuksekgonul, M., Bianchi, F., Boen, J. et al. Optimizing generative AI by backpropagating language model feedback. Nature 639, 609–616 (2025). https://doi.org/10.1038/s41586-025-08661-4 目录 Solution optimizationPrompt optimization for rea…...
位运算算法:解锁高效编程的钥匙
常见位运算场景: 5.消失的两个数字 1.判定字符是否唯一 解法一:使用HashSet 借助 HashSet 存储字符。HashSet 不允许有重复元素,在遍历字符串时尝试添加字符,若添加失败就表明有重复字符,返回 false;若遍…...
Burp Suite抓包实战:SQL注入漏洞挖掘
本文系统解析如何利用Burp Suite专业版开展SQL注入漏洞的定向挖掘,涵盖手动探测、自动化利用、WAF绕过等进阶技巧。通过电商、金融等行业的真实渗透案例,详解从流量拦截到漏洞利用的全链路方法论,实现单日最高挖掘23个高危注入点的实战成果。…...
使用HTML5和CSS3实现3D旋转相册效果
使用HTML5和CSS3实现3D旋转相册效果 这里写目录标题 使用HTML5和CSS3实现3D旋转相册效果项目介绍技术栈核心功能实现思路1. HTML结构2. CSS样式解析2.1 基础样式设置2.2 3D效果核心样式2.3 卡片样式 3. JavaScript交互实现3.1 旋转控制3.2 自动播放功能 技术要点总结项目亮点总…...
MyBatis Plus 中 update_time 字段自动填充失效的原因分析及解决方案
✅ MyBatis Plus 中 update_time 字段自动填充失效的原因分析及解决方案 前言一、问题现象二、原因分析1. 使用了 strictInsertFill/strictUpdateFill 导致更新失效2. 实体类注解配置错误3. MetaObjectHandler 未生效4. 使用自定义 SQL 导致自动填充失效5. 字段类型不匹配 三、…...
MySQL Binlog
MySQL Binlog MySQL Binlog 介绍查看 Binlog 位点开启和关闭 BinlogBinlog 的作用Binlog 记录的格式Binlog 的解析Binlog 加密Binlog 的清理根据Binlog文件名删除根据时间删除 Binlog 保留参数Binlog 的落盘Binlog 相关参数 MySQL主从复制:https://blog.csdn.net/a…...
SQL中累计求和与滑动求和函数sum() over()的用法
[TOC](SQL中累计求和与滑动求和函数sum() over()的用法) 一、窗口函数功能简介 sum(c) over(partition by a order by b) 按照一定规则汇总c的值,具体规则为以a分组,每组内按照b进行排序,汇总第一行至当前行的c的加和值。 sum():…...
第十四届MathorCup高校数学建模挑战赛-C题:基于 LSTM-ARIMA 和整数规划的货量预测与人员排班模型
目录 摘要 一、 问题重述 1.1 背景知识 1.2 问题描述 二、 问题分析 2.1 对问题一的分析 2.2 对问题二的分析 2.3 对问题三的分析 2.4 对问题四的分析 三、 模型假设 四、 符号说明 五、 问题一模型的建立与求解 5.1 数据预处理 5.2 基于 LSTM 的日货量预测模型 5.3 日货量预测…...
3d pose 指标和数据集
目录 3D姿态估计、3维重建指标: 数据集 EHF数据集 SMPL-X 3D姿态估计、3维重建指标: MVE、PMVE 和 p-MPJPE 都是用于评估3D姿态估计、三维重建等任务中预测结果与真实数据之间误差的指标。 MVE (Mean Vertex Error):是指模型重建过程中每个顶点的预测位置与真实位置之间…...
【MySQL】InnoDB中的MVCC
目录 1、背景2、设置事务的隔离级别3、MVCC【1】版本链【2】ReadView【3】ReadView的生成时机 4、总结 1、背景 MVCC叫做多版本并发控制,通过维护数据的多个历史版本实现读写分离:读操作访问快照版本,无需加锁,避免阻塞写操作&am…...
从DeepSeek到Qwen,AI大模型的移植与交互实战指南
在不久前发布的《技术实战 | OK3588-C开发板上部署DeepSeek-R1大模型的完整指南》一文中,小编为大家介绍了DeepSeek-R1在飞凌嵌入式OK3588-C开发板上的移植部署、效果展示以及性能评测,本篇文章不仅将继续为大家带来关于DeepSeek-R1的干货知识࿰…...
蓝桥杯Java组国赛G题(01背包问题的变形)
题目 解题思路 首先,解决此题的前置知识是需要掌握普通的 01 背包问题。当然,这题肯定不可能这么简单。题目相对于 01 背包来说,唯一的区别在于小蓝可以使用 1 次魔法。我们只需要多加一维状态记录是否使用了魔法即可。下面考虑动态规划&…...
QCW模式:准连续波驱动在VCSEL激光器中的技术解析与应用价值
点击下面图片,为您提供全新的嵌入式学习路线 文章目录 一、QCW模式的定义与工作原理二、QCW模式的技术优势三、典型应用场景分析四、PLD850-5mW-MV的QCW性能亮点五、总结 一、QCW模式的定义与工作原理 QCW(Quasi-Continuous Wave)…...
C++List模拟实现|细节|难点|易错点|全面解析|类型转换|
目录 1.模拟代码全部 2.四大块代码理解 1.最底层:ListNode部分 2.第二层:ListIterator部分 3.第三层:ReserveListIterator部分 4最终层:List 1.模拟代码全部 using namespace std; template<class T> struct ListNode …...
【安全】记录钓鱼邮件中木马病毒的分析溯源
转载请注明出处:小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你,欢迎[点赞、收藏、关注]哦~ 1、收到一封邮件,与以往钓鱼网站形式不同,这次是给了一个exe可执行文件。毫无疑问,肯定是植入木马用的。 下载后&am…...
三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满足 nums[i] nums[j] nums[k] 0 。请你返回所有和为 0 且不重复的三元组。 注意:答案中不可以包含重复的三元组。 示例 1&…...
AI 与 IT 从业者:共舞还是取代?
在当今数字化浪潮的汹涌冲击下,AI 的出现犹如一颗划破夜空的璀璨星辰,照亮了科技发展的新方向,也引发了无数关于未来职业走向的讨论。尤其是对于 IT 从业者而言,仿佛站在了一个十字路口,前方是机遇与挑战交织的未知旅程…...