远程方法调用的模仿实现(附系统的通信图)

RMI 即remote method invoke
远程方法调用:基于一种网络的技术
在本地执行一个方法,实际上是在服务器端完成的 即服务器端真正的执行了这个方法 并且通过网络进行函数执行结果的返回给客户端
下面我们逐步分析;
1 首先我们进行 服务器与客户端的建立
2 并通过 ***来进行方法的执行
3 最终返回给客户端

这是我们进行分析之后,得到这个工程的通信图

RMI源码git链接

我们可以开始设计代码了
客户端
客户端代码进行服务器的连接,
并且将执行方法的 名字以及参数 传递给服务器端
最后准备接收服务器端传过来的函数结果

public class RMIClient1 {
	
	private  int RMIport;
	private  String RMIip;
	private Socket socket;
	private DataInputStream dis;
	private DataOutputStream dos;
	
	//get 和set        ip 以及port

	public RMIClient1(int RMIport,String RMIip) {
		this.RMIip=RMIip;
		this.RMIport=RMIport;
	}
	
	
	void ConnectToServer() {
// 建立通信信道
		try {
			this.socket=new Socket(RMIip,RMIport);
			this.dis=new DataInputStream(socket.getInputStream());
			this.dos=new DataOutputStream(socket.getOutputStream());
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}


	public Object invokeMethod( Method method ,Object[] args) {
//		将要实现的方法传过去
		ArgumentMaker argmaker=new ArgumentMaker();
		for(int i=0;i<args.length;i++) {
			argmaker.addArg("arg"+i, args[i]);
		}
		String argStr=argmaker.toString();
		
		Object res=null;
		try {
			dos.writeUTF(String.valueOf(method.toString().hashCode()));
			dos.writeUTF(argStr);
			
			String result =dis.readUTF();
//			将由服务端返回的JSON字符串 结果传递回来给客户端
			res=ArgumentMaker.gson.fromJson(result, method.getReturnType());
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	
		
		return res;
	}
	}

我们再来看看服务器端
他需要完成自己的启动 以及进行方法的执行

public class RMIServer1 implements Runnable{
	private int port;
	private ServerSocket server;
	private volatile  boolean   goon;
	
	public int getPort() {
		return port;
	}
	public void setPort(int port) {
		this.port = port;
	}

	public RMIServer1(int port) {
		this.port=port;
		
	}
	void Startup() {
		if(goon==true) {
			return;
		}
		try {
			server=new ServerSocket(port);
			goon=true;
			new Thread(this,"serverThread").start();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	void shutdown() {
		if(goon==false) {
			return ;
		}
		goon=false;
		try {
			server.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	@Override
	public void run() {
//		进行 服务器端的 通信线道的建立
//		进行 客户端传过来方法的执行
		Socket socket;
		try {
			socket = server.accept();
			new RMIActioner1(socket) ;
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

这里要遵循软件工程中职责单一的原则 故我们在RMIActioner里面进行方法的执行

在进行编写的过程中,突然发现invoke 函数执行的时候需要methodname 以及
Object 故定义了 MethodDefination类 给出两个成员的get和set方法
所以 利用工厂模式 将这他们生产出来

工厂里面有methodpool 键必须唯一 所以想到
利用method的名字 做hashmap得到唯一的键

private static  Map<String ,RMIMethodDefination1 >  methodPool;
//	hashMap 的类型定义以及new一个hashmap的写法
	static {
		methodPool=new HashMap<>();
	}
		public void ScanPackage(String packagename) {
//	进行包扫描先得到有实现了有相应注解的接口
	}
	
	public void registryClass(Class<?> interfaces, Class<?> klass) {
//		注册  给methodPool里面填内容
		if (interfaces == null || klass == null 
				|| !interfaces.isInterface() || klass.isInterface()
				|| !interfaces.isAssignableFrom(klass))	{
//			isAssignableFrom 判断是不是实现实现接口的类
			return;
		}
		
	
	static RMIMethodDefination1 getMethod(String methodId) {
		return methodPool.get(methodId);
	}
	
}

进行方法的执行(参数以及方法的处理)

	public RMIActioner1(Socket socket) {
		try {
			dis=new DataInputStream(socket.getInputStream());
			dos=new DataOutputStream(socket.getOutputStream());
			new  Thread(this).start();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	@Override
	public void run() {
		try {
			String methodname=dis.readUTF();
			String arguments=dis.readUTF();
			
			RMIMethodDefination1  methodDefination=RMIMethodFactory1.getMethod(methodname);
			Object object=methodDefination.getObject();
			Method method=methodDefination.getMethod();
			
			Object values[] =getParameterValues(method, arguments);
			try {
				Object result=method.invoke(object, values);
				dos.writeUTF(ArgumentMaker.gson.toJson(result));
				//将执行结果传过去
				close();
			} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	private Object[] getParameterValues(Method method ,String argsString) {
		ArgumentMaker argumentMaker = new ArgumentMaker(argsString);
		Parameter[] parameters = method.getParameters();
		int ParameterCount=method.getParameterCount();
		if(ParameterCount<=0){
			return new Object[] {};
		}
		Object[] res=new Object[ParameterCount];
		for(int i=0;i<ParameterCount;i++) {
			String argname="arg"+i;
			Object argvalue=argumentMaker.getValue(argname,
					parameters[i].getParameterizedType());
			res[i]=argvalue;
		}
		//得到了  参数的一个数组并且里面存着参数的值
		return res;
	}


最后因为要实现 方法利用***机制执行
故创建clientProxy类

public class RMIClientProxy1 {
	private RMIClient1 client;

	public RMIClientProxy1() {	
	}
		public void setClient(RMIClient1 client) {
		this.client = client;
	}
	
	@SuppressWarnings("unchecked")
	public <T> T getProxy(Class<?>  klass) {
//		loader, interfaces, h
		return (T) Proxy.newProxyInstance(klass.getClassLoader(), 
				klass.getInterfaces(), 
				new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						client.ConnectToServer();
						return client.invokeMethod(method, args);
					}
		}
				);
	}
}

这就是整个RMI的实现了!!!

全部评论

相关推荐

09-27 10:54
重庆大学 C++
人已微死:致敬传奇耐测王。
投递小米集团等公司10个岗位
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务