java网络编程2 -- BIO

吃可爱长大的小学妹 提交于 2019-11-29 22:15:12

BIO

什么叫BIO , BIO 是 block input output 的缩写 , 意思就是阻塞式的IO。这个是为了区别后面的NIO , 有关NIO ,后面再做介绍。

举个简单的例子:我在使用bio的socket网络通信中,会有下面的代码:

        ServerSocket serverSocket = new ServerSocket(port);


        while (true) {
            Socket accept = serverSocket.accept();
            InputStream inputStream = accept.getInputStream();
            OutputStream outputStream = accept.getOutputStream();

            byte[] readBytes = new byte[100];
            inputStream.read(readBytes);
            System.out.println("===== " + new String(readBytes));
            outputStream.write((System.currentTimeMillis() + "").getBytes());
            accept.close();
        }

在 inputStream.read(readBytes);这一行,如果socket链接没有断,他会一直等待,直到读完socket的一次写入的所有的数据,或者是byte指定大小的数据。在链接没有断开的情况下,直到数据读完,才会执行下面的代码。(这个是bio的关键部分)。

所以,如果在服务端,如果是单线程的,上面的代码,如果一个客户端链接进来,没有关闭,则第二个用户链接,发送数据不会有相应的。

——————————

所以针对,bio 的socket,出现了下面三种模型:

socket bio -- 单线程模式:

这个比较简单,直接写demo :

public class BioSimpleServer {

    private static int port = 8888;

    private static void init() throws IOException {

        ServerSocket serverSocket = new ServerSocket(port);


        while (true) {
            Socket accept = serverSocket.accept();
            InputStream inputStream = accept.getInputStream();
            OutputStream outputStream = accept.getOutputStream();

            byte[] readBytes = new byte[100];
            inputStream.read(readBytes);
            String readString = new String(readBytes);
            System.out.println("===== " + readString);
            outputStream.write((System.currentTimeMillis() + "").getBytes());
            if (readString != null && readString.trim().equalsIgnoreCase("quit")){
                accept.close();
            }
        }


    }

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

这段代码,只是一个简单的demo , 没有考虑发送数据的长度,直接通过100个byte接收了。如果客户端,有数据过来,直接返回一个当前时间的毫秒数,如果请求的字符串是quit,则关闭连接。如果你采用telnet测试 , 只有第一个连接发送quit以后,第二个连接才会有相应。

socket bio -- 多线程模式:

上面可以看到,单线程模式的弊端是,一次只能服务一个客户端,如果有多个客户端过来链接,那就只能傻等,只有这个用户关闭链接以后,第二个客户端才可以链接的上,为了解决这个问题,出现了bio socket的多线程模式。

public class BioSimpleThread {
    private static int port = 8888;

    private static void init() throws IOException {

        ServerSocket serverSocket = new ServerSocket(port , 10);


        while (true) {
            final Socket accept = serverSocket.accept();

            new Thread() {
                @Override
                public void run() {

                    while (true){
                        try {
                            InputStream inputStream = accept.getInputStream();
                            OutputStream outputStream = accept.getOutputStream();
                            byte[] readBytes = new byte[100];
                            inputStream.read(readBytes);
                            String readString = new String(readBytes);
                            System.out.println("===== " + readString);
                            outputStream.write((System.currentTimeMillis() + "").getBytes());
                            if (readString != null && readString.trim().equalsIgnoreCase("quit")){
                                accept.close();
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }.start();
        }


    }

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


        init();
    }
}

代码比较简单,一看就懂,这里添加了一个线程,只要有socket accept过来,就起一个线程,处理客户端的请求,这样可以保证,可以同时处理多个用户请求的情况。

socket bio -- 线程池模式:

在看了多线程的bio 以后,我们发现,只要有请求过来,就启动一个线程,如果有10万的客户端轻轻,就会启动10万个线程 ,这样就会出现cpu由于多个线程切换,线程相互竞争,服务端会越来越慢,直到服务器被托跨的问题,

为了避免这种问题,我们就需要规划自己的用户规模,考虑服务器的性能,单台机器上面,最多可以支持多少的在线用户。采用线程池的模式。

public class BioThreadPool {

    private static int port = 8888;

    static ExecutorService cachedThreadPool = Executors.newFixedThreadPool(100);


    private static void init() throws IOException {

        ServerSocket serverSocket = new ServerSocket(port);


        while (true) {
            final Socket accept = serverSocket.accept();

            cachedThreadPool.execute(new Thread() {
                @Override
                public void run() {
                    try {
                        InputStream inputStream = accept.getInputStream();
                        OutputStream outputStream = accept.getOutputStream();

                        byte[] readBytes = new byte[100];
                        inputStream.read(readBytes);
                        String readString = new String(readBytes);
                        System.out.println("===== " + readString);
                        outputStream.write((System.currentTimeMillis() + "").getBytes());
                        if (readString != null && readString.trim().equalsIgnoreCase("quit")){
                            accept.close();
                        }
                    } catch (Exception e) {
                        if (accept != null){
                            try {
                                accept.close();
                            } catch (IOException e1) {
                                e1.printStackTrace();
                            }
                        }
                        e.printStackTrace();
                    }
                }
            });

        }


    }

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


}


这里采用了 static ExecutorService cachedThreadPool = Executors.newFixedThreadPool(100); , 避免单台服务器上面,启动过多的线程 , 将服务器托跨。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!