通訊協議 & 基於分佈式思想下的RPC方案

網絡模型

OSI模型(開放式系統互聯),它是由國際標準化組織(ISO)提出的。

在這裏插入圖片描述

TCP/IP系列協議

TCP/IP(Transmission Control Protocol/Internet Protocol)不只是TCP/IP兩個協議,而是有很多個協議組成,並且是在不同的層,是互聯網的基礎通信架構。

在這裏插入圖片描述

一個http請求瀏覽:應用層HTTP -> 傳輸層TCP -> 網絡層IP(數據包)、 ICMP(確保源地址和目的地址之間是網絡通)、IGMP(本地路由器和英特網的路由器聯通) ->鏈路層

直接使用網絡層協議的應用:ping命令,ICMP協議。

TCP的3次握手協議

第一次握手:Client將標誌位SYN置爲1,隨機產生一個值seq=J,並將該數據包發送給Server,Client進入SYN_SENT狀態,等待Server確認。
第二次握手:Server收到數據包後由標誌位SYN=1知道Client請求建立連接,Server將標誌位SYN和ACK都置爲1,ack=J+1,隨機產生一個值seq=K,並將該數據包發送給Client以確認連接請求,Server進入SYN_RCVD狀態。
第三次握手:Client收到確認後,檢查ack是否爲J+1,ACK是否爲1,如果正確則將標誌位ACK置爲1,ack=K+1,並將該數據包發送給Server,Server檢查ack是否爲K+1,ACK是否爲1,如果正確則連接建立成功,Client和Server進入ESTABLISHED狀態,完成三次握手,隨後Client與Server之間可以開始傳輸數據了。

TCP的4次揮手協議

(1)第一次揮手:Client發送一個FIN,用來關閉Client到Server的數據傳送,Client進入FIN_WAIT_1狀態。
(2)第二次揮手:Server收到FIN後,發送一個ACK給Client,確認序號爲收到序號+1(與SYN相同,一個FIN佔用一個序號),Server進入CLOSE_WAIT狀態。
(3)第三次揮手:Server發送一個FIN,用來關閉Server到Client的數據傳送,Server進入LAST_ACK狀態。
(4)第四次揮手:Client收到FIN後,Client進入TIME_WAIT狀態,接着發送一個ACK給Server,確認序號爲收到序號+1,Server進入CLOSED狀態,完成四次揮手。

TCP的通訊原理

“阻塞模式”:如果接收端,當然接收端緩衝區爲空的時候,調用Socket的read方法的線程會阻塞,阻塞到有數據進入接收緩衝區;另外對於寫數據到Socket中的線程來說,如果待發送的數據長度大於發送緩衝區空餘長度,則會阻塞在write方法上,等待發送緩衝區的報文被髮送到網絡上,所以呢這個就是TCP的阻塞。

滑動窗口協議
發送方和接收方都會維護一個數據幀的序列,這個序列被稱作窗口。發送方的窗口大小由接收方確認,目的是控制發送速度,以免接收方的緩存不夠大導致溢出,同時控制流量也可以避免網絡擁塞

HTTP協議
URI是請求的資源,URL是你請求的資源的地址也就是地址路徑。

UDP協議
那爲什麼UDP不可靠,我們還使用它了,在這種在線視頻中,丟失數據只會作爲干擾出現,並且這種干擾是可以容忍的,就比如看視頻的時候出現了畫面與聲音不同步的現象,大家還是會忍受的,同時UDP傳輸比較高效

實戰
TCP的實戰,因爲TCP是要建立連接的,所以需要Socket和ServerSocket之間建立連接。
TCP Server

package enjoy.protocol.tcp;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * TCP服務器
 */
public class TcpServer {
    public static void main(String[] args) throws  Exception{
        //創建一個ServerSocket監聽一個端:8888
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("TCP服務器已經啓動,端口是8888");
        //無限循環
        while (true){
            //等待客戶端的請求,如果有請求分配一個Socket
            Socket socket = serverSocket.accept();
            //根據標準輸入構造一個BufferedReader對象
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String buffer =null;
            //循環讀取輸入的每一行數據
            while ((buffer = reader.readLine()) !=null &&  !buffer.equals("")){
                System.out.println(buffer);//輸出每一行
            }
            //通過Socket對象得到輸出流,構造BufferedWrite對象
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            //模擬了http的請求頭信息
            writer.write("HTTP/1.1 200 OK \r\n Content-Type:text/html \r\n charset=UTF-8\r\n\r\n ");
            //寫一些html的體
            writer.write("<html><head><title>http請求</title></head><body><h1>這是一個HTTP請求!</h1></body></html>");
            //刷新輸出流,使得數據立馬發送
            writer.flush();
            //關閉
            reader.close();
            writer.close();
            socket.close();
        }

    }
}

TCP Client

package enjoy.protocol.tcp;

import java.io.PrintWriter;
import java.net.Socket;

/**
 * TCP客戶端
 */
public class TcpClient {
    public static void main(String[] args) throws Exception{
        String msg = "hello 13號技師!";
        //創建一個Socket,跟本機的8888端口進行連接
        Socket socket = new Socket("127.0.0.1",8888);
        //使用Socket創建一個PrintWriter進行寫數據
        PrintWriter printWriter = new PrintWriter(socket.getOutputStream());
        //發送數據
        printWriter.println(msg);
        //刷新一下,使得服務立馬可以收到請求信息
        printWriter.flush();
        printWriter.println(msg);
        printWriter.println(msg);
        printWriter.println(msg);
        printWriter.println(msg);

        //關閉資源
        printWriter.close();
        socket.close();
    }
}

UDP呢,首先UDP是沒有任何兩臺主機之間連接的概念,它只管發給誰就可以了,TCP可以使用流數據,而UDP不行,UDP在處理的時候以一個包的形式進行發送,要麼就發送到了,要麼就丟失。

UDP Recive

package enjoy.protocol.udp;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * UDP接收端
 */
public class ReciveDemo {
    public static void main(String[] args)throws  Exception {
        //創建一個DatagramSocket實例,並且把實例綁定到本機的地址,端口10005
        DatagramSocket datagramSocket = new DatagramSocket(10005);
        byte bytes[] = new byte[1024];
        //以一個空數組來創建 DatagramPacket,這個對象作用是接收DatagramSocket中的數據
        DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length);
        while(true){//無限循環是必須要的,因爲不知道數據何時來
            //接收到的數據包
            datagramSocket.receive(datagramPacket);
            //獲取接收的數據
            byte[] data = datagramPacket.getData();
            //把數組轉成字符
            String str = new String(data,0,datagramPacket.getLength());
            //如果數據包中是88的信息,則跳出並且關閉
            if("88".equals(str)){
                break;
            }
            //打印數據
            System.out.println("接收到的數據爲:"+str);
        }
        //關閉
        datagramSocket.close();

    }
}

UDP Send

package enjoy.protocol.udp;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * UDP發送端
 */
public class SendDemo {
    public static void main(String[] args)throws  Exception {
        //創建一個DatagramSocket實例
        DatagramSocket datagramSocket = new DatagramSocket();
        //使用鍵盤輸入構建一個BufferedReader
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        String line =null;
        while((line = bufferedReader.readLine())!=null){
            //轉成byte
            byte[] bytes = line.getBytes();
            //創建一個用於發送的DatagramPacket對象
            DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length, InetAddress.getByName("127.0.0.1"),10005);
            //發送數據包
            datagramSocket.send(datagramPacket);
            if("88".equals(line)){//當輸入88時結束髮送
                break;
            }
        }
        //關閉
        datagramSocket.close();
    }
}

在這裏插入圖片描述

分佈式:

RPC可以提高系統穩定性,比如說,我們的訂單服務程序更新出BUG,導致內存溢出,是這臺服務器宕機了,但是它只會影響的整個系統的訂單業務部分,對於用戶註冊登錄等業務沒有影響,同樣對於系統的日誌記錄也沒有影響。

RPC:

註冊中心:服務端會把它的服務註冊到註冊中心中,包括服務名稱、服務調用的ip地址、端口、協議、還有調用路徑等等。

RMI:

RMI接口和實現類不靈活,RMI必須繼承和實現Remote接口之類的。

rmi.1 繼承 Remote 接口

import java.rmi.Remote;
import java.rmi.RemoteException;
/**
 * RMI 訂單接口,繼承Remote類
 */
public interface IOrder extends Remote{
    //付款的方法
    public String  pay(String id) throws RemoteException;
}

rmi.2 繼承UnicastRemoteObject

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

/**
 * 接口實現類,繼承UnicastRemoteObject
 */
public class OrderImpl extends UnicastRemoteObject implements  IOrder {
    protected OrderImpl() throws RemoteException{
        super();
    }
    @Override
    public  String pay(String id ) throws  RemoteException{
        //默認返回成功
        return "支付成功!商品訂單號:"+id;
    }
}

rmi.3 服務提供方 1. LocateRegistry.createRegistry 2. Naming.bind

import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
/**
 * RMI--服務端
 */
public class Server {
    public static void main(String[] args)throws  Exception {
        //接口實例化
        IOrder iOrder = new OrderImpl();
        //本地的服務註冊到6666端口中
        LocateRegistry.createRegistry(6666);
        //把剛纔的實例綁定到本地端口上的一個路徑上
        Naming.bind("rmi://localhost:6666/order",iOrder);
        System.out.println("服務器已經啓動了!");
    }
}

rmi.4 服務消費方 Naming.lookup

import java.rmi.Naming;

/**
 * RMI-客戶端
 */
public class Client {
    public static void main(String[] args)throws  Exception {
        //通過RMI發現服務並且轉成一個對象
        IOrder iOrder = (IOrder)Naming.lookup("rmi://localhost:6666/order");
        //遠程調用下
        System.out.println(iOrder.pay("168888"));
    }
}

自己實現一個RPC 框架

需要掌握Socket通訊、動態代理和反射、Java反序列化

反射

demo 類

public class Tech {
    //洗腳服務
    public boolean XJ(String name) {
        System.out.println("13號技師爲("+name+")服務");
        return true;
    }
    @Override
    public String toString() {
        return "這是一名技師";
    }
}

反射使用 1. Class.forName 2. TechClazz.newInstance() 3. TechClazz.getMethods() 4. method.invoke

/**
 *類說明:演示反射的使用
 */
public class RefleDemo {

	public static void main(String[] args) throws ClassNotFoundException, 
	InstantiationException, IllegalAccessException {
	
        //通過全限定名拿到類的class對象
		Class TechClazz = Class.forName("cn.enjoyedu.refle.Tech");

        //通過class對象拿到類的實例
        Tech shapeInst = (Tech)TechClazz.newInstance();

        //通過class對象拿到方法列表
		Method[] methods = TechClazz.getMethods();
		for(Method method:methods) {
			System.out.println(method.getName());
			if(method.getName().equals("XJ")){//洗腳服務
				try {
				    //執行指定方法
					method.invoke(TechClazz.newInstance(),"king");
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

動態代理:

動態代理,兩個概念,一個是代理,一個是動態。

代理的本質就是代理模式,代理模式一定要有這三個要素:接口,提供服務的真實對象,代理對象。

三個要素之外還有重要的兩個動作,

首先是真實對象以及代理的對象都必須同時繼承一個指定的接口。
第二個,這個代理對象必須包含這個真實的對象。
第一個小夥伴,Proxy, 它就是一個調度器,這個是專門調度人.
第二個小夥伴就是Invocationhandler,這個是一個增強器,專門做增強的.
Invocationhandler的源碼,這個代碼就只有一個方法,我們把這個方法實現了就可以了。

爲什麼在RPC中用動態代理增強?-----增強了網絡遠程調用功能。

interface

public interface Girl {
	void date();
	void watchMovie();
}

implements

public class WangMeiLi implements Girl {
	@Override
	public void date() {
		System.out.println("王美麗說:跟你約會好開心啊");
		this.watchMovie();
	}
	@Override
	public void watchMovie() {
		System.out.println("王美麗說:這個電影我不喜歡看");
	}
}

Proxy.newProxyInstance & InvocationHandler invoke

public class WangMeiLiProxy implements InvocationHandler {
	private Girl gilr;
	public WangMeiLiProxy(Girl gilr) {
		super();
		this.gilr = gilr;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		//事前
		doSomeThingBefore();
		Object ret = method.invoke(gilr, args);
		//事後
		doSomeThingEnd();
		return ret;
	}
	private void doSomeThingBefore(){
		System.out.println("王美麗的父母說:我得先調查下這個男孩子的背景!");
	}
	private void doSomeThingEnd(){
		System.out.println("王美麗的父母說:他有沒有對你動手動腳啊?");
	}
	//代理:調度
	public Object getProxyInstance(){
		return Proxy.newProxyInstance(gilr.getClass().getClassLoader(), gilr.getClass().getInterfaces(), this);
	}
}

調用

public class King {
	public static void main(String[] args) {
		//隔壁有個女孩,叫王美麗
		Girl girl = new WangMeiLi();
		//他有個龐大的家庭,想要跟她約會必須徵得她家裏人的同意
		WangMeiLiProxy   family = new WangMeiLiProxy(girl);
		//有一次我去約王美麗,碰到了她的媽媽,我徵得了她媽媽的同意
		Girl mother = (Girl) family.getProxyInstance();
		//通過她的媽媽這個代理才能與王美麗約會
		mother.date();
	}
}
Bio通訊

Bio 服務端 new Thread(new ServerTask(serverSocket.accept())).start()

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
 *類說明:Bio通信的服務端
 */
public class Server {

    public static void main(String[] args) throws IOException {
        //服務端啓動必備
        ServerSocket serverSocket = new ServerSocket();
        //表示服務端在哪個端口上監聽
        serverSocket.bind(new InetSocketAddress(10001));
        System.out.println("Start Server ....");
        try{
            while(true){
                new Thread(new ServerTask(serverSocket.accept())).start();
            }
        }finally {
            serverSocket.close();
        }
    }

    //每個和客戶端的通信都會打包成一個任務,交個一個線程來執行
    private static class ServerTask implements Runnable{

        private Socket socket = null;
        public ServerTask(Socket socket){
            this.socket = socket;
        }

        @Override
        public void run() {
            //實例化與客戶端通信的輸入輸出流
            try(ObjectInputStream inputStream =
                    new ObjectInputStream(socket.getInputStream());
                ObjectOutputStream outputStream =
                    new ObjectOutputStream(socket.getOutputStream())){

                //接收客戶端的輸出,也就是服務器的輸入
                String userName = inputStream.readUTF();
                System.out.println("Accept client message:"+userName);

                //服務器的輸出,也就是客戶端的輸入
                outputStream.writeUTF("Hello,"+userName);
                outputStream.flush();
            }catch(Exception e){
                e.printStackTrace();
            }finally {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Bio 客戶端 socket.connect(inetSocketAddress)


import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;

/**
 *類說明:Bio通信的客戶端
 */
public class Client {

    public static void main(String[] args) throws IOException {
        //客戶端啓動必備
        Socket socket = null;
        //實例化與服務端通信的輸入輸出流
        ObjectOutputStream output = null;
        ObjectInputStream input = null;
        //服務器的通信地址
        InetSocketAddress addr = new InetSocketAddress("127.0.0.1",10001);

        try{
            socket = new Socket();
            socket.connect(addr);//連接服務器

            output = new ObjectOutputStream(socket.getOutputStream());
            input = new ObjectInputStream(socket.getInputStream());

            /*向服務器輸出請求*/
            output.writeUTF("Mark");
            output.flush();

            //接收服務器的輸出
            System.out.println(input.readUTF());
        }finally{
            if (socket!=null) socket.close();
            if (output!=null) output.close();
            if (input!=null) input.close();
        }
    }
}

ObjectOutputStream和ObjectInputStream結合Socket通訊就是我們實現的網絡增強部分的組成.

Java實現:

1.服務端定義接口和服務實現類並且註冊服務
2.客戶端查詢出服務
3.客戶端使用動態代理調用服務(動態代理)
4.客戶端代理把調用對象、方法、參數序列化成數據
5.客戶端與服務端通過Socket通訊傳輸數據
6.服務端反序列化數據成對象、方法、參數。
7.服務端代理拿到這些對象和參數後通過反射的機制調用服務的實例。

手擼服務提供方
在這裏插入圖片描述
服務註冊中心


import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 *類說明:服務註冊中心
 */
public class RegisterCenter {
    //線程池
    private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    //定義註冊中心的靜態對象
    private static final HashMap<String, Class> serviceRegistry = new HashMap<String, Class>();

    private static boolean isRunning = false;

    private static int port;

    public RegisterCenter(int port) {

        this.port = port;
    }

    //服務註冊中心啓動
    public void start() throws IOException {
        //服務器監聽
        ServerSocket server = new ServerSocket();
        //監聽綁定端口
        server.bind(new InetSocketAddress(port));
        System.out.println("start server");
        try {
            while (true) {
                // 1.監聽客戶端的TCP連接,接到TCP連接後將其封裝成task,由線程池執行,並且同時將socket送入(server.accept()=socket)
                executor.execute(new ServiceTask(server.accept()));
            }
        } finally {
            server.close();
        }
    }
    //服務的註冊:socket通訊+反射
    public void register(Class serviceInterface, Class impl) {

        serviceRegistry.put(serviceInterface.getName(), impl);
    }

    //服務的獲取運行
    private static class ServiceTask implements Runnable {
        //客戶端socket
        Socket clent = null;

        public ServiceTask(Socket client) {
            this.clent = client;
        }
        //遠程請求達到服務端,我們需要執行請求結果,並且把請求結果反饋至客戶端,使用Socket通訊
        public void run() {
            //反射
            //同樣適用object流
            ObjectInputStream inputStream = null;
            ObjectOutputStream outputStream = null;
            try {
                //1.客戶端發送的object對象拿到,2.在採用反射的機制進行調用,3.最後給返回結果
                inputStream = new ObjectInputStream(clent.getInputStream());
                //順序發送數據:類名、方法名、參數類型、參數值
                //拿到接口名
                String  serviceName = inputStream.readUTF();
                //拿到方法名
                String  methodName = inputStream.readUTF();
                //拿到參數類型
                Class<?>[] paramTypes = ( Class<?>[])inputStream.readObject();
                //拿到參數值
                Object[] arguments = (Object[])inputStream.readObject();
                //要到註冊中心根據 接口名,獲取實現類
                Class serviceClass =serviceRegistry.get(serviceName);
                //使用反射的機制進行調用
                Method method = serviceClass.getMethod(methodName,paramTypes);
                //反射調用方法,把結果拿到
                Object result = method.invoke(serviceClass.newInstance(),arguments);
                //通過執行socket返回給客戶端
                outputStream = new ObjectOutputStream(clent.getOutputStream());
                // /把結果返回給客戶端
                outputStream.writeObject(result);
                //記得關閉
                outputStream.close();
                inputStream.close();
                clent.close();

            }catch (Exception e){
                e.printStackTrace();
            }

        }

    }
}

rpc的服務端,提供服務

/**
 *類說明:rpc的服務端,提供服務
 */
public class Server {
    public static void main(String[] args) throws  Exception{
        new Thread(new Runnable() {
            public void run() {
                try {
                    //起一個服務中心
                    RegisterCenter serviceServer = new RegisterCenter(8888);
                    //註冊技師對象至註冊中心
                    serviceServer.register(TechInterface.class, TechImpl.class);
                    //運行我們的服務
                    serviceServer.start();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

手擼服務消費方
在這裏插入圖片描述

rpc框架的客戶端代理部分

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.net.Socket;

/**
 *類說明:rpc框架的客戶端代理部分
 */
public class RpcClientFrame {

    /*遠程服務的代理對象,參數爲客戶端要調用的的服務*/
    public static <T> T getRemoteProxyObj(final Class<?> serviceInterface)
            throws Exception {
        // 默認端口8888
        InetSocketAddress serviceAddr = new InetSocketAddress("127.0.0.1",8888);
        // 1.將本地的接口調用轉換成JDK的動態代理,在動態代理中實現接口的遠程調用
        //進行實際的服務調用(動態代理)
        return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class<?>[]{serviceInterface},new DynProxy(serviceInterface,serviceAddr));
    }

    /*動態代理類,實現了對遠程服務的訪問*/
    private static class DynProxy implements InvocationHandler {
        //接口
        private final Class<?> serviceInterface;
        //遠程調用地址
        private final InetSocketAddress addr;

        //構造函數
        public DynProxy(Class<?> serviceInterface, InetSocketAddress addr) {
            this.serviceInterface = serviceInterface;
            this.addr = addr;
        }

        /*動態代理類,增強:實現了對遠程服務的訪問*/
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            /* 網絡增強部分*/
            Socket socket = null;
            //因爲傳遞的大部分是 方法、參數,所以我們使用Object流對象
            ObjectInputStream objectInputStream = null;
            ObjectOutputStream objectOutputStream = null;
            try {
                //新建一個Socket
                socket = new Socket();
                //連接到遠程的地址和端口
                socket.connect(addr);
                //往遠端 發送數據,按照順序發送數據:類名、方法名、參數類型、參數值
                //拿到輸出的流
                objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
                //發送 調用方法的 類名,使用UTF-8避免亂碼
                objectOutputStream.writeUTF(serviceInterface.getName());
                //發送 方法名
                objectOutputStream.writeUTF(method.getName());
                //發送 參數類型,使用Object
                objectOutputStream.writeObject(method.getParameterTypes());
                //發送 參數的值,使用UTF-8避免亂碼
                objectOutputStream.writeObject(args);
                //刷新緩衝區,使得數據立馬發送
                objectOutputStream.flush();
                //立馬拿到遠程執行的結果
                objectInputStream = new ObjectInputStream(socket.getInputStream());
                //我們要把調用的細節打印出來
                System.out.println("遠程調用成功!" + serviceInterface.getName());
                //最後要網絡的請求返回給返回
                return objectInputStream.readObject();
            } finally {

                //最後記得關閉
                socket.close();
                objectOutputStream.close();
                objectInputStream.close();

            }
        }
    }
}

rpc的客戶端,調用遠端服務

/**
 *類說明:rpc的客戶端,調用遠端服務
 */
public class Client {
    public static void main(String[] args) throws Exception {
        //動態代理獲取我們的對象
        TechInterface techInterface = RpcClientFrame.getRemoteProxyObj(TechInterface.class);
        //進遠程調用我們的對象
        System.out.println(techInterface.XJ("king"));

    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章