学习 Netty(一):Java 网络编程

Java 网络编程是一个结合了网络、IO 和并发的技术

基于 Java IO 的🌰

public class Application {

    private int port;

    public static void main(String[] args) throws Exception {
        new Application(Integer.valueOf(args[0])).start();
    }

    public Application(int port) {
        this.port = port;
    }

    public void start() throws Exception {
        ExecutorService threadPool = Executors.newCachedThreadPool(); // 线程池

        try(ServerSocket serverSocket = new ServerSocket(this.port)) {
            while (true) {
                Socket socket = serverSocket.accept();
                threadPool.execute(() -> {
                    //TODO
                });
            }
        }
    }

}

socket_server

ServerSocket 上的 accept() 方法会一直阻塞到一个连接的建立,当接收到一个新的 Socket 时,从线程池中获取一个线程处理服务端与客户端之间的通信。

该方案的缺点:需要为每一个服务端与客户端的连接创建一个单独的线程,造成了资源的浪费。

基于 Java NIO 的🌰:

public class Application {

    private int port;

    public static void main(String[] args) throws Exception {
        new Application(Integer.valueOf(args[0])).start();
    }

    public Application(int port) {
        this.port = port;
    }

    public void start() throws IOException {
        Selector selector = Selector.open(); // ①
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // ②
        serverSocketChannel.bind(new InetSocketAddress(this.port));
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); // ③

        while (true) {
            selector.select(); // ④
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> selectionKeyIterator = selectionKeys.iterator();

            while (selectionKeyIterator.hasNext()) {
                SelectionKey selectionKey = selectionKeyIterator.next();

                if (selectionKey.isAcceptable()) { // ⑤
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    if (socketChannel != null) {
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
                    }
                }

                if (selectionKey.isReadable()) {
                    SelectableChannel channel = selectionKey.channel();
                    if (channel instanceof SocketChannel) {
                        SocketChannel socketChannel = (SocketChannel) channel;

                        //TODO
                    }
                }

                if (selectionKey.isWritable()) {
                    SelectableChannel channel = selectionKey.channel();
                    if (channel instanceof SocketChannel) {
                        SocketChannel socketChannel = (SocketChannel) channel;

                        //TODO
                    }
                }
            }
        }
    }

}

① 执行 Selector.open() 创建一个 Selector 实例

② 执行 ServerSocketChannel.open() 创建一个 ServerSocketChannel 实例,并绑定到一个网络地址监听连接。调用 configureBlocking(false) 方法,配置为非阻塞

③ 注册 ServerSocketChannel 到指定 selector 上,并配置其感兴趣的操作集:

  • SelectionKey.OP_ACCEPT selector 检测到 ServerSocketChannel 准备好接受一个新的连接
  • SelectionKey.OP_CONNECT selector 检测到 SocketChannel 连接就绪
  • SelectionKey.OP_READ selector 检测到 SocketChannel 读就绪
  • SelectionKey.OP_WRITE 写就绪

④ 调用 select() 方法,执行一个阻塞的选择操作。调用 selectedKeys() 方法,返回选择后的 selectionKey 集

⑤ 判断 ServerSocketChannel 是否准备好接受一个新的连接,接受一个新的 SocketChannel 连接,并注册到 selector 上

总结

Java IO 是面向 Stream 的、单工的、阻塞的

Java NIO 是面向 Buffer 的、双工的、非阻塞的

参考: