You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于Pcap4j捕获HTTP请求响应及实现持续抓包的技术咨询

问题描述

我现在有一段基于Pcap4j的Java代码,可以从指定网络接口捕获数据包,但不知道如何提取并打印包中的HTTP请求与响应数据(比如追踪Postman发起的REST请求及响应,类似Wireshark的功能)。目前只能捕获数据包,无法读取并打印包内的HTTP内容。我的代码如下:

try {
    InetAddress addr = InetAddress.getByName("10.227.178.25");
    PcapNetworkInterface device = Pcaps.getDevByAddress(addr);
    System.out.println("You chose: " + device);
    int snapshotLength = 64 * 1024; // in bytes
    int readTimeout = 50; // in milliseconds
    final PcapHandle handle;
    handle = device.openLive(snapshotLength, PromiscuousMode.PROMISCUOUS, readTimeout);
    String filter = "tcp port 80";
    handle.setFilter(filter, BpfCompileMode.OPTIMIZE);

    // Create a listener that defines what to do with the received packets
    PacketListener listener = new PacketListener() {
        @Override
        public void gotPacket(Packet packet) {
            // Override the default gotPacket() function and process packet
            System.out.println(handle.getTimestamp());
            System.out.println(packet);
            byte[] b = packet.getRawData();
            Packet p = packet.getPayload();
        }
    };

    // Tell the handle to loop using the listener we created
    try {
        int maxPackets = 50;
        handle.loop(maxPackets, listener);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    // Cleanup when complete
    handle.close();
}catch(Exception e) {
    e.printStackTrace();
}

我有两个问题:

  1. 如何捕获并在控制台打印HTTP请求与响应?
  2. 如何让Java代码持续运行以不断捕获数据包?

我查阅了Pcap4j文档,但仍未找到读取包中HTTP内容的方法。

解决方案

1. 捕获并打印HTTP请求与响应

说实话,Pcap4j并没有直接提供HTTP协议的解析封装,所以我们得自己动手从TCP的负载里提取HTTP内容。毕竟HTTP是跑在TCP之上的应用层协议,我们需要先逐层拆解数据包,拿到TCP的payload后再去解析HTTP文本。

先给你理清楚步骤:

  • 先把捕获到的原始包拆成以太网帧(如果是以太网接口的话)
  • 再从以太网帧里拆出IP包(不管是IPv4还是IPv6)
  • 接着从IP包中提取TCP包,拿到TCP的负载数据
  • 最后把负载转成字符串,判断是HTTP请求(比如开头是GET/POST这些方法)还是响应(开头是HTTP/xxx)

直接给你修改后的PacketListener实现,替换你原来的就行:

import java.nio.charset.StandardCharsets;
import org.pcap4j.packet.EthernetPacket;
import org.pcap4j.packet.IpPacket;
import org.pcap4j.packet.TcpPacket;

// ... 其他原有代码

PacketListener listener = new PacketListener() {
    @Override
    public void gotPacket(Packet packet) {
        try {
            // 解析以太网帧
            EthernetPacket ethernetPacket = packet.get(EthernetPacket.class);
            if (ethernetPacket == null) {
                return;
            }

            // 解析IP包
            IpPacket ipPacket = ethernetPacket.getPayload();
            if (!(ipPacket.getPayload() instanceof TcpPacket)) {
                return; // 不是TCP包直接跳过
            }

            TcpPacket tcpPacket = (TcpPacket) ipPacket.getPayload();
            byte[] tcpPayload = tcpPacket.getPayload() != null ? tcpPacket.getPayload().getRawData() : new byte[0];

            if (tcpPayload.length == 0) {
                return; // 没负载的TCP包也跳过
            }

            // HTTP默认用ISO-8859-1编码,转成字符串
            String httpContent = new String(tcpPayload, StandardCharsets.ISO_8859_1);

            // 判断是请求还是响应,然后打印
            if (httpContent.startsWith("GET") || httpContent.startsWith("POST") || 
                httpContent.startsWith("PUT") || httpContent.startsWith("DELETE")) {
                System.out.println("\n=== HTTP 请求 ===");
                System.out.println("时间戳: " + handle.getTimestamp());
                System.out.println(httpContent);
            } else if (httpContent.startsWith("HTTP/")) {
                System.out.println("\n=== HTTP 响应 ===");
                System.out.println("时间戳: " + handle.getTimestamp());
                System.out.println(httpContent);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
};

小提醒:这个代码只能处理单包里装下完整HTTP内容的情况,实际场景中HTTP请求/响应可能会被拆成多个TCP分片。如果要处理这种情况,你得维护每个TCP流的状态(通过源端口、目的端口、序列号来识别同一个流),把多个分片的payload拼接起来,直到拿到完整的HTTP消息才行。

2. 让代码持续捕获数据包

你现在用的handle.loop(50, listener)是只抓50个包就停,要持续运行的话,把第一个参数换成PcapHandle.LOOP_INFINITELY就行,这个是Pcap4j提供的常量,表示无限循环抓包。

修改后的循环代码:

try {
    // 无限循环捕获,直到手动中断
    handle.loop(PcapHandle.LOOP_INFINITELY, listener);
} catch (InterruptedException e) {
    System.out.println("捕获过程被中断");
    e.printStackTrace();
}

另外,为了优雅关闭,你可以加个 shutdown hook,这样程序退出的时候会自动关闭捕获句柄:

// 添加优雅关闭钩子
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
    if (handle != null && handle.isOpen()) {
        handle.close();
        System.out.println("已关闭网络捕获句柄");
    }
}));

这样你的程序就会一直运行,直到你按下Ctrl+C或者遇到异常啦。

内容的提问来源于stack exchange,提问作者arpit joshi

火山引擎 最新活动