一分钟使用Java实现socket消息传递

news2024/11/19 19:21:55

一、目的

       本程序旨在模拟航空器与塔台之间的实时消息传递,展示其在实际航空通讯中的应用。通过使用 Java 的 JFrame 进行图形用户界面(GUI)的设计,以及 socket 编程实现网络通信,该程序能够提供一个直观的界面来显示航空器和塔台之间的信息交换。

二、技术介绍

2.1 JFrame

       JFrame 是 Java 提供的一个顶级容器类,用于创建图形用户界面(GUI)。它是 javax.swing 包的一部分。

2.2 Socket

       Socket 是网络编程中的一个重要概念,代表了网络上的两个节点之间的双向通信端点。Java 提供了 java.net 包来支持 Socket 编程。

2.3 MySQL

       MySQL 是一个开源的关系型数据库管理系统(RDBMS),广泛应用于各种应用程序,从小型项目到大型企业系统。

三、具体实现

3.1 创建数据库

数据库名为 air_traffic

CREATE DATABASE air_traffic;

数据库表明为 messages 。

CREATE TABLE `messages` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) COLLATE utf8_bin DEFAULT NULL,
  `message` varchar(255) COLLATE utf8_bin NOT NULL,
  `timestamp` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
)

3.2 创建数据库工具类

public class DataBaseUtil {
        // 数据库地址
        private static final String jdbcURL = "jdbc:mysql://localhost:3306/air_traffic";
        // 数据库用户名
        private static final String dbUser = "root";
        // 数据库密码
        private static final String dbPassword = "root123";

        // 保存数据方法
        public static void saveToDatabase(String name, String message) {
                try (Connection connection = DriverManager.getConnection(jdbcURL, dbUser, dbPassword)) {
                        String sql = "INSERT INTO messages (name, message) VALUES (?,?)";
                        PreparedStatement statement = connection.prepareStatement(sql);
                        statement.setString(1, name);
                        statement.setString(2, message);
                        statement.executeUpdate();
                } catch (SQLException e) {
                        e.printStackTrace();
                }
        }
}

3.3 塔台服务

public class ServerHandler extends Thread {
    private BufferedReader in; // 用于读取服务器发送的消息
    private JTextArea displayArea; // 用于在 GUI 中显示消息的文本区域

    // 构造函数,用于初始化 BufferedReader 和 JTextArea
    public ServerHandler(BufferedReader in, JTextArea displayArea) {
        this.in = in;
        this.displayArea = displayArea;
    }

    // 运行方法,负责读取来自服务器的消息并显示在文本区域中
    @Override
    public void run() {
        try {
            String inputLine; // 用于存储每次读取到的消息
            while ((inputLine = in.readLine()) != null) {
                // 显示消息到 JTextArea 中
                displayMessage("塔台: " + inputLine);
                // 将消息保存到数据库
                DataBaseUtil.saveToDatabase("塔台", inputLine);
            }
        } catch (IOException e) {
            e.printStackTrace(); // 捕获并打印 IO 异常
        }
    }

    // 显示消息到 JTextArea 的方法,使用 SwingUtilities.invokeLater 确保线程安全
    private void displayMessage(String message) {
        SwingUtilities.invokeLater(() -> displayArea.append(message + "\n"));
    }
}

3.4 航空器服务

public class ClientHandler extends Thread {
    private Socket clientSocket; // 客户端 socket,用于与服务器通信
    private JTextArea displayArea; // 用于在 GUI 中显示消息的文本区域
    private BufferedReader in; // 用于读取来自客户端的输入流

    // 构造函数,用于初始化 Socket 和 JTextArea
    public ClientHandler(Socket socket, JTextArea displayArea) {
        this.clientSocket = socket;
        this.displayArea = displayArea;
    }

    // 运行方法,负责处理客户端的消息
    @Override
    public void run() {
        try {
            // 初始化 BufferedReader,从客户端的输入流读取数据
            in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            String inputLine; // 用于存储每次读取到的消息
            while ((inputLine = in.readLine()) != null) {
                // 显示消息到 JTextArea 中
                displayMessage("航空器: " + inputLine);
                // 将消息保存到数据库
                DataBaseUtil.saveToDatabase("航空器", inputLine);
            }
        } catch (IOException e) {
            e.printStackTrace(); // 捕获并打印 IO 异常
        } finally {
            // 关闭资源
            try {
                if (in != null) in.close();
                if (clientSocket != null && !clientSocket.isClosed()) clientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    // 显示消息到 JTextArea 的方法,使用 SwingUtilities.invokeLater 确保线程安全
    private void displayMessage(String message) {
        SwingUtilities.invokeLater(() -> displayArea.append(message + "\n"));
    }
}

3.5 塔台窗口

public class AirTrafficControlServer extends JFrame{
    private JTextArea displayArea; // 用于显示消息的文本区域
    private JTextField inputField; // 用于输入消息的文本字段
    private PrintWriter out; // 用于向客户端发送消息的输出流

    // 构造函数,初始化 UI 组件和服务器
    public AirTrafficControlServer() {
        setTitle("塔台"); // 设置窗口标题
        setSize(500, 400); // 设置窗口大小
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置关闭操作

        // 设置窗口背景色为黑色
        UIManager.put("Panel.background", Color.BLACK);
        UIManager.put("TextField.background", Color.BLACK);
        UIManager.put("TextArea.background", Color.BLACK);

        // 设置文本颜色为绿色
        UIManager.put("TextArea.foreground", Color.GREEN);
        UIManager.put("TextField.foreground", Color.GREEN);

        displayArea = new JTextArea(); // 创建用于显示消息的文本区域
        displayArea.setEditable(false); // 文本区域设置为不可编辑
        JScrollPane scrollPane = new JScrollPane(displayArea); // 为文本区域添加滚动条

        inputField = new JTextField(); // 创建用于输入消息的文本字段
        inputField.addActionListener(e -> sendMessage(inputField.getText())); // 为文本字段添加动作监听器

        add(scrollPane, BorderLayout.CENTER); // 将滚动面板添加到窗口中间
        add(inputField, BorderLayout.SOUTH); // 将文本字段添加到窗口底部

        setVisible(true); // 设置窗口可见
        startServer(); // 启动服务器
    }

    // 启动服务器的方法
    private void startServer() {
        Thread serverThread = new Thread(() -> {
            try (ServerSocket serverSocket = new ServerSocket(12345)) { // 创建服务器 socket,监听端口 12345
                while (true) {
                    Socket clientSocket = serverSocket.accept(); // 接受客户端连接
                    out = new PrintWriter(clientSocket.getOutputStream(), true); // 初始化输出流,用于向客户端发送消息
                    new ClientHandler(clientSocket, displayArea).start(); // 创建并启动新的客户端处理线程
                }
            } catch (IOException e) {
                e.printStackTrace(); // 捕获并打印 IO 异常
            }
        });
        serverThread.start(); // 启动服务器线程
    }

    // 发送消息的方法
    private void sendMessage(String message) {
        if (out != null) {
            out.println(message); // 通过输出流向客户端发送消息
            displayMessage("塔台: " + message); // 在文本区域显示发送的消息
            DataBaseUtil.saveToDatabase("塔台", message); // 将消息保存到数据库
        }
        inputField.setText(""); // 清空输入字段
    }

    // 显示消息到文本区域的方法,使用 SwingUtilities.invokeLater 确保线程安全
    private void displayMessage(String message) {
        SwingUtilities.invokeLater(() -> displayArea.append(message + "\n"));
    }
}

3.6 航空器窗口

public class AircraftClient extends JFrame{
    private JTextArea displayArea; // 用于显示消息的文本区域
    private JTextField inputField; // 用于输入消息的文本字段
    private PrintWriter out; // 用于向服务器发送消息的输出流
    private BufferedReader in; // 用于从服务器接收消息的输入流

    // 构造函数,初始化 UI 组件和客户端
    public AircraftClient() {
        setTitle("航空器"); // 设置窗口标题
        setSize(500, 400); // 设置窗口大小
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置关闭操作

        // 设置窗口背景色为黑色
        UIManager.put("Panel.background", Color.BLACK);
        UIManager.put("TextField.background", Color.BLACK);
        UIManager.put("TextArea.background", Color.BLACK);

        // 设置文本颜色为绿色
        UIManager.put("TextArea.foreground", Color.GREEN);
        UIManager.put("TextField.foreground", Color.GREEN);

        displayArea = new JTextArea(); // 创建用于显示消息的文本区域
        displayArea.setEditable(false); // 文本区域设置为不可编辑
        JScrollPane scrollPane = new JScrollPane(displayArea); // 为文本区域添加滚动条

        inputField = new JTextField(); // 创建用于输入消息的文本字段
        inputField.addActionListener(e -> sendMessage(inputField.getText())); // 为文本字段添加动作监听器

        add(scrollPane, BorderLayout.CENTER); // 将滚动面板添加到窗口中间
        add(inputField, BorderLayout.SOUTH); // 将文本字段添加到窗口底部

        setVisible(true); // 设置窗口可见
        startClient(); // 启动客户端
    }

    // 启动客户端的方法
    private void startClient() {
        try {
            Socket socket = new Socket("localhost", 12345); // 创建客户端 socket,连接到服务器的地址和端口
            out = new PrintWriter(socket.getOutputStream(), true); // 初始化输出流,用于向服务器发送消息
            in = new BufferedReader(new InputStreamReader(socket.getInputStream())); // 初始化输入流,用于从服务器接收消息
            new ServerHandler(in, displayArea).start(); // 创建并启动新的服务器处理线程
        } catch (IOException e) {
            e.printStackTrace(); // 捕获并打印 IO 异常
        }
    }

    // 发送消息的方法
    private void sendMessage(String message) {
        if (message.equals("7500")) {
            message = "飞机遭遇劫机或者飞机面临劫机危险的紧急情况!!!";
        } else if (message.equals("7600")) {
            message = "通讯故障,通讯失效或者无线电失联状况";
        } else if (message.equals("7700")) {
            message = "飞机出现了紧急情况,包括飞机发生机械故障或有机上人员突发疾病,但并不代表飞机处于危险状况";
        }

        if (out != null) {
            out.println(message); // 通过输出流向服务器发送消息
            displayMessage("航空器: " + message); // 在文本区域显示发送的消息
            DataBaseUtil.saveToDatabase("航空器", message); // 将消息保存到数据库
        }
        inputField.setText(""); // 清空输入字段
    }

    // 显示消息到文本区域的方法,使用 SwingUtilities.invokeLater 确保线程安全
    private void displayMessage(String message) {
        SwingUtilities.invokeLater(() -> displayArea.append(message + "\n"));
    }
}

3.7 塔台登录窗口


public class ServerLoginWindow extends JFrame {
    private JTextField usernameField; // 用于输入用户名的文本字段
    private JPasswordField passwordField; // 用于输入密码的密码字段

    // 构造函数,初始化登录窗口UI组件
    public ServerLoginWindow() {
        setTitle("塔台登录"); // 设置窗口标题
        setSize(300, 200); // 设置窗口大小
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置关闭操作
        setLayout(new GridLayout(3, 2)); // 使用网格布局,3行2列

        JLabel usernameLabel = new JLabel("用户名:"); // 创建用户名标签
        usernameField = new JTextField(); // 创建用于输入用户名的文本字段

        JLabel passwordLabel = new JLabel("密码:"); // 创建密码标签
        passwordField = new JPasswordField(); // 创建用于输入密码的密码字段

        JButton loginButton = new JButton("登录"); // 创建登录按钮
        loginButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String username = usernameField.getText(); // 获取输入的用户名
                String password = new String(passwordField.getPassword()); // 获取输入的密码
                if (username.equals("admin") && password.equals("123")) { // 验证用户名和密码
                    setVisible(false); // 隐藏登录窗口
                    new AirTrafficControlServer().setVisible(true); // 显示空中交通管制服务器窗口
                } else {
                    JOptionPane.showMessageDialog(ServerLoginWindow.this, "登录失败", "错误", JOptionPane.ERROR_MESSAGE);
                    // 显示登录失败的消息框
                }
            }
        });

        add(usernameLabel); // 将用户名标签添加到窗口
        add(usernameField); // 将用户名文本字段添加到窗口
        add(passwordLabel); // 将密码标签添加到窗口
        add(passwordField); // 将密码密码字段添加到窗口
        add(new JLabel());  // 添加一个占位标签,用于对齐布局
        add(loginButton); // 将登录按钮添加到窗口

        setLocationRelativeTo(null);  // 将窗口显示在屏幕中央
        setVisible(true); // 设置窗口可见
    }

    // 主方法,创建并运行服务器登录窗口实例
    public static void main(String[] args) {
        SwingUtilities.invokeLater(ServerLoginWindow::new); // 在事件调度线程中创建并显示登录窗口
    }
}

3.8 航空器登录窗口

public class ClientLoginWindow extends JFrame {
    private JTextField usernameField; // 用于输入用户名的文本字段
    private JPasswordField passwordField; // 用于输入密码的密码字段

    // 构造函数,初始化登录窗口UI组件
    public ClientLoginWindow() {
        setTitle("航空器登录"); // 设置窗口标题
        setSize(300, 200); // 设置窗口大小
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置关闭操作
        setLayout(new GridLayout(3, 2)); // 使用网格布局,3行2列

        JLabel usernameLabel = new JLabel("用户名:"); // 创建用户名标签
        usernameField = new JTextField(); // 创建用于输入用户名的文本字段

        JLabel passwordLabel = new JLabel("密码:"); // 创建密码标签
        passwordField = new JPasswordField(); // 创建用于输入密码的密码字段

        JButton loginButton = new JButton("登录"); // 创建登录按钮
        loginButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String username = usernameField.getText(); // 获取输入的用户名
                String password = new String(passwordField.getPassword()); // 获取输入的密码
                if (username.equals("001") && password.equals("123")) { // 验证用户名和密码
                    setVisible(false); // 隐藏登录窗口
                    new AircraftClient().setVisible(true); // 显示航空器客户端窗口
                } else {
                    JOptionPane.showMessageDialog(ClientLoginWindow.this, "登录失败", "错误", JOptionPane.ERROR_MESSAGE);
                    // 显示登录失败的消息框
                }
            }
        });

        add(usernameLabel); // 将用户名标签添加到窗口
        add(usernameField); // 将用户名文本字段添加到窗口
        add(passwordLabel); // 将密码标签添加到窗口
        add(passwordField); // 将密码密码字段添加到窗口
        add(new JLabel());  // 添加一个占位标签,用于对齐布局
        add(loginButton); // 将登录按钮添加到窗口

        setLocationRelativeTo(null);  // 将窗口显示在屏幕中央
        setVisible(true); // 设置窗口可见
    }

    // 主方法,创建并运行客户端登录窗口实例
    public static void main(String[] args) {
        SwingUtilities.invokeLater(ClientLoginWindow::new); // 在事件调度线程中创建并显示登录窗口
    }
}

四、运行展示

登录页面

消息传递

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1811504.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java(蓝桥杯)一维二维数组应用

介绍: 一维数组,用来熟悉代码,主要考察二维数组: 二维数组存储行、列数据,遍历,输出结果 二维数组的旋转 二维数组数据的找规律。等等 二维数组问题,不难,但是比较繁琐。需要细…

java程序在运行过程各个内部结构的作用

一:内部结构 一个进程对应一个jvm实例,一个运行时数据区,又包含多个线程,这些线程共享了方法区和堆,每个线程包含了程序计数器、本地方法栈和虚拟机栈接下来我们通过一个示意图介绍一下这个空间。 如图所示,当一个hell…

在Linux中进行Redis的yum安装与配置

redis安装在IP为x.x.x.x的服务器上 redis是使用内存作为主存,使用硬盘来实现数据持久化,而且redis是周期性的将数据写到硬盘上。这就意味着一旦服务器出现断电、重启之类的情况,很可能会出现数据丢失的情况,因此不建议使用redis来…

直播美颜插件详解:美颜SDK技术探讨

今天,小编将深入讲解美颜插件与美颜SDK技术,对其原理和应用进行详细解析。 一、美颜SDK技术简介 美颜SDK专门用于实现美颜功能。其核心技术包括图像处理算法和人工智能技术。美颜SDK可以实时捕捉摄像头画面,识别人脸,然后对人脸…

【附带源码】机械臂MoveIt2极简教程(三)、URDF/SRDF介绍

系列文章目录 【附带源码】机械臂MoveIt2极简教程(一)、moveit2安装 【附带源码】机械臂MoveIt2极简教程(二)、move_group交互 【附带源码】机械臂MoveIt2极简教程(三)、URDF/SRDF介绍 目录 系列文章目录1. URDF1.1 URDF相关资源1.2 小技巧1.2.1 关节名称1.2.2 安全限制…

云计算-期末复习题-选择/判断/填空/简答(1)

目录 填空题/简答题 单选题 多选题 判断题 云计算期末复习部分练习题,下一章会补全。祝大家好好复习,顺利通过课程。 填空题/简答题 >保障云基本安全的对策包括()、()和() &…

[AI Omost] 革命性AI图像合成技术,让你的创意几乎一触即发!

介绍 Omost 是一个创新的项目,它利用大型语言模型(LLM)的编码能力来生成图像,特别是通过图像合成技术。项目的名称“Omost”发音类似于“almost”,寓意着使用该项目后,用户的图像生成工作几乎可以完成。同…

超详解——python函数定义和调用——小白篇

目录 1. 函数的参数 位置参数: 关键字参数: 默认参数: 2. 关键字参数 3. 参数组 4. 函数重载 5. 函数的返回值 单个返回值: 多个返回值: 6. 函数的内置属性 __name__: __doc__: 7…

重温react-02

shopdemo import React, { Component } from react export default class shopDemo extends Component {state {goods: [{id: 1,name: 商品1,price: 100,number: 0,money: 0},{id: 2,name: 商品2,price: 200,number: 0,money: 0},{id: 3,name: 商品3,price: 300,number: 0,mo…

如何解决 Git 默认不区分文件名大小写和同名文件共存?

修改文件命名的大小写,不会有 git 记录 本文章的例子:将 demo.vue 文件命名改为 Demo.vue 1、在Git项目路径下执行该命令 git config core.ignorecase false (1)以上方法可以实现 git 区分 demo.vue 与 Demo.vue 文件&#xff0…

【投稿优惠|权威主办】2024年物联网、土木建筑与城市工程国际学术会议(ICITCEUE 2024)

2024年物联网、土木建筑与城市工程国际学术会议(ICITCEUE 2024) 2024 International Academic Conference on the Internet of Things, Civil Engineering and Urban Engineering(ICITCEUE 2024) ▶会议简介 2024年物联网、土木建…

【解读】核密度图

def:what 核密度估计(Kernel Density Estimation,简称KDE)是一种用来估计随机变量概率密度函数的非参数方法 实现:(库函数)how import seaborn as sns import matplotlib.pyplot as plt# 使用…

开源完全自动化的桌上足球机器人Foosbar;自动编写和修复代码的AI小工具;开源工具,可本地运行,作为Perplexity AI的替代方案

✨ 1: Foosbar Foosbar是一款完全自动化的桌上足球机器人,能与人类玩家对战,具备防守、传球和射门能力。 Foosbar是一个完全自动化的桌上足球机器人,它实现了一侧由机器人控制,另一侧由人类玩家对战的游戏模式。这个机器人能够自…

2024下半年软考高级没有高项,考哪门最容易上岸?

2024上半年软考考试已经结束,有不少小伙伴已经开始准备下半年软考了,但是大家要注意:今年高项仅考上半年一次,下半年考的高级科目只有这四个(系分、架构、网规、系规)! 一、那么这种情况下&…

go语音进阶 多任务

多任务 什么叫 多任务?简单说:就像是操作系统可以同时执行 多个任务。打个比方 你一边使用 浏览器上网,一遍在听MP3, 一边再用 word 赶作业。对于电脑来讲这就是多任务,还有很多任务悄悄的在后台同时运行着,只是桌面上…

npm 添加 electron 安装镜像变量,提交打包速度。

前言:项目中使用 electron-builder,打包运行 npm run build:win 时, electron-builder 默认会从 github 下载 electron 依赖包,导致打包缓慢。可以通过添加 electron 下载镜像地址来解决。 npm config ls -l 查看 npm 所有配置 …

SmartEDA:革新教育电路,点亮学生创新之光!

在当今快速发展的科技时代,电路设计与电子技术的融合已成为教育领域不可或缺的一部分。而SmartEDA作为一款新兴的电路设计工具,正以其独特的优势助力学生创新实践,引领教育界迈向更加智能化的未来。 SmartEDA不仅具备传统电路设计软件的基本…

Redis(十六) 集群

文章目录 前言什么是集群集群模式基本原理哈希求余一致性哈希算法哈希槽分区算法 docker模拟出一个集群集群中节点挂了会怎么办故障判定故障迁移 集群扩容 前言 前面我们学习了 redis 哨兵机制,哨兵机制是为了解决当主节点挂了之后,能够自动进行故障转移…

JVM对象分配和垃圾回收机制

一、对象创建 1.1 符号引用 new 创建一个对象,需要在JVM创建对象。 符号引用:目标对象采用一个符号表示,类A加载的时候,如果成员变量类B还没有被加载进来,采用一个符号(字面量)来表示&#x…

Linux C语言:指针的运算

一、指针的算术运算 1、指针运算 指针运算是以指针所存放的地址作为运算量而进行的指针运算的实质就是地址的计算 2、指针的算数运算 指针加上整数,指针减去整数, 指针递增,指针递减和两个指针相减。 指针加减一个n的运算: px n px - n 移动步长…