系统间通信(四)---优化篇(3)

    上一篇文章我们介绍了传统的阻塞式IO在java中的体现,本篇文章我们开始介绍另一种通信模型---非阻塞式通信模型。

1.实现原理:阻塞式通信模型是因为服务器端在accept和read方法的时候,如果没有外部网络连接请求或者外部网络的数据传输,那么就会处于等待状态。非阻塞式通信模型,主要是通过为accept和read方法设置等待时间,在超过等待时间之后可以让CPU暂时做一些其他处理,过一段时间再去监测accept方法和read方法。

2.服务端代码:

(1)为accept方法设置等待时间:

package BIO;


import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;


public class Server {


    public static void main(String[] args) {
        
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream is = null;
        OutputStream os = null;
        try {
            //服务器端接收客户端请求并回写信息
            serverSocket = new ServerSocket(9999);
            serverSocket.setSoTimeout(3000);
            
            while(true){
                try {
                    socket = serverSocket.accept();
                    is = socket.getInputStream();
                    os = socket.getOutputStream();
                    byte[] receiveByte = new byte[1024];
                    StringBuffer receiveMsg = new StringBuffer();
                    //接收客户端发送的数据
                    while(is.read()!=-1){
                        is.read(receiveByte);
                        receiveMsg.append(new String(receiveByte));
                        //收到“over”表示客户端已经发送完完整的信息,服务器可以处理请求并在完成之后写回客户端
                        if(receiveMsg.indexOf("。")!=-1){
                            System.out.println("客户端发送的信息为:"+receiveMsg);
                            break;
                        }
                    }
                    /*System.out.println(receiveMsg);
                    byte[] sendByte = new byte[1024];
                    String sendMsg = "服务端已经收到请求over";
                    sendByte = sendMsg.getBytes();
                    os.write(sendByte);
                    os.flush();*/
                } catch (SocketTimeoutException e) {
                    // TODO: handle exception
                    System.out.println("CPU执行其他操作"+System.currentTimeMillis());
                    continue;
                }
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally {
            try {
                os.close();
                is.close();
                socket.close();
                serverSocket.close();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

(2)为read方法设置等待时间:

package BIO;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;

public class Server1 {
public static void main(String[] args) {
        
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream is = null;
        OutputStream os = null;
        try {
            serverSocket = new ServerSocket(9999);
            
            //accept方法设置等待时间
            serverSocket.setSoTimeout(5000);
            
            while(true){
                //accept等待超时以后CPU进行其他操作
                try {
                    socket = serverSocket.accept();
                } catch (SocketTimeoutException e) {
                    System.out.println("accept方法CPU执行其他操作"+System.currentTimeMillis());
                    continue;
                }
                System.out.println("来自客户端的请求已经建立");
                is = socket.getInputStream();
                os = socket.getOutputStream();
                byte[] receiveByte = new byte[1024];
                //read方法设置等待时间
                socket.setSoTimeout(3000);
                while(true){
                    //read方法等待超时CPU做其他操作
                    try {
                        while(is.read()!=-1){
                            is.read(receiveByte);
                            String receiveMsg = new String(receiveByte);
                            System.out.println(receiveMsg);
                            //收到“。”表示客户端已经发送完完整的信息,服务器可以处理请求并在完成之后写回客户端
                            if(receiveMsg.indexOf("。")!=-1){
                                System.out.println("客户端发送的信息为:"+receiveMsg);
                                break;
                            }
                        }
                    } catch (Exception e) {
                        System.out.println("read方法CPU执行其他操作"+System.currentTimeMillis());
                        continue;
                    }
                }
            }   
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                os.close();
                is.close();
                socket.close();
                serverSocket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

3.客户端代码:

package BIO;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

public class Client {
    public static void main(String[] args) {
        Socket socket = null;
        InputStream is = null;
        OutputStream os = null;
        try {
            //客户端请求建立连接
            socket = new Socket("localhost", 9999);
            is = socket.getInputStream();
            os = socket.getOutputStream();
            byte[] sendBuffer = new byte[1024];
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入要发送到服务端的消息:");
            while(true){
                String sendMsg = sc.next();
                if(sendMsg.indexOf("。")!=-1){
                    sendBuffer = sendMsg.getBytes();
                    os.write(sendBuffer);
                    os.flush();
                    System.out.println("'"+sendMsg+"发送成功'");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                is.close();
                os.close();
                socket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }        
    }
}

4.实验结果:

(1)为accept方法设置等待时间:

先启动服务端(不启动客户端),此时没有客户端连接请求。服务器等待3秒,若没有客户端请求过来,则服务器可以在这个时候让CPU去做其他处理。运行结果如下图:


此时启动客户端,服务器收到客户端请求,处理客户端请求,并将处理结果返回给客户端。

服务器端运行结果如下图:


客户端运行结果如下图:


注:因为编码的不同,所以客户端发送的消息到了服务端可能会乱码。比如我在客户端输入的是“nihao。”,到服务端就成了“ihao。”。如果输入的是汉字的话就可以明显的看出来。所以在测试之前还需要调整编码,我这里就不调了。

(2)为read方法设置等待时间:

先启动服务端(不启动客户端),此时因为没有连接请求,所以accept方法在监测超时后CPU会进行一段时间的其他处理。


启动客户端,此时客户端发送连接请求,服务端收到连接请求以后,read方法开始监测数据传输,因为客户端没有发送任何数据,所以在read方法监测超时以后CPU也会进行一段时间的其他处理。


此时客户端发送数据,服务端read方法接收到数据,CPU先去处理read接收到的数据,处理完以后如果客户端不发送数据,那么read方法在等待超时之后,CPU会继续先进行一段时间的其他处理。


注:(1)可以看到因为没有设置编码的问题,确实是乱码的。(2)因为本例是单线程的,所以在有客户端连接以后,进入read方法,那么accept方法就不会再执行,也就不会再阻塞,所以不会再有“accept方法CPU执行其他操作“。

5.总结:(1)阻塞型通信模型到非阻塞型通信模型的转变,就是为accept和read方法设置等待时间。

             (2)虽然采用多线程和非阻塞通信模型会优化系统间通信,但是这只是程序级别的优化,操作系统底层的IO模型仍是“同步”的,即很多客户端请求到达服务端的时候,服务端在accept的时候还是一个一个的accept的,read也是同样的。

猜你喜欢

转载自blog.csdn.net/Dream_Ryoma/article/details/80431822