日期:2014-05-20  浏览次数:21241 次

局域网聊天工具
下午在公司闲的蛋疼,花了2个小时整了个局域网的聊天工具玩。界面模仿飞鸽传书,基于UDP协议,只有最基本的聊天功能。代码结构也比较乱,有兴趣的可以拿去玩玩。
Java code

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Hashtable;
import java.util.Map;
import java.util.UUID;

import javax.swing.JFrame;

import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JButton;
import javax.swing.border.EtchedBorder;
import javax.swing.table.DefaultTableModel;

import java.awt.SystemColor;
import javax.swing.ScrollPaneConstants;
import java.awt.Color;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JLabel;

public class Msg extends JFrame {
    private static final long serialVersionUID = 3170335398335303363L;
    private String uname = "qq";

    private DatagramSocket server;// UDP
    private InetAddress broadcastAddress;// 廣播地址
    private int port;// 監聽端口

    private final Map<String, UserInfo> userTable = new Hashtable<String, UserInfo>();// 用戶列表
    private final String guid = UUID.randomUUID().toString().replace("-", "").toUpperCase();// 本機唯一標識

    private JTable table;
    private JTextArea textArea;
    private JLabel lblNewLabel_1;

    public Msg() throws SocketException {
        this(5413);
    }

    public Msg(int port) throws SocketException {
        this.port = port;

        getContentPane().setLayout(null);
        getContentPane().setBackground(SystemColor.control);

        JButton btnNewButton = new JButton("刷新");
        btnNewButton.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                refresh();
            }
        });
        btnNewButton.setBackground(SystemColor.control);
        btnNewButton.setBounds(299, 119, 83, 28);
        getContentPane().add(btnNewButton);

        JScrollPane scrollPane = new JScrollPane();
        scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
        scrollPane.setBounds(12, 12, 275, 135);
        getContentPane().add(scrollPane);

        table = new JTable(new DefaultTableModel(new Object[][] { { null, null, null }, { null, null, null }, { null, null, null }, { null, null, null }, { null, null, null }, { null, null, null }, { null, null, null }, { null, null, null }, { null, null, null }, }, new String[] { "\u7528\u6236\u540D", "IP", "GUID" }) {
            private static final long serialVersionUID = -6259343924352571655L;

            public boolean isCellEditable(int row, int column) {
                return false;
            }
        });
        table.setBackground(new Color(255, 255, 255));
        scrollPane.setViewportView(table);
        table.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null));

        JScrollPane scrollPane_1 = new JScrollPane();
        scrollPane_1.setBounds(12, 159, 370, 104);
        getContentPane().add(scrollPane_1);

        textArea = new JTextArea();
        textArea.setLineWrap(true);
        textArea.addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent e) {
                if ((e.isControlDown() && e.getKeyCode() == KeyEvent.VK_ENTER) || (e.isAltDown() && e.getKeyCode() == KeyEvent.VK_S)) {
                    sendMessage();
                }
            }
        });
        scrollPane_1.setViewportView(textArea);

        JButton button = new JButton("發送");
        button.setBackground(SystemColor.control);
        button.setBounds(141, 275, 83, 28);
        button.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                sendMessage();
            }
        });
        getContentPane().add(button);

        JButton button_1 = new JButton("關閉");
        button_1.setBackground(SystemColor.control);
        button_1.setBounds(22, 275, 83, 28);
        button_1.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                Msg.this.dispatchEvent(new WindowEvent(Msg.this, WindowEvent.WINDOW_CLOSING));
            }
        });
        getContentPane().add(button_1);

        JLabel lblNewLabel = new JLabel("當前在線人數");
        lblNewLabel.setBounds(299, 29, 83, 18);
        getContentPane().add(lblNewLabel);

        lblNewLabel_1 = new JLabel("0");
        lblNewLabel_1.setBounds(305, 59, 55, 18);
        getContentPane().add(lblNewLabel_1);
        
        JLabel lblNewLabel_2 = new JLabel("Alt+S或Ctrl+Enter發送");
        lblNewLabel_2.setBounds(242, 280, 140, 18);
        getContentPane().add(lblNewLabel_2);

        this.setSize(400, 348);
        setBackground(SystemColor.control);
        setResizable(false);
        setTitle("局域網聊天");
        this.setLocationRelativeTo(null);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                try {
                    sendBroadcast((guid + "2").getBytes(), broadcastAddress);
                } catch (Exception e2) {
                }
            }
        });

        try {

            uname = JOptionPane.showInputDialog("請輸入你的暱稱");
            uname = uname == null ? "匿名" : uname;
            server = new DatagramSocket(port);

            broadcastAddress = InetAddress.getByName("255.255.255.255");

            new AcceptBroadcastThread().start();// 啟動監聽
            sendBroadcast((guid + "0" + uname).getBytes(), broadcastAddress);// 發送全網廣播,通知上線
        } catch (Exception e) {
            JOptionPane.showMessageDialog(this, "端口" + port + "被佔用!");
            System.exit(0);
        }
    }

    /**
     * 向外發送數據
     * 
     * @param data
     * @param address
     * @throws IOException
     */
    public void sendBroadcast(byte[] data, InetAddress address) throws IOException {
        DatagramPacket dp = new DatagramPacket(data, data.length, address, port);
        server.send(dp);
    }

    /**
     * 刷新列表
     */
    public void refresh() {
        // 清空列表
        userTable.clear();
        DefaultTableModel tableModel = ((DefaultTableModel) table.getModel());
        for (int j = table.getRowCount(); j > 0; j--) {
            tableModel.removeRow(j - 1);
        }
        try {
            sendBroadcast((guid + "3" + uname).getBytes(), broadcastAddress);// 重新發送全網廣播,通知上線
        } catch (Exception e) {
        }
    }

    public void alertMessage(final String msg) {
        new Thread() {
            public void run() {
                JOptionPane.showMessageDialog(Msg.this, msg);
            };
        }.start();
    }

    /**
     * 發送文字消息
     */
    public void sendMessage() {
        int[] selectRows = table.getSelectedRows();
        DefaultTableModel tableModel = (DefaultTableModel) table.getModel();
        String msg = guid + "1" + textArea.getText();
        byte[] data = string2Bytes(msg);
        for (int i = 0, j = selectRows.length; i < j; i++) {
            String key = (String) tableModel.getValueAt(selectRows[i], 2);
            UserInfo ui = key == null ? null : userTable.get(key);
            if (ui != null) {
                try {
                    textArea.setText("");
                    sendBroadcast(data, ui.getIpAddress());
                } catch (Exception e) {
                    JOptionPane.showMessageDialog(this, "發送消息到" + ui.getName() + "(" + ui.getIpAddress().getHostAddress() + ")失敗!", "發送失敗", JOptionPane.ERROR_MESSAGE);
                }
            }
        }
    }

    private byte[] string2Bytes(String str) {
        try {
            return str.getBytes("UTF-8");
        } catch (Exception e) {
        }
        return null;
    }

    class UserInfo {

        private String guid;
        private String name;
        private InetAddress ipAddress;

        public String getGuid() {
            return guid;
        }

        public void setGuid(String guid) {
            this.guid = guid;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public InetAddress getIpAddress() {
            return ipAddress;
        }

        public void setIpAddress(InetAddress ipAddress) {
            this.ipAddress = ipAddress;
        }
    }

    class AcceptBroadcastThread extends Thread {
        public void run() {
            while (true) {
                try {
                    DatagramPacket dp = new DatagramPacket(new byte[1024], 1024);
                    server.receive(dp);

                    String msg = new String(dp.getData(), 0, dp.getLength(), "UTF-8");// 解析廣播數據包
                    String uid = msg.substring(0, 32);
                    String type = msg.substring(32, 33);

                    if (type.equals("0") || type.equals("3")) {
                        UserInfo ui = new UserInfo();
                        ui.setGuid(uid);
                        ui.setName(msg.substring(33));
                        ui.setIpAddress(dp.getAddress());

                        // 刷新請求,只響應不做其他處理
                        if (userTable.containsKey(ui.getGuid()) && type.equals("3")) {
                            sendBroadcast((guid + "0" + uname).getBytes(), broadcastAddress);
                        }

                        // 上線通知
                        if (!userTable.containsKey(ui.getGuid())) {
                            userTable.put(ui.getGuid(), ui);// 將新用戶添加到用戶表
                            lblNewLabel_1.setText("" + userTable.size());

                            sendBroadcast((guid + "0" + uname).getBytes(), broadcastAddress);// 響應非本機廣播
                            ((DefaultTableModel) table.getModel()).insertRow(0, new String[] { ui.getName(), ui.getIpAddress().getHostAddress(), ui.getGuid() });
                        }
                    } else if (type.equals("1")) {
                        UserInfo ui = userTable.get(uid);
                        alertMessage(ui.getName() + "(" + ui.getIpAddress().getHostAddress() + ")-" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "\r\n" + msg.substring(33));
                    } else {
                        userTable.remove(uid);// 移除用戶
                        lblNewLabel_1.setText("" + userTable.size());

                        DefaultTableModel tableModel = ((DefaultTableModel) table.getModel());
                        for (int i = 0, j = table.getRowCount(); i < j; i++) {
                            if (tableModel.getValueAt(i, 2).equals(uid)) {
                                tableModel.removeRow(i);
                            }
                        }
                    }
                } catch (Exception e) {
                }
            }
        }
    }

    public static void main(String[] args) throws Exception {
        Msg msg = new Msg();
        msg.setVisible(true);
    }
}