前言
之前對rmi模模糊糊的,這次好好看看吧。如有錯誤,懇請指出
程式碼結構
RMI中有三個角色
- rmi客戶端
- rmi註冊中心
- rmi服務端(在jdk高版本必須和rmi註冊中心在同一臺主機)
rmiServerInterface介面
package com.Rmi;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface rmiServerInterface extends Remote {
/**
* RMI測試方法
* @return 返回測試字串
*/
String test(String name) throws RemoteException;
}
- 繼承Remote,表示方法可能不在本地虛擬機器執行
- 在客戶端和服務端都要有該介面,服務端需要有具體實現
RMI 註冊中心 和 rmi 服務端
rmiServerInterfaceImpl
rmiServerInterface 介面的具體實現,需要繼承UnicastRemoteObject
,表明是一個遠端物件。
public class rmiServerInterfaceImpl extends UnicastRemoteObject implements rmiServerInterface {
private static final long serialVersionUID = 1L;
protected rmiServerInterfaceImpl(int port) throws RemoteException {
super(port);
}
protected rmiServerInterfaceImpl() throws RemoteException {
super();
}
/**
* RMI測試方法
*
* @return 返回測試字串
*/
@Override
public String test(String name) throws RemoteException {
return "Hello "+ name.toUpperCase();
}
rmiRegister
這裡為了方便區分,把register
和server
分開寫來,(或者不對bind進行註釋,寫到一個檔案也可以)。
public class rmiRegister {
// RMI伺服器IP地址
public static final String RMI_HOST = "127.0.0.1";
// RMI服務埠
public static final int RMI_PORT = 1099;
public static void main(String[] args) {
try {
// 註冊RMI埠
LocateRegistry.createRegistry(RMI_PORT);
// registry.bind("test",new rmiServerInterfaceImpl(6666));
System.out.println("RMI服務啟動成功,服務地址:" + RMI_HOST+":" + RMI_PORT);
while(true) {
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
rmiServer
public class rmiServer {
public static void main(String[] args) {
try {
Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099);
registry.bind("test",new rmiServerInterfaceImpl(6666));
} catch (RemoteException e) {
} catch (AlreadyBoundException e) {
throw new RuntimeException(e);
}
}
}
除錯
註冊中心
新建一個Registrt
實現類
可以看到Registry
介面也繼承了Remote
方法,預設就開在1099埠。
public interface Registry extends Remote {
public static final int REGISTRY_PORT = 1099;
public Remote lookup(String name)
throws RemoteException, NotBoundException, AccessException;
public void bind(String name, Remote obj)
throws RemoteException, AlreadyBoundException, AccessException;
public void unbind(String name)
throws RemoteException, NotBoundException, AccessException;
public void rebind(String name, Remote obj)
throws RemoteException, AccessException;
public String[] list() throws RemoteException, AccessException;
}
RegistryImpl
生成一個LiveRef
例項
構造方法如下
public LiveRef(ObjID objID, int port) {
this(objID, TCPEndpoint.getLocalEndpoint(port), true);
}
objID
為註冊中心對應的唯一objID
第二個引數生成一個TCPEndpint物件,其中的TCPEndpint.transport用來處理網路請求。
第三個引數為true,表示是一個本地引用。
UnicastServerRef
即服務端遠端引用。遠端引用 表示遠端物件的控制代碼。RemoteStub 使用遠端引用對遠端物件執行遠端方法呼叫。
RegistryImpl.setup
private void setup(UnicastServerRef uref)
throws RemoteException
{
ref = uref; //
uref.exportObject(this, null, true); // 使用 UnicastServerRef去釋出RegistryImpl自己
}
UnicastServerRef.exportObject
public Remote exportObject(Remote impl, Object data,
boolean permanent)
throws RemoteException
{
//獲取Class,這裡就是 RegistryImpl
Class<?> implClass = impl.getClass();
//客戶端代理
Remote stub;
try {
//建立客戶端代理
stub = Util.createProxy(implClass, getClientRef(), forceStubUse);
} catch (IllegalArgumentException e) {
throw new ExportException(
"remote object implements illegal remote interface", e);
}
//如果stub是RemoteStub,則建立Skeleton
if (stub instanceof RemoteStub) {
setSkeleton(impl);
}
//封裝為target
Target target =
new Target(impl, this, stub, ref.getObjID(), permanent);
//使用 ref(也就是之前的Liveref釋出物件)
ref.exportObject(target);
hashToMethod_Map = hashToMethod_Maps.get(implClass);
return stub;
}
Util.createProxy
構造方法三個引數
-
第一個即遠端物件介面實現類
-
第二個為remoteRef 的客戶端遠端引用(ref屬性,即LiveRef物件的封裝)
-
第三個,是否則強制建立 RemoteStub,預設為false
public static Remote createProxy(Class<?> implClass,
RemoteRef clientRef,
boolean forceStubUse)
throws StubNotFoundException
{
Class<?> remoteClass;
try {
//主要判斷該類是否繼承了Remote介面
remoteClass = getRemoteClass(implClass);
} catch (ClassNotFoundException ex ) {
throw new StubNotFoundException(
"object does not implement a remote interface: " +
implClass.getName());
}
// 如果forceStubUse為true 或 忽略Stub為flase並且remoteClass的stub類存在時,建立createStub物件
if (forceStubUse ||
!(ignoreStubClasses || !stubClassExists(remoteClass)))
{
//註冊中心初始化時 ,進入該函式
return createStub(remoteClass, clientRef);
}
final ClassLoader loader = implClass.getClassLoader();
final Class<?>[] interfaces = getRemoteInterfaces(implClass);
final InvocationHandler handler =
new RemoteObjectInvocationHandler(clientRef);
/* REMIND: private remote interfaces? */
try {
return AccessController.doPrivileged(new PrivilegedAction<Remote>() {
public Remote run() {
return (Remote) Proxy.newProxyInstance(loader,
interfaces,
handler);
}});
} catch (IllegalArgumentException e) {
throw new StubNotFoundException("unable to create proxy", e);
}
}
Util.createStub
private static RemoteStub createStub(Class<?> remoteClass, RemoteRef ref)
throws StubNotFoundException
{
//之前判斷存在該_Stub類
String stubname = remoteClass.getName() + "_Stub";
try {
//反射初始化即可
Class<?> stubcl =
Class.forName(stubname, false, remoteClass.getClassLoader());
Constructor<?> cons = stubcl.getConstructor(stubConsParamTypes);
//需要一個客戶端引用(LiveRef)的封裝
return (RemoteStub) cons.newInstance(new Object[] { ref });
}
UnicastServerRefset.Skeleton
遠端物件的Skeleton是一個伺服器端實體,它將呼叫分派給實際的遠端物件實現
public void setSkeleton(Remote impl) throws RemoteException {
//同樣,首先判斷 遠端物件 是否存在 xxx_Skeleton類
if (!withoutSkeletons.containsKey(impl.getClass())) {
try {
//建立Skeleton
skel = Util.createSkeleton(impl);
} catch (SkeletonNotFoundException e) {
withoutSkeletons.put(impl.getClass(), null);
}
}
}
剩下的和Stub
類似
static Skeleton createSkeleton(Remote object)
throws SkeletonNotFoundException
{
Class<?> cl;
cl = getRemoteClass(object.getClass());
// now try to load the skeleton based ont he name of the class
String skelname = cl.getName() + "_Skel";
try {
Class<?> skelcl = Class.forName(skelname, false, cl.getClassLoader());
return (Skeleton)skelcl.newInstance();
}
}
Target
主要是之前物件的封裝。這裡permanent
為true(永久的?內建的?)
public Target(Remote impl, Dispatcher disp, Remote stub, ObjID id,
boolean permanent)
{
this.weakImpl = new WeakRef(impl, ObjectTable.reapQueue);
this.disp = disp;
this.stub = stub;
this.id = id;
this.acc = AccessController.getContext();
ClassLoader threadContextLoader =
Thread.currentThread().getContextClassLoader();
ClassLoader serverLoader = impl.getClass().getClassLoader();
if (checkLoaderAncestry(threadContextLoader, serverLoader)) {
this.ccl = threadContextLoader;
} else {
this.ccl = serverLoader;
}
this.permanent = permanent;
if (permanent) {
//會呼叫pinImpl。註釋解釋為: 在 Target 中 Pin impl。固定 WeakRef 物件,使其包含對物件的強引用,則不會在本地進行垃圾回收。這樣,只有一個物件負責弱 ref 機制。貌似和垃圾回收有關
pinImpl();
}
}
LiveRef.exportObject
=>呼叫內部的 TCPEndpoint.exportObject
=>呼叫內部的 TCPTransport.exportObject
主要程式碼
public void exportObject(Target target) throws RemoteException {
{
listen();
exportCount++;
super.exportObject(target);
}
}
Transport.exportObject
public void exportObject(Target target) throws RemoteException {
//將該Transport設定為exportedTransport
target.setExportedTransport(this);
//將targe加入到物件表中
ObjectTable.putTarget(target);
}
ObjectTable.putTarget
static void putTarget(Target target) throws ExportException {
// 獲取物件端點 封裝了ID(ref的,也就是LiveRef的,在這裡是RegisterImpl對應的ID)和Transport物件)
ObjectEndpoint oe = target.getObjectEndpoint();
WeakRef weakImpl = target.getWeakImpl();
//第一次進入時,會初始化dgcLog物件
if (DGCImpl.dgcLog.isLoggable(Log.VERBOSE)) {
DGCImpl.dgcLog.log(Log.VERBOSE, "add object " + oe);
}
synchronized (tableLock) {
if (target.getImpl() != null) {
if (objTable.containsKey(oe)) {
throw new ExportException(
"internal error: ObjID already in use");
} else if (implTable.containsKey(weakImpl)) {
throw new ExportException("object already exported");
}
//將oe和關聯的target壓入全域性靜態物件雜湊表中
objTable.put(oe, target);
//將weakImpl和關聯的target壓入全域性靜態物件雜湊表中
//weakImpl主要是RegisterImpl物件和對應的RemoteRef(ServerRef)
implTable.put(weakImpl, target);
if (!target.isPermanent()) {
incrementKeepAliveCount();
}
}
}
}
DGCImpl
static {
/*
* "Export" the singleton DGCImpl in a context isolated from
* the arbitrary current thread context.
*/
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
ClassLoader savedCcl =
Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(
ClassLoader.getSystemClassLoader());
try {
//建立DGC實現類,DGC也是一個繼承了Remote介面的介面。
//可以看作RMI自帶的一個服務
dgc = new DGCImpl();
//DGC的ID為2,其餘引數為0
ObjID dgcID = new ObjID(ObjID.DGC_ID);
//0,也就是隨機埠
LiveRef ref = new LiveRef(dgcID, 0);
//傳教服務端引用
UnicastServerRef disp = new UnicastServerRef(ref);
//傳教客戶端引用代理
Remote stub =
Util.createProxy(DGCImpl.class,
new UnicastRef(ref), true);
//設定Skeleton(負責處理具體程式碼邏輯)
disp.setSkeleton(dgc);
Permissions perms = new Permissions();
perms.add(new SocketPermission("*", "accept,resolve"));
ProtectionDomain[] pd = { new ProtectionDomain(null, perms) };
AccessControlContext acceptAcc = new AccessControlContext(pd);
//封裝為target物件
Target target = AccessController.doPrivileged(
new PrivilegedAction<Target>() {
public Target run() {
return new Target(dgc, disp, stub, dgcID, true);
}
}, acceptAcc);
//放入全域性靜態物件表中 objTable和implTable中
ObjectTable.putTarget(target);
} catch (RemoteException e) {
throw new Error(
"exception initializing server-side DGC", e);
}
} finally {
Thread.currentThread().setContextClassLoader(savedCcl);
}
return null;
}
});
}
所以在釋出完後是有兩個服務的
服務端
//這裡其實就是本地建立 Register_stub代理類,而不是向register請求(雖然register也生成了該類)
//進去可以看到和 註冊中心生成stub的動作是一致的
Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099);
使用registry
註冊服務
registry.bind("test",new rmiServerInterfaceImpl(6666));
先看生成服務的過程
UnicastRemoteObject
protected UnicastRemoteObject(int port) throws RemoteException
{
//指定埠,然後匯出 RemoteObject物件
this.port = port;
exportObject((Remote) this, port);
}
UnicastRemoteObject.exportObject#1
public static Remote exportObject(Remote obj, int port)
throws RemoteException
{
//新建UnicastServerRef,即主要是服務端引用(sket),匯出
return exportObject(obj, new UnicastServerRef(port));
}
UnicastServerRef物件初始化需要一個LiveRef
物件
public LiveRef(ObjID objID, int port) {
//LiveRef 隨機id, 端點類 是否本地
this(objID, TCPEndpoint.getLocalEndpoint(port), true);
}
UnicastRemoteObject.exportObject#2
private static Remote exportObject(Remote obj, UnicastServerRef sref)
throws RemoteException
{
// 是否繼承了 UnicastRemoteObject
if (obj instanceof UnicastRemoteObject) {
//設定服務端引用
((UnicastRemoteObject) obj).ref = sref;
}
return sref.exportObject(obj, null, false);
}
UnicastServerRef.exportObject
這個方法再除錯註冊中心時已經看到過了(也就是說RegisterImpl
和我們自己實現的rmiServerInterfaceImpl
地位是一樣的),區別為第三引數為false(不是永久的,內建的?)
public Remote exportObject(Remote impl, Object data,
boolean permanent)
throws RemoteException
{
Class<?> implClass = impl.getClass();
Remote stub;
try {
stub = Util.createProxy(implClass, getClientRef(), forceStubUse);
} catch (IllegalArgumentException e) {
throw new ExportException(
"remote object implements illegal remote interface", e);
}
//由於沒有實現 rmiInterfaceImpl_stub,故返回的是一個代理物件
if (stub instanceof RemoteStub) {
setSkeleton(impl);
}
//封裝為target
Target target =
new Target(impl, this, stub, ref.getObjID(), permanent);
//匯出
ref.exportObject(target);
hashToMethod_Map = hashToMethod_Maps.get(implClass);
return stub;
}
Util.createProxy
和reigster中的流程略有不同
public static Remote createProxy(Class<?> implClass,
RemoteRef clientRef,
boolean forceStubUse)
throws StubNotFoundException
{
Class<?> remoteClass;
try {
// impl的介面是否繼承了remote
remoteClass = getRemoteClass(implClass);
} catch (ClassNotFoundException ex ) {
throw new StubNotFoundException(
"object does not implement a remote interface: " +
implClass.getName());
}
//在stubClassExists 並沒有找到 rmiInterfaceImpl_stub,所以不會進入
if (forceStubUse ||
!(ignoreStubClasses || !stubClassExists(remoteClass)))
{
return createStub(remoteClass, clientRef);
}
//如果沒有實現自己的 stub 則對clientRef設定動態代理
final ClassLoader loader = implClass.getClassLoader();
final Class<?>[] interfaces = getRemoteInterfaces(implClass);
final InvocationHandler handler =
new RemoteObjectInvocationHandler(clientRef);
/* REMIND: private remote interfaces? */
try {
return AccessController.doPrivileged(new PrivilegedAction<Remote>() {
public Remote run() {
return (Remote) Proxy.newProxyInstance(loader,
interfaces,
handler);
}});
} catch (IllegalArgumentException e) {
throw new StubNotFoundException("unable to create proxy", e);
}
}
LiveRef.exportObject
後面的流程基本一致,略
bind
public void bind(String name, Remote server) throws AccessException, AlreadyBoundException, RemoteException {
try {
//呼叫 registerImpl_stub 代理 執行bind操作 (操作碼為0,對應雜湊為4905912898345647071L)
//在建立期間會寫入 0x50 call標識1位元組 關聯的LiveRef的ID 操作碼 和 雜湊值
RemoteCall call = super.ref.newCall(this, operations, 0, 4905912898345647071L);
try {
ObjectOutput var4 = call.getOutputStream();
//序列化server名稱 這裡為test
var4.writeObject(name);
//序列化 server
var4.writeObject(server);
} catch (IOException var5) {
throw new MarshalException("error marshalling arguments", var5);
}
super.ref.invoke(call);
super.ref.done(call);
} catch (RuntimeException var6) {
throw var6;
} catch (RemoteException var7) {
throw var7;
} catch (AlreadyBoundException var8) {
throw var8;
} catch (Exception var9) {
throw new UnexpectedException("undeclared checked exception", var9);
}
}
UnicastRef.invoke
public void invoke(RemoteCall call) throws Exception {
call.executeCall();
}
StreamRemoteCall.executeCall
public void executeCall() throws Exception {
byte returnType;
// read result header
DGCAckHandler ackHandler = null;
try {
if (out != null) {
ackHandler = out.getDGCAckHandler();
}
releaseOutputStream();
DataInputStream rd = new DataInputStream(conn.getInputStream());
//讀取標識 正常為 0x51
byte op = rd.readByte();
if (op != TransportConstants.Return) {
if (Transport.transportLog.isLoggable(Log.BRIEF)) {
Transport.transportLog.log(Log.BRIEF,
"transport return code invalid: " + op);
}
throw new UnmarshalException("Transport return code invalid");
}
getInputStream();
//讀取一個位元組 0x1 標識正常返回
returnType = in.readByte();
// id dgc相關
in.readID(); // id for DGC acknowledgement
} catch (UnmarshalException e) {
throw e;
} catch (IOException e) {
throw new UnmarshalException("Error unmarshaling return header",
e);
} finally {
if (ackHandler != null) {
ackHandler.release();
}
}
// read return value
switch (returnType) {
//正常返回 0x01
case TransportConstants.NormalReturn:
break;
//異常返回 0x02
case TransportConstants.ExceptionalReturn:
Object ex;
try {
//正常邏輯是反序列化一個異常類,但我們的服務端可以傳入一個惡意類,觸發反序列化漏洞
ex = in.readObject();
} catch (Exception e) {
throw new UnmarshalException("Error unmarshaling return", e);
}
if (ex instanceof Exception) {
exceptionReceivedFromServer((Exception) ex);
} else {
throw new UnmarshalException("Return type not Exception");
}
default:
if (Transport.transportLog.isLoggable(Log.BRIEF)) {
Transport.transportLog.log(Log.BRIEF,
"return code invalid: " + returnType);
}
throw new UnmarshalException("Return code invalid");
}
}
客戶端
和服務端其實差不多,呼叫RegisterImpl_stub
的lookup方法
RegisterImpl_stub.lookup
public Remote lookup(String name) throws AccessException, NotBoundException, RemoteException {
try {
RemoteCall call = super.ref.newCall(this, operations, 2, 4905912898345647071L);
try {
ObjectOutput var3 = call.getOutputStream();
var3.writeObject(name);
} catch (IOException var18) {
throw new MarshalException("error marshalling arguments", var18);
}
super.ref.invoke(var2);
Remote var23;
try {
ObjectInput var6 = var2.getInputStream();
var23 = (Remote)var6.readObject();
} catch (IOException var15) {
throw new UnmarshalException("error unmarshalling return", var15);
} catch (ClassNotFoundException var16) {
throw new UnmarshalException("error unmarshalling return", var16);
} finally {
super.ref.done(var2);
}
return var23;
} catch (RuntimeException var19) {
throw var19;
} catch (RemoteException var20) {
throw var20;
} catch (NotBoundException var21) {
throw var21;
} catch (Exception var22) {
throw new UnexpectedException("undeclared checked exception", var22);
}
}
UnicastRef.invoke
=>StreamRemoteCall.executeCall
=>UnicastRef.done
=>SteamRemoteCall.done
=>SteamRemoteCall.releaseInputStream
public void releaseInputStream() throws IOException {
/* WARNING: Currently, the UnicastRef.java invoke methods rely
* upon this method not throwing an IOException.
*/
try {
if (in != null) {
try {
in.done();
} catch (RuntimeException e) {
}
// 將剛才獲取的引用新增到 DGC 表
in.registerRefs();
/* WARNING: The connection being passed to done may have
* already been freed.
*/
in.done(conn);
}
conn.releaseInputStream();
} finally {
in = null;
}
}
registerRefs
void registerRefs() throws IOException {
//incomingRefTable為 將 Endpoints 對映到註冊的 LiveRef列表的雜湊表
if (!incomingRefTable.isEmpty()) {
for (Map.Entry<Endpoint, List<LiveRef>> entry :
incomingRefTable.entrySet()) {
DGCClient.registerRefs(entry.getKey(), entry.getValue());
}
}
}
DGC.registerRefs
根據ep查詢
static void registerRefs(Endpoint ep, List<LiveRef> refs) {
EndpointEntry epEntry;
do {
epEntry = EndpointEntry.lookup(ep);
} while (!epEntry.registerRefs(refs));
}
EndpointEntry為DGC的內部類
EndpointEntry.lookup
public static EndpointEntry lookup(Endpoint ep) {
synchronized (endpointTable) {
EndpointEntry entry = endpointTable.get(ep);
if (entry == null) {
//生成EndpointEntry例項
entry = new EndpointEntry(ep);
//新增到全域性靜態endpointTable表中
endpointTable.put(ep, entry);
if (gcLatencyRequest == null) {
gcLatencyRequest = GC.requestLatency(gcInterval);
}
}
return entry;
}
}
EndpointEntry
private EndpointEntry(final Endpoint endpoint) {
this.endpoint = endpoint;
try {
//生成DGC本地代理
LiveRef dgcRef = new LiveRef(dgcID, endpoint, false);
dgc = (DGC) Util.createProxy(DGCImpl.class,
new UnicastRef(dgcRef), true);
} catch (RemoteException e) {
throw new Error("internal error creating DGC stub");
}
//開啟一個新執行緒去執行
renewCleanThread = AccessController.doPrivileged(
new NewThreadAction(new RenewCleanThread(),
"RenewClean-" + endpoint, true));
renewCleanThread.start();
}
UnicastRef.invoke#2
public Object invoke(Remote obj,
Method method,
Object[] params,
long opnum)
throws Exception
{
//建立連線
Connection conn = ref.getChannel().newConnection();
RemoteCall call = null;
boolean reuse = true;
boolean alreadyFreed = false;
try {
//建立 RMI CALL上下文, 操作碼為 -1 hash為對應方法的hash值
call = new StreamRemoteCall(conn, ref.getObjID(), -1, opnum);
// 序列化引數
try {
ObjectOutput out = call.getOutputStream();
marshalCustomCallData(out);
Class<?>[] types = method.getParameterTypes();
for (int i = 0; i < types.length; i++) {
marshalValue(types[i], params[i], out);
}
} catch (IOException e) {
clientRefLog.log(Log.BRIEF,
"IOException marshalling arguments: ", e);
throw new MarshalException("error marshalling arguments", e);
}
// 跟進
call.executeCall();
try {
//獲取返回值型別
Class<?> rtype = method.getReturnType();
if (rtype == void.class)
return null;
ObjectInput in = call.getInputStream();
//會判斷是否基本資料型別,否則直接readObject
Object returnValue = unmarshalValue(rtype, in);
alreadyFreed = true;
ref.getChannel().free(conn, true);
return returnValue;
} catch (IOException e) {
....
} finally {
try {
call.done();
} catch (IOException e) {
reuse = false;
}
}
} catch (RuntimeException e) {
}
Register 監聽
private void listen() throws RemoteException {
assert Thread.holdsLock(this);
//獲取list中獲取端點類
TCPEndpoint ep = getEndpoint();
//獲取監聽埠
int port = ep.getPort();
if (server == null) {
try {
//建立socket
server = ep.newServerSocket();
//開啟新執行緒去執行
Thread t = AccessController.doPrivileged(
new NewThreadAction(new AcceptLoop(server),
"TCP Accept-" + port, true));
t.start();
} catch (java.net.BindException e) {
throw new ExportException("Port already in use: " + port, e);
} catch (IOException e) {
throw new ExportException("Listen failed on port: " + port, e);
}
} else {
}
}
AcceptLoop.run
AcceptLoop為TCPTransport內部類,呼叫了 TCPTransport.executeAcceptLoop
主要程式碼
private void executeAcceptLoop() {
while (true) {
Socket socket = null;
try {
socket = serverSocket.accept();
InetAddress clientAddr = socket.getInetAddress();
String clientHost = (clientAddr != null
? clientAddr.getHostAddress()
: "0.0.0.0");
//建立執行緒池,進行連線處理
try {
connectionThreadPool.execute(
new ConnectionHandler(socket, clientHost));
} catch (RejectedExecutionException e) {
closeSocket(socket);
tcpLog.log(Log.BRIEF,
"rejected connection from " + clientHost);
}
}
}
跟進ConnectionHandler類,ConnectionHandle也為TCPTransport的內部類
=> ConnectionHandler.run
=>ConnectionHandler.run0
private void run0() {
TCPEndpoint endpoint = getEndpoint();
int port = endpoint.getPort();
threadConnectionHandler.set(this);
//set socket 禁用 Nagle 演算法(始終立即傳送)
try {
socket.setTcpNoDelay(true);
} catch (Exception e) {
// if we fail to set this, ignore and proceed anyway
}
// 設定請求超時
try {
if (connectionReadTimeout > 0)
socket.setSoTimeout(connectionReadTimeout);
} catch (Exception e) {
// too bad, continue anyway
}
try {
//獲取輸入流
InputStream sockIn = socket.getInputStream();
InputStream bufIn = sockIn.markSupported()
? sockIn
: new BufferedInputStream(sockIn);
//讀取magic
bufIn.mark(4);
DataInputStream in = new DataInputStream(bufIn);
int magic = in.readInt();
//獲取請求方式,即POST的十六進位制
if (magic == POST) {
tcpLog.log(Log.BRIEF, "decoding HTTP-wrapped call");
// It's really a HTTP-wrapped request. Repackage
// the socket in a HttpReceiveSocket, reinitialize
// sockIn and in, and reread magic.
//可能是http協議封裝的RMI協議
bufIn.reset(); // unread "POST"
try {
socket = new HttpReceiveSocket(socket, bufIn, null);
remoteHost = "0.0.0.0";
sockIn = socket.getInputStream();
bufIn = new BufferedInputStream(sockIn);
in = new DataInputStream(bufIn);
magic = in.readInt();
} catch (IOException e) {
throw new RemoteException("Error HTTP-unwrapping call",
e);
}
}
// bufIn's mark will invalidate itself when it overflows
// so it doesn't have to be turned off
// read and verify transport header
short version = in.readShort();
// !(magic為JRMI &&version為2) 關閉連線
if (magic != TransportConstants.Magic ||
version != TransportConstants.Version) {
closeSocket(socket);
return;
}
OutputStream sockOut = socket.getOutputStream();
BufferedOutputStream bufOut =
new BufferedOutputStream(sockOut);
DataOutputStream out = new DataOutputStream(bufOut);
int remotePort = socket.getPort();
TCPEndpoint ep;
TCPChannel ch;
TCPConnection conn;
// 再讀一個位元組,預設是0x4b StreamProtocol
byte protocol = in.readByte();
switch (protocol) {
case TransportConstants.SingleOpProtocol:
// no ack for protocol
// create dummy channel for receiving messages
ep = new TCPEndpoint(remoteHost, socket.getLocalPort(),
endpoint.getClientSocketFactory(),
endpoint.getServerSocketFactory());
ch = new TCPChannel(TCPTransport.this, ep);
conn = new TCPConnection(ch, socket, bufIn, bufOut);
// read input messages
handleMessages(conn, false);
break;
case TransportConstants.StreamProtocol:
// 傳送 ack 0x4e
out.writeByte(TransportConstants.ProtocolAck);
// 傳送建議終端節點(在客戶端不知道主機名的情況下)
if (tcpLog.isLoggable(Log.VERBOSE)) {
tcpLog.log(Log.VERBOSE, "(port " + port +
") " + "suggesting " + remoteHost + ":" +
remotePort);
}
out.writeUTF(remoteHost);
out.writeInt(remotePort);
out.flush();
// 讀取並丟棄(可能是偽造的)端點
// REMIND: would be faster to read 2 bytes then skip N+4
String clientHost = in.readUTF();
// 這裡客戶端傳送的埠號預設為0
int clientPort = in.readInt();
if (tcpLog.isLoggable(Log.VERBOSE)) {
tcpLog.log(Log.VERBOSE, "(port " + port +
") client using " + clientHost + ":" + clientPort);
}
//建立用於接收訊息的虛擬通道
//端點 後面兩個引數預設為空
ep = new TCPEndpoint(remoteHost, socket.getLocalPort(),
endpoint.getClientSocketFactory(),
endpoint.getServerSocketFactory());
//通道
ch = new TCPChannel(TCPTransport.this, ep);
//連線
conn = new TCPConnection(ch, socket, bufIn, bufOut);
//讀取輸入訊息,此客戶端傳送的 RMI CALL
handleMessages(conn, true);
break;
case TransportConstants.MultiplexProtocol:
if (tcpLog.isLoggable(Log.VERBOSE)) {
tcpLog.log(Log.VERBOSE, "(port " + port +
") accepting multiplex protocol");
}
// send ack
out.writeByte(TransportConstants.ProtocolAck);
// suggest endpoint (in case client doesn't already have one)
if (tcpLog.isLoggable(Log.VERBOSE)) {
tcpLog.log(Log.VERBOSE, "(port " + port +
") suggesting " + remoteHost + ":" + remotePort);
}
out.writeUTF(remoteHost);
out.writeInt(remotePort);
out.flush();
// read endpoint client has decided to use
ep = new TCPEndpoint(in.readUTF(), in.readInt(),
endpoint.getClientSocketFactory(),
endpoint.getServerSocketFactory());
if (tcpLog.isLoggable(Log.VERBOSE)) {
tcpLog.log(Log.VERBOSE, "(port " +
port + ") client using " +
ep.getHost() + ":" + ep.getPort());
}
ConnectionMultiplexer multiplexer;
synchronized (channelTable) {
// create or find channel for this endpoint
ch = getChannel(ep);
multiplexer =
new ConnectionMultiplexer(ch, bufIn, sockOut,
false);
ch.useMultiplexer(multiplexer);
}
multiplexer.run();
break;
default:
// protocol not understood, send nack and close socket
out.writeByte(TransportConstants.ProtocolNack);
out.flush();
break;
}
} catch (IOException e) {
// socket in unknown state: destroy socket
tcpLog.log(Log.BRIEF, "terminated with exception:", e);
} finally {
closeSocket(socket);
}
}
TCPTransport.handleMessages
void handleMessages(Connection conn, boolean persistent) {
int port = getEndpoint().getPort();
try {
DataInputStream in = new DataInputStream(conn.getInputStream());
do {
int op = in.read(); // transport op
//socket已關閉
if (op == -1) {
if (tcpLog.isLoggable(Log.BRIEF)) {
tcpLog.log(Log.BRIEF, "(port " +
port + ") connection closed");
}
break;
}
if (tcpLog.isLoggable(Log.BRIEF)) {
tcpLog.log(Log.BRIEF, "(port " + port +
") op = " + op);
}
// 第一次 正常情況下0x50,也就是Call
switch (op) {
case TransportConstants.Call:
// service incoming RMI call
RemoteCall call = new StreamRemoteCall(conn);
if (serviceCall(call) == false)
return;
break;
case TransportConstants.Ping:
// send ack for ping
DataOutputStream out =
new DataOutputStream(conn.getOutputStream());
out.writeByte(TransportConstants.PingAck);
conn.releaseOutputStream();
break;
case TransportConstants.DGCAck:
DGCAckHandler.received(UID.read(in));
break;
default:
throw new IOException("unknown transport op " + op);
}
} while (persistent);
} catch (IOException e) {
// exception during processing causes connection to close (below)
if (tcpLog.isLoggable(Log.BRIEF)) {
tcpLog.log(Log.BRIEF, "(port " + port +
") exception: ", e);
}
} finally {
try {
conn.close();
} catch (IOException ex) {
// eat exception
}
}
}
TCPTransport.serviceCall
public boolean serviceCall(final RemoteCall call) {
try {
/* read object id */
final Remote impl;
ObjID id;
try {
//物件id
id = ObjID.read(call.getInputStream());
} catch (java.io.IOException e) {
throw new MarshalException("unable to read objID", e);
}
/* get the remote object */
//就是 this
Transport transport = id.equals(dgcID) ? null : this;
//得到transport和id後,封裝為ObjectEndpoint,然後用getTarget方法從全域性靜態物件表中獲取對應的Target
Target target =
ObjectTable.getTarget(new ObjectEndpoint(id, transport));
if (target == null || (impl = target.getImpl()) == null) {
throw new NoSuchObjectException("no such object in table");
}
//獲取Dispatcher,也就是UnicastServer物件。主要是jdk內建的Register_skel物件和LiveRef物件
final Dispatcher disp = target.getDispatcher();
target.incrementCallCount();
try {
transportLog.log(Log.VERBOSE, "call dispatcher");
final AccessControlContext acc =
target.getAccessControlContext();
ClassLoader ccl = target.getContextClassLoader();
ClassLoader savedCcl = Thread.currentThread().getContextClassLoader();
try {
setContextClassLoader(ccl);
currentTransport.set(this);
try {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction<Void>() {
public Void run() throws IOException {
checkAcceptPermission(acc);
//分發請求
disp.dispatch(impl, call);
return null;
}
}, acc);
}
try {
ObjectOutput out = call.getResultStream(false);
UnicastServerRef.clearStackTraces(e);
out.writeObject(e);
call.releaseOutputStream();
} catch (IOException ie) {
transportLog.log(Log.BRIEF,
"exception thrown marshalling exception: ", ie);
return false;
}
}
return true;
}
UnicastServerRef.dispatch
再讀取一個不為負數的int,呼叫olddispatch
public void oldDispatch(Remote obj, RemoteCall call, int op)
throws IOException
{
long hash;
try {
ObjectInput in;
try {
in = call.getInputStream();
try {
//反射 sun.rmi.transport.DGCImpl_Skel
Class<?> clazz = Class.forName("sun.rmi.transport.DGCImpl_Skel");
//useCodebaseOnly設為true
if (clazz.isAssignableFrom(skel.getClass())) {
((MarshalInputStream)in)useCodebaseOnly();
}
} catch (ClassNotFoundException ignore) { }
hash = in.readLong(); //讀取最後8位元組,也就是hash值
} catch (Exception readEx) {
throw new UnmarshalException("error unmarshalling call header",
readEx);
}
//根據之前的非負操作碼獲取操作
logCall(obj, skel.getOperations()[op]);
unmarshalCustomCallData(in);
//呼叫 RegisterImpl_skel去實現請求
skel.dispatch(obj, call, op, hash);
} catch (Throwable e) {
....
} finally {
call.releaseInputStream(); // in case skeleton doesn't
call.releaseOutputStream();
}
}
操作碼陣列具體如下,2,即進行查詢操作
private static final Operation[] operations = new Operation[]{
new Operation("void bind(java.lang.String, java.rmi.Remote)"),
new Operation("java.lang.String list()[]"),
new Operation("java.rmi.Remote lookup(java.lang.String)"),
new Operation("void rebind(java.lang.String, java.rmi.Remote)"),
new Operation("void unbind(java.lang.String)")};
RegisterImpl_skel.dispatch
之前提到的op2對應的是lookup操作,這裡會執行readObject
操作,然後
public void dispatch(Remote Impl, RemoteCall call, int op, long hash) throws Exception {
//對hash進行匹配
if (hash != 4905912898345647071L) {
throw new SkeletonMismatchException("interface hash mismatch");
} else {
RegistryImpl registryImpl = (RegistryImpl)Impl;
String name;
Remote serverProxy;
ObjectInput var10;
ObjectInput var11;
switch (op) {
case 0:
try {
var11 = call.getInputStream();
//bind 方法,會反序列化傳來的類
name = (String)var11.readObject();
serverProxy = (Remote)var11.readObject();
} catch (IOException var94) {
throw new UnmarshalException("error unmarshalling arguments", var94);
} catch (ClassNotFoundException var95) {
throw new UnmarshalException("error unmarshalling arguments", var95);
} finally {
call.releaseInputStream();
}
registryImpl.bind(name, serverProxy);
try {
call.getResultStream(true);
break;
} catch (IOException var93) {
throw new MarshalException("error marshalling return", var93);
}
case 1:
call.releaseInputStream();
String[] var97 = registryImpl.list();
try {
ObjectOutput var98 = call.getResultStream(true);
var98.writeObject(var97);
break;
} catch (IOException var92) {
throw new MarshalException("error marshalling return", var92);
}
case 2:
try {
//lookup方法,也會反序列化要查詢的物件名稱
var10 = call.getInputStream();
name = (String)var10.readObject();
} catch (IOException var89) {
throw new UnmarshalException("error unmarshalling arguments", var89);
} catch (ClassNotFoundException var90) {
throw new UnmarshalException("error unmarshalling arguments", var90);
} finally {
call.releaseInputStream();
}
//查詢名稱,返回對應服務的客戶端代理
var8 = registryImpl.lookup(name);
try {
ObjectOutput var9 = call.getResultStream(true);
//輸出序列化的代理物件
var9.writeObject(serverProxy);
break;
} catch (IOException var88) {
throw new MarshalException("error marshalling return", var88);
}
case 3:
try {
//rebind方法 反序列化++
var11 = call.getInputStream();
name = (String)var11.readObject();
var8 = (Remote)var11.readObject();
} catch (IOException var85) {
throw new UnmarshalException("error unmarshalling arguments", var85);
} catch (ClassNotFoundException var86) {
throw new UnmarshalException("error unmarshalling arguments", var86);
} finally {
call.releaseInputStream();
}
registryImpl.rebind(name, serverProxy);
try {
call.getResultStream(true);
break;
} catch (IOException var84) {
throw new MarshalException("error marshalling return", var84);
}
case 4:
try {
//解綁 readObject++
var10 = call.getInputStream();
name = (String)var10.readObject();
} catch (IOException var81) {
throw new UnmarshalException("error unmarshalling arguments", var81);
} catch (ClassNotFoundException var82) {
throw new UnmarshalException("error unmarshalling arguments", var82);
} finally {
call.releaseInputStream();
}
registryImpl.unbind(name);
try {
call.getResultStream(true);
break;
} catch (IOException var80) {
throw new MarshalException("error marshalling return", var80);
}
default:
throw new UnmarshalException("invalid method number");
}
}
}
lookup函式如下。
call.getResultStream
public ObjectOutput getResultStream(boolean success) throws IOException {
/* make sure result code only marshaled once. */
if (resultStarted)
throw new StreamCorruptedException("result already in progress");
else
resultStarted = true;
DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
//0x51 call
wr.writeByte(TransportConstants.Return);// transport op
getOutputStream(true); // creates a MarshalOutputStream
// return header, part 2 (read by client-side RemoteCall)
if (success) //
//0x01
out.writeByte(TransportConstants.NormalReturn);
else
out.writeByte(TransportConstants.ExceptionalReturn);
//ackID ,4+8+2 14位元組
out.writeID(); // write id for gcAck
return out;
}
Server監聽
根據ID和Transport從 表中獲取dgc Traget。
oldDispatch
public void oldDispatch(Remote obj, RemoteCall call, int op)
throws IOException
{
long hash; // hash for matching stub with skeleton
try {
// read remote call header
ObjectInput in;
try {
in = call.getInputStream();
try {
//是DGC_Skel
Class<?> clazz = Class.forName("sun.rmi.transport.DGCImpl_Skel");
if (clazz.isAssignableFrom(skel.getClass())) {
//禁止從codebase載入類
((MarshalInputStream)in).useCodebaseOnly();
}
} catch (ClassNotFoundException ignore) { }
hash = in.readLong();
} catch (Exception readEx) {
throw new UnmarshalException("error unmarshalling call header",
readEx);
}
// if calls are being logged, write out object id and operation
logCall(obj, skel.getOperations()[op]);
unmarshalCustomCallData(in);
// dispatch to skeleton for remote object
skel.dispatch(obj, call, op, hash);
} catch (Throwable e) {
logCallException(e);
ObjectOutput out = call.getResultStream(false);
if (e instanceof Error) {
e = new ServerError(
"Error occurred in server thread", (Error) e);
} else if (e instanceof RemoteException) {
e = new ServerException(
"RemoteException occurred in server thread",
(Exception) e);
}
if (suppressStackTraces) {
clearStackTraces(e);
}
out.writeObject(e);
} finally {
call.releaseInputStream(); // in case skeleton doesn't
call.releaseOutputStream();
}
}
DGCImpl_sekl的操作如下
new Operation[]{
new Operation("void clean(java.rmi.server.ObjID[], long, java.rmi.dgc.VMID, boolean)"), new Operation("java.rmi.dgc.Lease dirty(java.rmi.server.ObjID[], long, java.rmi.dgc.Lease)")
};
DGCImpl_sekl.dispatch
public void dispatch(Remote dgc, RemoteCall call, int op, long hash) throws Exception {
if (hash != -669196253586618813L) {
throw new SkeletonMismatchException("interface hash mismatch");
} else {
DGCImpl dgcImpl = (DGCImpl)dgc;
ObjID[] objid;
long sequenceNum;
switch (op) {
case 0:
VMID vmid;
boolean strong;
try {
ObjectInput var14 = call.getInputStream();
oid = (ObjID[])var14.readObject();
sequenceNum = var14.readLong();
vmid = (VMID)var14.readObject();
strong = var14.readBoolean();
} catch (IOException var36) {
throw new UnmarshalException("error unmarshalling arguments", var36);
} catch (ClassNotFoundException var37) {
throw new UnmarshalException("error unmarshalling arguments", var37);
} finally {
call.releaseInputStream();
}
dgcImpl.clean(objid, sequenceNum, vmid, strong);
try {
call.getResultStream(true);
break;
} catch (IOException var35) {
throw new MarshalException("error marshalling return", var35);
}
case 1:
Lease lease;
try {
ObjectInput var13 = call.getInputStream();
oid = (ObjID[])var13.readObject();
var8 = var13.readLong();
lease = (Lease)var13.readObject();
} catch (IOException var32) {
throw new UnmarshalException("error unmarshalling arguments", var32);
} catch (ClassNotFoundException var33) {
throw new UnmarshalException("error unmarshalling arguments", var33);
} finally {
call.releaseInputStream();
}
Lease var11 = dgcImpl.dirty(oid, sequenceNum, lease);
try {
ObjectOutput var12 = call.getResultStream(true);
var12.writeObject(var11);
break;
} catch (IOException var31) {
throw new MarshalException("error marshalling return", var31);
}
default:
throw new UnmarshalException("invalid method number");
}
}
}
預設會呼叫dirty方法,引數值具體如下
public Lease dirty(ObjID[] ids, long sequenceNum, Lease lease) {
//返回與lease(租約)關聯的客戶端 VMID。
VMID vmid = lease.getVMID();
//伺服器指定租約值 預設10分鐘
long duration = leaseValue;
//如果客戶端未提供 VMID,建立一個 VMID
if (vmid == null) {
vmid = new VMID();
}
//生成租約物件
lease = new Lease(vmid, duration);
synchronized (leaseTable) {
//透過vmi的獲取租約資訊
LeaseInfo info = leaseTable.get(vmid);
//如果沒有
if (info == null) {
//新建LeaseInfo,新增對映
leaseTable.put(vmid, new LeaseInfo(vmid, duration));
if (checker == null) {
checker = scheduler.scheduleWithFixedDelay(
new Runnable() {
public void run() {
checkLeases();
}
},
leaseCheckInterval,
leaseCheckInterval, TimeUnit.MILLISECONDS);
}
} else {
//如果存在,續租
info.renew(duration);
}
}
for (ObjID id : ids) {
// 處理給定 ObjID 的客戶端 VM 信令引用:轉發到相應的 Target 條目。如果在表中找不到 ObjID,則不執行任何操作。
//大致是使用ObjID從物件表中找到對應的Target,將vimd與其關聯
ObjectTable.referenced(id, sequenceNum, vmid);
}
// return the VMID used
return lease;
}
UnicastServerRef.dispatch
public void dispatch(Remote obj, RemoteCall call) throws IOException {
int num;
long op;
try {
ObjectInput in;
try {
in = call.getInputStream();
num = in.readInt();
//-1
if (num >= 0) {
if (skel != null) {
oldDispatch(obj, call, num);
return;
} else {
throw new UnmarshalException(
"skeleton class not found but required " +
"for client version");
}
}
//讀取hash
op = in.readLong();
} catch (Exception readEx) {
throw new UnmarshalException("error unmarshalling call header",
readEx);
}
MarshalInputStream marshalStream = (MarshalInputStream) in;
marshalStream.skipDefaultResolveClass();
//透過hash獲取method例項
Method method = hashToMethod_Map.get(op);
if (method == null) {
throw new UnmarshalException("unrecognized method hash: " +
"method not supported by remote object");
}
// if calls are being logged, write out object id and operation
logCall(obj, method);
// unmarshal parameters
Class<?>[] types = method.getParameterTypes();
Object[] params = new Object[types.length];
try {
//反序列化 這裡也是直接呼叫了readObject方法
unmarshalCustomCallData(in);
for (int i = 0; i < types.length; i++) {
params[i] = unmarshalValue(types[i], in);
}
} catch (java.io.IOException e) {
throw new UnmarshalException(
"error unmarshalling arguments", e);
} catch (ClassNotFoundException e) {
throw new UnmarshalException(
"error unmarshalling arguments", e);
} finally {
call.releaseInputStream();
}
// make upcall on remote object
Object result;
try {
//反射呼叫
result = method.invoke(obj, params);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
try {
ObjectOutput out = call.getResultStream(true);
Class<?> rtype = method.getReturnType();
if (rtype != void.class) {
marshalValue(rtype, result, out);
}
} catch (IOException ex) {
throw new MarshalException("error marshalling return", ex);
}
} catch (Throwable e) {
logCallException(e);
ObjectOutput out = call.getResultStream(false);
if (e instanceof Error) {
e = new ServerError(
"Error occurred in server thread", (Error) e);
} else if (e instanceof RemoteException) {
e = new ServerException(
"RemoteException occurred in server thread",
(Exception) e);
}
if (suppressStackTraces) {
clearStackTraces(e);
}
out.writeObject(e);
} finally {
call.releaseInputStream(); // in case skeleton doesn't
call.releaseOutputStream();
}
}
Server與Register互動
第一次 JRMI version2 StreamProtocol
主要向register指定了互動協議
第二次、第三次(ProtocolAck Continuation)
這兩次貌似沒啥用,單純的讀,扔掉,沒有具體邏輯
register->server
server->register
第四次 JRMI CAll
位元組流資料如下
@Magic - 0xac ed
@Version - 0x00 05
@Contents
TC_BLOCKDATA - 0x77
@Blockdata - 0x00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 44 15 4d c9 d4 e6 3b df
// 資料塊具體對應為
// uid 4位元組
// 時間戳8位元組
// count 2位元組
// Objnum 8位元組
// 操作碼1位元組
// 操作對應的hash值 8位元組
// 要繫結的物件名稱 字串型別 test
//註冊中心的 objID的14位元組都為0
TC_STRING - 0x74
@Handler - 8257536
@Length - 4 - 0x00 04
@Value - test - 0x74 65 73 74
// 服務端引用代理
TC_OBJECT - 0x73
TC_PROXYCLASSDESC - 0x7d
@Handler - 8257537
@InterfaceCount - 2 - 0x00 00 00 02
Index 0:
@Length - 15 - 0x00 0f
@Value - java.rmi.Remote - 0x6a 61 76 61 2e 72 6d 69 2e 52 65 6d 6f 74 65
Index 1:
@Length - 26 - 0x00 1a
@Value - com.Rmi.rmiServerInterface - 0x63 6f 6d 2e 52 6d 69 2e 72 6d 69 53 65 72 76 65 72 49 6e 74 65 72 66 61 63 65
@ClassAnnotations
Index 0
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
@SuperClassDesc
TC_CLASSDESC - 0x72
@ClassName
@Length - 23 - 0x00 17
@Value - java.lang.reflect.Proxy - 0x6a 61 76 61 2e 6c 61 6e 67 2e 72 65 66 6c 65 63 74 2e 50 72 6f 78 79
@SerialVersionUID - -2222568056686623797 - 0xe1 27 da 20 cc 10 43 cb
@Handler - 8257538
@ClassDescFlags - SC_SERIALIZABLE - 0x02
@FieldCount - 1 - 0x00 01
[]Fields
Index 0:
Object - L - 0x4c
@FieldName
@Length - 1 - 0x00 01
@Value - h - 0x68
@ClassName
TC_STRING - 0x74
@Handler - 8257539
@Length - 37 - 0x00 25
@Value - Ljava/lang/reflect/InvocationHandler; - 0x4c 6a 61 76 61 2f 6c 61 6e 67 2f 72 65 66 6c 65 63 74 2f 49 6e 76 6f 63 61 74 69 6f 6e 48 61 6e 64 6c 65 72 3b
[]ClassAnnotations
Index 0:
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
@SuperClassDesc
TC_NULL - 0x70
@Handler - 8257540
[]ClassData
@ClassName - java.lang.reflect.Proxy
{}Attributes
h
TC_OBJECT - 0x73
TC_CLASSDESC - 0x72
@ClassName
@Length - 45 - 0x00 2d
@Value - java.rmi.server.RemoteObjectInvocationHandler - 0x6a 61 76 61 2e 72 6d 69 2e 73 65 72 76 65 72 2e 52 65 6d 6f 74 65 4f 62 6a 65 63 74 49 6e 76 6f 63 61 74 69 6f 6e 48 61 6e 64 6c 65 72
@SerialVersionUID - 2 - 0x00 00 00 00 00 00 00 02
@Handler - 8257541
@ClassDescFlags - SC_SERIALIZABLE - 0x02
@FieldCount - 0 - 0x00 00
[]Fields
[]ClassAnnotations
Index 0:
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
@SuperClassDesc
TC_CLASSDESC - 0x72
@ClassName
@Length - 28 - 0x00 1c
@Value - java.rmi.server.RemoteObject - 0x6a 61 76 61 2e 72 6d 69 2e 73 65 72 76 65 72 2e 52 65 6d 6f 74 65 4f 62 6a 65 63 74
@SerialVersionUID - -3215090123894869218 - 0xd3 61 b4 91 0c 61 33 1e
@Handler - 8257542
@ClassDescFlags - SC_SERIALIZABLE|SC_WRITE_METHOD - 0x03
@FieldCount - 0 - 0x00 00
[]Fields
[]ClassAnnotations
Index 0:
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
@SuperClassDesc
TC_NULL - 0x70
@Handler - 8257543
[]ClassData
@ClassName - java.rmi.server.RemoteObject
{}Attributes
@ObjectAnnotation
TC_BLOCKDATA - 0x77
@Blockdata - 0x00 0a 55 6e 69 63 61 73 74 52 65 66 00 0c 31 39 32 2e 31 36 38 2e 31 2e 31 32 00 00 1a 0a 35 fb 5b a5 87 39 d9 1d ff b9 85 15 00 00 01 93 62 9d d7 d0 80 01 00
TC_ENDBLOCKDATA - 0x78
@ClassName - java.rmi.server.RemoteObjectInvocationHandler
{}Attributes
第五次 JRMI ReturnCall
@Magic - 0xac ed
@Version - 0x00 05
@Contents
TC_BLOCKDATA - 0x77
@Blockdata - 0x01 a3 ce da e2 00 00 01 93 62 4c 71 1f 80 02
// 正常返回0x01 dgc相關 id
Client與Register 互動
前面都一樣,略
第四次
@Magic - 0xac ed
@Version - 0x00 05
@Contents
TC_BLOCKDATA - 0x77
@Blockdata - 0x00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 44 15 4d c9 d4 e6 3b df
//Register的objID 都為0
//操作碼 0x02
//對應雜湊
// 要查詢的物件名稱
TC_STRING - 0x74
@Handler - 8257536
@Length - 4 - 0x00 04
@Value - test - 0x74 65 73 74
第五次
會把之前註冊的server代理返回
@Magic - 0xac ed
@Version - 0x00 05
@Contents
TC_BLOCKDATA - 0x77
@Blockdata - 0x01 8c 5c cc e4 00 00 01 93 62 9d 77 34 80 03
//0x01 返回正確
//返回代理
TC_OBJECT - 0x73
TC_PROXYCLASSDESC - 0x7d
@Handler - 8257536
@InterfaceCount - 2 - 0x00 00 00 02
Index 0:
@Length - 15 - 0x00 0f
@Value - java.rmi.Remote - 0x6a 61 76 61 2e 72 6d 69 2e 52 65 6d 6f 74 65
Index 1:
@Length - 26 - 0x00 1a
@Value - com.Rmi.rmiServerInterface - 0x63 6f 6d 2e 52 6d 69 2e 72 6d 69 53 65 72 76 65 72 49 6e 74 65 72 66 61 63 65
@ClassAnnotations
Index 0
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
@SuperClassDesc
TC_CLASSDESC - 0x72
@ClassName
@Length - 23 - 0x00 17
@Value - java.lang.reflect.Proxy - 0x6a 61 76 61 2e 6c 61 6e 67 2e 72 65 66 6c 65 63 74 2e 50 72 6f 78 79
@SerialVersionUID - -2222568056686623797 - 0xe1 27 da 20 cc 10 43 cb
@Handler - 8257537
@ClassDescFlags - SC_SERIALIZABLE - 0x02
@FieldCount - 1 - 0x00 01
[]Fields
Index 0:
Object - L - 0x4c
@FieldName
@Length - 1 - 0x00 01
@Value - h - 0x68
@ClassName
TC_STRING - 0x74
@Handler - 8257538
@Length - 37 - 0x00 25
@Value - Ljava/lang/reflect/InvocationHandler; - 0x4c 6a 61 76 61 2f 6c 61 6e 67 2f 72 65 66 6c 65 63 74 2f 49 6e 76 6f 63 61 74 69 6f 6e 48 61 6e 64 6c 65 72 3b
[]ClassAnnotations
Index 0:
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
@SuperClassDesc
TC_NULL - 0x70
@Handler - 8257539
[]ClassData
@ClassName - java.lang.reflect.Proxy
{}Attributes
h
TC_OBJECT - 0x73
TC_CLASSDESC - 0x72
@ClassName
@Length - 45 - 0x00 2d
@Value - java.rmi.server.RemoteObjectInvocationHandler - 0x6a 61 76 61 2e 72 6d 69 2e 73 65 72 76 65 72 2e 52 65 6d 6f 74 65 4f 62 6a 65 63 74 49 6e 76 6f 63 61 74 69 6f 6e 48 61 6e 64 6c 65 72
@SerialVersionUID - 2 - 0x00 00 00 00 00 00 00 02
@Handler - 8257540
@ClassDescFlags - SC_SERIALIZABLE - 0x02
@FieldCount - 0 - 0x00 00
[]Fields
[]ClassAnnotations
Index 0:
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
@SuperClassDesc
TC_CLASSDESC - 0x72
@ClassName
@Length - 28 - 0x00 1c
@Value - java.rmi.server.RemoteObject - 0x6a 61 76 61 2e 72 6d 69 2e 73 65 72 76 65 72 2e 52 65 6d 6f 74 65 4f 62 6a 65 63 74
@SerialVersionUID - -3215090123894869218 - 0xd3 61 b4 91 0c 61 33 1e
@Handler - 8257541
@ClassDescFlags - SC_SERIALIZABLE|SC_WRITE_METHOD - 0x03
@FieldCount - 0 - 0x00 00
[]Fields
[]ClassAnnotations
Index 0:
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
@SuperClassDesc
TC_NULL - 0x70
@Handler - 8257542
[]ClassData
@ClassName - java.rmi.server.RemoteObject
{}Attributes
@ObjectAnnotation
TC_BLOCKDATA - 0x77
@Blockdata - 0x00 0a 55 6e 69 63 61 73 74 52 65 66 00 0c 31 39 32 2e 31 36 38 2e 31 2e 31 32 00 00 1a 0a 35 fb 5b a5 87 39 d9 1d ff b9 85 15 00 00 01 93 62 9d d7 d0 80 01 01
TC_ENDBLOCKDATA - 0x78
@ClassName - java.rmi.server.RemoteObjectInvocationHandler
{}Attributes
Client與Server互動
第四次和第五次是在呼叫函式之前gc執行緒相關的動作,大致瞭解即可
第四次 JRMI CAll
@Magic - 0xac ed
@Version - 0x00 05
@Contents
TC_BLOCKDATA - 0x77
@Blockdata - 0x00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 f6 b6 89 8d 8b f2 86 43
//0x02 即 dgc target
//0x01 呼叫dirty方法
//ObjID[]陣列
TC_ARRAY - 0x75
TC_CLASSDESC - 0x72
@ClassName
@Length - 24 - 0x00 18
@Value - [Ljava.rmi.server.ObjID; - 0x5b 4c 6a 61 76 61 2e 72 6d 69 2e 73 65 72 76 65 72 2e 4f 62 6a 49 44 3b
@SerialVersionUID - -8713620060265225090 - 0x87 13 00 b8 d0 2c 64 7e
@Handler - 8257536
@ClassDescFlags - SC_SERIALIZABLE - 0x02
@FieldCount - 0 - 0x00 00
[]Fields
[]ClassAnnotations
Index 0:
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
@SuperClassDesc
TC_NULL - 0x70
@Handler - 8257537
@ArraySize - 1 - 0x00 00 00 01
[]Values
Index 0
TC_OBJECT - 0x73
TC_CLASSDESC - 0x72
@ClassName
@Length - 21 - 0x00 15
@Value - java.rmi.server.ObjID - 0x6a 61 76 61 2e 72 6d 69 2e 73 65 72 76 65 72 2e 4f 62 6a 49 44
@SerialVersionUID - -6386392263968365220 - 0xa7 5e fa 12 8d dc e5 5c
@Handler - 8257538
@ClassDescFlags - SC_SERIALIZABLE - 0x02
@FieldCount - 2 - 0x00 02
[]Fields
Index 0:
Long - J - 0x4a
@FieldName
@Length - 6 - 0x00 06
@Value - objNum - 0x6f 62 6a 4e 75 6d
Index 1:
Object - L - 0x4c
@FieldName
@Length - 5 - 0x00 05
@Value - space - 0x73 70 61 63 65
@ClassName
TC_STRING - 0x74
@Handler - 8257539
@Length - 21 - 0x00 15
@Value - Ljava/rmi/server/UID; - 0x4c 6a 61 76 61 2f 72 6d 69 2f 73 65 72 76 65 72 2f 55 49 44 3b
[]ClassAnnotations
Index 0:
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
@SuperClassDesc
TC_NULL - 0x70
@Handler - 8257540
[]ClassData
@ClassName - java.rmi.server.ObjID
{}Attributes
objNum
(long)-1948612731803990665 - 0xe4 f5 23 0b 6c 05 05 77
space
TC_OBJECT - 0x73
TC_CLASSDESC - 0x72
@ClassName
@Length - 19 - 0x00 13
@Value - java.rmi.server.UID - 0x6a 61 76 61 2e 72 6d 69 2e 73 65 72 76 65 72 2e 55 49 44
@SerialVersionUID - 1086053664494604050 - 0x0f 12 70 0d bf 36 4f 12
@Handler - 8257541
@ClassDescFlags - SC_SERIALIZABLE - 0x02
@FieldCount - 3 - 0x00 03
[]Fields
Index 0:
Short - S - 0x53
@FieldName
@Length - 5 - 0x00 05
@Value - count - 0x63 6f 75 6e 74
Index 1:
Long - J - 0x4a
@FieldName
@Length - 4 - 0x00 04
@Value - time - 0x74 69 6d 65
Index 2:
Integer - I - 0x49
@FieldName
@Length - 6 - 0x00 06
@Value - unique - 0x75 6e 69 71 75 65
[]ClassAnnotations
Index 0:
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
@SuperClassDesc
TC_NULL - 0x70
@Handler - 8257542
[]ClassData
@ClassName - java.rmi.server.UID
{}Attributes
count
(short)-32767 - 0x80 01
time
(long)1732528930352 - 0x00 00 01 93 62 c5 7e 30
unique
(integer)1789764046 - 0x6a ad a1 ce
//序列號
TC_BLOCKDATA - 0x77
@Blockdata - 0x80 00 00 00 00 00 00 00
//Lease物件
TC_OBJECT - 0x73
TC_CLASSDESC - 0x72
@ClassName
@Length - 18 - 0x00 12
@Value - java.rmi.dgc.Lease - 0x6a 61 76 61 2e 72 6d 69 2e 64 67 63 2e 4c 65 61 73 65
@SerialVersionUID - -5713411624328831948 - 0xb0 b5 e2 66 0c 4a dc 34
@Handler - 8257543
@ClassDescFlags - SC_SERIALIZABLE - 0x02
@FieldCount - 2 - 0x00 02
[]Fields
Index 0:
Long - J - 0x4a
@FieldName
@Length - 5 - 0x00 05
@Value - value - 0x76 61 6c 75 65
Index 1:
Object - L - 0x4c
@FieldName
@Length - 4 - 0x00 04
@Value - vmid - 0x76 6d 69 64
@ClassName
TC_STRING - 0x74
@Handler - 8257544
@Length - 19 - 0x00 13
@Value - Ljava/rmi/dgc/VMID; - 0x4c 6a 61 76 61 2f 72 6d 69 2f 64 67 63 2f 56 4d 49 44 3b
[]ClassAnnotations
Index 0:
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
@SuperClassDesc
TC_NULL - 0x70
@Handler - 8257545
[]ClassData
@ClassName - java.rmi.dgc.Lease
{}Attributes
value
(long)600000 - 0x00 00 00 00 00 09 27 c0
vmid
TC_OBJECT - 0x73
TC_CLASSDESC - 0x72
@ClassName
@Length - 17 - 0x00 11
@Value - java.rmi.dgc.VMID - 0x6a 61 76 61 2e 72 6d 69 2e 64 67 63 2e 56 4d 49 44
@SerialVersionUID - -538642295484486218 - 0xf8 86 5b af a4 a5 6d b6
@Handler - 8257546
@ClassDescFlags - SC_SERIALIZABLE - 0x02
@FieldCount - 2 - 0x00 02
[]Fields
Index 0:
Array - [ - 0x5b
@FieldName
@Length - 4 - 0x00 04
@Value - addr - 0x61 64 64 72
@ClassName
TC_STRING - 0x74
@Handler - 8257547
@Length - 2 - 0x00 02
@Value - [B - 0x5b 42
Index 1:
Object - L - 0x4c
@FieldName
@Length - 3 - 0x00 03
@Value - uid - 0x75 69 64
@ClassName
TC_REFERENCE - 0x71
@Handler - 8257539 - 0x00 7e 00 03
[]ClassAnnotations
Index 0:
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
@SuperClassDesc
TC_NULL - 0x70
@Handler - 8257548
[]ClassData
@ClassName - java.rmi.dgc.VMID
{}Attributes
addr
TC_ARRAY - 0x75
TC_CLASSDESC - 0x72
@ClassName
@Length - 2 - 0x00 02
@Value - [B - 0x5b 42
@SerialVersionUID - -5984413125824719648 - 0xac f3 17 f8 06 08 54 e0
@Handler - 8257549
@ClassDescFlags - SC_SERIALIZABLE - 0x02
@FieldCount - 0 - 0x00 00
[]Fields
[]ClassAnnotations
Index 0:
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
@SuperClassDesc
TC_NULL - 0x70
@Handler - 8257550
@ArraySize - 8 - 0x00 00 00 08
[]Values
00000000 d2 df e7 9a ac 5d f6 b8 |.....]..|
uid
TC_OBJECT - 0x73
TC_REFERENCE - 0x71
@Handler - 8257541 - 0x00 7e 00 05
@Handler - 8257551
[]ClassData
@ClassName - java.rmi.server.UID
{}Attributes
count
(short)-32767 - 0x80 01
time
(long)1732529447360 - 0x00 00 01 93 62 cd 61 c0
unique
(integer)1275808588 - 0x4c 0b 4b 4c
第五次 JRMI Return Call
@Magic - 0xac ed
@Version - 0x00 05
@Contents
TC_BLOCKDATA - 0x77
@Blockdata - 0x01 6a ad a1 ce 00 00 01 93 62 c5 7e 30 80 07
//0x01正常返回
//返回一個 lease(租約)物件
TC_OBJECT - 0x73
TC_CLASSDESC - 0x72
@ClassName
@Length - 18 - 0x00 12
@Value - java.rmi.dgc.Lease - 0x6a 61 76 61 2e 72 6d 69 2e 64 67 63 2e 4c 65 61 73 65
@SerialVersionUID - -5713411624328831948 - 0xb0 b5 e2 66 0c 4a dc 34
@Handler - 8257536
@ClassDescFlags - SC_SERIALIZABLE - 0x02
@FieldCount - 2 - 0x00 02
[]Fields
Index 0:
Long - J - 0x4a
@FieldName
@Length - 5 - 0x00 05
@Value - value - 0x76 61 6c 75 65
Index 1:
Object - L - 0x4c
@FieldName
@Length - 4 - 0x00 04
@Value - vmid - 0x76 6d 69 64
@ClassName
TC_STRING - 0x74
@Handler - 8257537
@Length - 19 - 0x00 13
@Value - Ljava/rmi/dgc/VMID; - 0x4c 6a 61 76 61 2f 72 6d 69 2f 64 67 63 2f 56 4d 49 44 3b
[]ClassAnnotations
Index 0:
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
@SuperClassDesc
TC_NULL - 0x70
@Handler - 8257538
[]ClassData
@ClassName - java.rmi.dgc.Lease
{}Attributes
value
(long)600000 - 0x00 00 00 00 00 09 27 c0
vmid
TC_OBJECT - 0x73
TC_CLASSDESC - 0x72
@ClassName
@Length - 17 - 0x00 11
@Value - java.rmi.dgc.VMID - 0x6a 61 76 61 2e 72 6d 69 2e 64 67 63 2e 56 4d 49 44
@SerialVersionUID - -538642295484486218 - 0xf8 86 5b af a4 a5 6d b6
@Handler - 8257539
@ClassDescFlags - SC_SERIALIZABLE - 0x02
@FieldCount - 2 - 0x00 02
[]Fields
Index 0:
Array - [ - 0x5b
@FieldName
@Length - 4 - 0x00 04
@Value - addr - 0x61 64 64 72
@ClassName
TC_STRING - 0x74
@Handler - 8257540
@Length - 2 - 0x00 02
@Value - [B - 0x5b 42
Index 1:
Object - L - 0x4c
@FieldName
@Length - 3 - 0x00 03
@Value - uid - 0x75 69 64
@ClassName
TC_STRING - 0x74
@Handler - 8257541
@Length - 21 - 0x00 15
@Value - Ljava/rmi/server/UID; - 0x4c 6a 61 76 61 2f 72 6d 69 2f 73 65 72 76 65 72 2f 55 49 44 3b
[]ClassAnnotations
Index 0:
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
@SuperClassDesc
TC_NULL - 0x70
@Handler - 8257542
[]ClassData
@ClassName - java.rmi.dgc.VMID
{}Attributes
addr
TC_ARRAY - 0x75
TC_CLASSDESC - 0x72
@ClassName
@Length - 2 - 0x00 02
@Value - [B - 0x5b 42
@SerialVersionUID - -5984413125824719648 - 0xac f3 17 f8 06 08 54 e0
@Handler - 8257543
@ClassDescFlags - SC_SERIALIZABLE - 0x02
@FieldCount - 0 - 0x00 00
[]Fields
[]ClassAnnotations
Index 0:
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
@SuperClassDesc
TC_NULL - 0x70
@Handler - 8257544
@ArraySize - 8 - 0x00 00 00 08
[]Values
00000000 d2 df e7 9a ac 5d f6 b8 |.....]..|
uid
TC_OBJECT - 0x73
TC_CLASSDESC - 0x72
@ClassName
@Length - 19 - 0x00 13
@Value - java.rmi.server.UID - 0x6a 61 76 61 2e 72 6d 69 2e 73 65 72 76 65 72 2e 55 49 44
@SerialVersionUID - 1086053664494604050 - 0x0f 12 70 0d bf 36 4f 12
@Handler - 8257545
@ClassDescFlags - SC_SERIALIZABLE - 0x02
@FieldCount - 3 - 0x00 03
[]Fields
Index 0:
Short - S - 0x53
@FieldName
@Length - 5 - 0x00 05
@Value - count - 0x63 6f 75 6e 74
Index 1:
Long - J - 0x4a
@FieldName
@Length - 4 - 0x00 04
@Value - time - 0x74 69 6d 65
Index 2:
Integer - I - 0x49
@FieldName
@Length - 6 - 0x00 06
@Value - unique - 0x75 6e 69 71 75 65
[]ClassAnnotations
Index 0:
TC_NULL - 0x70
TC_ENDBLOCKDATA - 0x78
@SuperClassDesc
TC_NULL - 0x70
@Handler - 8257546
[]ClassData
@ClassName - java.rmi.server.UID
{}Attributes
count
(short)-32767 - 0x80 01
time
(long)1732529447360 - 0x00 00 01 93 62 cd 61 c0
unique
(integer)1275808588 - 0x4c 0b 4b 4c
第六次 JRMI call
@Magic - 0xac ed
@Version - 0x00 05
@Contents
TC_BLOCKDATA - 0x77
@Blockdata - 0xe4 f5 23 0b 6c 05 05 77 6a ad a1 ce 00 00 01 93 62 c5 7e 30 80 01 ff ff ff ff 1e fa 3c dc 29 54 10 ef
//obj物件,屬性objnum 8位元組
//unique 4位元組
//time 8位元組
//count 2位元組
//運算元4位元組(-1)
//最後八位元組 方法對應的hash
TC_STRING - 0x74
@Handler - 8257536
@Length - 5 - 0x00 05
@Value - admin - 0x61 64 6d 69 6e
第七次 JRMI ReturnCAll
@Magic - 0xac ed
@Version - 0x00 05
@Contents
TC_BLOCKDATA - 0x77
@Blockdata - 0x01 6a ad a1 ce 00 00 01 93 62 c5 7e 30 80 0d
TC_STRING - 0x74
@Handler - 8257536
@Length - 11 - 0x00 0b
@Value - Hello ADMIN - 0x48 65 6c 6c 6f 20 41 44 4d 49 4e
總結
-
register和server在釋出Target(或者說在將Targert填入全域性靜態物件表中時)都會先釋出一個
dgc
服務。 -
register和server在本質上是一樣的。只不過jdk內建了
registerImpl_stub
,client透過指定ip
和port
來直接構造本地stub。register的Skeleton首先會解包,然後呼叫註冊中心的bind
、rebind
、unbind
、lookup
等方法。client透過呼叫lookup返回註冊在register中的stub -
server同樣透過構造本地register的stub(當然在一個檔案裡就直接呼叫bind方法了),呼叫遠端bind方法,向register傳遞自己的stub。
-
client透過register返回的stub呼叫遠端物件(一個埠可以有很多stub,主要是透過objID引數來區分不同的stub,如果知道objID就可跳過register直接呼叫遠端物件)
-
在低版本server和register可以在不同的主機上(jdk7已經不允許了,在往前看感覺也沒啥意義了),任意主機都可以呼叫
bind
、unbind
、rebind
等方法,是非常危險的 -
一些細節沒有細看,漏洞篇在學習