Java Language
Удаленный вызов метода (RMI)
Поиск…
замечания
RMI требует 3 компонента: клиент, сервер и общий удаленный интерфейс. Общий удаленный интерфейс определяет контракт клиент-сервер, указывая методы, которые должен выполнить сервер. Интерфейс должен быть видимым для сервера, чтобы он мог реализовать методы; интерфейс должен быть виден клиенту, чтобы он знал, какие методы («службы») предоставляют сервер.
Любой объект, реализующий удаленный интерфейс, должен взять на себя роль сервера. Таким образом, отношения клиент-сервер, в которых сервер также может вызывать методы в клиенте, на самом деле являются отношениями сервер-сервер. Это называется обратным вызовом, так как сервер может перезванивать «клиент». Имея это в виду, допустимо использовать клиент назначения для серверов, которые функционируют как таковые.
Общий удаленный интерфейс - это любой интерфейс, расширяющий Remote
. Объект, который функционирует как сервер, претерпевает следующие действия:
- Реализует общий удаленный интерфейс, явно или неявно, путем расширения
UnicastRemoteObject
который реализуетRemote
. - Экспортируется либо неявно, если он расширяет
UnicastRemoteObject
, либо явно передается вUnicastRemoteObject#exportObject
. - Связан в реестре, либо напрямую через
Registry
либо косвенно черезNaming
. Это необходимо только для установления первоначальной связи, поскольку дальнейшие заглушки могут быть переданы непосредственно через RMI.
В настройке проекта проекты клиента и сервера полностью не связаны друг с другом, но каждый указывает общий проект в своем пути сборки. Общий проект содержит удаленные интерфейсы.
Client-Server: вызов методов в одном JVM из другого
Общий удаленный интерфейс:
package remote;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface RemoteServer extends Remote {
int stringToInt(String string) throws RemoteException;
}
Сервер, реализующий общий удаленный интерфейс:
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();
}
}
}
Клиент, вызывающий метод на сервере (удаленно):
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();
}
}
}
Выход:
Полученный сервер: «120»
Полученный клиент: 120
Обратный вызов: вызов методов на «клиент»
обзор
В этом примере 2 клиента отправляют информацию друг другу через сервер. Один клиент отправляет серверу номер, который передается второму клиенту. Второй клиент уменьшает число и отправляет его первому клиенту через сервер. Первый клиент делает то же самое. Сервер останавливает связь, когда число, возвращаемое им любым из клиентов, меньше 10. Возвращаемое значение от сервера к клиентам (номер, который он преобразовал в строковое представление), затем возвращает процесс.
- Сервер входа в систему привязывается к реестру.
- Клиент просматривает сервер входа в систему и вызывает метод
login
вlogin
со своей информацией. Затем:- Клиентский сервер хранит информацию о клиенте. Он включает в себя заглушку клиента с методами обратного вызова.
- Сервер входа в систему создает и возвращает серверную заглушку («соединение» или «сеанс») для хранения клиента. Он включает в себя заглушку сервера с его методами, включая метод
logout
изlogout
(в этом примере не используется).
- Клиент вызывает
passInt
сервера с именем клиента-получателя иint
. - Сервер вызывает
half
на клиенте получателя с этимint
. Это инициирует обратную связь (обратные вызовы и обратные вызовы) до остановки сервера.
Общие удаленные интерфейсы
Сервер входа в систему:
package callbackRemote;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface RemoteLogin extends Remote {
RemoteConnection login(String name, RemoteClient client) throws RemoteException;
}
Сервер:
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;
}
Клиент:
package callbackRemote;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface RemoteClient extends Remote {
void half(int i) throws RemoteException;
}
Реализации
Сервер входа в систему:
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();
}
}
}
Сервер:
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);
}
}
Клиент:
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 + "\"");
}
}
Запуск примера:
- Запустите сервер входа.
- Запустите клиент с аргументами
Client2 Client1 1097
. - Запустите клиент с аргументами
Client1 Client2 1098
.
Выходы появятся на 3 консолях, так как есть 3 JVM. здесь они собраны вместе:
Клиент2 вошел в систему
Клиент1 вошел в систему
Сервер, полученный от Client1: 120
Сервер, полученный от Client2: 60
Сервер, полученный от Client1: 30
Сервер, полученный от Client2: 15
Сервер, полученный от Client1: 7
Клиент1 получил: «7»
Клиент2 получил: «15»
Клиент1 получил: «30»
Клиент2 получил: «60»
Простой пример RMI с внедрением Client и Server
Это простой пример RMI с пятью классами Java и двумя пакетами, сервером и клиентом .
Серверный пакет
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();
}
}
Клиентский пакет
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);
}
}
}
Проверьте свое приложение
- Начать основной метод Server.java. Выход:
Server : Registry created.
Server : PersonList registered
- Начните основной метод PersonTest.java. Выход:
Peter Pan
Pippi Langstrumpf