Socket的入门案例(中)

本文介绍了一个使用Socket进行远程方法调用的例子。客户端发送类名和方法签名至服务器,服务器利用反射技术调用本地方法并返回结果。文章提供了完整的客户端和服务端代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

   上一篇[《Socket的入门案例(上)》](http://blog.youkuaiyun.com/demoaop/article/details/78961746)讲到的是最简单的一个通信模型。即:客户端发送简单的字符串到服务器端,服务器端接收到字符串后再简单处理一下,返回给客户端加工后的数据。上一篇的重点在于了解Socket通信的基本模型和一些模板代码,此篇的案例进一步展开和深入。
    客户端传递的信息可以从没有含义的字符串变成一个有意义的字符串,比如:类名“com.aishang.socket.Computer”以及一个方法的签名“add(3,5)”,当客户端把类名和方法的签名通过Socket流传递到服务端后,服务器端先处理一下从这个字符串中切分出来类名:“com.aishang.socket.Computer”、方法名“add”和参数列表:“3,5”。而在服务器端其实是有这个业务类和方法的声明的:
package com.aishang.socket;
public class Computer {
    //加法计算
    public int add(int a,int b){
        return (a+b);
    }
}
   服务器端从Socket流中获得类名 “com.aishang.socket.Computer”后,利用反射技术构造一个类的实例:
   Class clazz = Class.forName(className);
   Object obj = clazz.newInstance();
   然后再从这个类的字节码文件对象中获得到方法对象
Method method = clazz.getMethod(methodName, int.class,int.class);
   最后通过反射的方式调用上面的Method对象
Object retValue = method.invoke(obj, Integer.parseInt(parameterList[0]), Integer.parseInt(parameterList[1]));
   最后将调用的结果发回到客户端。总结一下:客户端其实只是向服务器端发送了一堆字符串,这堆字符串不是普通的字符串,是包含了调用服务器端的类名、方法名、实参列表信息的有价值的文字,当服务器端利用Socket流接收到这些信息后,反射调用服务器上的方法。为何要用反射?其实传递过来的都是字符,这个时候只有反射能解决这个问题。其实这就是远程消息调用的雏形。

以下是完整的代码:

服务器端代码:ServerService.class

public class ServerService {
    public static void main(String[] args) throws IOException {  
        // 创建一个ServerSocket,绑定到服务器的3366端口上  
        ServerSocket server = new ServerSocket();  
        server.bind(new InetSocketAddress("localhost", 3366));  
        System.out.println("服务器开启了");  

        //服务器一直等着接受客户端的接入请求  
        while (true) {  
            //Socket.accept()方法是一个阻塞方法,会一直等待客户端  
            Socket socket = server.accept();  
            //当有客户端请求,启动服务器上的线程去执行任务,此时服务器继续等待客户端接入  
            System.out.println("启动了一个线程负责通信");  
            new Thread(new ServerTask(socket)).start();  
        }  
    }  
}

线程任务:ServerTask.class

public class ServerTask implements Runnable {
        Socket socket ;  
        InputStream in=null;  
        OutputStream out = null;  

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

        @Override  
        public void run() {  
            try {  
                //从socket连接中获取到与client之间的网络通信输入输出流   
                in = socket.getInputStream();  
                out = socket.getOutputStream();  

                //1.从网络通信输入流中读取客户端发送过来的数据  
                //将Socket输入流中的数据(客户端发送过来的数据在in中)用BufferedReader重新包装一下  
                //这样可以利用BufferedReader的方法按行读取  
                BufferedReader br = new BufferedReader(new InputStreamReader(in));  

                //注意:socketinputstream的读数据的方法都是阻塞的   
                //阻塞的意思:没有接受到数据的时候,服务端这个线程任务什么也不做,一直等待  
                //做点事情,获取客户端发送过来的信息,里面包含有要调用的类名、方法名,实参
                //根据通信双方的约定,客户端第一行发送类名,第二行发送方法名和实参列表

                //获取类名
                String className = br.readLine();  

                //方法名和实参列表
                String methodAndParameter = br.readLine();
                int beginIndex = methodAndParameter.indexOf("(");
                int endIndex = methodAndParameter.indexOf(")");
                //获取方法名
                String methodName = methodAndParameter.substring(0, beginIndex);
                //获取参数列表
                String parameterList[] = methodAndParameter.substring(beginIndex+1, endIndex).split(",");

                Object retValue = null;
                try {
                    //反射调用
                    //先弄到类的实例
                    Class clazz = Class.forName(className);
                    Object obj = clazz.newInstance();
                    //获取到字节码文件对象中的方法
                    Method method = clazz.getMethod(methodName, int.class,int.class);
                    retValue = method.invoke(obj, Integer.parseInt(parameterList[0]), Integer.parseInt(parameterList[1]));

                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }


                //2.将调用结果写到sokect的输出流中,以发送给客户端  
                //将Socket输出流用PrintWriter重新包装一下  
                PrintWriter pw = new PrintWriter(out);  
                pw.println(retValue.toString());  
                pw.flush();  
            } catch (IOException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            } finally{  
                try {  
                    in.close();  
                    out.close();  
                    socket.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
}

服务器端定义的业务方法(供远程调用):Computer.class

package com.aishang.socket;
public class Computer {
    //加法计算
    public int add(int a,int b){
        return (a+b);
    }
}

客户端:ClientService.class

public class ClientService {
    public static void main(String[] args){  

        Socket socket = null;  
        InputStream inputStream = null;  
        OutputStream outputStream = null;  
        try {  
            //创建Socket对象,向服务器发出请求建立连接  
            socket = new Socket("localhost", 3366);  

            // 从socket中获取输入输出流  
            inputStream = socket.getInputStream();  
            outputStream = socket.getOutputStream();  

            //通过Socket输出流,向服务器发送信息  
            PrintWriter pw = new PrintWriter(outputStream);  
            pw.println("com.aishang.socket.Computer");  
            pw.println("add(3,5)");
            pw.flush();  
            System.out.println("客户端发送了方法调用信息");  

            //在没收到服务器端的返回信息前,客户端一直阻塞  
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));  
            String result = br.readLine();  
            System.out.println("客户端收到服务端的信息:"+result);  
        } catch (Exception e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        } finally{  
            try {  
                //完成通信  
                inputStream.close();  
                outputStream.close();  
                socket.close();  
            } catch (IOException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
        }  
    }  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值