Java RMI - 启动聊天客户端

时间:2015-04-22 13:07:57

标签: java

我为聊天服务器,客户端和消息系统创建了接口和实现。我编译了所有的Java文件。然后我使用rmic编译我的实现类来创建存根。我已经启动了rmi注册表。我已经启动了服务器实现,它按预期运行;确认已向RMI注册并返回活动客户列表。 我现在陷入困境,因为当我尝试在命令

下运行客户端实现时
start java ChatClientImpl

一瞬间出现一个命令框并消失。我不知道让客户端运行。

有人会就如何启动和运行客户端提出任何建议吗?

以下是该应用程序的完整代码。

聊天客户端界面

   package p;
    /**
     * Defines the remote interface to be used by chat clients. A client
     * implementation must support all of the methods below. 
     */
    public interface ChatClient extends java.rmi.Remote {
        /**
         * Process a newly received message. 
         * 
         * @param inMessage
         * @throws java.rmi.RemoteException
         */
        public void processMessage(MessageImpl inMessage) throws    
        java.rmi.RemoteException;
        /**
         * Simple empty method which can be called by the server 
         * and other clients to verify that the client is still 
         * operating. 
         * 
         * @throws java.rmi.RemoteException
         */
        public void ping() throws java.rmi.RemoteException;
        /**
         * Returns the name of the client. 
         */
        public String getName() throws java.rmi.RemoteException;
    }

聊天客户端实施

package p;

import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;

/**
 * The client implements the ChatClient and the LocalClient interfaces.
 * 
 * A chat client is responsible for relaying on a message it has received from the 
 * user or from a different peer to all other peers that it knows about. 
 * 
 * The client should try to reduce the number of pointless relays of the message. 
 * Minimally if it has already received the message, it should not pass the message
 * on further. 
 */
public class ChatClientImpl extends UnicastRemoteObject implements ChatClient{

    private static final long serialVersionUID = 3056117484716596895L;

    private ChatServer listServer = null;

    private ArrayList<ChatClient> knownClients = null; 

    private String clientName = null; 

    private ArrayList<GUID> processedMessages = null; 

    private JChatClient gui = null; 

    public ChatClientImpl(String inServerName,                        
                          String inClientName) throws Exception{
        // set the client name
        this.clientName = inClientName; 

            // connect to chat server through RMI
        this.listServer = (ChatServer) Naming.lookup(inServerName);

        // create the empty list of processed messages
        this.processedMessages = new ArrayList<GUID>(); 

        // register with the ChatServer
        this.listServer.register(this);

        // request a set of clients.
        ChatClient[] newClients = this.listServer.getClients();

        // add the new set to our known set.
        this.knownClients = new ArrayList<ChatClient>(); 
        this.addClients(newClients);
    }

    protected void setGUI(JChatClient inGUI){
        this.gui = inGUI; 
    }

    /**
     * Add a list of clients to our known clients. 
     * 
     * @param inClients
     */
    private void addClients(ChatClient[] inClients){
        for(int i = 0; i < inClients.length; i++){
            if(!this.knownClients.contains(inClients[i])){
                this.knownClients.add(inClients[i]);
            }
            else{
                System.err.println("already know that peer - will not add.");
            }
        }
    }

    public void handleMessage(String inString){
        // create a message instance
        MessageImpl msg = new MessageImpl(this,inString);

        // broadcast to all clients
        Iterator<ChatClient> i = this.knownClients.iterator();
        while(i.hasNext()){
            ChatClient knownClient = i.next();
            try {
                knownClient.processMessage(msg);    
            } catch (RemoteException e) {
                // looks like the client isn't there any longer. 
                i.remove();
            }
        }       
    }

    /**
     * Process a newly received message. 
     * 
     * @param inMessage
     * @throws java.rmi.RemoteException
     */
    public void processMessage(MessageImpl inMessage) throws java.rmi.RemoteException{
        System.out.println("message received");

        // check the message to see if we have processed it before - discard if so
        if(this.processedMessages.contains(inMessage.id)) return;
        else{
            System.err.println(inMessage.id);
            this.processedMessages.add(inMessage.id);
            System.err.println("have now processed " + this.processedMessages.size());
        }

        // if we have not displayed it, then notify our viewer
        this.gui.updateDisplay(inMessage);;

        // get all new known clients from the message
        HashSet<ChatClient> recipients = inMessage.getRecipients();

        // add the names of any new recipients to known list
        for (ChatClient chatClient : recipients) {
            if(knownClients.contains(chatClient)){
                System.out.println("already know about that client");
            }
            else{
                System.out.println("discovered a new recipient");
                this.knownClients.add(chatClient);
            }
        }

        // pass the message on to any clients who have not already seen the message
        Iterator<ChatClient> iter = this.knownClients.iterator();
        while(iter.hasNext()){
            ChatClient chatClient = iter.next(); 
            if(recipients.contains(chatClient)){
                System.err.println("aready sent to that client");
            }
            else{
                System.out.println("sending to a new client");
                try {
                    chatClient.processMessage(inMessage);   
                } catch (RemoteException e) {
                    // looks like the client isn't there any longer. 
                    iter.remove();
                }
            }
        }
    }

    /**
     * Simple empty method which can be called by the server 
     * and other clients to verify that the client is still 
     * operating. 
     * 
     * @throws java.rmi.RemoteException
     */
    public void ping() throws java.rmi.RemoteException{
        /* doesn't have to do anything */
    }

    /**
     * Get the client name. 
     */
    public String getName() throws java.rmi.RemoteException{
        return this.clientName; 
    }
}

聊天服务器界面

package p;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface ChatServer extends Remote {

    /**
     * Register the ChatClient instance with the ChatServer. 
     * @param inClient
     */
    public abstract void register(ChatClient inClient) throws RemoteException;

    /**
     * Request a set of clients from the chat server. 
     */
    public abstract ChatClient[] getClients() throws RemoteException;
}

聊天服务器实施

package p;

import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * A chat server allows chat clients to locate other chat clients. 
 * The chat server is not used during message broadcast. It is 
 * only used when the chat clients initially connect to the chat session.
 * 
 * Communication with the server (as with all other modules) will be 
 * conducted with RMI rather than a custom TCP or UDP protocol. 
 * 
 * Each client only communicates with a small set of peers. The 
 * ChatServer is responsible for deciding upon the set of peers.   
 * 
public class ChatServerImpl extends UnicastRemoteObject implements ChatServer, Runnable{

    private static final long serialVersionUID = 1344922080333520010L;
    private static final int RETURN_SET_SIZE = 3;
    private static final int PING_SLEEP_DURATION = 60000;

    /**
     * Hashtable of all registered remote clients. 
     */
    private List<ChatClient> clients = null; 

    /**
     * Chat server is started once on a server. Students will have to 
     * start an instance themselves. If we were to have a number of 
     * students working together, one server would be started central.
     */
    public ChatServerImpl() throws RemoteException{
        this.clients = new ArrayList<ChatClient>(); 
    }

    /* (non-Javadoc)
     * @see p.ChatServer#register(p.ChatClient)
     */
    @Override
    public void register(ChatClient inClient) throws RemoteException{
        if(!this.clients.contains(inClient)) this.clients.add(inClient);
    }

    /* (non-Javadoc)
     * @see p.ChatServer#getClients()
     */
    @Override
    public synchronized ChatClient[] getClients() throws RemoteException{
        if(this.clients.size() > RETURN_SET_SIZE){
            List<ChatClient> clientSample = randomSample2(this.clients, RETURN_SET_SIZE);
            return (ChatClient[]) this.clients.toArray( new ChatClient[ this.clients.size() ] );
        }
        else return (ChatClient[]) this.clients.toArray( new ChatClient[ this.clients.size() ] );
    }

    /**
     * Generate the random subset. Based on an implementation of D. Knuth's 
     * algorithm. 
     * 
     * @param <T>
     * @param items
     * @param m
     * @return
     */
    private static <T> List<T> randomSample2(List<T> items, int m){
        Random rnd = new Random();
        for(int i=0;i<items.size();i++){
            int pos = i + rnd.nextInt(items.size() - i);
            T tmp = items.get(pos);
            items.set(pos, items.get(i));
            items.set(i, tmp);
        }
        return items.subList(0, m);
    }   

    /**
     * Run the server's main thread. The server will periodically 
     * iterate through all registered clients to find out if they 
     * are still alive. Any dead clients will be removed from the 
     * client list. 
     */
    public void run(){
        while(true){
            try {Thread.sleep(PING_SLEEP_DURATION);} catch (Exception e) {}
            System.out.println("Performing Update");
            // we don't want to lock the list, so we will make a copy of it
            // for the checking phase. 
            ArrayList<ChatClient> copy = new ArrayList<ChatClient>(this.clients);
            System.out.println("Checking " + copy.size() + " clients");
            for (ChatClient chatClient : copy) {
                try {
                    chatClient.ping();  
                } catch (RemoteException e) {
                    System.out.println("Removing a client.");
                    // client is no longer accessible. 
                    // We will remove the client from the main list
                    this.clients.remove(chatClient);
                }                
            }
        }
    }

    /**
     * Start the chat server. 
     * @param args
     */
    public static void main(String[] args) throws Exception{
        // create an instance of the chat server
        ChatServer server = new ChatServerImpl();

        // register the instance with the RMIRegistry
        Naming.rebind("rmi://localhost/chat", server);
        System.out.println("Registration with RMI complete.");

        // create the server thread and start it    
        Thread th = new Thread(((ChatServerImpl)server));
        th.start();
        System.out.println("Server thread now running.");
    }
}

消息接口

package p;

import java.util.HashSet;

public interface Message {

    /**
     * Add a chat recipient to the list of receivers
     * @param inClient
     */
    public abstract void addRecipient(ChatClient inClient);

    /**
     * Get the set of clients that have seen this message
     * @return
     */
    public abstract HashSet<ChatClient> getRecipients();

    /**
     * Get the message content.  
     * @return
     */
    public abstract String getContent();


    public abstract String getSource();
}

消息实施

package p;

import java.util.HashSet;

/**
 * Must have some unique identifier. Peer + number.  
 */
public class MessageImpl implements java.io.Serializable, Message {

    /**
     * Generated versionaID
     */
    private static final long serialVersionUID = 8914588083609635659L;

    /** 
     * The globally unique identifier for this message 
     */
    public final GUID id;

    /** 
     * All remote clients that this message has passed through,
     * including source 
     */
    public final HashSet<ChatClient> passedThrough = new HashSet<ChatClient>();

    /**
     * The content of the message. 
     */
    private String content = null;

    /**
     * The client who created the object
     */
    private ChatClient client = null; 

    /** 
     * Create a new Message instance. 
     */
    public MessageImpl(ChatClient s, String inContent) {
        this.id = new GUID(s);
        this.client = s; 
        this.passedThrough.add(s);
        this.content = inContent; 
    }

    /* (non-Javadoc)
     * @see p.MessageInterface#addRecipient(p.ChatClient)
     */
    @Override
    public void addRecipient(ChatClient inClient){
        this.passedThrough.add(inClient);
    }

    /* (non-Javadoc)
     * @see p.MessageInterface#getRecipients()
     */
    @Override
    public HashSet<ChatClient> getRecipients(){
        return this.passedThrough;
    }

    /* (non-Javadoc)
     * @see p.MessageInterface#getContent()
     */
    @Override
    public String getContent(){
        return this.content; 
    }

    /**
     * Get the name of the source of the message
     * @return
     */
    public String getSource(){
        try {
            return this.client.getName();   
        } catch (Exception e) {
            return "client uncreachable";
        }
    }
}

    /** 
     * Class used to generate globally unique identifiers for each message.
     * 
     * The GUID is based on the client plus an increment for each new
     * message instance. 
     */
    class GUID implements java.io.Serializable {
        /**
         * Generated versionaID
         */
        static final long serialVersionUID = 4928185858035458591L;

        /**
         * Reference to the client -- needed to generate a number. 
        */
        public final ChatClient source;

        /**
         * Next number that should be used - defined statically 
         */
        private static int nextID;

        /**
         * The message number to be used in the current GUID
         */
        private int id = 0;

        /**
         * Generate a GUID for use by a client. 
         * @param s
         */
        public GUID(ChatClient s) {
            this.source = s;
            synchronized(GUID.class) {
              this.id = nextID++;
            }
        }

          public final int hashCode() {
              return source.hashCode() + id;
          }

          public final boolean equals(Object that) {
              if (!(that instanceof GUID)) return false;
              GUID m2 = (GUID)that;
              return id == m2.id && source.equals(m2.source);
        }
   }

GUI

package p;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.rmi.RemoteException;

import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;

public class JChatClient extends JFrame implements java.awt.event.ActionListener{

    private ChatClientImpl model = null;

    /**
     * Field for users to enter text. 
     */
    protected JTextField inputField = null;

    /**
     * Text area where user and system text is printed out. 
     */
    private JTextPane historyPane = null;   

    /**
     * 
     * @param name
     * @param host
     * @param mname
     * @throws RemoteException
     */
    public JChatClient(ChatClient inClient, String inUserName) throws RemoteException {
        super("Chat: " + inUserName);
        this.model = (ChatClientImpl) inClient;
        this.model.setGUI(this);

        super.setLayout(new BorderLayout());

        // create the main output panel    
        this.historyPane = new JTextPane();
        this.historyPane.setAutoscrolls(true);
        this.historyPane.setEditable(false);         
        this.historyPane.setPreferredSize(new Dimension(100,100));

        // create a scrollpane to put the output panel in. This will then be added to the parent container.
        JScrollPane historyView = new JScrollPane(historyPane);        
        historyView.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        historyView.setAutoscrolls(true); 
        super.add(historyView, BorderLayout.CENTER);

        // create and add the input field
        this.inputField = new JTextField("", 20);
        this.inputField.addActionListener(this);
        JPanel bottom = new JPanel();
        bottom.setLayout(new BoxLayout(bottom,BoxLayout.X_AXIS));
        bottom.add(inputField);
        super.add(bottom, BorderLayout.SOUTH);          

        this.centerAndSetVisible();
    }

    /**
     * Updates the display with details of a new message. 
     * @param inMessage
     * @throws IOException
     */
    public void updateDisplay(MessageImpl inMessage) {
        this.printText("\n" + inMessage.getSource() + ": " + inMessage.getContent(),Color.BLACK);
    }

    /**
     * Called when the 'send' button has been hit. 
     * 
     * Must be extended to update all clients with information on the text that 
     * was entered by the user. 
     */
    public void actionPerformed(ActionEvent e) {
        String msg = inputField.getText();
        if (msg.length() > 0) {
            // Clear the chat input field
            inputField.setText("");

            // update (directly or indirectly) any peers of the message update.
            this.model.handleMessage(msg);
        }
    }

    /**
     * Utility method for doing the work of printing a message in a 
     * given colour to the output window. 
     * 
     * @param inText
     * @param inColor
     */
    private void printText(String inText, Color inColor){
        StyledDocument doc = historyPane.getStyledDocument();
        SimpleAttributeSet attr = new SimpleAttributeSet();
        StyleConstants.setForeground(attr, inColor);

        try {
            doc.insertString(doc.getLength(),inText,attr);      
            historyPane.setCaretPosition(doc.getLength()); 
        } catch (Exception ex) {
            ex.printStackTrace(); 
        }       
    }           

    /**
     * Put everything in the right place. 
     */
    private void centerAndSetVisible(){
        this.pack(); 
        this.setPreferredSize(new Dimension(300, 300));
        Dimension labelSize = this.getPreferredSize();
        Dimension sd = Toolkit.getDefaultToolkit().getScreenSize();
        int x = ( sd.width - labelSize.width )/2;
        int y = ( sd.height - labelSize.height )/2;
        this.setLocation(x, y);
        setSize(new Dimension(labelSize.width, labelSize.height));
        this.setVisible(true);
        this.setAlwaysOnTop(false); 
     }    

    public static void main(String[] args) throws Exception{
        // find address of chat name server
        ChatClientImpl client = new ChatClientImpl(args[0], args[1]);
        new JChatClient(client,args[1]);
    }       
}

2 个答案:

答案 0 :(得分:0)

start java ChatClientImpl

调试方法是删除start部分。当你这样做时,你会看到一个关于没有主要方法的例外。你开始错了。主要方法是JChatClientImpl.

答案 1 :(得分:-1)

终于意识到我做错了什么。启动rmi注册表并启动服务器后,我尝试从命令行运行以下命令:

start java JChatClient rmi://localhost/chat User1

聊天应用现在正在运行。感谢您的所有建议。