前言
在当今数字化时代,网络流量如同信息社会的血液,承载着海量的数据交互。对网络流量进行有效的抓包与实时分析,是保障网络安全、优化网络性能的关键环节。无论是及时发现潜在的网络攻击,还是排查网络拥塞等问题,都离不开精准、高效的流量分析手段。
而Pcap4j则是一个强大的Java库,它基于libpcap/winpcap,为Java开发者提供了便捷的网络数据包捕获与处理能力。将Spring Boot与Pcap4j相结合,能够充分发挥两者的优势,快速构建出一个功能完善、易于扩展的网络流量抓包与实时分析系统。
环境搭建
效果图
图片
依赖引入
复制
<dependencies>
<!-- Spring Boot WebSocket依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- Pcap4j 依赖 -->
<dependency>
<groupId>org.pcap4j</groupId>
<artifactId>pcap4j-core</artifactId>
<version>1.8.2</version>
</dependency>
<dependency>
<groupId>org.pcap4j</groupId>
<artifactId>pcap4j-packetfactory-static</artifactId>
<version>1.8.2</version>
</dependency>
</dependencies>1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.
安装 libpcap/winpcap:Pcap4j需要依赖底层的libpcap(Linux、Mac 统)或winpcap(Windows 系统)库。对于 Windows 系统,可以从官网下载winpcap安装程序并进行安装;对于Linux系统,通常可以通过包管理器进行安装,如yum install libpcap或apt-get install libpcap-dev。
核心功能实现
网络接口选择
在进行网络流量抓包之前,需要先选择要监听的网络接口。Pcap4j提供了获取网络接口列表的方法,我们可以通过编写代码让用户选择需要监听的接口。
首先,使用Pcaps.findAllDevs()方法获取所有可用的网络接口列表,然后遍历该列表,将每个接口的名称、描述等信息展示给用户,让用户进行选择。例如:
复制
List<PcapNetworkInterface> allDevs = Pcaps.findAllDevs();
if (allDevs.isEmpty()) {
throw new RuntimeException("No network interfaces found!");
}
// 展示网络接口信息
for (int i = 0; i < allDevs.size(); i++) {
PcapNetworkInterface dev = allDevs.get(i);
System.out.println(i + ": " + dev.getName() + " - " + dev.getDescription());
}
// 让用户选择接口
Scanner scanner = new Scanner(System.in);
int index = scanner.nextInt();
PcapNetworkInterface selectedDev = allDevs.get(index);1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.
数据包捕获
选择好网络接口后,就可以进行数据包的捕获了。使用 Pcap4j 的openLive()方法打开选中的网络接口,并设置捕获超时时间和缓冲区大小等参数。然后,通过loop()方法循环捕获数据包。
在捕获数据包的过程中,可以设置过滤器,只捕获符合特定条件的数据包,例如只捕获 TCP 协议的数据包、特定端口的数据包等。过滤器的语法遵循 libpcap 的过滤规则。
复制
// 打开网络接口
PcapHandle handle = selectedDev.openLive(65536, PcapNetworkInterface.PromiscuousMode.PROMISCUOUS, 1000);
// 设置过滤器,只捕获TCP协议且目的端口为80的数据包
String filter = "tcp dst port 80";
handle.setFilter(filter, BpfProgram.BpfCompileMode.OPTIMIZE);
// 循环捕获数据包
handle.loop(-1, new PacketListener() {
@Override
public void gotPacket(Packet packet) {
// 处理捕获到的数据包
processPacket(packet);
}
});1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.
数据包解析
捕获到数据包后,需要对其进行解析,提取出有用的信息,如源IP地址、目的IP地址、源端口、目的端口、协议类型、数据包长度等。Pcap4j提供了各种数据包类,如IpV4Packet、TcpPacket、UdpPacket等,可以根据数据包的类型进行相应的解析。
复制
private void processPacket(Packet packet) {
// 解析以太网帧
EthernetPacket ethernetPacket = packet.get(EthernetPacket.class);
if (ethernetPacket == null) {
return;
}
// 解析IP数据包
IpV4Packet ipV4Packet = ethernetPacket.get(IpV4Packet.class);
if (ipV4Packet == null) {
return;
}
Inet4Address srcIp = ipV4Packet.getHeader().getSrcAddr();
Inet4Address dstIp = ipV4Packet.getHeader().getDstAddr();
IpNumber protocol = ipV4Packet.getHeader().getProtocol();
// 解析传输层协议
int srcPort = -1;
int dstPort = -1;
if (protocol == IpNumber.TCP) {
TcpPacket tcpPacket = ipV4Packet.get(TcpPacket.class);
if (tcpPacket != null) {
srcPort = tcpPacket.getHeader().getSrcPort().valueAsInt();
dstPort = tcpPacket.getHeader().getDstPort().valueAsInt();
}
} elseif (protocol == IpNumber.UDP) {
UdpPacket udpPacket = ipV4Packet.get(UdpPacket.class);
if (udpPacket != null) {
srcPort = udpPacket.getHeader().getSrcPort().valueAsInt();
dstPort = udpPacket.getHeader().getDstPort().valueAsInt();
}
}
// 组装数据包信息为JSON
Map<String, Object> packetInfo = new HashMap<>();
packetInfo.put("timestamp", new Date());
packetInfo.put("srcIp", srcIp.getHostAddress());
packetInfo.put("dstIp", dstIp.getHostAddress());
packetInfo.put("protocol", protocol.name());
packetInfo.put("srcPort", srcPort);
packetInfo.put("dstPort", dstPort);
packetInfo.put("length", packet.length());
// 发送到前端
PacketWebSocketHandler.sendPacketInfo(new ObjectMapper().writeValueAsString(packetInfo));
}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.
实时分析与展示
为了实现实时分析与展示,我们可以将解析后的数据包信息存储到内存中的数据结构(如WebSocket、队列、列表等)中,然后通过Spring Boot的 Web功能将这些信息实时推送到前端页面进行展示。
复制
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new PacketWebSocketHandler(), "/packet").setAllowedOrigins("*");
}
}1.2.3.4.5.6.7.8.
编写WebSocket处理器,将解析后的数据包信息发送给前端:
复制
public class PacketWebSocketHandler extends TextWebSocketHandler {
private static final List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
sessions.add(session);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
sessions.remove(session);
}
public static void sendPacketInfo(String info) {
for (WebSocketSession session : sessions) {
if (session.isOpen()) {
try {
session.sendMessage(new TextMessage(info));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.