基于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包