1

基础版本

定义一个User类。

import java.io.Serializable;

public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    int id;
    String name;

    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

定义一个接口,返回User对象。

public interface IUserService {
    User findUserById(int id);
}

实现该接口,返回User对象。

public class IUserServiceImpl implements IUserService {
    @Override
    public User findUserById(int id) {
        return new User(id, "Alice");
    }
}

定义一个服务端,建立Socket连接,根据传入的ID值返回User对象信息。

import com.zebro.IUserService;
import com.zebro.IUserServiceImpl;
import com.zebro.User;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    private static boolean running = true;
    public static void main(String[] args) throws Exception {
        ServerSocket server = new ServerSocket(8888);
        //循环监听
        while(running){
            Socket client = server.accept();
            process(client);
            client.close();
        }
        server.close();
    }
    
    public static void process(Socket socket) throws Exception {
        DataInputStream dis = new DataInputStream(socket.getInputStream());
        DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
        
        //读取客户端传入的ID
        int id = dis.readInt();
        IUserService service = new IUserServiceImpl();
        User user = service.findUserById(id);
        dos.writeInt(user.getId());
        dos.writeUTF(user.getName());
        dos.flush();
    }
}

编写一个客户端,用于发送ID和接收返回的User对象信息。

import java.io.*;
import java.net.Socket;

public class Client {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket("127.0.0.1", 8888);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(baos);
        
        //发送给服务端
        dos.writeInt(123);
        socket.getOutputStream().write(baos.toByteArray());
        socket.getOutputStream().flush();
        
        //接收服务端返回的结果
        DataInputStream dis = new DataInputStream(socket.getInputStream());
        int id = dis.readInt();
        String name = dis.readUTF();
        
        //组装
        User user = new User(id,name);
        System.out.println(user);
        
        dos.close();
        socket.close();
    }
}

这时候客户端不需要知道服务端的具体方法名也能取得数据。

优化版本1

简化客户端的调用方式,引入客户端存根stub。

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

public class Stub {
    public User findUserById(int id) throws IOException {
        Socket socket = new Socket("127.0.0.1", 8888);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(baos);
        
        //发送给服务端
        dos.writeInt(id);
        socket.getOutputStream().write(baos.toByteArray());
        socket.getOutputStream().flush();
        
        //接收服务端返回的结果
        DataInputStream dis = new DataInputStream(socket.getInputStream());
        int idtmp = dis.readInt();
        if(idtmp != id) System.out.println("error");
        String name = dis.readUTF();
        User user = new User(id,name);
        
        return user;
    }
}
import java.io.IOException;

public class Client {
    public static void main(String[] args) throws IOException {
        Stub stub = new Stub();
        System.out.println(stub.findUserById(123));
    }
}

这时候客户端不需要知道服务端的具体方法名也能取得数据。

优化版本2

上述版本中,如果服务端方法较多,客户端存根需要提供大量的方法和返回值类型封装,引入动态代理优化相关逻辑。

import com.zebro.User;
import com.zebro.IUserService;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;

public class Stub {
    public static IUserService getStub(){
        InvocationHandler h = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Socket socket = new Socket("127.0.0.1", 8888);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                DataOutputStream dos = new DataOutputStream(baos);

                dos.writeInt((Int)args);

                //发送给服务端
                socket.getOutputStream().write(baos.toByteArray());
                socket.getOutputStream().flush();
                
                //接收服务端返回的结果
                DataInputStream dis = new DataInputStream(socket.getInputStream());
                int id = dis.readInt();
                String name = dis.readUTF();
                Object user = new User(id,name);

                return user;
            }
        };

        //通过动态代理,实例化一个代理对象
        Object o = Proxy.newProxyInstance(IUserService.class.getClassLoader(), new Class[]{IUserService.class}, h);
        System.out.println(o.getClass().getName());
        System.out.println(o.getClass().getInterfaces()[0]);
        return (IUserService) o;
    }
}
import com.zebro.IUserService;

public class Client {
    public static void main(String[] args) {
        IUserService stub = Stub.getStub();
        System.out.println(stub.findUserById(123));
    }
}

这时候客户端通过IUserService接口,可以知道服务端的具体方法名,也能取得数据。

优化版本3

上述版本中,客户端无论调用什么方法,服务端均调用findUserById处理逻辑并返回User对象,修改为动态方法优化相关逻辑。

import java.io.*;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;

public class Stub {
    static IUserService getStub(){
        InvocationHandler h = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Socket socket = new Socket("127.0.0.1", 8888);
                ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());

                //支持动态方法名
                oos.writeUTF(method.getName());
                oos.writeObject(method.getParameterTypes());
                oos.writeObject(args);
                oos.flush();

                //接收服务端返回的结果,object读入
                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                User user = (User)ois.readObject();
                
                oos.close();
                socket.close();
                return user;
            }
        };

        Object o = Proxy.newProxyInstance(IUserService.class.getClassLoader(), new Class[]{IUserService.class}, h);
        System.out.println(o.getClass().getName());
        System.out.println(o.getClass().getInterfaces()[0]);
        return (IUserService) o;
    }
}
import com.zebro.IUserService;
import com.zebro.IUserServiceImpl;
import com.zebro.User;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    private static boolean running = true;
    public static void main(String[] args) throws Exception {
        ServerSocket server = new ServerSocket(8088);
        while(running){
            Socket client = server.accept();
            process(client);
            client.close();
        }
        server.close();
    }
    
    public static void process(Socket socket) throws Exception {
        ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
        ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());

        //服务端支持动态方法和参数的调用
        String methodName = ois.readUTF();
        Class[] parameterTypes = (Class[]) ois.readObject();
        Object[] parameters = (Object[]) ois.readObject();
        
        //服务类型暂时还是写死的,不够灵活
        IUserService service = new IUserServiceImpl();
        Method method = service.getClass().getMethod(methodName, parameterTypes);
        User user = (User)method.invoke(service, parameters);
        oos.writeObject(user);
        oos.flush();
    }
}

优化版本4

上述版本中,客户端和服务端都只支持IUserService的方法调用,并且返回User对象,修改为支持任意接口方法的调用优化相关逻辑。

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.Socket;

public class Stub {
    static Object getStub(Class c){
        InvocationHandler h = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Socket socket = new Socket("127.0.0.1", 8888);
                ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());

                //服务类型
                oos.writeUTF(c.getName());
                oos.writeUTF(method.getName());
                oos.writeObject(method.getParameterTypes());
                oos.writeObject(args);
                oos.flush();

                //接收服务端返回的结果,object读入
                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                Object obj = ois.readObject();
                
                //改为返回通用对象
                return obj;
            }
        };
        
        //这里要写成通用的c,而不是固定的接口
        Object o = Proxy.newProxyInstance(c.getClassLoader(), new Class[]{c}, h);
        System.out.println(o.getClass().getName());
        System.out.println(o.getClass().getInterfaces()[0]);
        return o;
    }
}
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;

public class Server {
    private static boolean running = true;
    private static HashMap<String,Class> registerTable = new HashMap<>();
    
    static{
        registerTable.put(IUserService.class.getName(),IUserServiceImpl.class);
        registerTable.put(IProductService.class.getName(), IProductServiceImpl.class);
    }
    
    public static void main(String[] args) throws Exception {
        ServerSocket server = new ServerSocket(8888);
        while(running){
            Socket client = server.accept();
            process(client);
            client.close();
        }
        server.close();
    }
    
    public static void process(Socket socket) throws Exception {
        ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
        ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());

        //为了适应客户端通用化而做的改动
        String clazzName = ois.readUTF();
        String methodName = ois.readUTF();
        Class[] parameterTypes = (Class[]) ois.readObject();
        Object[] parameters = (Object[]) ois.readObject();

        //从注册表中查到服务类,如果使用spring甚至还可以直接根据配置注入bean然后根据bean查找。
        Object service = registerTable.get(clazzName).newInstance();
        Method method = service.getClass().getMethod(methodName, parameterTypes);
        Object o = method.invoke(service, parameters);
        oos.writeObject(o);
        oos.flush();
    }
}
import com.zebro.IProductService;
import com.zebro.IUserService;

public class Client {
    public static void main(String[] args) {
        IUserService userService = (IUserService) Stub.getStub(IUserService.class);
        IProductService productService = (IProductService)Stub.getStub(IProductService.class);
        
        System.out.println(userService.findUserById(123));
        System.out.println(productService.findProductByName("Bob"));
    }
}

小伍
139 声望4 粉丝