我为聊天服务器,客户端和消息系统创建了接口和实现。我编译了所有的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]);
}
}
答案 0 :(得分:0)
start java ChatClientImpl
调试方法是删除start
部分。当你这样做时,你会看到一个关于没有主要方法的例外。你开始错了。主要方法是JChatClientImpl.
答案 1 :(得分:-1)
终于意识到我做错了什么。启动rmi注册表并启动服务器后,我尝试从命令行运行以下命令:
start java JChatClient rmi://localhost/chat User1
聊天应用现在正在运行。感谢您的所有建议。