Java NIO 网络编程模型 Acceptor-Connector模式讲解

Java NIO 网络编程模型中的 Acceptor-Connector 模式是一种常用的模式,用于处理客户端连接请求和服务端响应请求。

在 Acceptor-Connector 模式中,服务端的 Acceptor 负责监听客户端的连接请求,并接受连接,同时启动一个新的线程池,把已连接的客户端传递给 Connector,由 Connector 负责处理客户端请求,包括读取数据、处理数据、写入数据等操作。

这种模式的优点在于,可以将 Acceptor 和 Connector 的职责分离,使得服务端的响应能力更强,同时能够处理多个客户端的请求,并发性能更高。

以下是一个基于 Acceptor-Connector 模式的示例代码,其中包含了 Channel、Buffer、Selector、ServerSocketChannel 和 SocketChannel 等的使用。代码实现了一个简单的 TCP 服务器,接收客户端发送的消息并回复相同的消息。

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

public class TcpServer {
    private static final int BUFFER_SIZE = 1024;
    private static final int PORT = 8888;

    private Selector selector;
    private ByteBuffer readBuffer = ByteBuffer.allocate(BUFFER_SIZE);
    private ByteBuffer writeBuffer = ByteBuffer.allocate(BUFFER_SIZE);

    public void start() throws IOException {
        selector = Selector.open();
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(PORT));
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("Server started on port " + PORT);

        while (true) {
            int readyChannels = selector.select();
            if (readyChannels == 0) continue;

            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> keyIterator = selectedKeys.iterator();

            while (keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();
                if (key.isAcceptable()) {
                    accept(key);
                } else if (key.isReadable()) {
                    read(key);
                } else if (key.isWritable()) {
                    write(key);
                }
                keyIterator.remove();
            }
        }
    }

    private void accept(SelectionKey key) throws IOException {
        ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
        SocketChannel socketChannel = serverSocketChannel.accept();
        socketChannel.configureBlocking(false);
        socketChannel.register(selector, SelectionKey.OP_READ);
        System.out.println("Accepted new connection from " + socketChannel.getRemoteAddress());
    }

    private void read(SelectionKey key) throws IOException {
        SocketChannel socketChannel = (SocketChannel) key.channel();
        readBuffer.clear();
        int numBytesRead = socketChannel.read(readBuffer);
        if (numBytesRead == -1) {
            System.out.println("Connection closed by " + socketChannel.getRemoteAddress());
            socketChannel.close();
            key.cancel();
            return;
        }
        String request = new String(readBuffer.array(), 0, numBytesRead).trim();
        System.out.println("Received request from " + socketChannel.getRemoteAddress() + ": " + request);
        key.interestOps(SelectionKey.OP_WRITE);
    }

    private void write(SelectionKey key) throws IOException {
        SocketChannel socketChannel = (SocketChannel) key.channel();
        String response = new String(readBuffer.array(), 0, readBuffer.position());
        writeBuffer.clear();
        writeBuffer.put(response.getBytes());
        writeBuffer.flip();
        socketChannel.write(writeBuffer);
        key.interestOps(SelectionKey.OP_READ);
    }

    public static void main(String[] args) {
        try {
            new TcpServer().start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在该代码中,首先通过 Selector.open() 创建一个 Selector 对象,然后通过 ServerSocketChannel.open() 创建一个 ServerSocketChannel 对象,绑定端口并注册到 Selector 上,以监听客户端连接请求。

在 while 循环中,调用 selector.select() 阻塞等待就绪的事件,一旦有事件就绪,就会从选择器的键集中获取到对应的键,并通过键来获取对应的通道,然后进行后续的操作,例如读取数据或写入数据等。