基于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<String, String> 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<String, String> 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);
            }
        }
    }

}

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

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
基于Java语言的语音通信软件设计需要满足以下几个方面的需求。 首先,需要实现音频的采集、编码、解码和播放。在Java平台下,可以使用Java Sound API来实现音频处理功能。它提供了访问音频硬件的底层接口和高层接口,能够实现音频的录制、播放、转换和混合等功能。需要注意的是,由于语音通信需要实时性,因此在音频采集和播放时需要考虑延迟的问题,通过合适的算法进行降低延迟的处理。 其次,需要实现网络通信的功能。可以使用Java Socket API来实现数据的传输。本设计中,可以采用客户端/服务器的模型,客户端通过Socket连接到服务器,实现语音的传输。需要考虑的问题包括网络带宽、连接稳定性、数据丢失、安全性等。 再次,需要考虑用户界面的设计。一个友好的用户界面可以提高用户的使用体验。可以采用Java Swing或JavaFX来实现GUI界面。用户界面需要实现基本的语音通信功能,如建立连接、断开连接、音频的启动/停止等。 最后,需要考虑语音通信的稳定性和效率。为了保证稳定性,需要进行系统测试和优化。通过合适的算法来降低网络延迟、提高音质、减少数据传输的次数等问题,来优化语音通信软件的效率。 综上所述,基于Java语言的语音通信软件设计需要考虑音频处理、网络通信、用户界面和系统稳定性等多个方面。通过合适的技术和算法,可以实现高效稳定的语音通信。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shejizuopin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值