Netty系列(四)阻塞模式和非阻塞模式

浮生半日闲 发布于 2023-03-12 14 次阅读


1、阻塞模式

我们通过代码来理解阻塞模式。

首先我们先创建服务器,代码如下:

public class Server {

    public static void main(String[] args) throws Exception {
        // 单线程模式
        List<SocketChannel> socketChannelList = new ArrayList<>();
        // 创建服务器
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(8000));
        System.out.println("创建服务器。");

        while (true) {
            // 建立socket连接
            System.out.println("准备和客户端进行连接...");
            SocketChannel socketChannel = serverSocketChannel.accept();
            socketChannelList.add(socketChannel);
            System.out.println("接收到客户端的连接:" + socketChannel);

            // 处理客户端发送的数据
            ByteBuffer buffer = ByteBuffer.allocate(24);
            for (SocketChannel item : socketChannelList) {
                System.out.println("准备处理客户端的数据...");
                item.read(buffer);

                // 读bytebuffer,看下里面的内容
                buffer.flip();
                System.out.println("接受到客户端消息:" + Charset.defaultCharset().decode(buffer));
                buffer.compact();
            }
        }
    }
}

这时,我们将服务器运行后,查看代码输出情况:

通过输出,我们可以看到当代码到达accept()方法时程序无法继续往下执行。此时我们建立一个客户端程序:

public class Client {

    public static void main(String[] args) throws Exception {
        // 连接客户端
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("localhost", 8000));

        System.out.println();
    }
}

运行客户端程序后,我们查看服务端的输出情况:

由此可以看出,accept()方法是一个阻塞方法,服务器端只有收到了客户端的连接请求后,才会继续往下执行

同时,服务器端在到达read()方法时,程序同样无法继续往下执行。此时,通过调试模式,在客户端发送消息给服务器:

此时查看服务器端的输出情况:

由此可以看出,read()方法同样是一个阻塞方法,服务器端只有收到了客户端发送的消息后,才会继续往下执行

并且,由于此时服务端的代码阻塞在客户端连接处,那么即使客户端继续发送消息,服务端也不会进行处理,直到等到下一个新的连接请求出现后,才会统一进行处理。

2、非阻塞模式

想要将上述阻塞模式代码改为非阻塞模式,那么仅需设置configureBlocking为false即可。

public class Server {

    public static void main(String[] args) throws Exception {
        List<SocketChannel> socketChannelList = new ArrayList<>();
        // 创建服务器
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(8000));
        serverSocketChannel.configureBlocking(false);
        System.out.println("创建服务器。");

        while (true) {
            // 建立socket连接
            System.out.println("准备和客户端进行连接...");
            SocketChannel socketChannel = serverSocketChannel.accept();
            if(socketChannel != null) {
                // 设置成非阻塞模式
                socketChannel.configureBlocking(false);
                socketChannelList.add(socketChannel);
            }
            System.out.println("接收到客户端的连接:" + socketChannel);

            // 处理客户端发送的数据
            ByteBuffer buffer = ByteBuffer.allocate(24);
            for (SocketChannel item : socketChannelList) {
                System.out.println("准备处理客户端的数据...");
                int result = item.read(buffer);
                if(result != 0) {
                    // 读bytebuffer,看下里面的内容
                    buffer.flip();
                    System.out.println("接受到客户端消息:" + Charset.defaultCharset().decode(buffer));
                    buffer.compact();
                }
            }
        }
    }
}

在阻塞模式下,accept()方法和read()方法都会进行阻塞,只有等到新的连接或者消息才会继续往下执行。而在非阻塞模式下,线程不会停止,accespt()方法如果有新的连接,那么就位连接channel,否则为null;而读消息时,如果没有新的消息,那么read()方法将返回0。

非阻塞模式,会导致CPU的浪费。