Java.Demo:GUI+Thread+NetWork

带界面的网络聊天Demo

基于目前学习的多线程、网络编程、图形用户界面编程的知识,编写一个简易的Demo,旨在复习和巩固学过的知识,温故而知新。

实现效果:

Thread部分 + Network部分

通过编写发送和接收两个线程来实现程序的同时接收和发送功能,多线程实现方式采用实现Runnable接口。
线程中通过DatagramSocket类和DatagramPacket类来实现网络上的发送和接收。

Send类

package xyz.lilmoon.threads;

import java.net.*;

public class Send implements Runnable{
    static DatagramSocket datagramSocket;

    @Override
    public void run() {
        try {
            datagramSocket = new DatagramSocket();
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    // 提供发送方法,方便Button的事件监听器进行调用
    public void send(String msg, String toIP, int toPort) {
        // 获取输入
        byte[] buffer = msg.getBytes();
        try {
            DatagramPacket datagramPacket = new DatagramPacket(buffer, 0, buffer.length, InetAddress.getByName(toIP), toPort);
            datagramSocket.send(datagramPacket);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 发送"bye"后断开连接
        if (msg.equals("bye")) datagramSocket.close();
    }
}

Receive类

package xyz.lilmoon.threads;

import xyz.lilmoon.gui.Gui;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class Receive implements Runnable{
    Gui client;
    int openPort;
    DatagramSocket datagramSocket;

    public Receive(Gui client, int openPort) {
        this.client = client;
        this.openPort = openPort;
        try {
            // 打开端口
            datagramSocket = new DatagramSocket(openPort);
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        byte[] buffer = new byte[1024];
        DatagramPacket datagramPacket = new DatagramPacket(buffer, 0, buffer.length);
        String msg;
        do {
            try {
                datagramSocket.receive(datagramPacket);
            } catch (IOException e) {
                e.printStackTrace();
            }
            msg = new String(datagramPacket.getData(), 0, datagramPacket.getLength());
            client.receiveMsg.setText("to " + client.getTitle() + ":" + msg);
        } while (!msg.equals("bye"));

        // 发送"bye"后断开连接
        datagramSocket.close();
    }
}

GUI部分

通过AWT类和Swing类来构建程序界面及各类事件监听器。

package xyz.lilmoon.gui;

import xyz.lilmoon.threads.Receive;
import xyz.lilmoon.threads.Send;

import javax.swing.*;
import java.awt.*;

public class Gui extends JFrame {
    int openPort;
    Send sender;
    // 将接收信息的文本框设为public,方便Receive类对其进行修改
    public TextArea receiveMsg;

    // 构造器
    public Gui(int openPort){
        sender = new Send();
        new Thread(sender).start();

        this.openPort = openPort;
		// 通过this将frame传入Receive类中,使其能够修改其中的文本框信息
        new Thread(new Receive(this, openPort)).start();
        System.out.println("Construct");
    }

    // 初始化方法
    public void init() {
        // 设置窗口属性
        setTitle("Client_" + openPort / 1111);
        setBounds(200, 200, 250, 500);
        setLayout(new FlowLayout());

        // 创建控件
        TextField toIP = new TextField("localhost", 12);
        TextField toPort = new TextField(4);
        TextArea input = new TextArea(10, 26);
        Button send = new Button("Send");
        receiveMsg = new TextArea(10, 26);

        // 添加事件监听器
        send.addActionListener(e -> {
            String msg = input.getText();
            String IP = toIP.getText();
            int port = Integer.parseInt(toPort.getText());

            // 发送信息
            sender.send(msg, IP, port);
        });

        // 添加控件
        add(new Label("toIP:"));
        add(toIP);
        add(new Label("toPort:"));
        add(toPort);
        add(new Label("Input:"));
        add(input);
        add(send);
        add(receiveMsg);
        setVisible(true);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }
}

调用类

遵循编程规范,将可复用的代码都抽取出来,main方法中只负责启动程序。
创建两个类调用Gui类来创建窗体,启动程序。
Client_1:

public class Client_1 extends JFrame {
    // main方法
    public static void main(String[] args) {
        new Gui(1111).init();
    }
}

Client_2:

public class Client_2 extends JFrame {
    // main方法
    public static void main(String[] args) {
        new Gui(2222).init();
    }
}

可优化的地方(因为懒而没做的部分)

  1. 在Receive类中传入发送消息的frame参数,用于获取消息来源的用户名
  2. 设置消息队列,依次输出消息
  3. 设置发送消息后清空输入框
tag(s): Java 
show comments · back · home