【JAVA】NIO总结

Socket/ServerSocket

  • 一次性连接
    • socket 用完在两端close 即可
  • 长连接
    • socket端
      • 发送:在每次write到OutputStream时,使用flush();
      • 接收:用一个线程专门监听socket对象的inputStream,如果available>0,则读取里面的数据
    • ServerSocket端
      • 接收: 用一个(或多个)线程监听已连接的socket,如果inputStrea的available>0,说明有数据
      • 发送:write完flush

SocketChannel/ServerSocketChannel

  • 阻塞模式 (blocking)
    • 一次性连接
      • socketChannel 用完在两端close() 即可
    • 长连接
      • socketChannel端
        • 发送:write即可;
        • 接收:用一个线程专门监听socketChannel.read()>0,则读取里面的数据
      • ServerSocketChannel 端
        • 接收:用一个线程专门监听socketChannel.read()>0,则读取里面的数据
        • 发送:write即可;
  • 非阻塞模式 (non-blocking)
    • 见下文
  • 若是使用 阻塞模式的SocketChannel,与传统Socket相比,两者都是阻塞的;在使用长连接的方式时,Socket可以通过InputStream的avaliable()得到未读字节从而确定一次发送是否结束, 而SocketChannel 由于无法获得剩余未读字节,不能判断是都结束一次发送。

    解决办法

    • 发送端write()时,在ByetBuffer 里的约定位置写入描述(可以是一个json的字节数组),储存本次发送byte的lenth、以及其他必要信息。在接收端,按照约定先把描述json的字节从ByteBuffer取出即可。
  • 如果通过Socket向ServerSocketChannel 发送数据,也是按照SocketChannel一样处理即可。

  • 网络Channel不支持直接缓冲区

DatagramChannel

~~
write\read读写数据,必须在connected状态下使用
send\receive读写数据,可以在不connected状态下使用,
但是这样每次都会做安全检查,
避免此项安全检查开销的方法是首先通过 connect 方法连接该套接字

  • 使用非阻塞模式的DatagramChannel时,需注意在注册selector时,只有 OP_WRITE\OP_READ 是允许的
  • 调用connect方法,并不会真实的连接目标地址,但是DatagramChannel对象的isConnected会改变
  • read、write 只有在connected状态下才可以使用,receive、send 不论是否connected状态都可以使用,但是未连接状态会多一步安全检查。

Selector

selector 是实现非阻塞式Socket的核心。
~~~
open
register
select
wakeUp如果有一个线程调用select()而阻塞,另一个线程调用该对象的wakeUp,
可使select()立即返回,即使没有已就绪的selectedKey。
close关闭selector,将会使注册在该对象上的selectedKey失效,与之绑定的通道不会受影响
创建selector
Selector selector = Selector.open();
注册channeld到selector
SelectionKey key = selector.register(channelObj,SelectionKey.OP_ACCEPT);
//register 返回监听channelObj的SelectionKey对象
//SelectionKey对象将监听channelObj的accept
获取selector中已经就绪的SelectionKey数量
int num = selector.select();
/*
    //select方法返回已经就绪的channel数量
    int select()    阻塞到至少有一个已经就绪的channel
    int select(long timeout)    timeout表示阻塞时限
    int selectNow()     非阻塞,如果没有就绪的cahnnel,返回0
*/
获取selector中已经就绪的SelectionKey集合
Set set =selector.selectedKeys();

完整demo

//创建channel
ServerSocketChannel channelObj = ServerSocketChannel.open(7777);
//创建selector
Selector selector = Selector.open();
//注册通道
SelectionKey key = selector.register(channelObj,SelectionKey.OP_ACCEPT);
while(selector.select()>0){
    Set set =selector.selectedKeys();
    //迭代已就绪的selectedKey
    Iterator<SelectionKey> iterator = set.iterator();
    while(iterator.hasNext()){
        SelectionKey key = set.next();
         //获得与selectedKey绑定的通道
        Channel target = key.channel();
        //判断就绪状态
        if(key.isAcceptable()){

        }else if(key.isConnected()){

        }else if(key.isReadable()){

        }else if(key.isWritable()){

        }
        iterator.remove(); 
    }
     //已经处理过的就绪selectedKey需要移除,否则下次获取selectedKeys集合时还会存在之前处理过的selectedKey
}

SelectionKey

静态常量~
OP_CONNECT连接就绪,channel成功连接
OP_ACCEPT接收就绪,ServerSocketChannel 准备好接收
OP_READ读取就绪,通道有数据可读
OP_WRITE写就绪
方法~
isAcceptable
isConnected
isReadable
isWritable
isVaild返回 选择键是否cancel或其选择器是否close或注册的通道是否关闭

cancel|请求取消此键的通道到其选择器的注册

  • SelectionKey用来与channel绑定,一个channel注册到Slector对象中时,便会产生一个SelectionKey对象,通过它可以获得channel处于何种就绪状态

Pipe

Unix系统中,管道被用来连接一个进程的输出和另一个进程的输入。java中的Pipe是进程内(在Java虚拟机进程内部)而非进程间使用的。

graph LR
A[A线程]-->|write|B[Pipe.Sink<br>Pipe.Source]
C[B线程]-->|read|B
    Pipe p = Pipe.open();
    //写入
    p.sink().write(ByteBuffer.wrap("管道测试".getBytes()));
    //读取
    ByteBuffer buff = ByteBuffer.allocate(1024);
    while(p.source().read(buff)>0) {
        System.out.println(new String(buff.array(),0,buff.limit()));
        buff.clear();
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值