1.4.3、写入内容过多
服务端
public class WriteServer {
public static void main(String[] args) throws IOException {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
Selector selector = Selector.open();
ssc.register(selector, SelectionKey.OP_ACCEPT);
ssc.bind(new InetSocketAddress(50001));
while (true) {
selector.select();
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()) {
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 30000000; i++) {
builder.append("a");
}
ByteBuffer buffer = Charset.defaultCharset().encode(builder.toString());
while (buffer.hasRemaining()) {
int write = sc.write(buffer);//实际写入的字节数
System.out.println(write);
}
}
}
}
}
}
客户端
public class WriteClient {
public static void main(String[] args) throws IOException {
SocketChannel sc = SocketChannel.open();
sc.connect(new InetSocketAddress(50001));
int count = 0;
while (true) {
ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024);
int read = sc.read(buffer);
count += read;
System.out.println(count);
buffer.clear();
}
}
}
结果
# 服务端 中间好多0,就是buffer满了
.....
0
0
0
2621420
1703923
4310084
# 客户端
....
30000000
这样显然不合适,解决
- 非阻塞模式下,无法保证把 buffer 中所有数据都写入 channel,因此需要追踪 write 方法的返回值(代表实际写入字节数)
- 用 selector 监听所有 channel 的可写事件,每个 channel 都需要一个 key 来跟踪 buffer,但这样又会导致占用内存过多,就有两阶段策略
- 当消息处理器第一次写入消息时,才将 channel 注册到 selector 上
- selector 检查 channel 上的可写事件,如果所有的数据写完了,就取消 channel 的注册
- 如果不取消,会每次可写均会触发 write 事件
private static void write2() throws IOException {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
Selector selector = Selector.open();
ssc.register(selector, SelectionKey.OP_ACCEPT);
ssc.bind(new InetSocketAddress(50001));
while (true) {
selector.select();
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()) {
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
SelectionKey scKey = sc.register(selector, SelectionKey.OP_READ);
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 30000000; i++) {
builder.append("a");
}
ByteBuffer buffer = Charset.defaultCharset().encode(builder.toString());
int write = sc.write(buffer);//实际写入的字节数
System.out.println(write);
if (buffer.hasRemaining()) {
//关注可写事件。把之前已有事件加上,防止覆盖
scKey.interestOps(scKey.interestOps() + SelectionKey.OP_WRITE);
//把未写完数据挂到sckey上
scKey.attach(buffer);
}
} else if (key.isWritable()) {
ByteBuffer buffer = (ByteBuffer) key.attachment();
SocketChannel sc = (SocketChannel) key.channel();
int write = sc.write(buffer);
System.out.println(write);
if (!buffer.hasRemaining()) {
key.attach(null);//清除附件
key.interestOps(key.interestOps() - SelectionKey.OP_WRITE);//去掉事件
}
}
}
}
}
标签:NIO,iterator,buffer,写入,selector,write,key,sc
From: https://www.cnblogs.com/jpymll/p/16784224.html