Java nio包SocketChannel详解

本文详细介绍了JavaNIO中的Selector和SocketChannel,包括Selector的选择操作、SocketChannel的连接、读写数据和非阻塞模式,以及它们在实现高效网络通信中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、Selector详解

1. 打开 Selector

2. 向 Selector 注册通道

3. 选择操作

4. 获取已选择的键集合

5. 处理事件

6. 取消键

7. 关闭 Selector

二、SocketChannel详解

1. 打开 SocketChannel

2. 连接到服务器

3. 读取数据

4. 写入数据

使用 write() 方法将数据从 ByteBuffer 写入到 SocketChannel。

5. 非阻塞模式

6. 关闭 SocketChannel

7. Selector 与 SocketChannel


SocketChannel 是 Java NIO 中用于实现基于 TCP 协议的套接字通信的通道。在介绍SocketChannel之前先讲下Selector

一、Selector详解

Selector 是 Java NIO 中的一个关键组件,用于实现非阻塞 I/O 操作。它允许一个线程管理多个通道,通过选择器监听通道上是否有事件发生,从而实现单线程处理多个通道的 I/O 操作。以下是 Selector 的一些关键操作:

1. 打开 Selector

使用 Selector.open() 静态方法打开一个 Selector

Selector selector = Selector.open();
2. 向 Selector 注册通道

通过 register() 方法向 Selector 注册通道,并指定要监听的事件类型(如读、写等)。

SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
3. 选择操作

调用 select() 方法选择通道上的操作。这个方法是阻塞的,直到至少有一个注册的通道有事件发生。

int selectedChannels = selector.select();
4. 获取已选择的键集合

使用 selectedKeys() 方法获取已选择的键集合,表示那些通道上有事件发生。

Set<SelectionKey> keys = selector.selectedKeys();
5. 处理事件

遍历已选择的键集合,处理每个键对应的通道上发生的事件。

for (SelectionKey key : keys) {
    if (key.isReadable()) {
        // 处理读事件
    } else if (key.isWritable()) {
        // 处理写事件
    }
    // 其他事件类型的处理...
}
6. 取消键

使用 cancel() 方法取消一个键的注册。

key.cancel();
7. 关闭 Selector

使用 close() 方法关闭 Selector

以下是一个简单的示例演示了如何使用 Selector 监听多个通道的读写事件:

import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class SelectorExample {
    public static void main(String[] args) {
        try {
            // 打开 Selector
            Selector selector = Selector.open();

            // 向 Selector 注册通道
            SocketChannel channel1 = SocketChannel.open();
            channel1.configureBlocking(false);
            channel1.register(selector, SelectionKey.OP_READ);

            SocketChannel channel2 = SocketChannel.open();
            channel2.configureBlocking(false);
            channel2.register(selector, SelectionKey.OP_WRITE);

            // 选择操作
            int selectedChannels = selector.select();

            // 获取已选择的键集合
            Set<SelectionKey> keys = selector.selectedKeys();

            // 处理事件
            Iterator<SelectionKey> iterator = keys.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();

                if (key.isReadable()) {
                    // 处理读事件
                } else if (key.isWritable()) {
                    // 处理写事件
                }

                // 其他事件类型的处理...

                // 取消键,防止重复处理
                iterator.remove();
            }

            // 关闭 Selector
            selector.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这个示例创建了两个非阻塞的 SocketChannel,并注册到一个 Selector 上,然后通过 select() 方法选择通道上的操作。

二、SocketChannel详解

SocketChannel 是 Java NIO 中用于实现基于 TCP 协议的套接字通信的通道。它提供了非阻塞的、可选择的、双向的连接,可以用于在客户端和服务器之间进行网络通信。

1. 打开 SocketChannel

可以通过 SocketChannel.open() 静态方法来打开一个 SocketChannel

SocketChannel socketChannel = SocketChannel.open();
2. 连接到服务器

使用 connect() 方法连接到服务器。SocketChannel 是非阻塞的,因此 connect() 方法可能在连接建立之前返回。

socketChannel.connect(new InetSocketAddress("example.com", 80));
3. 读取数据

使用 read() 方法从 SocketChannel 读取数据到 ByteBuffer 中。

ByteBuffer buffer = ByteBuffer.allocate(1024); 
int bytesRead = socketChannel.read(buffer);
4. 写入数据
使用 write() 方法将数据从 ByteBuffer 写入到 SocketChannel
ByteBuffer buffer = ByteBuffer.wrap("Hello, Server!".getBytes());
int bytesWritten = socketChannel.write(buffer);
5. 非阻塞模式

SocketChannel 默认是阻塞的,可以通过调用 configureBlocking(false) 将其设置为非阻塞模式。

socketChannel.configureBlocking(false);
6. 关闭 SocketChannel

使用 close() 方法关闭 SocketChannel

socketChannel.close();
7. Selector 与 SocketChannel

结合 Selector 使用,可以实现多路复用,使得一个线程可以处理多个 SocketChannel

Selector selector = Selector.open();

SocketChannel channel1 = SocketChannel.open();
channel1.configureBlocking(false);
channel1.register(selector, SelectionKey.OP_READ);

SocketChannel channel2 = SocketChannel.open();
channel2.configureBlocking(false);
channel2.register(selector, SelectionKey.OP_WRITE);

以下是一个简单的示例,演示了如何使用 SocketChannel 进行连接和数据传输:

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class SocketChannelExample {
    public static void main(String[] args) {
        try {
            // 打开 SocketChannel
            SocketChannel socketChannel = SocketChannel.open();

            // 设置为非阻塞模式
            socketChannel.configureBlocking(false);

            // 连接到服务器
            socketChannel.connect(new InetSocketAddress("example.com", 80));

            // 等待连接完成
            while (!socketChannel.finishConnect()) {
                // 可以在这里做其他事情,不阻塞
            }

            // 发送数据
            String message = "Hello, Server!";
            ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
            while (buffer.hasRemaining()) {
                socketChannel.write(buffer);
            }

            // 接收数据
            ByteBuffer readBuffer = ByteBuffer.allocate(1024);
            int bytesRead = socketChannel.read(readBuffer);

            while (bytesRead != -1) {
                readBuffer.flip();
                while (readBuffer.hasRemaining()) {
                    System.out.print((char) readBuffer.get());
                }
                readBuffer.clear();
                bytesRead = socketChannel.read(readBuffer);
            }

            // 关闭 SocketChannel
            socketChannel.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

总体而言,SocketChannel 提供了一种灵活、高效的方式来进行基于 TCP 的网络通信。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

境里婆娑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值