Java的IO演进历史以及使用JDK自带的BIO编写BioServer服务端以及BioClient客户端分析通信优缺点示例

674 阅读3分钟

Java的I/O演进历史

  • jdk1.4之前使用同步阻塞,也就是BIO。像0几年QQ,那么大并发量,java开发的话是不可能做到高并发的,那时采用C或者C++, 因为可以直接操作系统提供的异步IO,AIO
  • jdk1.4推出NIO,支持非阻塞IO,jdk1.7升级,推出NIO2.0,提供AIO的功能,支持文件和网络套接字的异步IO

(这里不懂什么IO类型的,可以看博主后出的文章,带你了解各种IO模型)

使用jdk自带的Bio编写一个同一时间服务

🌰:一个客户端向服务端发起请求,服务端返回对应的时间戳,每个请求都分一个线程

Step1:新建java项目

Step2:创建Server服务端

public class BioServer {

    //定义服务端端口号
    private static final int PORT = 8080;

    public static void main(String[] args) throws IOException {

        //建立Socket连接
        ServerSocket server = null;
        try {
            server = new ServerSocket(PORT);
            System.out.println("the time server is start in port:"+PORT);
            Socket socket = null;
            //等待客户端连接
            while (true){
                socket = server.accept();

                //传入Hander
                new Thread(new TimeServerHander(socket)).start();
            }
        }catch (Exception e){
            e.printStackTrace();

        }finally {
            if(server != null){
                System.out.println("the time server close");
                server.close();
            }
        }


    }
}

Step3:处理逻辑

public class TimeServerHander implements Runnable {

    //接收socket
    private Socket socket;

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

    /**
     * 处理逻辑
     */
    @Override
    public void run() {
        //输入
        BufferedReader in = null;
        //输出
        PrintWriter out = null;

        try {
            //拿取输入流
            //包装设计模式 提高性能
            in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));

            //拿取输出流 自动刷新
            out = new PrintWriter(this.socket.getOutputStream(),true);

            //循环读取内容
            String body = null;
            while ((body = in.readLine())  != null && body.length()!=0){
                System.out.println("the time server receive msg:"+body);
                //输出
                out.println(new Date().toString());
                //out.flush(); 上面写入了true 自动刷新
            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //关闭
            if(in != null){
                try {
                    in.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
            if(out != null){
                try {
                    out.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
            if(this.socket != null){
                try {
                    this.socket.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
}

Step4:创建客户端

public class BioClient {

    //定义服务端口号
    private static final int PORT= 8080;

    private static final String HOST = "127.0.0.1";

    public static void main(String [] args){
        Socket socket = null;

        //获取服务端返回的值
        BufferedReader in = null;
        //发送数据给服务端
        PrintWriter out = null;

        try {

            socket = new Socket(HOST,PORT);
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new PrintWriter(socket.getOutputStream(),true);
            out.println("向服务端发送的消息:我是客户端");
            String resp = in.readLine();
            System.out.println("接收服务端返回的消息:当前服务器时间是:"+resp);

        }catch (Exception e){

        }finally {
            if(in != null){
                try {
                    in.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
            if(out != null){
                try {
                    out.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
            if(socket != null){
                try {
                    socket.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
}

Step5:测试

先启动server端准备接收消息

WechatIMG239.png

server会一直启动等待连接,再启动client端向服务端发消息,server服务端返回当前时间戳

WechatIMG240.png

服务端接收的消息

WechatIMG241.png

BIO编写Client/server通信优缺点分析

BIO优缺点,为什么不能高并发情况下性能弱?

优点

模型简单、编码简单(请求,分发 BIO模型非常简单 )

缺点

性能瓶颈 每个请求都分配一个线程 如果一万个请求过来 分配一万个线程,电脑CPU处理线程多的时候需要上下文切换,损耗很大,如果这个线程处理还需要读数据库,编解码,这个线程就会一直耗着(请求书和线程数属于N:N关系)高并发情况下 CPU切换线程上下文损耗大

缺点的存在:JDK旧版本是没有NIO的 都是BIO通信 案例:Tomcat 7 之前都是使用BIO通信 7之后使用NIO(一个线程去维护多个操作 或者是线程池)

改进:伪NIO(也就是使用线程池去做 请求会在等待队列里) 文切换,损耗很大,如果这个线程处理还需要读数据库,编解码,这个线程就会一直耗着(请求书和线程数属于N:N关系)高并发情况下 CPU切换线程上下文损耗大

WechatIMG244.jpeg

感觉不错的大佬点个赞呗~ 手敲截图演示不易