目录
使用 write() 方法将数据从 ByteBuffer 写入到 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 的网络通信。