Netty剖析之BIO

什么是BIO?

BIO(blocking I/O),同步阻塞IO,服务器实现模式为一个线程处理一个连接,即每当有一个客户端连接时,就会启动一个独立的线程来进行处理,如果这个连接不做任何事情,就会阻塞到那里,从而引起不必要的线程开销;

BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,程序简单易理解;

BIO工作原理

在这里插入图片描述

  1. 服务器端启动一个ServerSocket
  2. 客户端启动Socket对服务器进行通信,默认情况下服务器端需要对每个客户建立一个线程与之通讯
  3. 客户端发出请求后, 先咨询服务器是否有线程响应,如果没有则会等待,或者被拒绝
  4. 如果有响应,客户端线程会等待请求结束后,再继续执行

BIO代码示例

public class BIOServer {

    public static void main(String[] args) throws Exception {
        // 创建一个线程池
        ExecutorService executorService = Executors.newCachedThreadPool();

        // 创建Socket服务,端口为8888
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("socket server start success");

        while (true) {
            Thread currentThread = Thread.currentThread();
            System.out.println("main Thread id:[" + currentThread.getId() + "]&name:[" + currentThread.getName() + "]");

            System.out.println("client connect start.........");
            // 如果此时没有客户端连接,此处会阻塞掉,直到有客户端连接为止
            final Socket socket = serverSocket.accept();
            System.out.println("client connect end.........");
            executorService.execute(new Runnable() {
                // 重写run方法
                public void run() {
                    // 与客户端通讯处理
                    handler(socket);
                }
            });
        }
    }

    public static void handler(Socket socket) {
        Thread currentThread = Thread.currentThread();
        System.out.println("Handler Thread id:[" + currentThread.getId() + "]&name:[" + currentThread.getName() + "]");

        try {
            byte[] bytes = new byte[1024];
            // 获取输入流
            InputStream inputStream = socket.getInputStream();
            System.out.println("read start.............");

            while (true) {
                // 读取的时候,如果没有内容,此处会阻塞掉,直到能够读取到内容为止
                int read = inputStream.read(bytes);
                if (read != -1) {
                    System.out.println(new String(bytes, 0, read));
                } else {
                    break;
                }
            }

            System.out.println("read end.............");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

启动之后,如果没有客户都连接,执行serverSocket.accept()代码就会发生阻塞
控制台打印:

socket server start success
main Thread id:[1]&name:[main]
client connect start.........

我们通过telnet方式进行连接,打开cmd命令行,输入telnet [ip] [port](如果出现不是内部或外部命令,则需要到控制面板 →程序和功能→启用或关闭windows功能→找到 telnet客户端 并勾选上,点击确定即可,注:不同版本操作系统操作方式可能不一致,此处是以Win10为例),然后按’Ctrl+]’:

C:\Users\Administrator>telnet 127.0.0.1 8888

连接成功之后:

欢迎使用 Microsoft Telnet Client

Escape 字符为 'CTRL+]'


Microsoft Telnet>

此时控制台也会打印出连接分配的线程信息,由于客户端没有发送任何信息,所以阻塞到了读取内容的代码上了:

client connect start.........
Handler Thread id:[12]&name:[pool-1-thread-1]
read start.............

在客户端发送消息,服务端也会即时打印:

Microsoft Telnet> send 123
发送字符串 123
Microsoft Telnet> send hello
发送字符串 hello
Microsoft Telnet>
client connect start.........
Handler Thread id:[12]&name:[pool-1-thread-1]
read start.............
123
hello

我们再新打开一个命令行窗口进行连接:

Microsoft Telnet> send client2
发送字符串 client2
Microsoft Telnet>
client connect start.........
Handler Thread id:[13]&name:[pool-1-thread-2]
read start.............
client2

可以看到又开启了一个新的线程来处理此客户端的连接

BIO存在的问题

  1. 每个请求都需要创建独立的线程,与对应的客户端进行数据交互
  2. 当并发较大时,需要创建大量的线程来进行处理,服务器资源消耗较大
  3. 连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞到read操作上了,造成线程资源浪费
发布了107 篇原创文章 · 获赞 19 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/chen_changying/article/details/104116694