有人可以告诉我这个RMI聊天应用程序为什么不能正常工作的地方,目的是通过远程对象或序列化对象实现客户端,服务器和逻辑之间的分离。
import javax.swing.*; import java.awt.event.*; import java.rmi.*; import java.rmi.server.*; public class ChatClient1 implements ICallback { JFrame frame = new JFrame("Chat Client"); private JTextArea myText; private static JTextArea TAUinDispMsg; private JScrollPane myTextScroll; private JScrollPane TAUinDispMsgScroll; private String textString = ""; private boolean firstMessage = true; private static String name = null; private static final int HOR_SIZE = 400; private static final int VER_SIZE = 150; protected static ServerServices chatServer; MessageImpl remomsg ; public ChatClient1() throws RemoteException { super(); try { this.chatServer = (ServerServices) Naming.lookup("rmi://localhost" + "/ChatServer"); UnicastRemoteObject.exportObject(this); chatServer.register(this); } catch (Exception e) { System.err.println("RemoteException: " + e.getMessage()); System.exit(0); } ; java.awt.EventQueue.invokeLater(new Runnable() { public void run() { initComponents(); } }); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { try { if (name != null) { // chatServer.leave(displayChat, name); } } catch (Exception ex) { TAUinDispMsg.append("Exit failed."); } System.exit(0); } }); } private void initComponents() { myText = new JTextArea(); myTextScroll = new JScrollPane(myText); myTextScroll .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); myTextScroll .setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); myTextScroll.setMaximumSize(new java.awt.Dimension(HOR_SIZE, VER_SIZE)); myTextScroll.setMinimumSize(new java.awt.Dimension(HOR_SIZE, VER_SIZE)); myTextScroll .setPreferredSize(new java.awt.Dimension(HOR_SIZE, VER_SIZE)); myText.addKeyListener(new java.awt.event.KeyAdapter() { public void keyTyped(java.awt.event.KeyEvent evt) { textTyped(evt); } }); frame.getContentPane().add(myTextScroll, java.awt.BorderLayout.NORTH); TAUinDispMsg = new JTextArea(); TAUinDispMsgScroll = new JScrollPane(TAUinDispMsg); TAUinDispMsg.setBackground(new java.awt.Color(200, 200, 200)); TAUinDispMsgScroll .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); TAUinDispMsgScroll .setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); TAUinDispMsgScroll.setMaximumSize(new java.awt.Dimension(HOR_SIZE, VER_SIZE)); TAUinDispMsgScroll.setMinimumSize(new java.awt.Dimension(HOR_SIZE, VER_SIZE)); TAUinDispMsgScroll.setPreferredSize(new java.awt.Dimension(HOR_SIZE, VER_SIZE)); TAUinDispMsg.setEditable(false); frame.getContentPane().add(TAUinDispMsgScroll, java.awt.BorderLayout.CENTER); frame.pack(); frame.setVisible(true); } private void textTyped(java.awt.event.KeyEvent evt) { try { remomsg = new MessageImpl(); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } char c = evt.getKeyChar(); if (c == '\n') { try { if (firstMessage) { name = textString; // .join(name); firstMessage = false; } else { remomsg.sendMessage(name, textString); } } catch (RemoteException ie) { TAUinDispMsg.append("Failed to send message."); System.err.println(ie.getMessage()); } textString = ""; } else { textString = textString + c; } } @Override public void updateClients(final String msg) throws RemoteException { // TODO Auto-generated method stub System.out.println("Recived Message: " + msg); SwingUtilities.invokeLater(new Runnable() { public void run() { TAUinDispMsg.append(name + " says: " + msg + "\n"); } }); } public static void main(String args[]) throws RemoteException { ChatClient1 ch = null; try { ch = new ChatClient1(); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } **ICallback.java: interface for callbacks from server to client** import java.rmi.*; public interface ICallback extends Remote { void updateClients(String msg) throws RemoteException; }
我想用作业务逻辑(游戏逻辑)的对象。
import java.rmi.*; public interface Message extends Remote { public void sendMessage(String name, String message) throws RemoteException; public void updateClients() throws RemoteException; }
其实现:
import java.io.Serializable; import java.rmi.*; import java.rmi.server.*; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class MessageImpl extends UnicastRemoteObject implements Message { private String name; private String message; public MessageImpl() throws RemoteException { super(); } public MessageImpl(ArrayList clients2) throws RemoteException { // TODO Auto-generated constructor stub this.clients = clients2; } static ServerServicesImpl si; // static ArrayListDemo az; private List<ICallback> clients = new ArrayList<ICallback>(); // private List<ICallback> clients; private ServerEngine serverEngine = new ServerEngine(); // Notice this one not called remotely public void setName(String name) throws RemoteException { this.name = name; } public String getName() { return name; } public void setServerList(ServerEngine serverList2) throws RemoteException { this.serverEngine = serverList2; } public void setClientList(List<ICallback> aclients) throws RemoteException { this.clients = (ArrayList<ICallback>) aclients; System.err.println("in setClientlist"); } public ServerEngine getServerList() { return serverEngine; } // Notice this one not called remotely public void setMessage(String message) throws RemoteException { this.message = message; } public String getMessage() { return message; } public void updateClients() throws RemoteException { si = new ServerServicesImpl(); ArrayList j = si.getClientNames(); System.out.println(j.size()); if (clients != null) { System.out.println(clients.size()); for (ICallback aClient : clients) { aClient.updateClients(message); } } else System.err.println("Clientlist is empty"); if (clients != null) { System.out.println(j.size()); } else System.err.println("Clientlist is empty"); } public void sendMessage(String name, String message1) throws RemoteException { setName(name); setMessage(message1); updateClients(); } }
管理客户端线程的类
import java.lang.*; import java.util.*; class ServerEngine { private Collection<ICallback> threadList = new ArrayList<ICallback>(); private int counter = 0; // Get the lock on threadList, and wait until the counter is zero - that //is, no reads are taking place. Then it's safe to add the thread. public synchronized void add(ICallback item) { try { while (counter > 0) { wait(); } threadList.add(item); } catch (InterruptedException e) { System.out.println("Addition interrupted."); } finally{ notifyAll(); } } // Similarly for removal. public synchronized void remove(ICallback item) { try { while (counter > 0) { wait(); } threadList.remove(item); } catch (InterruptedException e) { System.out.println("Removal interrupted."); } finally { notifyAll(); } } // Similarly for changing counter public synchronized void incCounter() { counter++; notifyAll(); } public synchronized void decCounter() { counter--; notifyAll(); } //This is because it would be too much effort to make this class implement //Collection, return it's own Iterator etc. etc...\ //Note it is *not* a bug that it isn't synchronized public Collection getCollection() { return threadList; } }
Service.java:服务器将要注册并绑定到的对象。
import java.rmi.*; import java.util.ArrayList; public interface ServerServices extends Remote { // added so client can register self with server for callbacks public void register(ICallback newClient) throws RemoteException; public ArrayList getClients() throws RemoteException; }
ServerServicesImpl.java:服务器的实现端
import java.io.Serializable; import java.rmi.*; import java.rmi.server.*; import java.util.*; class ServerServicesImpl extends UnicastRemoteObject implements ServerServices, Serializable { String message; MessageImpl msgimpl; static Vector data = new Vector(); private static ArrayList Aclients = new ArrayList<ICallback>(); private static ArrayList testlist; public ServerServicesImpl() throws RemoteException { super(); testlist = new ArrayList(); } public synchronized void register(ICallback newClient) throws RemoteException { data.addElement(newClient); Aclients.add(newClient); // //serverEngine.add(newClient); testlist.add("testing"); System.out.println("testlist size =" + testlist.size()); System.out.println(Aclients.size()); setClientList(Aclients); } ArrayList getClientNames() { // Aclients.add(ic); System.out.println("vector size =" + data.size()); System.out.println("testlist size =" + testlist.size()); System.out.println(" Aclientlist size =" + Aclients.size()); return Aclients; } public void setClientList(ArrayList aclients2) { this.Aclients = aclients2; } } // the server which will publish the above remote object import java.net.MalformedURLException; import java.rmi.*; public class ServiceLoader { public static void main(String[] args) { try { // Install a security manager System.setSecurityManager(new RMISecurityManager()); ServerServicesImpl obj = new ServerServicesImpl(); Naming.rebind("ChatServer", obj); System.out.println("ServiceLoader running."); } catch (MalformedURLException e) { System.err.println(e.getMessage()); } catch (RemoteException e) { System.err.println(e.getMessage()); } } }
正如 Ernest Friedman-Hill 在链接文本 http://www.coderanch.com/t/508960/java/java/pass- reference上 回答的那样:
问题的根源在于RMI不支持按引用传递,因此使消息类可序列化并在该可序列化类中创建ServerServices的远程实例可以使此应用程序正常工作
要么
在客户端类中创建Message类的远程实例并从RMI注册表中发布它也可以工作。
在此代码中,使用本地引用而不是远程引用,因此它从Serverservices类获取列表中的0元素。
再次感谢: 欧内斯特·弗里德曼·希尔 。