Java RMI初识
Java RMI 定义
Java RMI(Java Remote Method Invocation),即Java远程方法调用。是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口。 Java RMI 使用 JRMP(Java Remote Message Protocol,Java远程消息交换协议)实现,使得客户端运行的程序可以调用远程服务器上的对象。是实现RPC的一种方式。
JRMP:Java Remote Message Protocol ,Java 远程消息交换协议。这是运行在Java RMI之下、TCP/IP之上的线路层协议。
注:Java RMI默认使用JRMP协议,而Weblogic RMI使用的是T3协议,此处应进行区分
RMI的交互
此处引用文章JAVA RMI 原理和使用浅析的一张图演示RMI的交互过程
对于RMI流程的解释,网上有很多不错的回答,此处不过多阐述。通俗来讲,便是RMI Registry作为client和server的中间人,假设服务端是商品仓库,客户端是购买者,则RMI Registry就是中介,只负责告诉客户可以售卖的商品相关信息。仓库在中介处登记可售商品,客户从中介处查看可购买的商品。客户从中介处获得商品的相关信息,然后交给跑腿(存根stub)去跟仓库人员(骨架skeleton)取货,双方商品信息确认一致,进行交易,客户获得商品。(可能比喻有点不恰当,别揍我qwq...
RMI 尝试
此处为了更能体会rmi原理,不把server和client放在同一个机器,而是将服务器放在虚拟机中,测试环境均为1.8u211
编写远程服务接口,该接口必须继承 java.rmi.Remote 接口,方法必须抛出java.rmi.RemoteException 异常;
编写远程接口实现类,该实现类必须继承 java.rmi.server.UnicastRemoteObject 类;
运行 RMI 编译器(rmic),创建客户端 stub 类和服务端 skeleton 类;
启动一个 RMI 注册表,以便驻留这些服务;
在 RMI 注册表中注册服务;
客户端查找远程对象,并调用远程方法;
注意:这里踩了个小坑,RMI 抛出异常 no security manager: RMI class loader disabled,新手容易遇到这个问题。这是因为在编译class文件的时候,这个class文件内部已经包含了package test.org;这样的内容,如果在部署的时候,不严格按照这个目录部署的话,服务器就会找不到。所以服务器和客户端在路径上要保持一致。
Server测试例
org
└─test
├─RMI
│ RMIServer.java
│
└─Server
Hello.java
//RMIServer.java
package org.test.RMI;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;
import org.test.Server.Hello;
public class RMIServer {
public class RemoteHelloWorld extends UnicastRemoteObject implements Hello {
protected RemoteHelloWorld() throws RemoteException {
super();
}
public String hello() throws RemoteException {
System.out.println("call from");
return "Hello DEADF1SH_CAT~";
}
}
private void start() throws Exception {
RemoteHelloWorld h = new RemoteHelloWorld();
LocateRegistry.createRegistry(1099);
Naming.rebind("rmi://127.0.0.1:1099/Hello", h);
}
public static void main(String[] args) throws Exception {
new RMIServer().start();
}
}
//Hello.java
package org.test.Server;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Hello extends Remote {
public String hello() throws RemoteException;
}
Client测试例
org
└─test
├─client
│ TrainMain.java
│
└─Server
Hello.java
//TrainMain.java
package org.test.client;
import java.rmi.Naming;
import org.test.Server.Hello;
public class TrainMain {
public static void main(String[] args) throws Exception {
Hello hello = (Hello) Naming.lookup("rmi://192.168.247.128:1099/Hello");
String ret = hello.hello();
System.out.println(ret);
}
}
//Hello.java
package org.test.Server;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Hello extends Remote {
public String hello() throws RemoteException;
}
服务器输出
客户端输出
可以看出客户端成功调用了服务器的RemoteHelloWorld对象的hello方法
RMI数据包分析
对上述调用过程进行抓包分析
1-5为tcp三次握手和网关转发报文
6-12为RMI通信数据(其中TCP报文为确认报文)
来看看9号RMI Call报文(远程对象请求)
用SerializationDumper这个工具对Java序列化数据进行分析
F:\Java>java -jar SerializationDumper.jar aced00057722000000000000000000000000000000000000000000000000000244154dc9d4e63bdf74000548656c6c6f
STREAM_MAGIC - 0xac ed #声明使用了序列化协议
STREAM_VERSION - 0x00 05 #序列化协议版本
Contents
TC_BLOCKDATA - 0x77 #块数据
Length - 34 - 0x22
Contents - 0x000000000000000000000000000000000000000000000000000244154dc9d4e63bdf
TC_STRING - 0x74 #字符串
newHandle 0x00 7e 00 00
Length - 5 - 0x00 05
Value - Hello - 0x48656c6c6f
在Object Serialization Stream Protocol文档中,可以发现TC_BLOCKDATA 这部分对应的是 contents -> content -> blockdata -> blockdatashort , TC_STRING 这部分对应的是 contents -> content -> object-> newString。整个序列化数据含义并不难猜,其实就是告诉我们获取远程的Hello对象。
接下来看看11号RMI ReturnData报文(返回远程对象)
F:\Java> java -jar .\SerializationDumper.jar aced0005770f0175f02f4b00000170fd2fe646800e737d00000002000f6a6176612e726d692e52656d6f746500156f72672e746573742e5365727665722e48656c6c6f70787200176a6176612e6c616e672e7265666c6563742e50726f7879e127da20cc1043cb0200014c0001687400254c6a6176612f6c616e672f7265666c6563742f496e766f636174696f6e48616e646c65723b7078707372002d6a6176612e726d692e7365727665722e52656d6f74654f626a656374496e766f636174696f6e48616e646c65720000000000000002020000707872001c6a6176612e726d692e7365727665722e52656d6f74654f626a656374d361b4910c61331e0300007078707738000a556e6963617374526566000f3139322e3136382e3234372e3132380000c3ec8f6141e4b20d3e0e75f02f4b00000170fd2fe64680010178
STREAM_MAGIC - 0xac ed
STREAM_VERSION - 0x00 05
Contents
TC_BLOCKDATA - 0x77
Length - 15 - 0x0f
Contents - 0x0175f02f4b00000170fd2fe646800e
TC_OBJECT - 0x73
TC_PROXYCLASSDESC - 0x7d
newHandle 0x00 7e 00 00
Interface count - 2 - 0x00 00 00 02
proxyInterfaceNames
0:
Length - 15 - 0x00 0f
Value - java.rmi.Remote - 0x6a6176612e726d692e52656d6f7465
1:
Length - 21 - 0x00 15
Value - org.test.Server.Hello - 0x6f72672e746573742e5365727665722e48656c6c6f
classAnnotations
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
superClassDesc
TC_CLASSDESC - 0x72
className
Length - 23 - 0x00 17
Value - java.lang.reflect.Proxy - 0x6a6176612e6c616e672e7265666c6563742e50726f7879
serialVersionUID - 0xe1 27 da 20 cc 10 43 cb
newHandle 0x00 7e 00 01
classDescFlags - 0x02 - SC_SERIALIZABLE
fieldCount - 1 - 0x00 01
Fields
0:
Object - L - 0x4c
fieldName
Length - 1 - 0x00 01
Value - h - 0x68
className1
TC_STRING - 0x74
newHandle 0x00 7e 00 02
Length - 37 - 0x00 25
Value - Ljava/lang/reflect/InvocationHandler; - 0x4c6a6176612f6c616e672f7265666c6563742f496e766f636174696f6e48616e646c65723b
classAnnotations
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
superClassDesc
TC_NULL - 0x70
newHandle 0x00 7e 00 03
classdata
java.lang.reflect.Proxy
values
h
(object)
TC_OBJECT - 0x73
TC_CLASSDESC - 0x72
className
Length - 45 - 0x00 2d
Value - java.rmi.server.RemoteObjectInvocationHandler - 0x6a6176612e726d692e7365727665722e52656d6f74654f626a656374496e766f636174696f6e48616e646c6572
serialVersionUID - 0x00 00 00 00 00 00 00 02
newHandle 0x00 7e 00 04
classDescFlags - 0x02 - SC_SERIALIZABLE
fieldCount - 0 - 0x00 00
classAnnotations
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
superClassDesc
TC_CLASSDESC - 0x72
className
Length - 28 - 0x00 1c
Value - java.rmi.server.RemoteObject - 0x6a6176612e726d692e7365727665722e52656d6f74654f626a656374
serialVersionUID - 0xd3 61 b4 91 0c 61 33 1e
newHandle 0x00 7e 00 05
classDescFlags - 0x03 - SC_WRITE_METHOD | SC_SERIALIZABLE
fieldCount - 0 - 0x00 00
classAnnotations
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
superClassDesc
TC_NULL - 0x70
newHandle 0x00 7e 00 06
classdata
java.rmi.server.RemoteObject
values
objectAnnotation
TC_BLOCKDATA - 0x77
Length - 56 - 0x38
Contents - 0x000a556e6963617374526566000f3139322e3136382e3234372e3132380000c3ec8f6141e4b20d3e0e75f02f4b00000170fd2fe646800101 #里面包含RMI Server和端口
TC_ENDBLOCKDATA - 0x78
java.rmi.server.RemoteObjectInvocationHandler
values
内容突然多了起来,不过看到一些跟java.lang.reflect.Proxy对象有关的东西,其实就是返回了调用远程方法的服务器地址和端口。怎么确定呢,观察后续tcp交互的报文即可知道。
再看19号数据包,也是一串aced开头的反序列数据
丢进去工具分析一下序列化数据
F:\Java> java -jar .\SerializationDumper.jar 50aced000577220000000000000002000000000000000000000000000000000001f6b6898d8bf28643757200185b4c6a6176612e726d692e7365727665722e4f626a49443b871300b8d02c647e02000070787000000001737200156a6176612e726d692e7365727665722e4f626a4944a75efa128ddce55c0200024a00066f626a4e756d4c000573706163657400154c6a6176612f726d692f7365727665722f5549443b7078708f6141e4b20d3e0e737200136a6176612e726d692e7365727665722e5549440f12700dbf364f12020003530005636f756e744a000474696d65490006756e69717565707870800100000170fd2fe64675f02f4b77088000000000000000737200126a6176612e726d692e6467632e4c65617365b0b5e2660c4adc340200024a000576616c75654c0004766d69647400134c6a6176612f726d692f6467632f564d49443b70787000000000000927c0737200116a6176612e726d692e6467632e564d4944f8865bafa4a56db60200025b0004616464727400025b424c000375696471007e0003707870757200025b42acf317f8060854e002000070787000000008ed0c29a151acf1bb7371007e0005800100000170fd4ea68fcc0083e6
RMI Call - 0x50
STREAM_MAGIC - 0xac ed
STREAM_VERSION - 0x00 05
Contents
TC_BLOCKDATA - 0x77
Length - 34 - 0x22
Contents - 0x0000000000000002000000000000000000000000000000000001f6b6898d8bf28643
TC_ARRAY - 0x75
TC_CLASSDESC - 0x72
className
Length - 24 - 0x00 18
Value - [Ljava.rmi.server.ObjID; - 0x5b4c6a6176612e726d692e7365727665722e4f626a49443b
serialVersionUID - 0x87 13 00 b8 d0 2c 64 7e
newHandle 0x00 7e 00 00
classDescFlags - 0x02 - SC_SERIALIZABLE
fieldCount - 0 - 0x00 00
classAnnotations
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
superClassDesc
TC_NULL - 0x70
newHandle 0x00 7e 00 01
Array size - 1 - 0x00 00 00 01
Values
Index 0:
(object)
TC_OBJECT - 0x73
TC_CLASSDESC - 0x72
className
Length - 21 - 0x00 15
Value - java.rmi.server.ObjID - 0x6a6176612e726d692e7365727665722e4f626a4944
serialVersionUID - 0xa7 5e fa 12 8d dc e5 5c
newHandle 0x00 7e 00 02
classDescFlags - 0x02 - SC_SERIALIZABLE
fieldCount - 2 - 0x00 02
Fields
0:
Long - L - 0x4a
fieldName
Length - 6 - 0x00 06
Value - objNum - 0x6f626a4e756d
1:
Object - L - 0x4c
fieldName
Length - 5 - 0x00 05
Value - space - 0x7370616365
className1
TC_STRING - 0x74
newHandle 0x00 7e 00 03
Length - 21 - 0x00 15
Value - Ljava/rmi/server/UID; - 0x4c6a6176612f726d692f7365727665722f5549443b
classAnnotations
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
superClassDesc
TC_NULL - 0x70
newHandle 0x00 7e 00 04
classdata
java.rmi.server.ObjID
values
objNum
(long)-72056500129022450 - 0x8f 61 41 e4 b2 0d 3e 0e
space
(object)
TC_OBJECT - 0x73
TC_CLASSDESC - 0x72
className
Length - 19 - 0x00 13
Value - java.rmi.server.UID - 0x6a6176612e726d692e7365727665722e554944
serialVersionUID - 0x0f 12 70 0d bf 36 4f 12
newHandle 0x00 7e 00 05
classDescFlags - 0x02 - SC_SERIALIZABLE
fieldCount - 3 - 0x00 03
Fields
0:
Short - S - 0x53
fieldName
Length - 5 - 0x00 05
Value - count - 0x636f756e74
1:
Long - L - 0x4a
fieldName
Length - 4 - 0x00 04
Value - time - 0x74696d65
2:
Int - I - 0x49
fieldName
Length - 6 - 0x00 06
Value - unique - 0x756e69717565
classAnnotations
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
superClassDesc
TC_NULL - 0x70
newHandle 0x00 7e 00 06
classdata
java.rmi.server.UID
values
count
(short)-32767 - 0x80 01
time
(long)-47192506 - 0x00 00 01 70 fd 2f e6 46
unique
(int)1978675019 - 0x75 f0 2f 4b
TC_BLOCKDATA - 0x77
Length - 8 - 0x08
Contents - 0x8000000000000000
TC_OBJECT - 0x73
TC_CLASSDESC - 0x72
className
Length - 18 - 0x00 12
Value - java.rmi.dgc.Lease - 0x6a6176612e726d692e6467632e4c65617365
serialVersionUID - 0xb0 b5 e2 66 0c 4a dc 34
newHandle 0x00 7e 00 07
classDescFlags - 0x02 - SC_SERIALIZABLE
fieldCount - 2 - 0x00 02
Fields
0:
Long - L - 0x4a
fieldName
Length - 5 - 0x00 05
Value - value - 0x76616c7565
1:
Object - L - 0x4c
fieldName
Length - 4 - 0x00 04
Value - vmid - 0x766d6964
className1
TC_STRING - 0x74
newHandle 0x00 7e 00 08
Length - 19 - 0x00 13
Value - Ljava/rmi/dgc/VMID; - 0x4c6a6176612f726d692f6467632f564d49443b
classAnnotations
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
superClassDesc
TC_NULL - 0x70
newHandle 0x00 7e 00 09
classdata
java.rmi.dgc.Lease
values
value
(long)600000 - 0x00 00 00 00 00 09 27 c0
vmid
(object)
TC_OBJECT - 0x73
TC_CLASSDESC - 0x72
className
Length - 17 - 0x00 11
Value - java.rmi.dgc.VMID - 0x6a6176612e726d692e6467632e564d4944
serialVersionUID - 0xf8 86 5b af a4 a5 6d b6
newHandle 0x00 7e 00 0a
classDescFlags - 0x02 - SC_SERIALIZABLE
fieldCount - 2 - 0x00 02
Fields
0:
Array - [ - 0x5b
fieldName
Length - 4 - 0x00 04
Value - addr - 0x61646472
className1
TC_STRING - 0x74
newHandle 0x00 7e 00 0b
Length - 2 - 0x00 02
Value - [B - 0x5b42
1:
Object - L - 0x4c
fieldName
Length - 3 - 0x00 03
Value - uid - 0x756964
className1
TC_REFERENCE - 0x71
Handle - 8257539 - 0x00 7e 00 03
classAnnotations
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
superClassDesc
TC_NULL - 0x70
newHandle 0x00 7e 00 0c
classdata
java.rmi.dgc.VMID
values
addr
(array)
TC_ARRAY - 0x75
TC_CLASSDESC - 0x72
className
Length - 2 - 0x00 02
Value - [B - 0x5b42
serialVersionUID - 0xac f3 17 f8 06 08 54 e0
newHandle 0x00 7e 00 0d
classDescFlags - 0x02 - SC_SERIALIZABLE
fieldCount - 0 - 0x00 00
classAnnotations
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
superClassDesc
TC_NULL - 0x70
newHandle 0x00 7e 00 0e
Array size - 8 - 0x00 00 00 08
Values
Index 0:
(byte)-19 - 0xed
Index 1:
(byte)12 - 0x0c
Index 2:
(byte)41 (ASCII: )) - 0x29
Index 3:
(byte)-95 - 0xa1
Index 4:
(byte)81 (ASCII: Q) - 0x51
Index 5:
(byte)-84 - 0xac
Index 6:
(byte)-15 - 0xf1
Index 7:
(byte)-69 - 0xbb
uid
(object)
TC_OBJECT - 0x73
TC_REFERENCE - 0x71
Handle - 8257541 - 0x00 7e 00 05
newHandle 0x00 7e 00 0f
classdata
java.rmi.server.UID
values
count
(short)-32767 - 0x80 01
time
(long)-45177201 - 0x00 00 01 70 fd 4e a6 8f
unique
(int)-872381466 - 0xcc 00 83 e6
数据太多,基于自身知识有限,先留个坑(别打我,真不会哇,后面专门开一篇文章分析本篇文章的几份序列化数据,不过通过这波抓包分析流程,结合上面的流程图,相信大家对RMI调用流程也有一定的理解了。
相关链接