·网络聊天室(NetChatRoom)1
·作品预览
客户端
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; } }