JAVA_RMI(理論篇)

cyyyyi發表於2024-11-26

前言

之前對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

這裡為了方便區分,把registerserver分開寫來,(或者不對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實現類

image-20241125113547296

可以看到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例項

image-20241125114230849

構造方法如下

    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;
            }
        });
    }

所以在釋出完後是有兩個服務的

image-20241125210257883

服務端

//這裡其實就是本地建立 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函式如下。

image-20241125150949510

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方法,引數值具體如下

image-20241125215641090

    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互動

image-20241125172126359

第一次 JRMI version2 StreamProtocol

主要向register指定了互動協議

image-20241125172204315

第二次、第三次(ProtocolAck Continuation)

這兩次貌似沒啥用,單純的讀,扔掉,沒有具體邏輯

register->server

image-20241125172241773

server->register

image-20241125172344019

第四次 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透過指定ipport來直接構造本地stub。register的Skeleton首先會解包,然後呼叫註冊中心的bindrebindunbindlookup等方法。client透過呼叫lookup返回註冊在register中的stub

  • server同樣透過構造本地register的stub(當然在一個檔案裡就直接呼叫bind方法了),呼叫遠端bind方法,向register傳遞自己的stub。

  • client透過register返回的stub呼叫遠端物件(一個埠可以有很多stub,主要是透過objID引數來區分不同的stub,如果知道objID就可跳過register直接呼叫遠端物件)

  • 在低版本server和register可以在不同的主機上(jdk7已經不允許了,在往前看感覺也沒啥意義了),任意主機都可以呼叫bindunbindrebind等方法,是非常危險的

  • 一些細節沒有細看,漏洞篇在學習

相關文章