Channel (Java NIO)

【正文】netty源码死磕1.3:  html

Java NIO Channel 编程

1. Java NIO Channel

1.1. Java NIO Channel的特色

和老的OIO相比,通道和NIO流(非阻塞IO)主要有如下几点区别:缓存

(1)OIO流通常来讲是单向的(只能读或者写),通道能够读也能够写。服务器

(2)OIO流值读写阻塞的,而通道能够异步读写。网络

(3)通道老是基于缓冲区Buffer来读写。dom

1.2. Channel类型

下面列出Java NIO中最重要的集中Channel的实现:异步

(1)FileChannelsocket

(2)DatagramChannel性能

(3)SocketChannel学习

(4)ServerSocketChannel

四种通道的说明以下:

FileChannel用于文件的数据读写。

DatagramChannel用于UDP的数据读写。

SocketChannel用于TCP的数据读写。

ServerSocketChannel容许咱们监听TCP连接请求,每一个请求会建立会一个SocketChannel。

这个四种通道,涵盖了 UDP 和 TCP网络 IO以及文件 IO的操做。下面从通道的新建、读取、写入、关闭等四个操做,四种通道进行简单的介绍。

1.3. FileChannel

FileChannel 是操做文件的Channel,咱们能够经过 FileChannel 从一个文件中读取数据,也能够将数据写入到文件中。

注意,FileChannel 不能设置为非阻塞模式。

操做一:打开 FileChannel通道

RandomAccessFile aFile     = new RandomAccessFile("test.txt","rw");

FileChannel      inChannel = aFile.getChannel();

操做二:读取数据

ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buf);

操做三:写入数据

String newData = "New String to write to file..." + System.currentTimeMillis();

ByteBuffer buf = ByteBuffer.allocate(48);

buf.clear();

buf.put(newData.getBytes());

buf.flip();

while(buf.hasRemaining())

{

    channel.write(buf);

}

操做四:关闭

channel.close();

当咱们对 FileChannel 的操做完成后,必须将其关闭。

操做五:强制刷新磁盘

channel.force(true);

FileChannel的force()方法将全部未写入的数据从通道刷新到磁盘中。在你调用该force()方法以前,出于性能缘由,操做系统可能会将数据缓存在内存中,所以您不能保证写入通道的数据实际上写入磁盘。

1.4. SocketChannel

有两种Socket通道,一个是客户端的SocketChannel,一个是负责服务器端的Socket通道ServerSocketChannel。SocketChannel与OIO中的Socket类对应,ServerSocketChannel对应于OIO中的ServerSocket类相NIO。

两种Socket通道新增的通道都支持阻塞和非阻塞两种模式。在阻塞模式下的通道的建立、关闭、读写操做以下:

操做一:建立

SocketChannel socketChannel = SocketChannel.open();

socketChannel.connect(new InetSocketAddress("127.0.0.1",80));

这个是客户端的建立。当一个服务器端的ServerSocketChannel 接受到链接请求时,也会返回一个 SocketChannel 对象。

操做二:读取

ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = socketChannel.read(buf);

若是 read()返回 -1,那么表示链接中断了.

操做三:写入数据

String newData = "New String to write to file..." + System.currentTimeMillis();

ByteBuffer buf = ByteBuffer.allocate(48);

buf.clear();

buf.put(newData.getBytes());

buf.flip();

while(buf.hasRemaining()) {

    channel.write(buf);

}

操做四:关闭

socketChannel.close();

在非阻塞模式,咱们能够设置 SocketChannel 为异步模式,这样咱们的 connect,read,write 都是异步的了.

操做一:链接

socketChannel.configureBlocking(false);

socketChannel.connect(new InetSocketAddress("127.0.0.1",80));

while(! socketChannel.finishConnect() ){

    //wait,or do something else...

}

在异步模式中,或许链接尚未创建,socketChannel.connect 方法就返回了,所以咱们不断的自旋,检查当前是不是链接到了主机。

操做二:非阻塞读写

在异步模式下,读写的方式是同样的.

在读取时,由于是异步的,所以咱们必须检查 read 的返回值,来判断当前是否读取到了数据.

ServerSocketChannel

ServerSocketChannel 顾名思义,是用在服务器为端的,能够监听客户端的 TCP 链接,例如:

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

serverSocketChannel.socket().bind(new InetSocketAddress(9999));

while(true){

    SocketChannel socketChannel =

            serverSocketChannel.accept();

    //do something with socketChannel...

}

操做四:关闭

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

serverSocketChannel.close();

1.4.1. 监听链接

咱们可使用ServerSocketChannel.accept()方法来监听客户端的 TCP 链接请求,accept()方法会阻塞,直到有链接到来,当有链接时,这个方法会返回一个 SocketChannel 对象:

while(true){

    SocketChannel socketChannel =

            serverSocketChannel.accept();

    //do something with socketChannel...

}
1.4.2. 非阻塞模式

在非阻塞模式下,accept()是非阻塞的,所以若是此时没有链接到来,那么 accept()方法会返回null:

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

serverSocketChannel.socket().bind(new InetSocketAddress(9999));

serverSocketChannel.configureBlocking(false);

while(true){

    SocketChannel socketChannel =

            serverSocketChannel.accept();

    if(socketChannel != null){

        //do something with socketChannel...

        }

}

1.5. DatagramChannel

DatagramChannel 是用来处理 UDP 链接的.

操做一:打开

DatagramChannel channel = DatagramChannel.open();

channel.socket().bind(new InetSocketAddress(9999));

操做二:读取数据

ByteBuffer buf = ByteBuffer.allocate(48);

buf.clear();

channel.receive(buf);

操做三:发送数据

String newData = "New String to write to file..."

                    + System.currentTimeMillis();

ByteBuffer buf = ByteBuffer.allocate(48);

buf.clear();

buf.put(newData.getBytes());

buf.flip();

int bytesSent = channel.send(buf,new InetSocketAddress("example.com",80));

链接到指定地址

由于 UDP 是非链接的,所以这个的 connect 并非向 TCP 同样真正意义上的链接,所以咱们仅仅能够从指定的地址中读取或写入数据.

channel.connect(new InetSocketAddress("example.com",80));


源码:


代码工程:  JavaNioDemo.zip

下载地址:在疯狂创客圈QQ群文件共享。




无编程不创客,无案例不学习。疯狂创客圈,一大波高手正在交流、学习中!

疯狂创客圈 Netty 死磕系列 10多篇深度文章博客园 总入口】  QQ群:104131248