• 基于Java的网络流量分析软件设计


    网络流量分析软件
    目录
    网络流量分析软件 1
    一、选题背景 1
    二、方案论证(设计理念) 2
    程序后台(数据模块) 2
    (1) 数据抓取:pcap4j 库 2
    (2) Web 框架:jetty 库 2
    (3) WebSocket 交互逻辑 2
    (4) 认证与加密 3
    程序前端(展示模块) 3
    (1) 文件存取:原生 tcpdump 标准 pcap 文件 3
    (2) 大量数据与性能:分页与动态读取 3
    (3) 数据分析:独立模块 4
    三、过程论述 4

    1. 总体架构与引用第三方库 4
    2. 定义前后端交互接口 4
      客户端 -> 服务器 4
      ① action=hello 握手消息 4
      ② action=ping 存活确认消息 5
      ③ action=command 控制命令消息 5
      服务器 -> 客户端 5
    3. 后台:定义前后端交互的 Packet 模型 6
    4. 后台:定义可能用到的辅助类 7
    5. 后台:完成实时捕获后台核心部分 8
    6. 后台:实现实时捕获模块的后端接口 9
    7. 前端:实现向接口发送控制命令和从接口获取实时数据 10
    8. 后台:添加 HTTPS 支持 10
    9. 综合:添加 AES 加密传输 11
    10. 后台:添加解析命令行参数功能,灵活配置程序参数 12
    11. 前端:添加断线重连功能 12
    12. 后台:添加代理识别,不向代理客户端发送实时数据 13
    13. 前端:重构提取可重用部分,为实现文件管理模块做准备 13
    14. 综合:实现文件管理模块接口 13
    15. 整合调试,打包发布 14
      四、结果分析 14
    16. 运行结果 14
      运行结果及分析 14
    17. 捕获模块稳定性和资源占用分析 21
    18. 手机端 24
    19. 环境要求 27
      附:使用手册(程序使用方法) 27
    20. 使用方法 28
    21. 注意事项 28
      五、毕业设计总结 29
      二、方案论证(设计理念)
      程序后台(数据模块)
      (1)数据抓取:pcap4j 库
      Pcap4J 是一款十分优秀的数据报文抓取第三方 Java 库,要自己编写报文抓取代码是十分困难的,但是在这个第三方库的支持下,对数据报文的抓取和处理将会容易,大大降低了开发难度。尽管如此,这一部分也将是程序的核心,报文的获取、转换、存储、读取都将在这个部分完成。
      (2)Web 框架:jetty 库
      本程序虽然以 web 作为主要展示端和控制端,但 web 后台并不是核心所在,所以只需要简单轻量的内嵌式 web 后台就足够了,jetty 便是非常适合的库,能比较容易的实现 web 后台。其中文件上传下载使用 servlet,其他数据交互使用 WebSocket。
      (3)WebSocket 交互逻辑
      WebSocket 属于 web 的一部分,是前端获取数据的主要来源,它将是整

    个程序中最复杂的模块之一,除了负责后端报文的转发和接收前端控制命令外,还需要检测用户的合法性,以及将用户的合法请求信息转换为可识别的控制信息。
    (4)认证与加密
    程序启动时生成随机 256 位密钥,浏览器的认证与加密将依赖这个密钥。浏览器获取密钥之后,建立 WebSocket 连接和调用后台 API 都将使用这个密钥进行认证和加密。
    为确保密钥不会在传输过程中被窃听,使用浏览器地址#hash 作为密钥的来源,这样密钥将不会通过浏览器传输,自然就没有被网络窃听的可能。值得注意的是,在设计的初期是使用 HTTPS 协议进行认证和加密的,但
    在开放过程中意识到,SSL 只提供传输的保密性,却并不会提供用户认证的功能,虽然能够较好地防止信息被窃听,却无法保证用户是合法用户。
    所以后期加上了 AES 加密,将与 HTTPS 同时存在,并在程序入口处加上了灵活的控制,文档后面会有详细的说明。
    程序前端(展示模块)
    (1)展示页面:单页面入口,使用 React 实现,进入 React 前统一从 URL 中获取密钥并保存在 sessionStorage 中(这样刷新浏览器也能继续完成认证,且在浏览器或标签关闭时失效)
    (2)控制接口:WebSocket 接口
    (3)数据获取接口:WebSocket 接口
    (4)文件接口:上传与下载均使用 servlet
    数据分析与存储
    (1)文件存取:原生 tcpdump 标准 pcap 文件
    Pcap4J 库可以直接存取 tcpdump 标准 pcap 文件。
    (2)大量数据与性能:分页与动态读取
    当数据量很大时,在一个页面显示会严重影响性能,甚至程序崩溃,必须做好分页。然而分页也需要一定的技巧,由于 pcap 文件本身是流式的, 而且没有索引,所以很难从一个大的 pcap 文件中分页,只能采用多个文件的方式,借用操作系统的文件管理来辅助分页,规定一个文件最大存多少个包,这样就解决了后台分页问题。
    还有就是前端的数据,除了能实时监控数据包之外,还应能够获取历史数据,另外当数据包越积越多,本文转载自http://www.biyezuopin.vip/onews.asp?id=14910前端的内存消耗会越来越大,即使前端分页也无法缓解内存负荷,必须在合适的时间舍弃旧的数据包,需要的时候再向

    后台请求获取。
    (3)数据分析:独立模块
    本程序的数据分析主要有两种形式:一是从开始捕获数据包开始动态统计数据包类型,加以分析;二是从一个 pcap 文件中读取并分析。
    这两种分析方式的分析过程是一样的,但是数据来源不同,如果能将分析程序独立出来,就能从不同的来源获取数据后使用统一的接口。另外,动态分析将是一直存在与全局空间的,只有一个,而文件分析则对应每一个文件,可以有多个。
    从连接上看,两种连接都应该支持多客户端,不同之处只是实时捕获是多个客户端控制同一个捕获程序,而文件管理部分则是每个客户端对应一个文件管理程序。

    package com.xinsane.traffic_analysis;
    
    import com.xinsane.traffic_analysis.helper.AESCryptHelper;
    import com.xinsane.traffic_analysis.helper.ArgumentsResolver;
    import com.xinsane.traffic_analysis.servlet.DownloadServlet;
    import com.xinsane.traffic_analysis.servlet.UploadServlet;
    import com.xinsane.traffic_analysis.websocket.WSHandler;
    import org.eclipse.jetty.http.HttpVersion;
    import org.eclipse.jetty.server.*;
    import org.eclipse.jetty.server.handler.ContextHandler;
    import org.eclipse.jetty.server.handler.HandlerList;
    import org.eclipse.jetty.server.handler.ResourceHandler;
    import org.eclipse.jetty.server.handler.gzip.GzipHandler;
    import org.eclipse.jetty.servlet.ServletContextHandler;
    import org.eclipse.jetty.util.ssl.SslContextFactory;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    
    public class Application {
        private static final Logger logger = LoggerFactory.getLogger(Application.class);
        public static ArgumentsResolver.Result config = new ArgumentsResolver.Result();
        public static int http_port = -1;
        public static int https_port = -1;
        public static String dumper_dir = "./dump/";
        public static int slice_number = 500;
        private static String keystore_path = "./certs/keystore";
        private static String keystore_password = "traffic";
        public static boolean no_aes = false;
    
        public static void main(String[] args) {
            try {
                resolveArguments(args);
                if (!no_aes)
                    generateKey();
                createDumpDir();
                startWeb();
            } catch (Exception e) {
                e.printStackTrace();
                System.exit(-1);
            }
        }
    
        private static void resolveArguments(String[] args) {
            Map validOptions = new HashMap<>();
            validOptions.put("http", "specify the http listen port.");
            validOptions.put("https", "specify the https listen port.");
            validOptions.put("dump", "specify the pcap files directory. default ./dump/");
            validOptions.put("slice", "specify how many packets per pcap file in file mode. default 500");
            validOptions.put("keystore", "specify the path of SSL keystore file. default ./certs/keystore");
            validOptions.put("keystore_pass", "specify the password of SSL keystore file. default traffic");
            Map validFeatures = new HashMap<>();
            validFeatures.put("no_aes", "data will be transferred without encryption if --no_aes is set");
            config = ArgumentsResolver.resolve(args, validOptions, validFeatures);
    
            if (config.args.size() > 0) {
                ArgumentsResolver.die("Unknown arguments. You can use this program with options and features below.",
                        validOptions, validFeatures);
            }
    
            if (config.options.containsKey("http")) {
                http_port = Integer.parseInt(config.options.get("http"));
                logger.debug("http port will be listening on " + http_port);
            }
            if (config.options.containsKey("https")) {
                https_port = Integer.parseInt(config.options.get("https"));
                logger.debug("https port will be listening on " + https_port);
            }
            if (config.options.containsKey("dump"))
                dumper_dir = config.options.get("dump");
            if (config.options.containsKey("slice"))
                slice_number = Integer.parseInt(config.options.get("slice"));
            if (config.options.containsKey("keystore"))
                keystore_path = config.options.get("keystore");
            if (config.options.containsKey("keystore_pass"))
                keystore_password = config.options.get("keystore_pass");
    
            if (config.features.containsKey("no_aes")) {
                no_aes = true;
                logger.debug("AES encryption turn off. Data transfer may be at risk.");
            }
    
            if (http_port <= 0 && https_port <= 0) {
                ArgumentsResolver.die("You should specify either -http or -https option at least.",
                        validOptions, validFeatures);
            }
            if (http_port > 0 && no_aes) {
                logger.warn("it will be at risk to specify a http port with --no_aes set.");
            }
        }
    
        private static void generateKey() {
            String keyString = bytes2Hex(AESCryptHelper.key.getEncoded());
            logger.debug("generate a new key: " + keyString);
            // 把生成的密钥写入文件
            File file = new File("./key.txt");
            try {
                if (file.exists()) {
                    if (!file.delete())
                        logger.error("can not delete old key file.");
                }
                if (file.createNewFile()) {
                    file.deleteOnExit(); // 程序结束后删除
                    BufferedWriter out = new BufferedWriter(new FileWriter(file));
                    out.write(keyString);
                    out.flush();
                    out.close();
                } else
                    logger.error("can not write key to the file: " + file.getAbsolutePath());
            } catch (IOException e) {
                e.printStackTrace();
                logger.error("can not write key to the file: " + file.getAbsolutePath());
            }
        }
    
        private static String bytes2Hex(byte[] bytes) {
            StringBuilder builder = new StringBuilder(bytes.length * 2);
            for(byte b : bytes)
                builder.append(String.format("%02x", b & 0xff));
            return builder.toString();
        }
    
        private static void startWeb() throws Exception {
            Server server = new Server();
    
            if (http_port > 0) {
                // HTTP Connector
                ServerConnector httpConnector = new ServerConnector(server);
                httpConnector.setPort(http_port);
                server.addConnector(httpConnector);
            }
    
            if (https_port > 0) {
                // SSL Context Factory
                SslContextFactory sslContextFactory = new SslContextFactory();
                sslContextFactory.setKeyStorePath(keystore_path);
                sslContextFactory.setKeyStorePassword(keystore_password);
    
                // HTTPS Configuration
                HttpConfiguration https_config = new HttpConfiguration();
                https_config.setSecureScheme("https");
                https_config.setSecurePort(https_port);
                https_config.addCustomizer(new SecureRequestCustomizer());
    
                // HTTPS Connector
                ServerConnector httpsConnector = new ServerConnector(server,
                        new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()),
                        new HttpConnectionFactory(https_config));
                httpsConnector.setPort(https_port);
                server.addConnector(httpsConnector);
            }
    
            HandlerList gzipHandlerList = new HandlerList();
    
            // Resource Handler
            ResourceHandler resourceHandler = new ResourceHandler();
            resourceHandler.setDirectoriesListed(true);
            String resource_path = Application.class.getResource("/static").toString();
            resourceHandler.setResourceBase(resource_path);
            gzipHandlerList.addHandler(resourceHandler);
    
            // WebSocket Handler
            ContextHandler wsHandler = new ContextHandler();
            wsHandler.setContextPath("/websocket");
            wsHandler.setHandler(new WSHandler());
            gzipHandlerList.addHandler(wsHandler);
    
            // GZIP Support
            GzipHandler gzip = new GzipHandler();
            gzip.setHandler(gzipHandlerList);
    
            HandlerList handlerList = new HandlerList();
            handlerList.addHandler(gzip);
    
            // Servlet Handler
            ServletContextHandler servletHandler = new ServletContextHandler();
            servletHandler.addServlet(UploadServlet.class, "/upload");
            servletHandler.addServlet(DownloadServlet.class, "/download/*");
            handlerList.addHandler(servletHandler);
    
            server.setHandler(handlerList);
    
            // Start Server
            server.setStopAtShutdown(true);
            server.start();
        }
    
        private static void createDumpDir() {
            File dir = new File(dumper_dir);
            if (!dir.exists()) {
                if (!dir.mkdirs()) {
                    logger.error("无法创建dump目录!");
                    System.exit(-1);
                }
            }
        }
    
    }
    
    

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    ERP是数字化的底座,你数字化转型的巨人肩膀
    Rust-后端服务调试入坑记
    SQL Server 2019企业版和标准版的区别?
    Python Turtle Graphics 绘制I Love You字符
    立体视觉四十二章经第01章:世界坐标系及不同坐标系的转换
    数据库实验二:图书信息管理系统数据查询与数据更新
    运行检测Java版本的代码出现Failed to resolve SDK
    Idea快速修改实体类属性的数据类型
    令人愉快的 Nuxt3 教程 (一): 应用的创建与配置
    说一说HTTP1.0、1.1、2.0版本区别和优化
  • 原文地址:https://blog.csdn.net/sheziqiong/article/details/127040064