纯净、安全、绿色的下载网站

首页|软件分类|下载排行|最新软件|IT学院

当前位置:首页IT学院IT技术

Java NIO的Write事件 小白也可以学会的Java NIO的Write事件

JavaEdge.   2021-06-01 我要评论
想了解小白也可以学会的Java NIO的Write事件的相关内容吗JavaEdge.在本文为您仔细讲解Java NIO的Write事件的相关知识和一些Code实例欢迎阅读和指正我们先划重点:Java,NIO的Write事件,java事件下面大家一起来学习吧。

一、NIO Server端

1.1 多路复用开发一般步骤

//打开选择器
Selector selector = Selector.open();
//打开通到
ServerSocketChannel socketChannel = ServerSocketChannel.open();
//配置非阻塞模型
socketChannel.configureBlocking(false);
//绑定端口
socketChannel.bind(new InetSocketAddress(8080));
//注册事件OP_ACCEPT只适用于ServerSocketChannel 
socketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
    selector.select();
    Set<SelectionKey> selectionKeys = selector.selectedKeys();
    Iterator<SelectionKey> iter = selectionKeys.iterator();
    while(iter.hasNext()) {
        SelectionKey key = iter.next();
        if(key.isAcceptable()) {
            SocketChannel channel = ((ServerSocketChannel)key.channel()).accept();
            channel.configureBlocking(false);
            channel.register(selector,SelectionKey.OP_READ);
        }
        
        if(key.isWritable()) {
        }
        
        if(key.isReadable()) {
            SocketChannel channel = (SocketChannel) key.channel();
            ByteBuffer readBuffer = ByteBuffer.allocate(1024);
            channel.read(readBuffer);
            readBuffer.flip();
            // handler Buffer
            // 一般是响应客户端的数据
            // 直接是write写不就完事了嘛为啥需要write事件?
            // channel.write(...)
        }
        iter.remove();
    }
}

1.2 解惑写事件

对NIO的写操作:

  • 为什么要注册写事件
  • 何时注册写事件
  • 为什么写完之后要取消注册写事件

如果有channel在Selector上注册了SelectionKey.OP_WRITE在调用selector.select();时系统会检查内核写缓冲区是否可写:

  • 如果可写selector.select();立即返回进入key.isWritable()
  • 何时不可写?比如缓冲区已满channel调用了shutdownOutPut等

当然除了注册写事件你也可以在channel直接调用write(…)也可以将数据发出去但这样不够灵活而且可能浪费CPU。

比如服务端需要发送一个200M的Buffer看看是否使用OP_WRITE事件的区别。

二、不使用事件

程序运行到这会等到200M文件发送完成后才继续往下执行不符合异步事件模型的思想。若缓冲区一直处不可写状态则该过程一直在这里死循环浪费CPU。

// 200M的Buffer
ByteBuffer buffer = .... 

while(buffer.hasRemaining()) {
    // 该方法只会写入小于socket's output buffer空闲区域的任何字节数
    // 并返回写入的字节数可能是0字节
    channel.write(buffer);
}

三、使用事件

if(key.isReadable()) {
	// 200M Buffer
    ByteBuffer buffer = .... 
    // 注册写事件
    key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
    // 绑定Buffer
    key.attach(buffer);
}
// 可写分支
if(key.isWritable()) {
    ByteBuffer buffer = (ByteBuffer) key.attachment();
    SocketChannel channel = (SocketChannel) key.channel();
    if (buffer.hasRemaining()) {
        channel.write(buffer)
    } else {
        // 发送完了就取消写事件否则下次还会进入写事件分支(因为只要还可写就会进入)
        key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
    }
}

要触发写事件需要先向 selector 注册该通道的写事件跟注册读事件一样当底层写缓冲区有空闲就会触发写事件了而一般来说底层的写缓冲区大部分都是空闲的。所以一般只要注册了写事件就会立马触发了为了避免 cpu 空转在写操作完成后需要把写事件取消掉然后下次再有写操作时重新注册写事件。

四、NIO Client端

开发的一般步骤

// 打开选择器
Selector selector = Selector.open();
// 打开通道
SocketChannel socketChannel = SocketChannel.open();
// 配置非阻塞模型
socketChannel.configureBlocking(false);
// 连接Server
socketChannel.connect(new InetSocketAddress("127.0.0.1",8080));
// 注册事件
socketChannel.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ);
// 循环处理
while (true) {
    selector.select();
    Set<SelectionKey> keys = selector.selectedKeys();
    Iterator<SelectionKey> iter = keys.iterator();
    while(iter.hasNext()) {
        SelectionKey key = iter.next();
        if(key.isConnectable()) {
            // 连接建立或者连接建立不成功
            SocketChannel channel = (SocketChannel) key.channel();
            // 完成连接建立
            if(channel.finishConnect()) {
                
            }
        }
        
        if(key.isReadable()) {
            SocketChannel channel = (SocketChannel) key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(500 * 1024 * 1024);
            buffer.clear();
            channel.read(buffer);
            // buffer Handler
        }
        iter.remove();
    }
}

起初对OP_CONNECT事件还有finishConnect不理解OP_CONNECT事件何时触发特别是为什么要在key.isConnectable()分支里调用finishConnect方法后才能进行读写操作。

首先在non-blocking模式下调用socketChannel.connect(new InetSocketAddress(“127.0.0.1”,8080));连接远程主机如果连接能立即建立就像本地连接一样该方法会立即返回true否则该方法会立即返回false,然后系统底层进行三次握手建立连接。连接有两种结果一种是成功连接第二种是异常但是connect方法已经返回无法通过该方法的返回值或者是异常来通知用户程序建立连接的情况所以由OP_CONNECT事件和finishConnect方法来通知用户程序。不管系统底层三次连接是否成功selector都会被唤醒继而触发OP_CONNECT事件如果握手成功并且该连接未被其他线程关闭finishConnect会返回true然后就可以顺利的进行channle读写。如果网络故障或者远程主机故障握手不成功用户程序可以通过finishConnect方法获得底层的异常通知进而处理异常。


相关文章

猜您喜欢

  • C++推箱子小游戏 C/C++实现推箱子小游戏

    想了解C/C++实现推箱子小游戏的相关内容吗两片空白在本文为您仔细讲解C++推箱子小游戏的相关知识和一些Code实例欢迎阅读和指正我们先划重点:C++,推箱子下面大家一起来学习吧。..
  • Python转换音频格式 Python使用pydub模块转换音频格式以及对音频进行剪辑

    想了解Python使用pydub模块转换音频格式以及对音频进行剪辑的相关内容吗il_持之以恒_li在本文为您仔细讲解Python转换音频格式的相关知识和一些Code实例欢迎阅读和指正我们先划重点:python转换音频格式,python,pydub,混音,python音频处理下面大家一起来学习吧。..

网友评论

Copyright 2020 www.Videogametimes.com 【视游时光】 版权所有 软件发布

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 点此查看联系方式