news 2026/10/5 7:22:25

Java端口扫描器:从课程设计到网络协议栈实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java端口扫描器:从课程设计到网络协议栈实战

简介:这是一份面向计算机网络课程学习者与Java初学者的端口扫描器实践项目,聚焦TCP/UDP协议层探测原理,适用于课程设计、大作业或工程实训场景。资源以Java实现多线程端口扫描核心功能,支持自定义IP、起始/结束端口(0–65535)及线程数(1–200),实时显示开放端口并支持结果保存,配套图形化界面提升交互体验。压缩包共12个文件,含2个核心Java源码(.java)、3个编译后类文件(.class)、2份Markdown说明文档(含README与项目介绍)、Eclipse工程配置文件(.project、.classpath、.prefs)及界面截图(png),整体仅96KB,轻量易部署。目前已有131人学习下载,读者可直接运行调试、理解Socket通信、多线程并发控制与Swing GUI构建逻辑,并通过源码结构快速掌握网络扫描器的模块划分与异常处理机制。

1. 为什么一个“课程设计级”的 TCP/UDP 端口扫描器,反而成了网络工程岗面试时最常被深挖的 Java 实战题?

你写完《计算机网络》课设报告,把PortScanner.java提交到头歌平台、通过了 HNU 实验一的自动评测、甚至在湖科大教书匠的“自顶向下”配套实验里跑通了——但面试官盯着你简历上那行“基于 Java 实现的 TCP/UDP 端口扫描器”,突然问:“如果目标主机开了防火墙,SYN 包发出去没回包,你是等 3 秒还是 300 毫秒?这个超时值怎么定?Java 的Socket.connect()底层到底触发了几次重传?DatagramSocket.send()发 UDP 包后,你怎么判断对方端口是 open 还是 filtered?”
这时候,80% 的同学当场卡壳。不是不会写 for 循环扫 1–65535,而是根本没碰过真实网络环境里的协议栈行为边界:TCP 三次握手失败 ≠ 端口关闭,UDP 无响应 ≠ 端口关闭,java.net.SocketTimeoutException和java.io.IOException: Connection refused背后是完全不同的网络状态机。这个项目表面是“用 Java 调 API”,实质是用代码撬开操作系统 TCP/IP 协议栈的黑匣子——它逼你直面netsh interface tcp show global输出的InitialRtt、MaxSynRetransmissions,逼你理解iperf3 -u打流时 UDP 包为何不触发 ICMP port unreachable。适合刚学完《计算机网络》第八版“运输层”章节、正准备蓝桥杯或 Java 开发岗实习面试的学生:它不考算法复杂度,但考你敢不敢把connect()的 timeout 从 5000 改成 50,然后看 Wireshark 里 SYN 重传次数怎么跳变。


2. 从零手写核心扫描逻辑:用原生 Socket API 绕过所有“高级库幻觉”

提示:别碰 Apache Commons Net 或 Nmap 的 Java 封装。课程设计要的是对java.net底层行为的肌肉记忆,不是调包能力。所有面试追问都来自你亲手写的new Socket()那一行。

2.1 TCP SYN 扫描的“伪实现”与真实约束

Java 标准库不提供原始套接字(raw socket)权限,无法像 nmap 那样直接构造 SYN 包(绕过内核 TCP 状态机)。所以所谓“TCP SYN 扫描”在 Java 里本质是TCP Connect 扫描的优化变体:利用Socket.connect(InetSocketAddress, timeout)的非阻塞连接尝试,通过超时和异常类型反推端口状态。关键不是“模拟 SYN”,而是用 connect() 的返回路径倒推网络路径状态。

// TCP 端口探测核心逻辑(单线程版,用于理解原理) public static PortState tcpProbe(String host, int port, int timeoutMs) { try (Socket socket = new Socket()) { // 关键:设置 SO_TIMEOUT 影响读取,但 connect timeout 是独立参数 socket.connect(new InetSocketAddress(host, port), timeoutMs); // 连接成功 → port is OPEN return PortState.OPEN; } catch (SocketTimeoutException e) { // 连接超时 → 可能是 filtered(防火墙丢包)或 closed(但未发 RST) return PortState.FILTERED; } catch (IOException e) { // 明确收到 RST 或 ICMP port unreachable → port is CLOSED String msg = e.getMessage().toLowerCase(); if (msg.contains("connection refused") || msg.contains("errno 111")) { return PortState.CLOSED; } // 其他 IO 异常:host unreachable / network unreachable return PortState.HOST_UNREACHABLE; } }

逻辑说明与参数深挖:

  • timeoutMs不是“等待响应总时间”,而是connect() 系统调用的超时上限。Linux 内核中,connect()对 TCP 的行为是:发 SYN → 等 SYN-ACK → 若超时则发 RST 并返回错误。这个超时值直接影响你能否区分FILTERED和CLOSED。
  • SocketTimeoutException触发条件:内核在timeoutMs内既没收到 SYN-ACK,也没收到 RST/ICMP。此时链路可能被防火墙静默丢弃(filtered),也可能目标主机宕机(host unreachable),但 Java 层无法区分——这是协议栈不可见性的典型体现。
  • Connection refused异常:明确收到 RST 报文,证明目标主机 TCP 协议栈已运行,且该端口无进程监听。这是唯一能 100% 确认CLOSED的信号。

2.2 UDP 端口探测:为什么“发包即判断”是最大玄学

UDP 是无连接协议,DatagramSocket.send()调用成功只代表数据报进入本机发送队列,不代表到达目标。更残酷的是:UDP 端口开放与否,无法靠“发包是否成功”判断。标准做法是:发送一个应用层可识别的探测包(如 DNS 查询、NTP 请求),再监听是否收到响应。但课程设计中常简化为“发空包 + 等 ICMP port unreachable”。

// UDP 端口探测(简化版,依赖 ICMP 错误报文) public static PortState udpProbe(String host, int port, int timeoutMs) { try (DatagramSocket socket = new DatagramSocket()) { socket.setSoTimeout(timeoutMs); // 发送空 UDP 包(仅占位,不保证应用层语义) DatagramPacket sendPacket = new DatagramPacket(new byte[0], 0, InetAddress.getByName(host), port); socket.send(sendPacket); // 等待 ICMP port unreachable 响应(需目标主机返回该 ICMP) byte[] recvBuf = new byte[64]; DatagramPacket recvPacket = new DatagramPacket(recvBuf, recvBuf.length); socket.receive(recvPacket); // 此处会阻塞,直到超时或收到 ICMP // 收到 ICMP port unreachable → 证明端口 CLOSED(有 IP 层响应) return PortState.CLOSED; } catch (SocketTimeoutException e) { // 无任何响应 → 可能 OPEN(服务不回包)、FILTERED(防火墙丢 ICMP)、或 host down return PortState.OPEN_OR_FILTERED; } catch (IOException e) { // 其他错误(如地址不可达) return PortState.HOST_UNREACHABLE; } }

参数与现象强关联说明:

  • setSoTimeout(timeoutMs)控制receive()的等待时间,不是 send() 的超时。UDP send 永远“成功”,因为只是进内核队列。
  • OPEN_OR_FILTERED是 UDP 探测的必然模糊态:目标服务若为 DNS(端口 53),它收到空包会静默丢弃(OPEN);若为防火墙,则可能丢弃探测包且不发 ICMP(FILTERED);若目标主机关机,则无任何响应(HOST_UNREACHABLE)。三者在 Java 层表现完全一致——这就是为什么企业级扫描器(如 nmap)对 UDP 默认做 100+ 次重试并结合多种探测载荷。
  • 关键限制:Linux 默认禁用 ICMP port unreachable 响应(net.ipv4.icmp_echo_ignore_all=0但icmp_port_unreachable可能为 0)。用sysctl net.ipv4.icmp_port_unreachable查看,值为 0 则你的 UDP 探测永远收不到 ICMP,全判为OPEN_OR_FILTERED。这是学生调试时最常翻车的点。

2.3 多线程并发扫描:用 ExecutorService 控制“网络友好度”

暴力扫 65535 个端口,单线程耗时以小时计。但并发数不是越大越好——ExecutorService.newFixedThreadPool(1000)会瞬间打爆本地端口(ephemeral port exhaustion)和目标主机连接队列。合理并发数需满足:并发数 ≤ min(本地可用端口数 / 2, 目标主机 SYN queue size)。

// 生产级并发控制器(课程设计可简化,但必须理解原理) public class PortScanner { private static final int DEFAULT_CONCURRENCY = 50; // 经验值:50 线程在千兆网下较稳 private static final int CONNECT_TIMEOUT_MS = 1000; // TCP connect 超时:1s private static final int UDP_TIMEOUT_MS = 2000; // UDP receive 超时:2s public List<PortResult> scan(String host, IntRange portRange) { ExecutorService executor = Executors.newFixedThreadPool(DEFAULT_CONCURRENCY); List<Future<PortResult>> futures = new ArrayList<>(); for (int port = portRange.start(); port <= portRange.end(); port++) { final int currentPort = port; futures.add(executor.submit(() -> { PortState state = PortState.UNKNOWN; long startTime = System.nanoTime(); if (isTcpScan) { state = tcpProbe(host, currentPort, CONNECT_TIMEOUT_MS); } else { state = udpProbe(host, currentPort, UDP_TIMEOUT_MS); } long durationNs = System.nanoTime() - startTime; return new PortResult(host, currentPort, state, durationNs / 1_000_000); })); } // 收集结果(注意:Future.get() 会阻塞,实际应加超时) List<PortResult> results = new ArrayList<>(); for (Future<PortResult> future : futures) { try { results.add(future.get(5, TimeUnit.SECONDS)); // 防止单个任务卡死 } catch (TimeoutException e) { results.add(new PortResult(host, -1, PortState.TIMEOUT, 5000)); } catch (Exception e) { results.add(new PortResult(host, -1, PortState.ERROR, 0)); } } executor.shutdown(); return results; } }

参数决策依据:

  • DEFAULT_CONCURRENCY = 50:实测在校园网环境下,50 线程可维持稳定吞吐,而 200 线程会导致大量java.net.BindException: Address already in use(本地端口耗尽)。可通过netstat -an | grep :* | wc -l查看当前 ESTABLISHED 连接数。
  • CONNECT_TIMEOUT_MS = 1000:比默认 5s 更激进。三次握手正常应在 200ms 内完成(局域网),1s 超时可过滤掉大部分FILTERED状态,避免扫描器卡在防火墙后。但若扫公网服务器,需调至 3000–5000ms。
  • future.get(5, TimeUnit.SECONDS):防止某个端口探测因网络抖动无限阻塞,强制 5s 放弃。这是课程设计与工业级扫描器的关键分水岭——后者会动态调整超时(如根据前 10 个端口的平均响应时间)。

3. 真实网络环境避坑指南:那些让头歌平台通过但企业网直接翻车的 5 个血泪问题

3.1 现象:头歌平台显示“全部端口扫描成功”,但用 Wireshark 抓包发现只发了 10 个 SYN 包就结束了

原因:头歌或 HNU 实验平台使用 Docker 容器运行你的 Java 程序,容器内ulimit -n(文件描述符上限)默认为 1024。每个Socket占用 1 个 fd,50 线程 × 每线程同时打开多个 Socket → 快速触达上限,后续new Socket()抛IOException: Too many open files,但你的代码没捕获此异常,导致扫描中断。
解决:在main()开头添加资源检查,并优雅降级:

try { long maxFd = new File("/proc/self/limits").lines() .filter(line -> line.startsWith("Max open files")) .map(line -> Long.parseLong(line.split("\\s+")[3])) .findFirst().orElse(1024L); if (maxFd < 2048) { System.err.println("警告:文件描述符限制过低(" + maxFd + "),自动降级并发数至 " + (int)(maxFd/50)); DEFAULT_CONCURRENCY = (int)(maxFd / 50); } } catch (Exception ignored) {}

3.2 现象:UDP 扫描结果全是OPEN_OR_FILTERED,但用nmap -sU -p 53 192.168.1.1明确显示53/open

原因:你的 UDP 探测发的是空包(new byte[0]),而 DNS 服务只响应符合 DNS 协议格式的查询包。空包被 DNS 服务静默丢弃,不触发 ICMP,也不回复——所以 Java 层永远收不到任何东西,只能判OPEN_OR_FILTERED。
解决:对常见 UDP 端口发协议特定探测包。例如 DNS 端口(53):

// DNS 查询最小包(Transaction ID=0x1234, QR=0, Opcode=QUERY, QDCOUNT=1) byte[] dnsQuery = new byte[]{ 0x12, 0x34, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01 }; DatagramPacket sendPacket = new DatagramPacket(dnsQuery, dnsQuery.length, addr, 53);

3.3 现象:扫描 localhost(127.0.0.1)时,TCP 扫描大量报Connection refused,但扫 192.168.1.100 却全是超时

原因:localhost走的是 loopback 接口,内核 bypass 了防火墙规则,connect()能快速收到 RST;而192.168.1.100经过物理网卡,若目标主机启用了 iptablesDROP规则(而非REJECT),则 SYN 包被静默丢弃,Java 层只能超时。这不是你的代码 bug,是 Linux netfilter 的DROPvsREJECT行为差异。
验证:在目标主机执行sudo iptables -L INPUT -n --line-numbers,若看到DROP all -- 0.0.0.0/0 0.0.0.0/0,则必现此现象。
解决:课程设计中可忽略,但面试时必须答出:“REJECT发 RST → Java 收到Connection refused;DROP静默丢包 → Java 超时 → 判FILTERED”。

3.4 现象:tcpProbe()在 Windows 上超时时间不稳定,有时 1000ms 超时,有时 3000ms 才返回

原因:Windows TCP/IP 协议栈的 SYN 重传策略与 Linux 不同。Windows 默认MaxSynRetransmissions=2,重传间隔为 3s、6s,导致connect()最长等待约 9s。而 Java 的timeoutMs是内核 connect 系统调用的软超时,受系统参数影响。
解决:在 Windows 上扫描前,用管理员权限执行:

netsh interface tcp set global initialrto=1000 netsh interface tcp set global maxsynretransmissions=1

将初始 RTO 设为 1000ms,重传次数设为 1,使超时行为更可控。

3.5 现象:扫描结果导出 CSV 后,Excel 打开显示乱码,中文字段全变成方块

原因:JavaFileWriter默认用平台编码(Windows 是 GBK),而 Excel 2016+ 默认用 UTF-8 BOM 解析 CSV。GBK 编码的中文写入文件,Excel 用 UTF-8 解析 → 乱码。
解决:强制用 UTF-8 with BOM 写入:

// 写 CSV 文件(带 BOM,兼容 Excel) String bom = "\uFEFF"; try (FileWriter writer = new FileWriter("result.csv", StandardCharsets.UTF_8)) { writer.write(bom); // 关键:写入 BOM 头 writer.write("Host,Port,State,TimeMs\n"); for (PortResult r : results) { writer.write(String.format("%s,%d,%s,%d\n", r.getHost(), r.getPort(), r.getState(), r.getDurationMs())); } }

4. 扫描结果可信度验证:用三重交叉法揪出“假阳性”与“假阴性”

光跑通代码不够,面试官会问:“你怎么证明你扫出来的OPEN真是开着的服务,而不是误判?”——答案是不用信任单一工具,用协议栈行为交叉验证。以下是我在湖科大网络实验和蓝桥杯备赛中验证扫描结果的三步法:

4.1 第一层:用telnet/nc手动复现 TCP 连接

对扫描结果中标记为OPEN的端口,用系统命令手动连接,观察底层行为:

# 测试 TCP 端口 22(SSH) $ time telnet 192.168.1.100 22 # 若立即返回 "Connected to 192.168.1.100." → 确认 OPEN # 若卡住 30s 后报 "Unable to connect to remote host" → 你的 Java 扫描器 timeout 设太短,应调大 # 测试 TCP 端口 80(HTTP) $ time nc -zv 192.168.1.100 80 # nc 的 -v 参数会打印详细过程:"Connection to 192.168.1.100 80 port [tcp/http] succeeded!"

关键洞察:telnet和nc的连接逻辑与 JavaSocket.connect()完全一致(都是调用connect()系统调用),但它们的超时策略、重传次数由系统net.ipv4.tcp_syn_retries决定。若nc能连上而你的 Java 扫描器判FILTERED,说明你的timeoutMs设置过小。

4.2 第二层:用 Wireshark 抓包,看三次握手完整流程

启动 Wireshark,过滤ip.addr == 192.168.1.100 and tcp.port == 22,然后运行你的扫描器。重点观察:

  • 是否发出 SYN 包?
  • 是否收到 SYN-ACK?(确认OPEN)
  • 是否收到 RST?(确认CLOSED)
  • 是否只发 SYN,无任何响应?(确认FILTERED)

抓包技巧:在扫描前执行sudo sysctl -w net.ipv4.tcp_tw_reuse=1,避免 TIME_WAIT 端口占用导致抓包混乱。Wireshark 中右键 SYN 包 → “Follow → TCP Stream”,可直观看到连接建立/拒绝全过程。

4.3 第三层:用nmap权威结果反向校准你的扫描器

nmap是网络扫描的黄金标准,用其结果校准你的 Java 实现:

# TCP 扫描(等价于你的 connect 扫描) nmap -sT -p 1-1000 -T4 192.168.1.100 # UDP 扫描(需 root 权限,且慢) sudo nmap -sU -p 53,67,68,123,161 192.168.1.100

将nmap输出与你的 CSV 结果对比,生成差异报告:

PortYour ResultNmap Result差异分析
22OPENopen✅ 一致
53OPEN_OR_FILTEREDopen❌ 你的 UDP 探测包无效,需改发 DNS 查询
137FILTEREDclosed❌ 你的 TCP timeout 太短,目标主机 RST 延迟 >1000ms

校准原则:当你的结果与nmap不一致时,优先信nmap,然后检查你的超时参数、探测载荷、并发控制。这是工程师思维的核心——不迷信自己写的代码,用事实(抓包、权威工具)驱动迭代。


5. 进阶技巧:用 JNA 调用 native 函数实现真正的 TCP SYN 扫描(绕过 connect() 限制)

注意:此技巧超出课程设计要求,但却是 Java 网络编程岗面试的“王炸问题”。若面试官问“Java 能否实现真正 SYN 扫描?”,答“不能”是及格,答“能,用 JNA 调 libpcap”是优秀。

Java 标准库禁用 raw socket,但可通过 Java Native Access(JNA)调用 C 函数,借助libpcap(Unix)或WinPcap(Windows)直接发包。这需要你理解struct tcphdr的内存布局和pcap_inject()的调用约定。

5.1 环境准备:安装 libpcap 并配置 JNA

Linux(Ubuntu):

sudo apt-get install libpcap-dev # 下载 jna-5.13.0.jar 和 jna-platform-5.13.0.jar(Maven 仓库最新版)

Windows:下载 WinPcap Developer's Pack,解压后将WpdPack\Lib\x64\wpcap.lib和WpdPack\Include\*.h加入项目。

5.2 用 JNA 定义 pcap 接口(精简版)

// 定义 pcap_t 结构(简化,实际需完整映射) public interface Pcap extends Library { Pcap INSTANCE = Native.load("pcap", Pcap.class); // 打开设备 Pointer pcap_open_live(String device, int snaplen, int promisc, int to_ms, Pointer errbuf); // 构造原始包(需自行填充以太网帧、IP 头、TCP 头) int pcap_inject(Pointer p, byte[] buf, int size); // 关闭 void pcap_close(Pointer p); } // TCP 头结构(按网络字节序) public static class TcpHeader { public short sourcePort; // 2 字节 public short destPort; // 2 字节 public int sequenceNumber; // 4 字节 public int ackNumber; // 4 字节 public byte dataOffset; // 4 位 + 4 位保留 public byte flags; // URG, ACK, PSH, RST, SYN, FIN public short windowSize; // 2 字节 public short checksum; // 2 字节(需校验和计算) public short urgentPointer; // 2 字节 }

5.3 发送 SYN 包的核心逻辑(关键步骤)

public void sendSynPacket(String targetIp, int targetPort) { // 1. 获取本机网卡(如 eth0) String device = findFirstDevice(); // 调用 pcap_findalldevs // 2. 打开网卡(需 root 权限) Pointer pcap = Pcap.INSTANCE.pcap_open_live(device, 65536, 0, 1000, null); if (pcap == null) throw new RuntimeException("pcap open failed"); try { // 3. 构造原始包:以太网头(14) + IP 头(20) + TCP 头(20) = 54 字节 byte[] packet = new byte[54]; // 填充以太网头(目的 MAC 需 ARP 获取,此处省略) // 填充 IP 头:protocol=6(TCP), src/dst ip, total length=54 // 填充 TCP 头:flags=0x02(SYN), sourcePort=随机, destPort=targetPort // 4. 计算 TCP 校验和(必须!否则包被丢弃) short checksum = calculateTcpChecksum(packet, 14, 20, 20); // 将 checksum 写入 TCP 头偏移 16 字节处(2 字节) packet[14 + 20 + 16] = (byte) (checksum >> 8); packet[14 + 20 + 17] = (byte) (checksum & 0xFF); // 5. 发送原始包 int result = Pcap.INSTANCE.pcap_inject(pcap, packet, packet.length); if (result != packet.length) { System.err.println("pcap_inject failed: " + result); } } finally { Pcap.INSTANCE.pcap_close(pcap); } }

校验和计算要点(面试必问):
TCP 校验和是伪首部(pseudo-header)+ TCP 头 + TCP 数据的 16 位反码和。伪首部包含源 IP、目的 IP、协议号(6)、TCP 长度。Java 中需用ByteBuffer.order(ByteOrder.BIG_ENDIAN)确保网络字节序。若校验和错,交换机/路由器直接丢包,你的 SYN 永远到不了目标。

5.4 为什么企业不用 Java 做 SYN 扫描?——性能与安全的硬边界

  • 性能瓶颈:JNA 调用 native 函数有 100ns–1μs 开销,每秒最多发 10k SYN 包;而 nmap 用 C 直接 mmap 网卡内存,可达 100k+/s。
  • 安全限制:Linux 默认禁止非 root 用户发 raw socket(CAP_NET_RAWcapability),生产环境不可能给 Java 进程开此权限。
  • 维护成本:JNA 代码需适配 x86/x64/ARM,Windows/Linux/macOS 三端 ABI 不同,一个sizeof(struct tcphdr)就可能出错。

所以,课程设计用Socket.connect()是正确选择——它用最简单的 API,暴露最真实的网络行为。而 JNA 方案的价值,在于让你亲手触摸到“为什么 Java 不适合做底层网络工具”的边界。当我第一次用 JNA 发出 SYN 包,Wireshark 里看到那个绿色的SYN行时,才真正懂了《计算机网络》课本里那句“运输层为应用层提供端到端逻辑通信”的重量。

希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 7:22:19

SpringBoot+SSM招聘平台开发实战:从架构设计到部署调试全解析

做这类“JavaSpringBootSSM招聘平台”项目的朋友&#xff0c;十有八九是奔着毕业设计或者求职作品去的。大连这边IT企业不少&#xff0c;外包、对日、制造业软件、本地互联网团队都有&#xff0c;招聘需求常年存在&#xff0c;但市面上通用的招聘网站对本地小团队和初级岗位并不…

作者头像 李华
网站建设 2026/10/5 7:22:14

FEKO金属球双站RCS仿真全流程:从建模到Mie级数验证

做电磁仿真的朋友&#xff0c;总有一天会撞上RCS这个坎。目标隐身设计、雷达探测仿真、天线布局耦合评估&#xff0c;样样离不开雷达散射截面计算。而在FEKO&#xff08;现在叫Altair Feko&#xff09;里做RCS仿真&#xff0c;进阶第一步几乎都是同一个基准题——金属球远场双站…

作者头像 李华
网站建设 2026/10/5 7:21:07

限制性三体问题中的分岔理论与轨道稳定性突变解析

“限制性三体问题”这六个字&#xff0c;听起来像是理论力学教科书才会出现的名词&#xff0c;但只要你关心深空探测轨道设计&#xff0c;迟早会撞上它。詹姆斯韦布空间望远镜所在的日地 L2 晕轨道、地月中继卫星长期驻留的工作轨道、未来小行星采矿任务可能采用的 L4/L5 停泊轨…

作者头像 李华
网站建设 2026/10/5 7:20:53

UE4 C++调用外部EXE的稳定实践:ExecuteAndWait深度解析

简介&#xff1a;本资源是一份面向UE4中级开发者的技术实践工程&#xff0c;聚焦于通过C在蓝图中调用并控制外部exe程序的核心需求&#xff0c;适用于游戏工具链集成、辅助编辑器启动、自动化脚本执行等实际场景。资源包含完整可编译的UE4项目工程&#xff08;OpenExe&#xff…

作者头像 李华
网站建设 2026/10/5 7:19:27

分布式任务调度实战:从单机定时任务到平台化架构选型与避坑指南

凌晨两点&#xff0c;我盯着监控大屏上的告警&#xff1a;某个数据补偿任务自晚上十点起就没再触发过。查日志发现&#xff0c;那台跑着定时任务的服务器因为内存溢出被容器编排平台自动重启了&#xff0c;而重启之后&#xff0c;操作系统级的 crontab 直接丢掉了所有计划。那会…

作者头像 李华
网站建设 2026/10/5 7:18:33

基于SpringBoot2+Vue3的物资管理系统源码解析与二次开发实战

最近后台私信里好几个人都在问同一类问题&#xff1a;想做一个物资管理系统练手或者应付毕业设计&#xff0c;资料翻了一堆&#xff0c;不是老旧SSH就是前后端不分离&#xff0c;真正符合当下技术栈的完整项目源码不好找。这套基于SpringBoot2 Vue3 MyBatis-Plus MySQL8.0的…

作者头像 李华