Java Language
Zdalne wywołanie metody (RMI)
Szukaj…
Uwagi
RMI wymaga 3 komponentów: klienta, serwera i wspólnego interfejsu zdalnego. Współdzielony zdalny interfejs definiuje umowę klient-serwer, określając metody, które serwer musi wdrożyć. Interfejs musi być widoczny dla serwera, aby mógł implementować metody; interfejs musi być widoczny dla klienta, aby wiedział, jakie metody („usługi”) zapewnia serwer.
Każdy obiekt implementujący zdalny interfejs może pełnić rolę serwera. W związku z tym relacja klient-serwer, w której serwer może również wywoływać metody w kliencie, jest w rzeczywistości relacją serwer-serwer. Jest to nazywane oddzwanianiem, ponieważ serwer może oddzwonić do „klienta”. Mając to na uwadze, dopuszczalne jest użycie klienta desygnacji dla serwerów, które działają jako takie.
Współdzielony zdalny interfejs to dowolny interfejs rozszerzający Remote
. Obiekt, który działa jak serwer, podlega:
- Implementuje udostępniony interfejs zdalny, jawnie lub niejawnie, poprzez rozszerzenie
UnicastRemoteObject
który implementujeRemote
. - Eksportowane, niejawnie, jeśli rozszerza
UnicastRemoteObject
, lub jawnie przez przekazanie doUnicastRemoteObject#exportObject
. - Powiązane w rejestrze, bezpośrednio przez
Registry
lub pośrednio przezNaming
. Jest to konieczne tylko do nawiązania początkowej komunikacji, ponieważ kolejne kody pośredniczące mogą być przekazywane bezpośrednio przez RMI.
W konfiguracji projektu projekty klienta i serwera są całkowicie niezwiązane, ale każdy określa wspólny projekt na ścieżce kompilacji. Udostępniony projekt zawiera zdalne interfejsy.
Klient-serwer: wywoływanie metod w jednej maszynie JVM z drugiej
Współdzielony zdalny interfejs:
package remote;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface RemoteServer extends Remote {
int stringToInt(String string) throws RemoteException;
}
Serwer implementujący współdzielony zdalny interfejs:
package server;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import remote.RemoteServer;
public class Server implements RemoteServer {
@Override
public int stringToInt(String string) throws RemoteException {
System.out.println("Server received: \"" + string + "\"");
return Integer.parseInt(string);
}
public static void main(String[] args) {
try {
Registry reg = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
Server server = new Server();
UnicastRemoteObject.exportObject(server, Registry.REGISTRY_PORT);
reg.rebind("ServerName", server);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
Klient wywołuje metodę na serwerze (zdalnie):
package client;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import remote.RemoteServer;
public class Client {
static RemoteServer server;
public static void main(String[] args) {
try {
Registry reg = LocateRegistry.getRegistry();
server = (RemoteServer) reg.lookup("ServerName");
} catch (RemoteException | NotBoundException e) {
e.printStackTrace();
}
Client client = new Client();
client.callServer();
}
void callServer() {
try {
int i = server.stringToInt("120");
System.out.println("Client received: " + i);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
Wynik:
Otrzymano serwer: „120”
Klient otrzymał: 120
Oddzwonienie: wywoływanie metod na „kliencie”
Przegląd
W tym przykładzie 2 klientów wysyła do siebie informacje za pośrednictwem serwera. Jeden klient wysyła do serwera numer, który jest przekazywany do drugiego klienta. Drugi klient dzieli połowę numeru i wysyła go z powrotem do pierwszego klienta za pośrednictwem serwera. Pierwszy klient robi to samo. Serwer przerywa komunikację, gdy liczba zwracana mu przez któregokolwiek z klientów jest mniejsza niż 10. Wartość zwracana z serwera do klientów (liczba, którą przekonwertowała na ciąg znaków) następnie śledzi proces.
- Serwer logowania wiąże się z rejestrem.
- Klient wyszukuje serwer logowania i wywołuje metodę
login
z jego informacjami. Następnie:- Serwer logowania przechowuje informacje o kliencie. Zawiera kod pośredniczący klienta z metodami wywołania zwrotnego.
- Serwer logowania tworzy i zwraca kod pośredniczący serwera („połączenie” lub „sesję”) do klienta w celu zapisania. Zawiera kod pośredniczący serwera wraz z jego metodami, w tym metodą
logout
(nieużywaną w tym przykładzie).
- Klient wywołuje
passInt
serwera z nazwą klienta odbiorcy iint
. - Serwer wywołuje
half
na kliencie odbiorcy z tąint
. To inicjuje komunikację w obie strony (połączenia i połączenia zwrotne) do momentu zatrzymania przez serwer.
Współdzielone zdalne interfejsy
Serwer logowania:
package callbackRemote;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface RemoteLogin extends Remote {
RemoteConnection login(String name, RemoteClient client) throws RemoteException;
}
Serwer:
package callbackRemote;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface RemoteConnection extends Remote {
void logout() throws RemoteException;
String passInt(String name, int i) throws RemoteException;
}
Klient:
package callbackRemote;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface RemoteClient extends Remote {
void half(int i) throws RemoteException;
}
Wdrożenia
Serwer logowania:
package callbackServer;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
import java.util.Map;
import callbackRemote.RemoteClient;
import callbackRemote.RemoteConnection;
import callbackRemote.RemoteLogin;
public class LoginServer implements RemoteLogin {
static Map<String, RemoteClient> clients = new HashMap<>();
@Override
public RemoteConnection login(String name, RemoteClient client) {
Connection connection = new Connection(name, client);
clients.put(name, client);
System.out.println(name + " logged in");
return connection;
}
public static void main(String[] args) {
try {
Registry reg = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
LoginServer server = new LoginServer();
UnicastRemoteObject.exportObject(server, Registry.REGISTRY_PORT);
reg.rebind("LoginServerName", server);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
Serwer:
package callbackServer;
import java.rmi.NoSuchObjectException;
import java.rmi.RemoteException;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.server.Unreferenced;
import callbackRemote.RemoteClient;
import callbackRemote.RemoteConnection;
public class Connection implements RemoteConnection, Unreferenced {
RemoteClient client;
String name;
public Connection(String name, RemoteClient client) {
this.client = client;
this.name = name;
try {
UnicastRemoteObject.exportObject(this, Registry.REGISTRY_PORT);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void unreferenced() {
try {
UnicastRemoteObject.unexportObject(this, true);
} catch (NoSuchObjectException e) {
e.printStackTrace();
}
}
@Override
public void logout() {
try {
UnicastRemoteObject.unexportObject(this, true);
} catch (NoSuchObjectException e) {
e.printStackTrace();
}
}
@Override
public String passInt(String recipient, int i) {
System.out.println("Server received from " + name + ":" + i);
if (i < 10)
return String.valueOf(i);
RemoteClient client = LoginServer.clients.get(recipient);
try {
client.half(i);
} catch (RemoteException e) {
e.printStackTrace();
}
return String.valueOf(i);
}
}
Klient:
package callbackClient;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import callbackRemote.RemoteClient;
import callbackRemote.RemoteConnection;
import callbackRemote.RemoteLogin;
public class Client implements RemoteClient {
RemoteConnection connection;
String name, target;
Client(String name, String target) {
this.name = name;
this.target = target;
}
public static void main(String[] args) {
Client client = new Client(args[0], args[1]);
try {
Registry reg = LocateRegistry.getRegistry();
RemoteLogin login = (RemoteLogin) reg.lookup("LoginServerName");
UnicastRemoteObject.exportObject(client, Integer.parseInt(args[2]));
client.connection = login.login(client.name, client);
} catch (RemoteException | NotBoundException e) {
e.printStackTrace();
}
if ("Client1".equals(client.name)) {
try {
client.connection.passInt(client.target, 120);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void half(int i) throws RemoteException {
String result = connection.passInt(target, i / 2);
System.out.println(name + " received: \"" + result + "\"");
}
}
Uruchamianie przykładu:
- Uruchom serwer logowania.
- Uruchom klienta z argumentami
Client2 Client1 1097
. - Uruchom klienta z argumentami
Client1 Client2 1098
.
Wyjścia pojawią się w 3 konsolach, ponieważ są 3 maszyny JVM. tutaj są zlepione:
Klient2 zalogowany
Klient1 zalogowany
Serwer odebrany od klienta1: 120
Serwer otrzymany od klienta2: 60
Serwer otrzymał od klienta1: 30
Serwer otrzymany od klienta2: 15
Serwer otrzymany od klienta1: 7
Klient1 otrzymał: „7”
Otrzymano klient2: „15”
Klient1 otrzymał: „30”
Klient2 otrzymał: „60”
Prosty przykład RMI z implementacją klienta i serwera
Jest to prosty przykład RMI z pięcioma klasami Java i dwoma pakietami, serwerem i klientem .
Pakiet serwera
PersonListInterface.java
public interface PersonListInterface extends Remote
{
/**
* This interface is used by both client and server
* @return List of Persons
* @throws RemoteException
*/
ArrayList<String> getPersonList() throws RemoteException;
}
PersonListImplementation.java
public class PersonListImplementation
extends UnicastRemoteObject
implements PersonListInterface
{
private static final long serialVersionUID = 1L;
// standard constructor needs to be available
public PersonListImplementation() throws RemoteException
{}
/**
* Implementation of "PersonListInterface"
* @throws RemoteException
*/
@Override
public ArrayList<String> getPersonList() throws RemoteException
{
ArrayList<String> personList = new ArrayList<String>();
personList.add("Peter Pan");
personList.add("Pippi Langstrumpf");
// add your name here :)
return personList;
}
}
Server.java
public class Server {
/**
* Register servicer to the known public methods
*/
private static void createServer() {
try {
// Register registry with standard port 1099
LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
System.out.println("Server : Registry created.");
// Register PersonList to registry
Naming.rebind("PersonList", new PersonListImplementation());
System.out.println("Server : PersonList registered");
} catch (final IOException e) {
e.printStackTrace();
}
}
public static void main(final String[] args) {
createServer();
}
}
Pakiet klienta
PersonListLocal.java
public class PersonListLocal {
private static PersonListLocal instance;
private PersonListInterface personList;
/**
* Create a singleton instance
*/
private PersonListLocal() {
try {
// Lookup to the local running server with port 1099
final Registry registry = LocateRegistry.getRegistry("localhost",
Registry.REGISTRY_PORT);
// Lookup to the registered "PersonList"
personList = (PersonListInterface) registry.lookup("PersonList");
} catch (final RemoteException e) {
e.printStackTrace();
} catch (final NotBoundException e) {
e.printStackTrace();
}
}
public static PersonListLocal getInstance() {
if (instance == null) {
instance = new PersonListLocal();
}
return instance;
}
/**
* Returns the servers PersonList
*/
public ArrayList<String> getPersonList() {
if (instance != null) {
try {
return personList.getPersonList();
} catch (final RemoteException e) {
e.printStackTrace();
}
}
return new ArrayList<>();
}
}
PersonTest.java
public class PersonTest
{
public static void main(String[] args)
{
// get (local) PersonList
ArrayList<String> personList = PersonListLocal.getInstance().getPersonList();
// print all persons
for(String person : personList)
{
System.out.println(person);
}
}
}
Przetestuj swoją aplikację
- Uruchom główną metodę Server.java. Wynik:
Server : Registry created.
Server : PersonList registered
- Uruchom główną metodę PersonTest.java. Wynik:
Peter Pan
Pippi Langstrumpf