基于Java RMI的网络聊天室

  基于Java RMI的局域网聊天室,可根据IP地址进行连接登录并通讯。

 

 

 

 

 

  • chat包

    ChatServer类

package chat;

import java.rmi.RemoteException;

// 聊天服务器接口
public interface ChatServer extends java.rmi.Remote {
    /**
     * 注册新的聊天用户
     */
    public void login(String name, Chatter chatter) throws RemoteException;
    
    /**
     * 用户退出
     */
    public void logout(String name) throws RemoteException;
    
    /**
     * 用户调用此函数将消息发给所有用户
     */
    public void chat(String name, String message) throws RemoteException;
}

    Chatter类

package chat;

import java.rmi.RemoteException;

public interface Chatter extends java.rmi.Remote {
    /**
     * 通知用户加入
     */
    public void receiveEnter(String name, Chatter chatter, boolean hasEntered) throws RemoteException;
    
    /**
     * 通知用户离开聊天室
     */
    public void receiveExit(String name) throws RemoteException;
    
    /**
     * 用户发言
     */
    public void receiveChat(String name, String message) throws RemoteException;
    
    /**
     * 私聊
     */
    public void receiveWhisper(String name, String message) throws RemoteException;
    
    /**
     * 通知服务器停止
     */
    public void serverStop() throws RemoteException;
}
  • chatclient包

    ChatClient类

package chatclient;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.rmi.RemoteException;
import java.util.Hashtable;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.text.JTextComponent;
import javax.swing.text.Keymap;

import chat.ChatServer;
import chat.Chatter;

public class ChatClient extends JFrame{
    // 存储已有聊天用户,key:用户名,value:对应的Chatter对象
    Hashtable hash = new Hashtable();
    // 自己的用户名
    String my_name = "chatter";
    // 服务器地址
    String serverAddr;
    // 代表客户端到远程对象
    Chatter chatter;
    // 服务器端到远程对象
    ChatServer server;
    JTextArea displayBox;
    JTextArea inputBox;
    JComboBox usersBox;
    JButton sendButton;
    JLabel statusLabel;
    ConnectionAction connectAction = new ConnectionAction();
    // 让用户输入用户名和服务器地址到对话框
    ConnectDlg dlg = new ConnectDlg(this);
    
    public static void main(String[] args) {
        new ChatClient();
    }
    
    public ChatClient() {
        super("聊天-客户端");
        layoutComponent();
        setupMenu();
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                exit();
            }
        });
        show();
    }
    
    private void setupMenu() {
        JMenuBar menuBar = new JMenuBar();
        JMenuItem conn = new JMenuItem(connectAction);
        JMenuItem exit = new JMenuItem("退出");
        exit.addActionListener(new AbstractAction() {
            
            @Override
            public void actionPerformed(ActionEvent arg0) {
                exit();
            }
        });
        JMenu file = new JMenu("连接");
        file.add(conn);
        menuBar.add(file);
        setJMenuBar(menuBar);
    }
    
    private void exit() {
        destroy();
        System.exit(0);
    }
    
    // 编辑页面控件元素
    public void layoutComponent() {
        setSize(400, 400);
        JPanel contentPane = new JPanel();
        GridBagLayout gridbag = new GridBagLayout();
        GridBagConstraints c = new GridBagConstraints();
        contentPane.setLayout(gridbag);
        c.fill = GridBagConstraints.BOTH;
        c.gridwidth = GridBagConstraints.REMAINDER;
        c.gridheight = 6;
        c.weightx = 100;
        c.weighty = 100;
        c.insets.top = 5;
        displayBox = new JTextArea();
        displayBox.setLineWrap(true);
        displayBox.setEditable(false);
        displayBox.setMargin(new Insets(5, 5, 5, 5));
        JScrollPane scrollPane = new JScrollPane(displayBox);
        contentPane.add(scrollPane, c);
        c.gridheight = 1;
        c.weightx = 0;
        c.weighty = 0;
        c.insets.top = 10;
        JLabel msgLabel = new JLabel("消息:");
        contentPane.add(msgLabel, c);
        c.gridheight = 6;
        c.insets.top = 0;
        c.gridwidth = GridBagConstraints.RELATIVE;
        c.weightx = 100;
        inputBox = new JTextArea();
        addKeymapBindings();
        inputBox.setLineWrap(true);
        inputBox.setWrapStyleWord(true);
        JScrollPane inputScrollPane = new JScrollPane(inputBox);
        inputScrollPane.setPreferredSize(new Dimension(250, 50));
        inputScrollPane.setMinimumSize(new Dimension(250, 50));
        contentPane.add(inputScrollPane, c);
        c.weightx = 0;
        c.gridwidth = GridBagConstraints.REMAINDER;
        sendButton = new JButton(new ImageIcon(getClass().getResource("../images/send.gif")));
        sendButton.setToolTipText("发送消息");
        sendButton.setPreferredSize(new Dimension(50, 50));
        sendButton.setMinimumSize(new Dimension(50, 50));
        sendButton.addActionListener(new ActionListener() {
            
            @Override
            public void actionPerformed(ActionEvent arg0) {
                sendMessage();
            }
        });
        contentPane.add(sendButton, c);
        
        c.weightx = 0;
        c.fill = GridBagConstraints.HORIZONTAL;
        c.gridheight = 1;
        JLabel sendtoLabel = new JLabel("发送给:");
        contentPane.add(sendtoLabel, c);
        usersBox = new JComboBox();
        usersBox.setBackground(Color.WHITE);
        usersBox.addItem("所有用户");
        contentPane.add(usersBox, c);
        JPanel statusPane = new JPanel(new GridLayout(1, 1));
        statusLabel = new JLabel("未连接");
        statusPane.add(statusLabel);
        contentPane.add(statusPane, c);
        setContentPane(contentPane);
        try {
            chatter = new ChatterImpl(this);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
    }
    
    public void destroy() {
        try {
            disconnect();
        } catch (java.rmi.RemoteException ex) {
            ex.printStackTrace();
        }
    }
    
    public void connect() throws java.rmi.RemoteException, java.net.MalformedURLException, java.rmi.NotBoundException{
        server = (ChatServer) java.rmi.Naming.lookup("//" + serverAddr + "/ChatServer");
        server.login(my_name, chatter);
    }
    
    protected void disconnect() throws java.rmi.RemoteException {
        if (server != null)
            server.logout(my_name);
    }
    
    public void receiveEnter(String name, Chatter chatter, boolean hasEntered) {
        if (name != null && chatter != null) {
            hash.put(name, chatter);
            if (!name.equals(my_name)) {
                // 对新加入聊天的用户,在displayBox给出提示
                if (!hasEntered)
                    display(name + " 进入聊天室");
                usersBox.addItem(name);
            }
        }
    }
    
    public void receiveExit(String name) {
        if (name != null && chatter != null)
            hash.remove(name);
        for (int i = 0; i < usersBox.getItemCount(); i++) {
            if (name.equals((String) usersBox.getItemAt(i))) {
                usersBox.remove(i);
                break;
            }
        }
        display(name + " 离开聊天室");
    }
    
    public void receiveChat(String name, String message) {
        display(name + ": " + message);
    }
    
    public void receiveWhisper(String name, String message) {
        display(name + " 私聊: " + message);
    }
    
    // 绑定键盘事件
    protected void addKeymapBindings() {
        Keymap keymap = JTextComponent.addKeymap("MyBindings", inputBox.getKeymap());
        Action action = null;
        KeyStroke key = null;
        // 用户在消息框中按回车发送消息
        action = new AbstractAction() {
            
            @Override
            public void actionPerformed(ActionEvent arg0) {
                sendMessage();
            }
        };
        key = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
        keymap.addActionForKeyStroke(key, action);
        
        // Ctrl+Enter实现换行
        action = new AbstractAction() {
            
            @Override
            public void actionPerformed(ActionEvent arg0) {
                inputBox.append("\n");
            }
        };
        key = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, Event.CTRL_MASK);
        keymap.addActionForKeyStroke(key, action);
        
        inputBox.setKeymap(keymap);
    }
    
    private void display(String s) {
        if (!s.endsWith("\n")) {
            displayBox.append(s + "\n");
        } else {
            displayBox.append(s);
        }
        
        int length = displayBox.getText().length() - 1;
        displayBox.select(length, length);
    }
    
    private void sendMessage() {
        String message = inputBox.getText();
        if (message != null && message.length() > 0) {
            inputBox.setText(null);
            inputBox.setCaretPosition(0);
            display(my_name + ":" + message);
            if (server != null) {
                if ("所有用户".equals(usersBox.getSelectedItem())) {// 发送给所有用户
                    try {
                        server.chat(my_name, message);
                    } catch (java.rmi.RemoteException ex) {
                        ex.printStackTrace();
                    }
                } else {// 私聊,发给所选用户
                    String destUserName = (String) usersBox.getSelectedItem();
                    Chatter destChatter = (Chatter) hash.get(destUserName);
                    try {
                        destChatter.receiveWhisper(my_name, message);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        inputBox.requestFocus();
    }
    
    public void serverStop() {
        display("服务器停止");
        server = null;
        hash.clear();
        connectAction.setEnabled(true);
    }
    
    class ConnectionAction extends AbstractAction {
        public ConnectionAction() {
            super("连接");
            putValue(Action.SHORT_DESCRIPTION, "连接到服务器");
            putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control C"));
        }
        
        public void actionPerformed(ActionEvent evt) {
            dlg.pack();
            dlg.setLocationRelativeTo(ChatClient.this);
            dlg.setVisible(true);
            if (dlg.getValue() == JOptionPane.OK_OPTION) {
                try {
                    my_name = dlg.getUserName();
                    serverAddr = dlg.getServerAddr();
                    connect();
                    inputBox.setEditable(true);
                    displayBox.setText("");
                    statusLabel.setText(my_name + " 已连接");
                    this.setEnabled(false);
                } catch (Exception e) {
                    e.printStackTrace();
                    statusLabel.setText("不能连接到服务器");
                    return;
                }
            }
        }
    }
}

    ChatterImpl类

package chatclient;

import java.rmi.RemoteException;

import chat.*;

// 客户端聊天接口
public class ChatterImpl extends java.rmi.server.UnicastRemoteObject implements Chatter {
    ChatClient client;
    
    public ChatterImpl(ChatClient client) throws java.rmi.RemoteException {
        this.client = client;
    }
    
    public void receiveEnter(String name, Chatter chatter, boolean hasEntered) throws java.rmi.RemoteException {
        client.receiveEnter(name, chatter, hasEntered);
    }
    
    public void receiveExit(String name) throws java.rmi.RemoteException {
        client.receiveExit(name);
    }
    
    public void receiveChat(String name, String message) throws java.rmi.RemoteException {
        client.receiveChat(name, message);
    }
    
    public void receiveWhisper(String name, String message) throws java.rmi.RemoteException {
        client.receiveWhisper(name, message);
    }
    
    public void serverStop() throws RemoteException {
        client.serverStop();
    }
}

    ConnectDlg类

package chatclient;

import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.IllegalFormatCodePointException;

import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JTextField;

// 连接对话
public class ConnectDlg extends JDialog implements PropertyChangeListener {
    JTextField serverAddrField;
    JTextField userNameField;
    JOptionPane optionPane;
    String serverAddr, userName;
    int value = -1;
    
    public ConnectDlg(Frame frame) {
        super(frame, "连接", true);
        serverAddrField = new JTextField(15);
        userNameField = new JTextField(20);
        Object[] array = { "服务器地址:", serverAddrField, "用户名:", userNameField };
        optionPane = new JOptionPane(array, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
        setContentPane(optionPane);
        
        optionPane.addPropertyChangeListener(this);
        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent we) {
                setVisible(false);
            }
        });
    }
    
    public void propertyChange(PropertyChangeEvent e) {
        String prop = e.getPropertyName();
        if (isVisible() && (e.getSource() == optionPane) && (JOptionPane.VALUE_PROPERTY.equals(prop) ||
                JOptionPane.INPUT_VALUE_PROPERTY.equals(prop))) {
            if (optionPane.getValue() == JOptionPane.UNINITIALIZED_VALUE) {
                return;
            }
            value = ((Integer) optionPane.getValue()).intValue();
            optionPane.setValue(JOptionPane.UNINITIALIZED_VALUE);
            if (value == JOptionPane.OK_OPTION) {
                if ("".equals(serverAddrField.getText()) || "".equals(userNameField.getText())) {
                    JOptionPane.showMessageDialog(this, "输入用户名或服务器地址");
                    if ("".equals(serverAddrField.getText()))
                        serverAddrField.requestFocus();
                    else if ("".equals(userNameField.getText()))
                        userNameField.requestFocus();
                } else {
                    setServerAddr(serverAddrField.getText());
                    setUserName(userNameField.getText());
                    setVisible(false);
                }
            } else {
                setVisible(false);
            }
        }
    }

    public String getServerAddr() {
        return serverAddr;
    }

    public void setServerAddr(String serverAddr) {
        this.serverAddr = serverAddr;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public int getValue() {
        return value;
    }
    
}
  • chatserver包

    ChatServerEvent类

package chatserver;

import java.util.EventObject;

// 服务器事件
public class ChatServerEvent extends EventObject {
    String message;
    
    public ChatServerEvent(Object src, String message) {
        super(src);
        setMessage(message);
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

}

    ChatServerImpl类

package chatserver;

import java.net.MalformedURLException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import chat.ChatServer;
import chat.Chatter;

// 服务器接口
public class ChatServerImpl extends java.rmi.server.UnicastRemoteObject implements ChatServer {
    static ChatServerImpl server = null;
    private final static String BINDNAME = "ChatServer";
    private final static String[] STATEMSG = new String[] { "服务器启动", "服务器停止" };
    List chatters = new ArrayList();
    List listeners = new ArrayList();
    
    protected ChatServerImpl() throws java.rmi.RemoteException {
        
    }
    
    public static ChatServerImpl getInstance() {
        try {
            if (server == null) {
                server = new ChatServerImpl();
            }
        } catch (RemoteException e) {
            e.printStackTrace();
            return null;
        }
        return server;
    }
    
    public void start() throws RemoteException, MalformedURLException {
        java.rmi.Naming.rebind(BINDNAME, server);
        notifyListener(STATEMSG[0]);
    }
    
    public void stop() throws RemoteException, NotBoundException, MalformedURLException {
        notifyListener(STATEMSG[1]);
        Iterator itr = chatters.iterator();
        while (itr.hasNext()) {
            UserInfo u = (UserInfo) itr.next();
            u.getChatter().serverStop();
        }
        java.rmi.Naming.unbind(BINDNAME);
    }
    
    public void addListener(ChatServerListener listener) {
        listeners.add(listener);
    }
    
    public void removeListense(ChatServerListener listener) {
        listeners.remove(listener);
    }
    
    void notifyListener(String msg) {
        Iterator itr = listeners.iterator();
        ChatServerEvent evt = new ChatServerEvent(this, msg);
        while (itr.hasNext()) {
            ((ChatServerListener) itr.next()).serverEvent(evt);
        }
    }
    
    public void login(String name, Chatter c) throws java.rmi.RemoteException {
        if (c != null && name != null) {
            UserInfo u = new UserInfo(name, c);
            notifyListener(u.getName() + " 进入聊天室");
            Iterator itr = chatters.iterator();
            while (itr.hasNext()) {
                UserInfo u2 = (UserInfo) itr.next();
                u2.getChatter().receiveEnter(name, c, false);
                c.receiveEnter(u2.getName(), u2.getChatter(), true);
            }
            chatters.add(u);
        }
    }
    
    public void logout(String name) throws java.rmi.RemoteException {
        if (name == null) {
            System.out.println("null name on logout: cannot remove chatter");
            return;
        }
        UserInfo u_gone = null;
        Iterator itr = null;
        
        synchronized (chatters) {
            for (int i = 0; i < chatters.size(); i++) {
                UserInfo u = (UserInfo) chatters.get(i);
                if (u.getName().equals(name)) {
                    notifyListener(name + " 离开聊天室");
                    u_gone = u;
                    chatters.remove(i);
                    itr = chatters.iterator();
                    break;
                }
            }
        }
        
        if (u_gone == null || itr == null) {
            System.out.println("no user by name of " + name + " found: not removing chatter");
            return;
        }
        
        while (itr.hasNext()) {
            UserInfo u = (UserInfo) itr.next();
            u.getChatter().receiveExit(name);
        }
    }
    
    public void chat(String name, String message) throws java.rmi.RemoteException {
        Iterator itr = chatters.iterator();
        
        while (itr.hasNext()) {
            UserInfo u = (UserInfo) itr.next();
            if (!name.equals(u.getName()))
                u.getChatter().receiveChat(name, message);
        }
        notifyListener(name + ":" + message);
    }
}

    ChatServerListener类

package chatserver;

import java.util.EventListener;

// 服务器监听接口
public interface ChatServerListener extends EventListener {
    public void serverEvent(ChatServerEvent evt);
}

    Main类

package chatserver;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.security.KeyStore;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;

// 服务器主函数
public class Main extends JFrame implements ChatServerListener {
    ChatServerImpl server = ChatServerImpl.getInstance();
    JTextArea textArea;
    JMenuBar menuBar;
    JToolBar toolBar;
    StarServerAction startAction = new StarServerAction();
    StopServerAction stopAction = new StopServerAction();
    
    public static void main(String[] args) {
        Main main = new Main();
        main.show();
    }
    
    public Main() {
        super("聊天-服务器");
        try {
            LocateRegistry.createRegistry(1099);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        setSize(300, 500);
        layoutComponents();
        String s1=this.getClass().getResource("").getPath();
        System.out.println(s1);
    }
    
    private void layoutComponents() {
        setupMenu();
        setupToolBar();
        textArea = new JTextArea();
        textArea.setSize(200, 300);
        textArea.setEditable(false);
        JScrollPane scrollPane = new JScrollPane(textArea);
        getContentPane().add(scrollPane, BorderLayout.CENTER);
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
    }
    
    private void setupMenu() {
        menuBar = new JMenuBar();
        JMenuItem startServer = new JMenuItem(startAction);
        JMenuItem stopServer = new JMenuItem(stopAction);
        JMenuItem exit = new JMenuItem("离开");
        exit.addActionListener(new AbstractAction() {
            
            @Override
            public void actionPerformed(ActionEvent evt) {
                exit();
            }
        });
        JMenu server = new JMenu("服务器");
        server.add(startServer);
        server.add(stopServer);
        server.add(exit);
        menuBar.add(server);
        setJMenuBar(menuBar);
    }
    
    private void setupToolBar() {
        toolBar = new JToolBar();
        JButton button = null;
        addTool(toolBar, startAction);
        addTool(toolBar, stopAction);
        
        getContentPane().add(toolBar, BorderLayout.NORTH);
    }
    
    JButton addTool(JToolBar toolBar, AbstractAction action) {
        JButton b = new JButton();
        b.setAction(action);
        b.setText(null);
        toolBar.add(b);
        return b;
    }
    
    private void exit() {
        try {
            server.stop();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.exit(0);
    }
    
    public void serverEvent(ChatServerEvent evt) {
        textArea.append(evt.getMessage() + "\n");
    }
    
    class StarServerAction extends AbstractAction {
        public StarServerAction() {
            super("启动");
            putValue(Action.SMALL_ICON, new ImageIcon(getClass().getResource("../images/start.gif")));
            putValue(Action.SHORT_DESCRIPTION, "启动聊天服务器");
            putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control A"));
        }
        
        public void actionPerformed(ActionEvent evt) {
            try {
                server.addListener(Main.this);
                textArea.setText("");
                server.start();
                stopAction.setEnabled(true);
                this.setEnabled(false);
            } catch (Exception ex) {
                textArea.append("服务器启动错误\n");
                server.removeListense(Main.this);
                ex.printStackTrace();
                return;
            }
        }
    }
    
    class StopServerAction extends AbstractAction {
        public StopServerAction() {
            super("停止");
            putValue(Action.SMALL_ICON, new ImageIcon(getClass().getResource("../images/stop.gif")));
            putValue(Action.SHORT_DESCRIPTION, "停止聊天服务器");
            putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control O"));
            this.setEnabled(false);
        }
        
        public void actionPerformed(ActionEvent arg0) {
            try {
                server.stop();
                server.removeListense(Main.this);
                startAction.setEnabled(true);
                this.setEnabled(false);
            } catch (Exception e) {
                textArea.append("服务器停止错误\n");
                e.printStackTrace();
                return;
            }
        }
    }
}

    UserInfo类

package chatserver;

import chat.Chatter;

// 用户信息类
public class UserInfo {
    // 用户名
    private String name;
    // 远程客户端对象
    private Chatter chatter;
    
    public UserInfo(String name, Chatter chatter) {
        setName(name);
        setChatter(chatter);
    }

    public String getName() {
        return name;
    }

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

    public Chatter getChatter() {
        return chatter;
    }

    public void setChatter(Chatter chatter) {
        this.chatter = chatter;
    }

}
  • images包

 

posted @ 2021-07-07 09:00  熊猫耳朵  阅读(325)  评论(0编辑  收藏  举报