Java NIO 聊天室

简介

Java NIO(New IO)是Java提供的一种非阻塞IO的API。相比传统的Java IO操作,Java NIO提供了更高效的IO处理方式,特别适用于处理大量并发连接的场景。本文将通过一个聊天室的示例,介绍Java NIO的基本概念和使用方法。

什么是聊天室?

聊天室是一个允许多个用户通过网络进行实时交流的应用程序。用户可以发送消息给其他用户,也可以接收其他用户发送的消息。聊天室通常由一个服务器和多个客户端组成,服务器负责接收和分发消息,而客户端则负责发送和接收消息。

Java NIO的基本概念

在介绍聊天室的示例之前,我们先来了解一些Java NIO的基本概念:

  • 通道(Channel):通道是Java NIO中与I/O操作进行交互的对象。通道可以用于读取和写入数据,并且可以异步地进行操作。

  • 缓冲区(Buffer):缓冲区是一个固定大小的内存块,用于临时存储数据。在进行读取和写入操作时,数据将通过缓冲区进行传输。

  • 选择器(Selector):选择器是Java NIO中实现非阻塞IO的关键。可以使用选择器同时监视多个通道上的事件,例如连接就绪、数据可读等。这样,单个线程就可以管理多个通道,提高了系统的响应能力。

聊天室示例

下面我们通过一个简单的聊天室示例来演示Java NIO的使用方法。示例包括一个服务器端和多个客户端,客户端可以通过连接服务器来发送和接收消息。

服务器端代码

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

public class ChatServer {
    private Selector selector;
    private ServerSocketChannel serverSocketChannel;
    private ByteBuffer buffer = ByteBuffer.allocate(1024);

    public void start(int port) throws IOException {
        // 创建选择器
        selector = Selector.open();
        
        // 打开服务器通道
        serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        
        // 绑定端口
        serverSocketChannel.bind(new InetSocketAddress(port));
        
        // 将服务器通道注册到选择器上,监听ACCEPT事件
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        
        System.out.println("Server started on port " + port + "...");
        
        // 循环处理事件
        while (true) {
            selector.select();
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();
                if (key.isAcceptable()) {
                    // 接收客户端连接
                    ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
                    SocketChannel clientChannel = serverChannel.accept();
                    clientChannel.configureBlocking(false);
                    clientChannel.register(selector, SelectionKey.OP_READ);
                    System.out.println("Client connected: " + clientChannel.getRemoteAddress());
                } else if (key.isReadable()) {
                    // 读取客户端消息
                    SocketChannel clientChannel = (SocketChannel) key.channel();
                    buffer.clear();
                    int bytesRead = clientChannel.read(buffer);
                    if (bytesRead == -1) {
                        key.cancel();
                        clientChannel.close();
                        System.out.println("Client disconnected: " + clientChannel.getRemoteAddress());
                        continue;
                    }
                    String message = new String(buffer.array(), 0, bytesRead);
                    System.out.println("Received message: " + message);
                    // 广播消息给其他客户端
                    broadcastMessage(clientChannel, message);
                }
            }
        }
    }

    private void broadcastMessage(SocketChannel senderChannel, String message) throws IOException {
        buffer.clear();
        buffer.put(message.getBytes());
        buffer.flip();
        for (SelectionKey key : selector.keys()) {
            if (key.isValid() && key.channel() instanceof SocketChannel && key.channel() != senderChannel) {
                SocketChannel clientChannel = (SocketChannel) key.channel();
                clientChannel.write(buffer);
            }
        }
    }

    public static void main(String[] args) throws IOException {
        ChatServer server = new ChatServer();
        server.start(8888);
    }
}

客户端代码

import java.io.IOException;
import java.net.In