SpringBoot 网络流量抓包与分析系统
下面我将设计一个基于SpringBoot的网络流量抓包与实时分析系统。这个系统将能够捕获网络数据包,进行实时分析,并通过Web界面展示结果。
系统架构设计
用户界面 (Web)↑↓ HTTP/WebSocket
SpringBoot控制器层↑↓ 内部调用
流量分析服务层↑↓ 事件驱动
数据包捕获层 (Jpcap/Pcap4J)↑↓ 原始网络接口
网络设备
实现步骤
- 添加Maven依赖
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- 网络抓包库 --><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>
- 数据包捕获服务
@Service
public class PacketCaptureService {private static final Logger logger = LoggerFactory.getLogger(PacketCaptureService.class);private PcapHandle handle;private volatile boolean isCapturing = false;private final PacketProcessor packetProcessor;public PacketCaptureService(PacketProcessor packetProcessor) {this.packetProcessor = packetProcessor;}public void startCapture(String networkInterface) throws PcapNativeException, NotOpenException {if (isCapturing) {logger.warn("Capture is already running");return;}// 获取网络接口PcapNetworkInterface nif = Pcaps.getDevByName(networkInterface);// 打开接口handle = nif.openLive(65536, PromiscuousMode.PROMISCUOUS, 100);isCapturing = true;// 启动捕获线程new Thread(() -> {try {handle.loop(0, (Packet packet) -> {packetProcessor.processPacket(packet);});} catch (Exception e) {logger.error("Error during packet capture", e);}}).start();logger.info("Started packet capture on interface: {}", networkInterface);}public void stopCapture() {isCapturing = false;if (handle != null && handle.isOpen()) {handle.breakLoop();handle.close();}logger.info("Stopped packet capture");}public boolean isCapturing() {return isCapturing;}
}
- 数据包处理器
@Component
public class PacketProcessor {private static final Logger logger = LoggerFactory.getLogger(PacketProcessor.class);// 存储统计分析结果private final ConcurrentHashMap<String, TrafficStats> trafficStats = new ConcurrentHashMap<>();private final SimpMessagingTemplate messagingTemplate;public PacketProcessor(SimpMessagingTemplate messagingTemplate) {this.messagingTemplate = messagingTemplate;}public void processPacket(Packet packet) {if (packet.contains(IpV4Packet.class)) {IpV4Packet ipV4Packet = packet.get(IpV4Packet.class);String srcIp = ipV4Packet.getHeader().getSrcAddr().getHostAddress();String dstIp = ipV4Packet.getHeader().getDstAddr().getHostAddress();int length = packet.length();// 更新统计数据updateStats(srcIp, dstIp, length);// 实时发送到前端sendRealTimeUpdate(srcIp, dstIp, length);}}private void updateStats(String srcIp, String dstIp, int length) {// 更新源IP统计trafficStats.compute(srcIp, (ip, stats) -> {if (stats == null) {stats = new TrafficStats(ip);}stats.addOutgoingTraffic(length);return stats;});// 更新目标IP统计trafficStats.compute(dstIp, (ip, stats) -> {if (stats == null) {stats = new TrafficStats(ip);}stats.addIncomingTraffic(length);return stats;});}private void sendRealTimeUpdate(String srcIp, String dstIp, int length) {Map<String, Object> update = new HashMap<>();update.put("timestamp", System.currentTimeMillis());update.put("source", srcIp);update.put("destination", dstIp);update.put("length", length);messagingTemplate.convertAndSend("/topic/traffic", update);}public Map<String, TrafficStats> getTrafficStats() {return new HashMap<>(trafficStats);}
}@Data
@AllArgsConstructor
class TrafficStats {private String ipAddress;private long incomingBytes = 0;private long outgoingBytes = 0;private int packetCount = 0;public TrafficStats(String ipAddress) {this.ipAddress = ipAddress;}public void addIncomingTraffic(int bytes) {this.incomingBytes += bytes;this.packetCount++;}public void addOutgoingTraffic(int bytes) {this.outgoingBytes += bytes;this.packetCount++;}
}
- Web控制器
@RestController
@RequestMapping("/api/traffic")
public class TrafficController {private final PacketCaptureService captureService;private final PacketProcessor packetProcessor;public TrafficController(PacketCaptureService captureService, PacketProcessor packetProcessor) {this.captureService = captureService;this.packetProcessor = packetProcessor;}@PostMapping("/start")public ResponseEntity<String> startCapture(@RequestParam String interfaceName) {try {captureService.startCapture(interfaceName);return ResponseEntity.ok("Capture started on interface: " + interfaceName);} catch (Exception e) {return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Failed to start capture: " + e.getMessage());}}@PostMapping("/stop")public ResponseEntity<String> stopCapture() {captureService.stopCapture();return ResponseEntity.ok("Capture stopped");}@GetMapping("/stats")public Map<String, TrafficStats> getStats() {return packetProcessor.getTrafficStats();}@GetMapping("/interfaces")public List<String> getNetworkInterfaces() throws PcapNativeException {return Pcaps.findAllDevs().stream().map(PcapNetworkInterface::getName).collect(Collectors.toList());}
}
- WebSocket配置
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {@Overridepublic void configureMessageBroker(MessageBrokerRegistry config) {config.enableSimpleBroker("/topic");config.setApplicationDestinationPrefixes("/app");}@Overridepublic void registerStompEndpoints(StompEndpointRegistry registry) {registry.addEndpoint("/traffic-websocket").withSockJS();}
}
- 前端界面 (HTML + JavaScript)
<!DOCTYPE html>
<html>
<head><title>网络流量监控</title><script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script><script src="https://cdn.jsdelivr.net/npm/stompjs@2/lib/stomp.min.js"></script><script src="https://cdn.jsdelivr.net/npm/chart.js"></script><style>body { font-family: Arial, sans-serif; margin: 20px; }.container { display: flex; flex-direction: column; gap: 20px; }.controls { display: flex; gap: 10px; align-items: center; }table { border-collapse: collapse; width: 100%; }th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }th { background-color: #f2f2f2; }.chart-container { position: relative; height: 300px; width: 100%; }</style>
</head>
<body><div class="container"><h1>网络流量监控系统</h1><div class="controls"><select id="interfaceSelect"><option value="">选择网络接口</option></select><button id="startBtn" onclick="startCapture()">开始抓包</button><button id="stopBtn" onclick="stopCapture()" disabled>停止抓包</button><span id="status">状态: 未启动</span></div><div><h2>实时流量</h2><div class="chart-container"><canvas id="trafficChart"></canvas></div></div><div><h2>流量统计</h2><table id="statsTable"><thead><tr><th>IP地址</th><th>流入流量 (字节)</th><th>流出流量 (字节)</th><th>数据包数量</th></tr></thead><tbody></tbody></table></div></div><script>let stompClient = null;let trafficChart = null;let chartData = {labels: [],datasets: [{label: '流量 (字节)',data: [],borderColor: 'rgb(75, 192, 192)',tension: 0.1}]};// 初始化页面window.onload = function() {// 获取网络接口列表fetch('/api/traffic/interfaces').then(response => response.json()).then(interfaces => {const select = document.getElementById('interfaceSelect');interfaces.forEach(iface => {const option = document.createElement('option');option.value = iface;option.textContent = iface;select.appendChild(option);});});// 初始化图表const ctx = document.getElementById('trafficChart').getContext('2d');trafficChart = new Chart(ctx, {type: 'line',data: chartData,options: {responsive: true,maintainAspectRatio: false,scales: {y: {beginAtZero: true}}}});};// 连接WebSocketfunction connectWebSocket() {const socket = new SockJS('/traffic-websocket');stompClient = Stomp.over(socket);stompClient.connect({}, function(frame) {console.log('Connected: ' + frame);stompClient.subscribe('/topic/traffic', function(message) {const trafficData = JSON.parse(message.body);updateTrafficChart(trafficData);});});}// 更新流量图表function updateTrafficChart(data) {const now = new Date().toLocaleTimeString();chartData.labels.push(now);chartData.datasets[0].data.push(data.length);// 保持最后20个数据点if (chartData.labels.length > 20) {chartData.labels.shift();chartData.datasets[0].data.shift();}trafficChart.update();}// 开始抓包function startCapture() {const interfaceName = document.getElementById('interfaceSelect').value;if (!interfaceName) {alert('请选择网络接口');return;}fetch(`/api/traffic/start?interfaceName=${encodeURIComponent(interfaceName)}`, {method: 'POST'}).then(response => response.text()).then(result => {document.getElementById('status').textContent = '状态: 运行中';document.getElementById('startBtn').disabled = true;document.getElementById('stopBtn').disabled = false;connectWebSocket();startUpdatingStats();}).catch(error => console.error('Error:', error));}// 停止抓包function stopCapture() {fetch('/api/traffic/stop', {method: 'POST'}).then(response => response.text()).then(result => {document.getElementById('status').textContent = '状态: 已停止';document.getElementById('startBtn').disabled = false;document.getElementById('stopBtn').disabled = true;if (stompClient !== null) {stompClient.disconnect();}clearInterval(statsInterval);}).catch(error => console.error('Error:', error));}// 定期更新统计数据let statsInterval;function startUpdatingStats() {statsInterval = setInterval(() => {fetch('/api/traffic/stats').then(response => response.json()).then(stats => {const tbody = document.querySelector('#statsTable tbody');tbody.innerHTML = '';for (const [ip, data] of Object.entries(stats)) {const row = document.createElement('tr');row.innerHTML = `<td>${data.ipAddress}</td><td>${data.incomingBytes}</td><td>${data.outgoingBytes}</td><td>${data.packetCount}</td>`;tbody.appendChild(row);}});}, 2000);}</script>
</body>
</html>
系统功能说明
- 网络接口选择:用户可以从下拉菜单中选择要监控的网络接口
- 流量捕获控制:可以启动和停止数据包捕获
- 实时流量展示:通过WebSocket实时推送流量数据,使用图表展示
- 流量统计分析:展示每个IP地址的流入/流出流量和数据包数量
- 数据持久化:可根据需要扩展,将流量数据保存到数据库
运行注意事项
- 需要安装WinPcap(Windows)或libpcap(Linux/Mac)等底层抓包库
- 在Linux系统上可能需要以root权限运行应用
- 可根据需要添加过滤器,只捕获特定协议或端口的流量
这个系统提供了基本的网络流量监控功能,可以根据实际需求进一步扩展,比如添加协议分析、流量告警、历史数据查询等功能。