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

一、目的

       本程序旨在模拟航空器与塔台之间的实时消息传递,展示其在实际航空通讯中的应用。通过使用 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); // 在事件调度线程中创建并显示登录窗口
    }
}

四、运行展示

登录页面

消息传递

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 实现Socket连接池可以使用Apache Commons Pool库中的GenericObjectPool类。首先需要实现一个工厂类,它将创建和销毁Socket连接。然后,使用GenericObjectPool类来创建一个连接池并维护连接。具体步骤如下: 1. 创建一个工厂类,实现org.apache.commons.pool2.PooledObjectFactory接口,并重写create()、destroy()、validate()等方法。 2. 创建一个GenericObjectPool实例,并将刚才创建的工厂类作为参数传入。 3. 使用borrowObject()方法从连接池中获取一个连接,使用returnObject()方法将连接返回给池。 4. 在使用完后,调用close()方法销毁连接池。 代码示例: ```java //工厂类 public class SocketFactory implements PooledObjectFactory<Socket> { private String host; private int port; public SocketFactory(String host, int port) { this.host = host; this.port = port; } @Override public PooledObject<Socket> makeObject() throws Exception { Socket socket = new Socket(host, port); return new DefaultPooledObject<>(socket); } @Override public void destroyObject(PooledObject<Socket> p) throws Exception { p.getObject().close(); } @Override public boolean validateObject(PooledObject<Socket> p) { return p.getObject().isConnected(); } //... } //使用示例 String host = "localhost"; int port = 8080; GenericObjectPool<Socket> pool = new GenericObjectPool<>(new SocketFactory(host, port)); pool.setMaxTotal(10); Socket socket = pool.borrowObject(); //使用socket pool.returnObject(socket); pool.close(); ``` 上面是一个简单的示例,在实际使用中可能需要进行更多设置,例如设置连接池 ### 回答2: 使用Java实现socket连接池可以通过以下步骤: 首先,我们需要创建一个Socket连接池的类,该类需要包含连接池的相关属性和方法。在连接池中,我们可以维护一个空闲连接和一个正在使用的连接的列表。这些连接可以是Socket对象或者Socket连接的封装对象。 其次,我们需要实现连接池的初始化方法。在初始化方法中,我们可以创建一定数量的连接并将它们添加到空闲连接列表中。 接下来,我们需要实现一个从连接池中获取连接的方法。在该方法中,我们可以从空闲连接列表中获取一个可用的连接,并将其移动到正在使用的连接列表中。 然后,我们需要实现一个释放连接的方法。在该方法中,我们将使用完的连接从正在使用的连接列表中移除,并将其返回给空闲连接列表。 此外,为了防止连接过多,我们还可以实现一个定时器任务,定期检查连接池中的连接数,并根据需要创建或释放连接。 最后,当我们不再使用连接池时,我们需要关闭连接池。在关闭连接池时,我们需要关闭所有的连接,并清空空闲连接和正在使用的连接列表。 通过以上步骤,我们可以实现一个简单的socket连接池。使用连接池可以提高系统性能,减少了创建和关闭连接的开销,并且可以重复利用连接,提高了系统的稳定性和可靠性。 ### 回答3: 使用Java实现socket连接池的主要目的是为了提高socket连接的复用性和效率。下面是一个简单的示例实现: ```java import java.io.IOException; import java.net.Socket; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; public class SocketConnectionPool { private static final int MAX_POOL_SIZE = 10; // 连接池的最大容量 private static final String HOST = "localhost"; // 连接的主机 private static final int PORT = 8080; // 连接的端口 private BlockingQueue<Socket> connectionPool; // 存放socket连接的连接池 public SocketConnectionPool() { connectionPool = new ArrayBlockingQueue<>(MAX_POOL_SIZE); initializePool(); } private void initializePool() { for (int i = 0; i < MAX_POOL_SIZE; i++) { try { Socket socket = new Socket(HOST, PORT); connectionPool.add(socket); } catch (IOException e) { e.printStackTrace(); } } } public Socket getConnection() throws InterruptedException { return connectionPool.take(); // 从连接池中取出一个socket连接 } public void releaseConnection(Socket socket) { connectionPool.add(socket); // 将socket连接放回连接池中 } } ``` 上述代码中,我们使用了一个`BlockingQueue`来实现连接池,最大容量为`MAX_POOL_SIZE`。在初始化连接池时,我们通过循环创建了一定数量的socket连接并将其添加到连接池中。通过`getConnection()`方法可以获取一个空闲的连接,如果连接池中没有可用连接,则会阻塞等待。使用完连接后,可以通过`releaseConnection(Socket socket)`方法将连接放回连接池以便复用。 当需要使用socket连接时,可以按照下面的方式进行调用: ```java SocketConnectionPool connectionPool = new SocketConnectionPool(); Socket socket = null; try { socket = connectionPool.getConnection(); // 使用socket连接进行业务操作 } catch (InterruptedException e) { e.printStackTrace(); } finally { if (socket != null) { connectionPool.releaseConnection(socket); } } ``` 通过这种方式,我们可以有效地重复使用已有的socket连接,减少创建和销毁连接的开销,提高应用程序的性能和效率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序是我的命

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

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

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

打赏作者

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

抵扣说明:

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

余额充值