这篇博文一个多月前就发表过,但因当时在课程上该实验还未结束提交而删除了;对老师教学造成的不便深感歉意;写这些不是为了什么,也知道自己写的不好,只是记录自己的学习心历路程,同时也当作一个笔记,将来翻看的时候,也能有所收获。
一、实验目的与要求
实验目的:理解JavaRMI的体系结构,熟悉Java RMI程序的编写。
实验要求:请独立完成本实验。
二、实验内容
1. 下面的代码是一个Java RMI程序的主要代码,根据这些代码构建一个Java RMI程序,要求能正常运行,并显示运行截图(包括服务器端和客户端)。
Java RMI ,即远程方法调用,和Java Socket相比,RMI也是C/S模式,但是Socket主要是为了客户端和服务器之间的相互发生消息/传输文件(通过输入/输出缓冲流),比如 多人聊天 这样的程序就用Socket来实现比较方便。 而RMI(Remote Method Invoke)远程方法调用,顾名思义,客户端可以通过网络(把参数以及方法名传给服务器)远程调用 服务器端的方法(服务器端接受到客服端的请求和传递过来的参数本地运行客户请求的方法),服务器运行方法后再将运行结果返回给客户端(通过网络)。Java RMI封装了具体实现的过程,对客户端来说,它就像是调用了一个自己本地的一个方法一样(尽管它自己没有这个方法)。简单来说,Java RMI 在服务器端声明了一个“RMI注册表“,把可以做为远程调用的一个对象和一个名字绑定在一起放入注册表中,客户想要调用服务器上的远程对象(的方法),通过在注册表中查找名字(之前在服务器和对象绑定在一起的名字),获得一个远程对象(实例),可以调用该实例的所有方法,即实现了远程调用。
当然实际上Java RMI原理并不是以上说的那么简单,具体还涉及了很多更底层的东西,可以百度Java RMI有很多博客有讲解。
本实验第1小题的程序是 在服务器实现了两个方法:【发送消息】和【计算阶层】。在客户端,通过 参数-f 或-m来选择用哪个功能,然后调用服务器的方法。
- // 接口
- package ex.server;
- import java.rmi.Remote;
- import java.rmi.RemoteException;
- public interface RMIExample extends Remote
- {
- public String PostMsg(String strMsg) throws RemoteException;
- public long Factorial(long lVal) throws RemoteException;
- }
声明接口,RMIExample 继承于 Remote类
- // 接口实现类
- package ex.server;
- import java.rmi.RemoteException;
- import java.rmi.server.UnicastRemoteObject;
- public class RMIExampleImpl extends UnicastRemoteObject implements RMIExample
- {
- protected static String m_strName;
- public RMIExampleImpl() throws RemoteException
- {
- super(); // call base class constructor
- }
- public String PostMsg(String strMsg) throws RemoteException
- {
- System.out.println("Server: PostMsg() invoked...");
- System.out.println("Server: Message > " + strMsg);
- return "The Message has been posted !";
- }
- public long Factorial(long lVal) throws RemoteException
- {
- long lRes = FactorialEx(lVal);
- System.out.println("Server: Factorial() invoked...");
- System.out.println("Server: Factorial("+lVal+") = " + lRes);
- return lRes;
- }
- protected long FactorialEx(long lVal)
- {
- if (lVal <= 1)
- return 1;
- else
- return lVal * FactorialEx(lVal-1);
- }
- }
实现接口声明的两个方法,类继承于UnicastRemoteObject,说明该类自动成为可远程调用的类
- //服务器
- package ex.server;
- import java.rmi.Naming;
- import java.rmi.registry.LocateRegistry;
- import java.rmi.registry.Registry;
- public class RMIServer {
- public static void main(String argv[])
- {
- try
- {
- String m_strName = "TheRMIExample";
- System.out.println("Server: Registering RMIExampleImpl as \"" + m_strName +"\"");
- Registry registry = LocateRegistry.createRegistry(5678); //声明注册表
- RMIExampleImpl Example = new RMIExampleImpl();
- String StrName = "rmi://localhost:5678/TheRMIExample";
- Naming.rebind(StrName, Example);
- System.out.println("Server: Ready...");
- }
- catch (Exception e)
- {
- System.out.println("Server: Failed to register RMIExampleImpl: " + e);
- }
- }
- }
注意,这个名字在注册表中是唯一的。
- //客户端
- package ex.server;
- import java.rmi.Naming;
- public class RMIClient {
- /**
- * @param argv
- */
- public static void main(String argv[])
- {
- // Validate command line parameters
- if (argv.length < 2)
- {
- System.out.println("Usage: java RMIClient [-m \"MESSAGE\"] [-f INTEGER]");
- System.exit(1);
- }
- // Command line option flags
- boolean bMessage = false;
- boolean bFactorial = false;
- String strMsg = "No message.";
- long lVal = 1;
- // Determine data to be processed
- for (int i=0; i<argv.length; i++)
- {
- if (argv[i].equals("-m"))
- {
- bMessage = true;
- strMsg = argv[++i];
- }
- if (argv[i].equals("-f"))
- {
- bFactorial = true;
- lVal = Long.parseLong(argv[++i]);
- }
- }
- //System.setSecurityManager(new RMISecurityManager());
- String strName = "rmi://localhost:5678/TheRMIExample";
- System.out.println("Client: Looking up " + strName + "...");
- RMIExample RemRMIExample = null;
- try
- {
- RemRMIExample = (RMIExample)Naming.lookup(strName); //注意这里是接口对象,不是实现类对象
- }
- catch (Exception e)
- {
- System.out.println("Client: Exception thrown looking up " + strName);
- System.exit(1);
- }
- // Send a messge to the remote object
- if (bMessage)
- {
- try
- {
- //if (!RemRMIExample.PostMsg(strMsg))
- // System.out.println("Client: Remote PostMsg() call failed.");
- System.out.println(RemRMIExample.PostMsg(strMsg));
- }
- catch (Exception e)
- {
- System.out.println("Client: Exception thrown calling PostMsg()." + e);
- System.exit(1);
- }
- }
- // Calculate the factorial
- if (bFactorial)
- {
- try
- {
- long lRes = RemRMIExample.Factorial(lVal);
- System.out.println("Client: Factorial(" + lVal + ") = " + lRes);
- }
- catch (Exception e)
- {
- System.out.println("Client: Excpetion thrown calling Factorial().");
- System.exit(1);
- }
- }
- }
- }
2. 附件的zip文件中包含有几个java文件,但是要正常运行还需要一些代码,补充所需要的代码,使得这个Java RMI程序能够运行,并显示运行截图(包括服务器端和客户端)。
注意到,第一小题的一般Java RMI,只有客户端远程调用服务器对象,服务器并没有调用客户端的对象。但是,有些需求要求服务器也要调用客户端对象。比如,假设有一种游戏,有一个角色是【法官】(相当于服务器),有一些角色【玩家】(相当于客户端),游戏规则是,玩家随时可能向法官询问信息(客户调用服务器对象)以做出下一步选择 ,也可以设定让法官在某件事情发生时,特别的提醒一下他 (这时候,服务器就要调用到客户端对象了) 。这就是RMI回调。客户回调允许对象客户在远程回调对象服务器上注册自己(注释1),以便服务器可以在所等待事件发生时,向客户发起远程方法调用的一个特征。
注释1:一般来说,需要在服务器端建立一个列表,客户可以把自己的远程调用接口“注册”到这个列表中,可以使用vector。
由此可以知道,客户端也应该提供一个和服务器端类似的回调接口,和同样实现了Remote接口并
继承自UnicastRemotObject类,里面提供即将被服务器回调的方法(函数)。
本实验的第2小题,是一个简单RMI客户回调的样例。
- //客户端接口
- import java.rmi.Remote;
- import java.rmi.RemoteException;
- public interface CallbackClientInterface extends Remote {
- public String notifyMe(String message)throws RemoteException;
- }
- //服务器接口
- import java.rmi.Remote;
- import java.rmi.RemoteException;
- public interface CallbackServerInterface extends Remote {
- public String sayHello( ) throws RemoteException;
- public void registerForCallback(CallbackClientInterface callbackClientObject) throws RemoteException;
- //synchronized
- public void unregisterForCallback(CallbackClientInterface callbackClientObject) throws RemoteException;
- }
- //客户接口实现类
- import java.rmi.*;
- import java.rmi.server.*;
- /**
- * This class implements the remote interface
- * CallbackClientInterface.
- * @author M. L. Liu
- */
- public class CallbackClientImpl extends UnicastRemoteObject
- implements CallbackClientInterface {
- public CallbackClientImpl() throws RemoteException {
- super( );
- }
- public String notifyMe(String message){
- String returnMessage = "Call back received: " + message;
- System.out.println(returnMessage);
- return returnMessage;
- }
- }// end CallbackClientImpl class
- //服务器接口实现类
- import java.rmi.*;
- import java.rmi.server.*;
- import java.util.Vector;
- /**
- * This class implements the remote interface
- * CallbackServerInterface.
- * @author M. L. Liu
- */
- public class CallbackServerImpl extends UnicastRemoteObject implements CallbackServerInterface {
- private Vector clientList;
- public CallbackServerImpl() throws RemoteException {
- super( );
- clientList = new Vector();
- }
- public String sayHello( ) throws RemoteException {
- return("hello");
- }
- public synchronized void registerForCallback(CallbackClientInterface callbackClientObject)
- throws RemoteException{
- // store the callback object into the vector
- if (!(clientList.contains(callbackClientObject))) {
- clientList.addElement(callbackClientObject);
- System.out.println("Registered new client ");
- doCallbacks(); //在注册的同时就回调!
- } // end if
- }
- // This remote method allows an object client to
- // cancel its registration for callback
- // @param id is an ID for the client; to be used by
- // the server to uniquely identify the registered client.
- public synchronized void unregisterForCallback(CallbackClientInterface callbackClientObject)
- throws RemoteException{
- if (clientList.removeElement(callbackClientObject)) {
- System.out.println("Unregistered client ");
- } else {
- System.out.println(
- "unregister: clientwasn't registered.");
- }
- }
- private synchronized void doCallbacks( ) throws java.rmi.RemoteException{
- // make callback to each registered client
- System.out.println(
- "**************************************\n"
- + "Callbacks initiated ---");
- for (int i = 0; i < clientList.size(); i++){
- System.out.println("doing "+ i +"-th callback\n");
- // convert the vector object to a callback object
- CallbackClientInterface nextClient = (CallbackClientInterface)clientList.elementAt(i); //从注册列表中获得客户端对象
- // invoke the callback method
- nextClient.notifyMe("Number of registered clients = " + clientList.size()); //调用客户端方法,即回调
- }// end for
- System.out.println("********************************\n" + "Server completed callbacks ---");
- } // doCallbacks
- }// end CallbackServerImpl class
- //服务器程序
- import java.rmi.*;
- import java.rmi.registry.Registry;
- import java.rmi.registry.LocateRegistry;
- import java.io.*;
- /**
- * This class represents the object server for a distributed
- * object of class Callback, which implements the remote
- * interface CallbackInterface.
- * @author M. L. Liu
- */
- public class CallbackServer{
- public static void main(String args[]) {
- InputStreamReader is = new InputStreamReader(System.in);
- BufferedReader br = new BufferedReader(is);
- String portNum, registryURL;
- try{
- System.out.println("Enter the RMIregistry port number:");
- portNum = (br.readLine()).trim();
- int RMIPortNum = Integer.valueOf(portNum);
- startRegistry(RMIPortNum); //关键语句
- CallbackServerImpl exportedObj = new CallbackServerImpl();
- registryURL = "rmi://localhost:" + portNum + "/callback";
- Naming.rebind(registryURL, exportedObj); //关键语句
- System.out.println("Callback Server ready.");
- }
- catch (Exception re) {
- System.out.println("Exception in HelloServer.main: " + re);
- }
- }
- //This method starts a RMI registry on the local host, if
- //it does not already exists at the specified port number.
- private static void startRegistry(int RMIPortNum)
- throws RemoteException{
- try {
- Registry registry = LocateRegistry.getRegistry(RMIPortNum);
- registry.list( );
- // This call will throw an exception
- // if the registry does not already exist
- }
- catch (RemoteException e) {
- // No valid registry at that port.
- Registry registry = LocateRegistry.createRegistry(RMIPortNum);
- }
- } // end startRegistry
- } // end class
- //客户端程序
- import java.io.*;
- import java.rmi.*;
- /**
- * This class represents the object client for a
- * distributed object of class CallbackServerImpl,
- * which implements the remote interface
- * CallbackServerInterface. It also accepts callback
- * from the server.
- *
- * @author M. L. Liu
- */
- public class CallbackClient {
- public static void main(String args[]) {
- try {
- int RMIPort;
- String hostName;
- InputStreamReader is = new InputStreamReader(System.in);
- BufferedReader br = new BufferedReader(is);
- System.out.println("Enter the RMIRegistry host namer:");
- hostName = br.readLine();
- System.out.println("Enter the RMIregistry port number:");
- String portNum = br.readLine();
- RMIPort = Integer.parseInt(portNum);
- System.out.println("Enter how many seconds to stay registered:");
- String timeDuration = br.readLine();
- int time = Integer.parseInt(timeDuration);
- String registryURL = "rmi://localhost:" + portNum + "/callback";
- // find the remote object and cast it to an interface object
- CallbackServerInterface h =(CallbackServerInterface)Naming.lookup(registryURL); //远程调用服务器,获得一个远程对象
- System.out.println("Lookup completed ");
- System.out.println("Server said " + h.sayHello()); //远程调用服务器对象方法,并输出获取的返回值
- CallbackClientInterface callbackObj = new CallbackClientImpl();
- // register for callback
- h.registerForCallback(callbackObj); //在服务器上在注册一个回调
- System.out.println("Registered for callback.");
- try {
- Thread.sleep(time * 1000);
- }
- catch (InterruptedException ex){ // sleep over
- }
- h.unregisterForCallback(callbackObj);
- System.out.println("Unregistered for callback.");
- } // end try
- catch (Exception e) {
- System.out.println("Exception in CallbackClient: " + e);
- } // end catch
- } //end main
- }//end class
3. 编写一个Java RMI程序,服务器可以将一个英文单词翻译为中文,客户端可以输入一个英文单词,然后调用服务器的这个功能完成翻译。英文单词的中英文对照如附件1000words.txt所示。显示程序关键代码和运行截图。
这题比较简单,最普通的RMI调用,注意在服务器实现一下翻译功能的函数就行了。
- //定义接口
- import java.rmi.Remote;
- import java.rmi.RemoteException;
- public interface TransInterfcae extends Remote{
- public String getChinese(String str) throws RemoteException;
- }
- //接口实现
- import java.io.BufferedReader;
- import java.io.FileNotFoundException;
- import java.io.FileReader;
- import java.io.IOException;
- import java.rmi.RemoteException;
- import java.rmi.server.UnicastRemoteObject;
- import java.util.*;
- public class TransImpl extends UnicastRemoteObject implements TransInterfcae
- {
- HashMap <String,String> map = new HashMap<String,String>();
- public TransImpl () throws RemoteException,FileNotFoundException
- {
- init();
- }
- public String getChinese(String str)
- {
- String ans = map.get(str);
- if(ans == null)
- return "Sorrry, this word is not within 1000 words";
- return ans;
- }
- public void init() throws FileNotFoundException
- {
- map.clear();
- try
- {
- BufferedReader reader = new BufferedReader(new FileReader("1000words.txt"));
- String str,key,value;
- while( (str = reader.readLine()) != null)
- {
- key = "";
- StringTokenizer st = new StringTokenizer(str);
- value = st.nextToken();
- while(st.hasMoreTokens()) //Fuck……有几个英文不止一个单词
- {
- key = key + " " + st.nextToken();
- }
- //System.out.println(key.trim());
- map.put(key.trim(), value);
- }
- reader.close();
- }
- catch (IOException e1) {
- e1.printStackTrace();
- }
- }
- }
- //服务器
- import java.rmi.Naming;
- import java.rmi.registry.LocateRegistry;
- public class Server {
- public static void main(String argv[])
- {
- try
- {
- //System.out.println("服务器准备就绪,可接受翻译!");
- LocateRegistry.createRegistry(4567);
- TransImpl Example = new TransImpl();
- String StrName = "rmi://localhost/translation";
- Naming.rebind(StrName, Example);
- System.out.println("Server: Ready...");
- }
- catch (Exception e)
- {
- System.out.println("Server: Failed to register RMIExampleImpl: " + e);
- }
- }
- }
- //客户端
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.rmi.Naming;
- public class Client {
- public static void main(String argv[]) throws IOException
- {
- TransInterfcae t = null;
- try{
- t = (TransInterfcae) Naming.lookup("rmi://localhost:4567/translation");
- }
- catch (Exception e)
- {
- System.out.println("Client: Fail to lookup " + e);
- System.exit(1);
- }
- System.out.println("*******Game start*******");
- System.out.println("Please enter the words you want to translate: ");
- while(true)
- {
- BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
- String key = br.readLine().trim();
- if(key.length() == 0 ) continue;
- if(key.equals("end"))
- break;
- System.out.println(t.getChinese(key));
- }
- System.out.println("*******Game over*******");
- }
- }