Java实现PING程序:ICMP协议与原始套接字详解
简介这份计算机网络课程设计报告围绕PING程序的设计与实现展开面向高校计算机、网络工程等专业需要完成课程设计的学生以及希望理解ICMP协议与Java网络编程的初学者。报告以模拟Windows下ping命令为目标涵盖问题描述、概要设计、详细设计、调试分析、心得体会与用户手册等完整章节可直接作为课程设计范文或模板参考。资源包共1个doc文件约1.88MB内容包含Ping、Timer、From、ImagePanel等类的设计思路pingRang与pingAddress核心算法代码以及InetAddress.isReachable方法的调用说明和异常处理方案。目前已有512人学习下载。读者可从中获得完整的课程设计结构框架、Java实现ping连通性检测的代码示例、Eclipse调试经验与常见问题解决思路适合作为网络课程设计报告撰写与网络编程入门的实践素材。1. 从一份课程设计报告说起PING 程序到底在测什么很多人第一次拿到「PING 程序设计与实现」这个题目脑子里冒出来的第一个念头是ping命令我天天用敲一下ping www.baidu.com看通不通就完事了这有什么好设计的但真坐到 Eclipse 前面打开 Java 准备写的时候问题就来了——ping到底发的是什么包为什么它不需要建立连接就能知道对面活着InetAddress.isReachable()为什么有时候返回 true 有时候返回 false跟系统权限有什么关系这份课程设计报告的核心其实不是让你复刻一个命令行工具而是逼你把 ICMP 协议、原始套接字、Java 网络编程这三块知识串起来。你需要自己构造 ICMP 回显请求报文自己算校验和自己解析回显应答最后统计往返时延和丢包率。适合正在做网络课程设计的学生也适合想从「会用 ping」进阶到「懂 ping 怎么实现」的后端开发。下面我按实际动手的顺序把这条路走一遍。2. ICMP 报文结构与 Java 原始套接字的选型理由2.1 为什么不能直接用 URLConnection 或 Socketping走的是 ICMP 协议不是 TCP 也不是 UDP。Java 标准库里的Socket和DatagramSocket分别对应 TCP 和 UDP它们封装的是传输层而 ICMP 属于网络层协议没有端口号的概念。所以你不能用new Socket()去发 ICMP 包这条路从根上就走不通。那 Java 有没有现成的 ICMP 接口有InetAddress.isReachable(int timeout)底层就是发 ICMP 回显请求。但它有几个致命限制第一它只返回一个布尔值你拿不到 RTT往返时延也拿不到 TTL第二在 Windows 上它有时候会退化成 TCP 的 7 号端口探测结果不准第三你没法控制 ICMP 报文的标识符和序列号做不了多包统计。课程设计要的是完整的 ping 功能——发包、收包、算时延、统计丢包——所以必须自己构造 ICMP 报文。常见做法是用java.net.DatagramSocket配合原始套接字或者用InetAddress拿到目标 IP 后通过DatagramPacket发送自定义字节流。但这里有个坑普通用户权限下操作系统不允许你直接发 ICMP 包。Linux 上需要root或者给 Java 进程加CAP_NET_RAW能力Windows 上需要管理员权限。这是整个课程设计里第一个卡住大多数人的地方。2.2 ICMP 回显请求报文的字节布局ICMP 报文是嵌在 IP 数据报里面的。IP 头 20 字节ICMP 头 8 字节后面跟数据部分。我们构造的时候只需要关心 ICMP 部分IP 头由操作系统内核帮我们填。ICMP 回显请求Type 8和回显应答Type 0的格式完全一样区别只在 Type 字段。8 字节的 ICMP 头结构如下偏移量长度字段名说明01 字节Type请求为 8应答为 011 字节Code恒为 022 字节Checksum整个 ICMP 报文的校验和42 字节Identifier标识符通常填进程 ID62 字节Sequence Number序列号从 1 开始递增8N 字节Data载荷通常填时间戳校验和的计算方法是把 ICMP 报文按 16 位分组所有分组做反码求和最后取反。注意计算时 Checksum 字段本身先填 0。这个算法在 Java 里用short数组循环累加就能实现但要注意 Java 没有无符号类型累加的时候要用int接最后强转回short。2.3 用 Java 构造 ICMP 包的完整代码下面这段代码是课程设计里最核心的部分直接决定你能不能发出一个合法的 ICMP 回显请求。import java.net.*; import java.nio.ByteBuffer; public class IcmpPacketBuilder { // 计算 ICMP 校验和 private static short computeChecksum(byte[] data) { int sum 0; int length data.length; int i 0; // 按 16 位一组累加 while (length 1) { sum ((data[i] 8) 0xFF00) | (data[i 1] 0xFF); i 2; length - 2; } // 如果字节数为奇数最后一个字节单独处理 if (length 1) { sum (data[i] 8) 0xFF00; } // 将高 16 位溢出加到低 16 位 while ((sum 16) ! 0) { sum (sum 0xFFFF) (sum 16); } return (short) ~sum; } // 构造 ICMP 回显请求报文 public static byte[] buildEchoRequest(int identifier, int sequence) { int dataSize 32; // 载荷 32 字节 byte[] packet new byte[8 dataSize]; packet[0] 8; // Type 8回显请求 packet[1] 0; // Code 0 // Checksum 先填 0后面计算 packet[2] 0; packet[3] 0; // Identifier 占 2 字节大端序 packet[4] (byte) ((identifier 8) 0xFF); packet[5] (byte) (identifier 0xFF); // Sequence Number 占 2 字节 packet[6] (byte) ((sequence 8) 0xFF); packet[7] (byte) (sequence 0xFF); // 填充载荷当前时间戳用于计算 RTT long timestamp System.currentTimeMillis(); ByteBuffer buffer ByteBuffer.allocate(dataSize); buffer.putLong(timestamp); // 剩余字节填固定值方便校验 for (int i 8; i dataSize; i) { buffer.put((byte) i); } System.arraycopy(buffer.array(), 0, packet, 8, dataSize); // 计算校验和并填入 short checksum computeChecksum(packet); packet[2] (byte) ((checksum 8) 0xFF); packet[3] (byte) (checksum 0xFF); return packet; } }这段代码的逻辑说明computeChecksum方法先把报文按 16 位分组累加处理奇数长度的情况然后把高 16 位的溢出折返加到低 16 位最后取反得到校验和。buildEchoRequest方法按 ICMP 头格式逐字节填充Identifier 和 Sequence 都用大端序写入载荷部分前 8 字节放当前时间戳后面填递增字节。参数identifier一般用System.currentTimeMillis() 0xFFFF或者进程 IDsequence从 1 开始每发一个包加 1。注意Java 的byte是有符号的范围 -128 到 127。写入大于 127 的值时要先跟 0xFF 做与运算再强转否则会变成负数。这是血泪经验校验和算错十有八九是这里翻的车。3. 发送与接收用 DatagramSocket 跑通第一个回显应答3.1 创建套接字并发送 ICMP 包拿到构造好的字节数组之后下一步是把它发出去。Java 里发 ICMP 包不能用DatagramSocket直接发因为DatagramSocket走的是 UDP操作系统会在 IP 头里把协议号标成 17UDP而不是 1ICMP。目标主机收到之后发现协议号不对直接丢弃你永远收不到回显应答。那怎么办常见做法有两种。第一种是用InetAddress.isReachable()做探测但前面说了拿不到 RTT。第二种是用java.net.DatagramSocket配合setSoTimeout在某些平台上操作系统会允许你发送原始 ICMP 包但这不是标准行为跨平台不可靠。真正靠谱的做法是用 JNI 调用 C 的socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)或者用Runtime.exec()直接调系统的ping命令然后解析输出。对于课程设计来说我一般推荐第三种折中方案用DatagramSocket发 UDP 包到目标主机的某个端口同时用InetAddress.isReachable()做辅助判断。但这样测的不是 ICMP报告里写清楚就行。如果指导老师要求必须实现 ICMP那就老老实实写 JNI或者用 Java 的ProcessBuilder调系统 ping。下面是用ProcessBuilder调系统 ping 并解析输出的代码这是最稳的路子import java.io.*; import java.util.regex.*; public class PingExecutor { public static void main(String[] args) throws Exception { String target www.baidu.com; // Windows 用 -n 指定次数Linux/macOS 用 -c String os System.getProperty(os.name).toLowerCase(); String countFlag os.contains(win) ? -n : -c; ProcessBuilder pb new ProcessBuilder(ping, countFlag, 4, target); pb.redirectErrorStream(true); Process process pb.start(); BufferedReader reader new BufferedReader( new InputStreamReader(process.getInputStream(), GBK)); String line; // 匹配 timexxms 或 time1ms Pattern pattern Pattern.compile(time[](\\d)ms); int received 0; long totalRtt 0; while ((line reader.readLine()) ! null) { System.out.println(line); Matcher matcher pattern.matcher(line); if (matcher.find()) { int rtt Integer.parseInt(matcher.group(1)); totalRtt rtt; received; } } process.waitFor(); System.out.println(--- 统计 ---); System.out.println(发送 4 个包收到 received 个); if (received 0) { System.out.println(平均 RTT: (totalRtt / received) ms); } } }逻辑说明ProcessBuilder启动系统 ping 命令redirectErrorStream(true)把错误流合并到标准输出避免缓冲区满了导致进程卡死。读取输出时用正则匹配timexxms或time1ms累加 RTT 并计数。参数countFlag根据操作系统自动选择Windows 是-nLinux 和 macOS 是-c。编码用 GBK 是因为 Windows 中文系统的 ping 输出是 GBK 编码用 UTF-8 读会乱码。3.2 解析回显应答与计算 RTT如果你坚持用原始套接字实现收到回显应答之后的解析逻辑是这样的先跳过 IP 头通常 20 字节但如果有选项字段可能更长然后读 ICMP 头的 Type 字段如果是 0 说明是回显应答接着读 Identifier 和 Sequence 确认是自己发的那个包最后从载荷里取出时间戳用当前时间减去时间戳就是 RTT。这里有个容易翻车的点IP 头的长度不是固定的。标准 IP 头是 20 字节但如果带了选项字段比如记录路由长度会变成 24 或 28 字节。IP 头的第一个字节低 4 位是 IHLInternet Header Length表示 IP 头有多少个 32 位字。所以实际 IP 头长度是(packet[0] 0x0F) * 4。直接写死 20 字节在某些网络环境下会解析出错。注意Linux 上普通用户没有权限创建原始套接字会抛SocketException: Permission denied。解决办法是用sudo运行或者给 Java 二进制文件加setcap cap_net_rawep。Windows 上需要以管理员身份运行 Eclipse 或者命令行。4. 避坑与排查课程设计里最容易翻车的五个地方4.1 现象程序编译通过但运行时报权限错误原因原始套接字需要系统特权。Linux 下普通用户默认没有CAP_NET_RAW能力Windows 下非管理员账户不允许创建原始套接字。解决Linux 用sudo java -cp . YourPingClass运行或者执行sudo setcap cap_net_rawep $(which java)给 Java 解释器授权。Windows 右键 Eclipse 图标选「以管理员身份运行」。如果是在 IDE 里跑注意 IDE 本身也要用管理员权限启动否则它启动的子进程继承不到权限。4.2 现象校验和算出来跟 Wireshark 抓包不一致原因Java 的byte是有符号的data[i] 8的时候如果data[i]是负数左移之后高位全是 1跟 0xFF00 做与运算结果不对。另外累加的时候用short会溢出必须用int接。解决所有字节转 int 的时候先跟 0xFF 做与运算确保变成 0 到 255 的正数。累加用int最后折返溢出位的时候用while ((sum 16) ! 0)循环处理不要只折一次。4.3 现象能收到回显应答但 RTT 是负数原因载荷里存的时间戳是发送时的时间接收时用System.currentTimeMillis()减去它。如果发送和接收用了不同的时间基准或者时间戳在字节序转换时搞反了就会算出负数。解决发送和接收都用System.currentTimeMillis()不要混用nanoTime()。字节序统一用大端序网络字节序写入用ByteBuffer.putLong()默认就是大端读取用ByteBuffer.getLong()也是大端不要手动移位。4.4 现象ping 域名通ping IP 不通原因域名解析走的是 DNS跟 ICMP 是两回事。InetAddress.getByName(www.baidu.com)返回的是解析后的 IP如果 DNS 配置有问题会抛UnknownHostException。但如果你直接 ping IP 不通说明网络层有问题可能是路由表、防火墙或者目标主机禁了 ICMP。解决先用nslookup或dig确认域名能解析出 IP再用ping直接测那个 IP。如果 IP 也不通检查本机路由表route printWindows或ip routeLinux确认默认网关存在。有些云服务器默认禁 ICMP需要在安全组里放行。4.5 现象Eclipse 里跑得好好的导出 jar 包双击运行没反应原因Eclipse 运行的时候工作目录是项目根目录导出 jar 之后双击运行工作目录变成了 jar 所在目录相对路径全变了。另外如果代码里用了System.getProperty(os.name)判断操作系统在 jar 里跑结果是一样的问题不在这。解决导出 jar 的时候在 manifest 里指定Main-Class运行时用java -jar your.jar在命令行跑这样能看到报错信息。如果双击没反应大概率是抛了异常但控制台窗口一闪而过。在代码最外层加try-catch把异常写到日志文件里或者用ProcessBuilder调 ping 的时候把错误流也读出来打印。5. 进阶技巧用 Wireshark 验证你的 ICMP 包到底长什么样课程设计报告里如果只贴代码和运行截图分数不会太高。真正能拉开差距的是抓包分析——用 Wireshark 抓下你程序发出的 ICMP 包跟标准 ping 命令发的包做对比看 Type、Code、Checksum、Identifier、Sequence 这几个字段对不对。这一步做完报告的技术深度直接上一个档次。具体操作先在 Wireshark 里选好网卡过滤器输入icmp然后运行你的 Java 程序。抓到的包如果 Type 是 8 说明是回显请求Type 是 0 说明是回显应答。点开 ICMP 详情看 Checksum 字段是不是correct如果显示incorrect说明你的校验和算错了。再看 Identifier 和 Sequence确认跟你代码里设的值一致。如果你是用ProcessBuilder调系统 ping抓到的包跟你自己构造的包格式应该一模一样因为系统 ping 也是按 RFC 792 实现的。对比的时候重点看 Data 部分系统 ping 默认填的是递增的字节序列你的程序填的是时间戳这个差异不影响功能但报告里可以写一句「载荷设计为时间戳以便于 RTT 计算」。还有一个进阶玩法把 TTL 字段也解析出来。TTL 在 IP 头里偏移量是 8。每经过一个路由器 TTL 减 1所以初始 TTL - 收到的 TTL 1就是经过的跳数。Linux 默认初始 TTL 是 64Windows 是 128。你可以在报告里加一个「路由跳数推测」的小节用收到的 TTL 反推目标主机大概经过了几跳这比单纯统计 RTT 有意思得多。最后说一个我自己的习惯每次写完网络程序先在本机ping 127.0.0.1跑通再ping局域网网关最后才ping外网域名。这样出问题的时候能快速定位是代码问题、局域网问题还是外网问题。课程设计答辩的时候老师问「你怎么保证程序是对的」你就把这个排查顺序讲出来比背概念管用。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

CjDotEnv的.env书写避坑清单:6个引号与转义场景一看就会

CjDotEnv的.env书写避坑清单:6个引号与转义场景一看就会

CjDotEnv的.env书写避坑清单:6个引号与转义场景一看就会 【免费下载链接】cjdotenv A Cangjie library to load environment variables from .env. 项目地址: https://gitcode.com/Cangjie-SIG/cjdotenv CjDotEnv 是一个用于仓颉(Cangjie&#xf…

2026/9/24 13:39:15 阅读更多 →
AI模型系列(1) | 大模型幻觉到底是什么?

AI模型系列(1) | 大模型幻觉到底是什么?

大模型幻觉不是模型坏了,而是它在优化一个与使用者不同的目标:把句子续得流畅,而不是只在有证据时才开口。所以问它一条没学过的事实,它不会沉默,而会用语言模式补出一个语气笃定的答案——答错和答对是同一个动作。这…

2026/9/24 13:39:15 阅读更多 →
推荐开源项目:Eventyay 支持FAQ平台

推荐开源项目:Eventyay 支持FAQ平台

推荐开源项目:Eventyay 支持FAQ平台 【免费下载链接】open-event-documentation Archived documentation 项目地址: https://gitcode.com/gh_mirrors/su/open-event-documentation 项目介绍 Eventyay 支持FAQ是一个全面的资源库,为活动组织者、参…

2026/9/24 13:38:15 阅读更多 →

最新新闻

基于Java开发的小程序地图定位:从后端签名到前端选点完整链路

基于Java开发的小程序地图定位:从后端签名到前端选点完整链路

简介:这是一份面向Java后端开发者与小程序入门者的实战型项目源码,围绕「小程序地图定位」这一常见移动场景,演示如何用Java技术栈配合前端完成位置服务。资源共38个文件,以15张png界面截图与图标、6个js逻辑脚本、5个wxss样式、4…

2026/9/24 18:53:29 阅读更多 →
Windows 部署 OpenClaw 实操记录,避开环境配置各类坑点

Windows 部署 OpenClaw 实操记录,避开环境配置各类坑点

OpenClaw 一体化安装包|可视化部署,简化 AI 自动化环境搭建 传统 AI 自动化工具部署流程繁琐,需要手动配置各类运行环境,对于不熟悉开发的用户门槛很高。OpenClaw 整合全套依赖,提供一体化安装包,通过图形…

2026/9/24 18:53:29 阅读更多 →
IO多路复用精讲:从select/poll到epoll高并发实战TCP回显服务器

IO多路复用精讲:从select/poll到epoll高并发实战TCP回显服务器

做Linux网络编程的人,迟早会碰到IO多路复用这个词。不管是写高并发服务端、嵌入式socket应用,还是准备面试,select、poll、epoll这三个东西都绕不开。作为系列第二篇,我会直接按工程落地的思路来讲:先把这个东西解决的…

2026/9/24 18:53:29 阅读更多 →
Hashcat实战:数据库提权场景下的口令破解与安全审计经验

Hashcat实战:数据库提权场景下的口令破解与安全审计经验

我把自己这几年做授权渗透测试和口令安全审计时用Hashcat的经验完整梳理了一遍。这篇文章不打算写成工具手册式的罗列,而是按真实项目里的思考路径来走:拿到哈希之后怎么判断形态、怎么选攻击方式、怎么设计掩码和规则、遇到瓶颈怎么调、踩过哪些坑。全程…

2026/9/24 18:53:29 阅读更多 →
OpenClaw容器化部署全指南:从Docker环境准备到常见报错排查

OpenClaw容器化部署全指南:从Docker环境准备到常见报错排查

最近身边好几个做AI应用的朋友都在折腾OpenClaw,这项目本质上是个把大模型API、消息渠道、会话管理、记忆存储全部串起来的Agent框架。它对运行环境的依赖相当挑——Python版本、Node版本、系统库版本稍微对不上,启动时就会冒出各种奇怪的报错。我的建议…

2026/9/24 18:53:29 阅读更多 →
测试环境管理实战:用GitLab CI/CD和Docker Engine打造动态测试环境

测试环境管理实战:用GitLab CI/CD和Docker Engine打造动态测试环境

聊到CI/CD优化,很多人第一反应是压缩流水线时间:并行执行、缓存依赖、精简镜像。我做了几年持续交付落地,发现真正拖垮交付效率的,往往不是流水线本身,而是下游那个不起眼的“接收站”——测试环境管理。代码构建从10分…

2026/9/24 18:52:29 阅读更多 →

日新闻

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:19 阅读更多 →
单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:19 阅读更多 →
C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/24 0:00:19 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →