# Rmi, RMI basierter Client / Server Chat



## girl2005 (4. Januar 2006)

hallo ,

wer hat Erfahrung mit RMI und TIC TAC TOE. ich muss ein Programm schreiben, und ich weiss  nicht wie ich die Spielzüge zwischen den beiden Spieler  mit Rmi übertrage .


----------



## Thomas Darimont (5. Januar 2006)

*Re: Rmi*

Hallo!

 Schau doch mal hier:
http://www.tutorials.de/tutorials231847.html

 Gruß Tom


----------



## girl2005 (5. Januar 2006)

*Re: Rmi*

hallo,
danke für deine Antwort,
ich weiss das Ganze mit interfaces und implementierungen.
aber das Problem, ich weiss nicht wie ich das Spiel zwischen 2 Spieler programmieren kann.
wenn ein Spieler ein Zug in seinem Gui  spielt, wie programmiere ich das Ganze damit dieser Zug bei dem anderen in seinem Gui sichtbar ist?


----------



## Thomas Darimont (5. Januar 2006)

*Re: Rmi*

Hallo!

 Ich bau dazu morgen mal ein kleines Beispiel...

 Gruss Tom


----------



## girl2005 (5. Januar 2006)

*Re: Rmi*

danke, das wäre sehr sehr nett.++++++++++++


----------



## TommyMo (5. Januar 2006)

*Re: Rmi*

HI!

Du könntest das Ganze über den Server lösen. Jeder Mitspieler registriert sich am Server (Subject/Observer) und wird dann bei einem auftretenden Event (z.B. neuer Spielzug des Gegners) benachrichtig, und kann dann so seine GUI updaten. Die Kommunikation zwischen Client und Server wird im Normalfall über ein Model abgewickelt. So würde ich mal auf die Schnelle vorgehen    

Aber Tom wird das wahrscheinlich genauso oder ähnlich in seinem Beispiel anführen   

Gruß
TOM


----------



## girl2005 (5. Januar 2006)

*Re: Rmi*

hi i danke für deine Tipp.
Ich muss einen Programm für nur zwei Spieler schreiben, das heisst jeder spielt die Rolle eines Clients und eines Servers.


----------



## girl2005 (5. Januar 2006)

*Re: Rmi*

hi Tom,

jetzt habe ich vertsanden dass ich die *URLConnection * benutzen soll wenn ich etwas übertragen will. kannst du deinen Beispiel mit URLConnection schreiben. Ich muss nämlich auch einen Programm füe Chat schreiben.


----------



## TommyMo (5. Januar 2006)

*Re: Rmi*



			
				girl2005 hat gesagt.:
			
		

> Ich muss einen Programm für nur zwei Spieler schreiben, das heisst jeder spielt die Rolle eines Clients und eines Servers.



Wie meinst du das genau? Du brauchst doch eigentlich nur einen einzigen Server. 

Gruß
TOM


----------



## girl2005 (5. Januar 2006)

*Re: Rmi*

ja genau  einer spielt die Rolle des Servers und der andere den Client.


----------



## girl2005 (5. Januar 2006)

*Re: Rmi*

hi, ich habe einen Chat programm geschrieben , 
ich habe einen client und einen Server.
*der Client und der Server sollen miteinander chätten.*
ich weiss jetzt nicht wie ich es schaffe...dass wenn der Client in seinem JTextField was schreibt, es auf dem Gui des Servers taucht.
vielleicht hast du ja eine idee?


----------



## Thomas Darimont (5. Januar 2006)

*Re: Rmi*

Hallo!

  Okay hier nun mal wie gewünscht eine kleine RMI basierte Chat Anwendung:
 (Funktioniert so (wegen "Stubless" RMI nur unter Java 5)

  Unser ChatServer Interface:

```
/**
   * 
   */
  package de.tutorials;
  
  import java.rmi.Remote;
  import java.rmi.RemoteException;
  
  
  /**
   * @author Tom
   * 
   */
  public interface IChatServer extends Remote {
  	
  	final static String BIND_NAME = "ChatServer";
  	
  	void clientConnect(String clientName, IMessageCallback listener)
  			throws RemoteException;
  
  	void clientDisconnect(String clientName) throws RemoteException;
  
  	int getClientCount() throws RemoteException;
  
  	void publishMessage(String message) throws RemoteException;
  }
```
 
  Unsere ChatServer Implementierung:

```
/**
   * 
   */
  package de.tutorials;
  
  import java.rmi.RemoteException;
  import java.rmi.registry.LocateRegistry;
  import java.rmi.registry.Registry;
  import java.rmi.server.UnicastRemoteObject;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.Map;
  
  
  /**
   * @author Tom
   * 
   */
  public class ChatServerImpl extends UnicastRemoteObject implements IChatServer {
  
  	Map<String,IMessageCallback> clients;
  
  	protected ChatServerImpl() throws RemoteException {
  		super();
 		clients = Collections.synchronizedMap(new HashMap<String,IMessageCallback>());
  		System.out.println("Server started!");
  	}
  
  	public void clientDisconnect(String clientName) throws RemoteException {
  		clients.remove(clientName);
  		System.out.println(clientName + " disconnected!");
  	}
  
  	public int getClientCount() throws RemoteException {
  		return clients.size();
  	}
  
  	public void clientConnect(String clientName, IMessageCallback messageCallback)
  			throws RemoteException {
  		if (clients.containsKey(clientName)) {
  			throw new RemoteException("Client " + clientName
 					+ " already connected");
  		}else{
  			clients.put(clientName,messageCallback);
  			System.out.println(clientName + " connected!");
  		}
  	}
  
  	public synchronized void publishMessage(String message) throws RemoteException {
  		System.out.println("publishing message: " + message);
 		for (Iterator<IMessageCallback> iter = clients.values().iterator(); iter.hasNext();) {
  			IMessageCallback messageCallback = iter.next();
  			messageCallback.onMessage(message);
  		}
  	}
  
  	/**
  	 * @param args
  	 */
  	public static void main(String[] args) throws Exception {
  		Registry registry = LocateRegistry.getRegistry();
  		IChatServer chatServer = new ChatServerImpl();
  		registry.bind(IChatServer.BIND_NAME,chatServer);
  	}
  }
```
 
  Unser ChatClient:

```
/**
   * 
   */
  package de.tutorials;
  
  import java.awt.BorderLayout;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  import java.rmi.RemoteException;
  import java.rmi.registry.LocateRegistry;
  import java.rmi.registry.Registry;
  
  import javax.swing.JButton;
  import javax.swing.JFrame;
  import javax.swing.JTextArea;
  import javax.swing.JTextField;
  import javax.swing.SwingUtilities;
  
  /**
   * @author Tom
   * 
   */
  public class ChatClient extends JFrame {
  
  	JTextArea textArea;
  
  	JTextField textField;
  
  	JButton button;
  
  	IChatServer chatServer;
  
  	IMessageCallback messageCallback;
  
  	String nickname;
  
  	public ChatClient(final String nickname) {
  		super("ChatClient: " + nickname);
  		setDefaultCloseOperation(EXIT_ON_CLOSE);
  
  		this.nickname = nickname;
  
  		textArea = new JTextArea(10, 45);
  		textArea.setEditable(false);
  		textField = new JTextField(45);
  		button = new JButton("send");
  		button.addActionListener(new ActionListener() {
  			public void actionPerformed(ActionEvent e) {
  				try {
  		    	    chatServer.publishMessage(nickname + ": "
 		 		 	+ textField.getText());
  				} catch (RemoteException e1) {
 					e1.printStackTrace();
  				}
  				textField.setText("");
  			};
  		});
  
  		add(textArea, BorderLayout.NORTH);
  		add(textField, BorderLayout.CENTER);
  		add(button, BorderLayout.EAST);
  
  		try {
  			messageCallback = new MessageCallbackImpl(this);
  		} catch (RemoteException e2) {
  			e2.printStackTrace();
  		}
  
  		chatServer = lookupChatServer();
  		try {
  			chatServer.clientConnect(nickname, messageCallback);
  		} catch (RemoteException e1) {
  			e1.printStackTrace();
  		}
  
  		Runtime.getRuntime().addShutdownHook(new Thread() {
  			public void run() {
  				try {
 					messageCallback.shutdown();
  		    	    chatServer.clientDisconnect(getNickname());
  				} catch (RemoteException e) {
 					e.printStackTrace();
  				}
  			}
  		});
  
  		pack();
  		setVisible(true);
  	}
  
  	public void onMessage(final String message) {
  		System.out.println("Recived Message: " + message);
  		SwingUtilities.invokeLater(new Runnable() {
  			public void run() {
 				textArea.append(message + "\n");
  			}
  		});
  	}
  
  	private IChatServer lookupChatServer() {
  
  		try {
  			Registry registry = LocateRegistry.getRegistry();
 			return (IChatServer) registry.lookup(IChatServer.BIND_NAME);
  		} catch (Exception e) {
  			e.printStackTrace();
  		}
  		return null;
  	}
  
  	public String getNickname() {
  		return nickname;
  	}
  
  	public void setNickname(String nickname) {
  		this.nickname = nickname;
  	}
  
  	/**
  	 * @param args
  	 */
  	public static void main(String[] args) {
  		new ChatClient(args[0]);
  	}
  }
```
 
  Zur Kommunikation von Server zum Client brauchen wir einen Callback Mechanismus -> MessageCallback:

  Unser MessageCallback interface:

```
/**
   * 
   */
  package de.tutorials;
  
  import java.rmi.Remote;
  import java.rmi.RemoteException;
  
  public interface IMessageCallback extends Remote{
  	final static String BIND_NAME_PREFIX ="MessageCallback";
  	void onMessage(String message) throws RemoteException;
  	void setBindName(String bindName) throws RemoteException;
  	String getBindName() throws RemoteException;
  	void shutdown() throws RemoteException;
  }
```
 
  Unsere MessageCallback implementierung:

```
/**
   * 
   */
  package de.tutorials;
  
  import java.rmi.AlreadyBoundException;
  import java.rmi.RemoteException;
  import java.rmi.registry.LocateRegistry;
  import java.rmi.registry.Registry;
  import java.rmi.server.UnicastRemoteObject;
  
  public class MessageCallbackImpl extends UnicastRemoteObject implements
  		IMessageCallback {
  
  	ChatClient chatClient;
  
  	String bindName;
  
  	transient Registry registry = LocateRegistry.getRegistry();
  
  	protected MessageCallbackImpl(ChatClient chatClient) throws RemoteException {
  		super();
  		this.chatClient = chatClient;
  
  		this.bindName = IMessageCallback.BIND_NAME_PREFIX + "_"
  				+ chatClient.getNickname();
  		try {
  			registry.bind(bindName, this);
  		} catch (AlreadyBoundException e) {
 			throw new RemoteException("Could not bind MessageCallback", e);
  		}
  	}
  
  	public void onMessage(String message) throws RemoteException {
  		chatClient.onMessage(message);
  	}
  
  	public String getBindName()  throws RemoteException{
  		return bindName;
  	}
  
  	public void setBindName(String bindName) throws RemoteException{
  		this.bindName = bindName;
  	}
  
  	public void shutdown()  throws RemoteException {
  		try {
  			System.out.println("Shutting down MessageCallback: "
 					+ this.bindName);
  			registry.unbind(bindName);
  			UnicastRemoteObject.unexportObject(this, true);
  		} catch (Exception e) {
  			e.printStackTrace();
  		}
  	}
  }
```
 
  Um das ganze nun einmal auszuprobieren, müssen wir zuerst die rmiregistry starten:

```
rmiregistry -J-Djava.class.path=E:\eclipse\3.1.1\eclipse\workspace\de.tutorials.training\bin -J-Djava.rmi.server.hostname=192.168.76.98
```
 Mit dem Parameter -J-Djava.class.path gibt man on wie die RMI Registry die Klassen findet. Den Parameter -J-Djava.rmi.server.hostname brauche ich weil ich zwei Netzwerkkarten habe, damit binde ich die RMIRegistry an das Network-Interface mit der IP 192.168.76.98

  Anschließend starten wir den ChatServer:

```
E:\eclipse\3.1.1\eclipse\workspace\de.tutorials.training\bin>start java de.tutorials.ChatServerImpl
```
 
  Anschließend starten wir ein paar Clients:

```
E:\eclipse\3.1.1\eclipse\workspace\de.tutorials.training\bin>for /L %i in (1,1,4) do start java de.tutorials.ChatClient User%i
```
 
  Beispielausgabe auf dem Server:

```
Server started!
  User4 connected!
  User3 connected!
  User1 connected!
  User2 connected!
  publishing message: User2: hallo welt
  publishing message: User4: tutorials.de -user helfen usern-
  User1 disconnected!
  publishing message: User4: java rocks :)
  User4 disconnected!
  User2 disconnected!
  User3 disconnected!
```
 
  HTH,

  Gruß Tom


----------



## anna_2010 (5. Juni 2010)

vielen dank!
kompakt, praktisch, leicht nachzuvollziehen!


----------



## Thomas Darimont (6. Juni 2010)

Hallo,

hier mal noch eine kleineres Beispiel für einen RMI basierten Chat:

ChatClient:

```
package de.tutorials.chat;

import java.io.Serializable;
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface ChatClient extends Remote, Serializable {
    final static int CHAT_SERVER_PORT = Integer.getInteger("chatServer.port",4711);
    final static String CHAT_SERVER_NAME = System.getProperty("chatServer.name","ChatServer");
    String getName() throws RemoteException;
    void receiveMessage(ChatClient client, String message) throws RemoteException;
}
```

ChatServer

```
package de.tutorials.chat;

import java.rmi.RemoteException;

public interface ChatServer extends ChatClient {
    void connect(ChatClient client) throws RemoteException;
    void disconnect(ChatClient client) throws RemoteException;
}
```

ChatServer Implementierung

```
package de.tutorials.chat;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

public class Server extends Client implements ChatServer {

    Map<String, ChatClient> clients = new ConcurrentHashMap<String, ChatClient>();
    
    public Server() {
        super("ChatServer");
    }

    public static void main(String[] args) throws Exception {
        ChatServer chatServer = new Server();
        Registry registry = LocateRegistry.createRegistry(CHAT_SERVER_PORT);
        registry.bind(CHAT_SERVER_NAME, UnicastRemoteObject.exportObject(chatServer, 0));
        System.out.println("Server ready...");
        TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
    }

    public void connect(ChatClient client) throws RemoteException {
        clients.put(client.getName(), client);
        super.receiveMessage(this, "Client connected: " + client.getName());
    }

    public void disconnect(ChatClient client) throws RemoteException {
        clients.remove(client.getName());
        super.receiveMessage(this, "Client disconnected: " + client.getName());
    }

    public void receiveMessage(ChatClient client, String message) throws RemoteException {
        super.receiveMessage(client, message);
        for (Map.Entry<String, ChatClient> chatClientEntry : clients.entrySet()) {
            String clientName = chatClientEntry.getKey();
            if (!clientName.equals(client.getName())) {
                ChatClient chatClient = chatClientEntry.getValue();
                chatClient.receiveMessage(client, message);
            }
        }
    }
}
```

ChatClient Implementierung:

```
package de.tutorials.chat;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;

public class Client implements ChatClient {

    String name;

    public Client(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void receiveMessage(ChatClient client, String message) throws RemoteException {
        SimpleDateFormat format = new SimpleDateFormat("hh:MM:ss");
        System.out.printf("[%s] %s: %s\n", format.format(new Date()), client.getName(), message);
    }

    public static void main(String[] args) throws Exception {
        Registry registry = LocateRegistry.getRegistry(CHAT_SERVER_PORT);
        String clientName = System.getProperty("chatClient.name","Client " + System.currentTimeMillis());
        Client client = new Client(clientName);
        
        ChatClient chatClient = (ChatClient) UnicastRemoteObject.exportObject(client, 0);
        ChatServer chatServer = (ChatServer) registry.lookup(CHAT_SERVER_NAME);
        chatServer.connect(chatClient);

        Scanner scanner = new Scanner(System.in);
        
        while (true) {
            String line = scanner.nextLine();

            if (line.trim().equalsIgnoreCase("exit")) {
                chatServer.disconnect(chatClient);
                System.out.println("Bye bye...");
                break;
            } else {
                chatServer.receiveMessage(chatClient, line);
            }
        }
        
        UnicastRemoteObject.unexportObject(client, true);
    }
}
```

Starten des Servers mit 5 Test-Clients:

```
D:\workspaces\sts2.3.3M1\de.tutorials.training>start java -cp bin de.tutorials.chat.Server
D:\workspaces\sts2.3.3M1\de.tutorials.training>for /L %i in (1,1,5) do start java -DchatClient.name=client%i -cp bin de.tutorials.chat.Client
```

Gruß Tom


----------

