本系列博客转载自:海纳的知乎专栏
https://www.zhihu.com/people/hinus/activities
JAVA NIO系列,本人补充一些“作业”
nio(2):channel
https://zhuanlan.zhihu.com/p/27365009
本节课是小密圈《进击的Java》新人第十六周第二课,这一节课,我们讲一下Java NIO中另外第二个重要的结构,这就是channel。
NIO中通过channel封装了对数据源的操作,通过 channel 我们可以操作数据源,但又不必关心数据源的具体物理结构。
这个数据源可能是多种的。比如,可以是文件,也可以是网络socket。在大多数应用中,channel 与文件描述符或者 socket 是一一对应的。
在Java IO中,基本上可以分为文件类和Stream类两大类。Channel 也相应地分为了FileChannel 和 Socket Channel,其中 socket channel 又分为三大类,一个是用于监听端口的ServerSocketChannel,第二类是用于TCP通信的SocketChannel,第三类是用于UDP通信的DatagramChannel。
Channel的作用
channel 最主要的作用还是用于非阻塞式读写。这一条我们以后会讲,今天,主要看一下如何使用Channel进行编程。
Channel可以使用 ByteBuffer 进行读写,这是它的一个方便之处。我们先看一个例子,这个例子中,创建了一个ServerSocketChannel 并且在本机的8000端口上进行监听。可以看到,bind, listen这些操作与我们之前讲的这一节课:Java网络编程(二):套接字 - 知乎专栏,是完全对应的,可以猜想到SocketChannel的具体实现,本质上也是对socket的一种封装而已。
public class WebServer {
public static void main(String args[]) {
try {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress("127.0.0.1", 8000));
SocketChannel socketChannel = ssc.accept();
ByteBuffer readBuffer = ByteBuffer.allocate(128);
socketChannel.read(readBuffer);
readBuffer.flip();
while (readBuffer.hasRemaining()) {
System.out.println((char)readBuffer.get());
}
socketChannel.close();
ssc.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
用静态的 open( )工厂方法创建一个新的 ServerSocketChannel 对象,将会返回同一个未绑定的 java.net.ServerSocket 关联的通道。这个相关联的 ServerSocket 可以通过在 ServerSocketChannel 上调用 socket( )方法来获取。我们在这个例子中,使用了socket().bind来实现socket的绑定。
客户端代码:
public class WebClient {
public static void main(String[] args) {
SocketChannel socketChannel = null;
try {
socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8000));
ByteBuffer writeBuffer = ByteBuffer.allocate(128);
writeBuffer.put("hello world".getBytes());
writeBuffer.flip();
socketChannel.write(writeBuffer);
socketChannel.close();
} catch (IOException e) {
}
}
}
新创建的 SocketChannel 虽已打开却是未连接的。在一个未连接的 SocketChannel 对象上尝试一 个 I/O 操作会导致 NotYetConnectedException 异常。我们可以通过在通道上直接调用 connect() 方法或在通道关联的 Socket 对象上调用 connect()来实现socket的连接。一旦一个 socket 通道被连接,它将保持连接状态直到被关闭。我们可以通过调用 isConnected()方法来测试某个 SocketChannel 是否已连接。
本节课程所使用的ByteBuffer上一课也有讲解,如果对flip,get, put 等操作还是感到不理解的,可以去查看上一节课程。
先运行服务端,再运行客户端,所得到的结果如下:
就是说,我们把客户端发送过来的字符串逐字符地打印出来了。
其实,熟悉Java IO的读者都知道,我们同样可以使用InputStream和OutputStream进行字节流的读写,而且看起来,ByteBuffer似乎还没有直接使用byte[] 进行读写来得直观。实际上,channel 最大的作用并不仅限于此,它的最大作用是封装了异步操作,后面我会在 selector 的地方详细解释。
Scatter / Gather
Channel 提供了一种被称为 Scatter/Gather 的新功能,也称为本地矢量 I/O。Scatter/Gather 是指在多个缓冲区上实现一个简单的 I/O 操作。对于一个 write 操作而言,数据是从几个缓冲区(通常就是一个缓冲区数组)按顺序抽取(称为 gather)并使用 channel 发送出去。缓冲区本身并不需要具备这种 gather 的能力。gather 过程等效于全部缓冲区的内容被连结起来,并在发送数据前存放到一个大的缓冲区中。对于 read 操作而言,从 通道读取的数据会按顺序被散布(称为 scatter)到多个缓冲区,将每个缓冲区填满直至通道中的数据或者缓冲区的最大空间被消耗完。
大多数现代操作系统都支持本地矢量 I/O(native vectored I/O)。我们在一个通道上发起一个 Scatter/Gather 操作时,该请求会被翻译为适当的本地调用来直接填充或抽取缓冲区。这是一个很大的进步,因为减少或避免了缓冲区拷贝和系统调用。Scatter/Gather 应该使用直接的 ByteBuffers 以从本地 I/O 获取最大性能优势。
例如,我们可以把客户端改写成这个样子:
public class WebClient {
public static void main(String[] args) {
SocketChannel socketChannel = null;
try {
socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8000));
ByteBuffer writeBuffer = ByteBuffer.allocate(128);
ByteBuffer buffer2 = ByteBuffer.allocate(16);
writeBuffer.put("hello ".getBytes());
buffer2.put("world".getBytes());
writeBuffer.flip();
buffer2.flip();
ByteBuffer[] bufferArray = {writeBuffer, buffer2};
socketChannel.write(bufferArray);
socketChannel.close();
} catch (IOException e) {
}
}
}
这样就实现了一个Gather IO。Gather IO 在现在还看不出来有什么作用。我们后面会看到,在并发场景下,这是一个非常好用的特性。
今天就讲解到这里了。作业:
1. 把服务端改造成Scatter IO
2. 想一想聊天室背后的网络设计是怎么样的?
===========================================================================
完成作业现在:
1、
public class WebServer {
public static void main(String args[]) {
try {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress("127.0.0.1", 8000));
SocketChannel socketChannel = ssc.accept();
ByteBuffer readBuffer1 = ByteBuffer.allocate(5);
ByteBuffer readBuffer2 = ByteBuffer.allocate(5);
ByteBuffer[] bufferArray = { readBuffer1, readBuffer2 };
socketChannel.read(bufferArray);
readBuffer1.flip();
readBuffer2.flip();
while (readBuffer1.hasRemaining()) {
System.out.println((char)readBuffer1.get());
}
System.out.print("second buffer");
while (readBuffer2.hasRemaining()) {
System.out.println((char)readBuffer2.get());
}
socketChannel.close();
ssc.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
上面代码主要看分配之快。
转一段 http://ifeve.com/java-nio-scattergather/
注意buffer首先被插入到数组,然后再将数组作为channel.read() 的输入参数。read()方法按照buffer在数组中的顺序将从channel中读取的数据写入到buffer,当一个buffer被写满后,channel紧接着向另一个buffer中写。
Scattering Reads在移动下一个buffer前,必须填满当前的buffer,这也意味着它不适用于动态消息(译者注:消息大小不固定)。换句话说,如果存在消息头和消息体,消息头必须完成填充(例如 128byte),Scattering Reads才能正常工作
如果,我把第一个缓冲区分配到11,那么结果将是
h
e
l
l
o
w
o
r
l
d
second buffer
也就是第二个缓冲区是空的。验证了Scattering Reads先填满一个缓冲区再填满第二个缓冲区的逻辑。
2、聊天室背后的网络设计。
最简单的想法就是多个客户端对应一个服务端。
可以是阻塞IO和非阻塞IO
服务端启动,不断监控端口,等待连接,当有连接就绪之后,开始准备读写数据。
客户端,同样不断监控输入,然后通过socket想服务端发送数据,服务端将数据显示到界面。
阻塞IO可以用多线程或者线程池实现多客户端同时连接。
NIO可以用单线程或多线程实现多客户端同时连接。
上一篇:
http://zxp209.iteye.com/admin/blogs/2396769
分享到:
相关推荐
### Java-NIO2教程知识点详解 #### I/O发展简史 - **JDK1.0-1.3**: 在此期间,Java的I/O模型主要依赖于传统的阻塞I/O方式,这种模式下,应用程序在等待I/O操作完成时无法执行其他任务,导致效率低下。此外,当时的...
赠送jar包:xnio-nio-3.8.0.Final.jar; 赠送原API文档:xnio-nio-3.8.0.Final-javadoc.jar; 赠送源代码:xnio-nio-3.8.0.Final-sources.jar; 赠送Maven依赖信息文件:xnio-nio-3.8.0.Final.pom; 包含翻译后的API...
Java-NIO非阻塞服务器示例 本资源主要讲解了Java-NIO非阻塞服务器的示例,通过使用Java-NIO包来实现非阻塞的服务器端模式。下面是从代码中提取的知识点: 一、Java-NIO包简介 Java-NIO(New I/O)包是Java 1.4...
本示例"JAVA-NIO-DEMO"提供了关于Java NIO的实际应用,通过Anontion(注解)、Applet(小程序)和NIO的Demo,帮助开发者更深入地理解和掌握这些概念。 首先,让我们深入了解Java NIO。NIO的核心组件包括: 1. **...
Java NIO (Non-blocking Input/Output) 是Java平台中用于高效处理I/O操作的一种机制,它与传统的IO模型( Blocking I/O)相比,提供了更高级别的抽象,允许应用程序以非阻塞的方式读写数据,提高了并发性能。...
赠送jar包:httpcore-nio-4.4.10.jar; 赠送原API文档:httpcore-nio-4.4.10-javadoc.jar; 赠送源代码:httpcore-nio-4.4.10-sources.jar; 赠送Maven依赖信息文件:httpcore-nio-4.4.10.pom; 包含翻译后的API文档...
基于java的开发源码-NIO网络框架 xSocket.zip 基于java的开发源码-NIO网络框架 xSocket.zip 基于java的开发源码-NIO网络框架 xSocket.zip 基于java的开发源码-NIO网络框架 xSocket.zip 基于java的开发源码-NIO网络...
赠送jar包:xnio-nio-3.8.4.Final.jar; 赠送原API文档:xnio-nio-3.8.4.Final-javadoc.jar; 赠送源代码:xnio-nio-3.8.4.Final-sources.jar; 赠送Maven依赖信息文件:xnio-nio-3.8.4.Final.pom; 包含翻译后的API...
Java NIO,全称为New Input/Output,是Java在1.4版本引入的一个新特性,是对传统的I/O模型的一种改进。传统的Java I/O基于字节流和字符流,而NIO则提供了通道(Channels)和缓冲区(Buffers)的概念,以及非阻塞I/O...
《深入解析httpcore-nio-4.3.jar:构建高性能的Java非阻塞网络通信》 在Java网络编程中,高效、稳定且可扩展的通信框架至关重要。Apache HttpComponents项目中的HttpCore NIO模块(httpcore-nio)就是这样一个框架...
赠送jar包:httpcore-nio-4.4.10.jar; 赠送原API文档:httpcore-nio-4.4.10-javadoc.jar; 赠送源代码:httpcore-nio-4.4.10-sources.jar; 赠送Maven依赖信息文件:httpcore-nio-4.4.10.pom; 包含翻译后的API文档...
赠送jar包:httpcore-nio-4.4.5.jar; 赠送原API文档:httpcore-nio-4.4.5-javadoc.jar; 赠送源代码:httpcore-nio-4.4.5-sources.jar; 赠送Maven依赖信息文件:httpcore-nio-4.4.5.pom; 包含翻译后的API文档:...
赠送jar包:xnio-nio-3.8.4.Final.jar; 赠送原API文档:xnio-nio-3.8.4.Final-javadoc.jar; 赠送源代码:xnio-nio-3.8.4.Final-sources.jar; 赠送Maven依赖信息文件:xnio-nio-3.8.4.Final.pom; 包含翻译后的API...
Java NIO(Non-blocking Input/Output)是一种在Java中处理I/O操作的新方式,相比于传统的BIO( Blocking I/O),NIO提供...通过理解和实践"java-NIO-demo",你将能够更好地掌握NIO的精髓,并在实际项目中发挥其优势。
赠送jar包:xnio-nio-3.8.0.Final.jar; 赠送原API文档:xnio-nio-3.8.0.Final-javadoc.jar; 赠送源代码:xnio-nio-3.8.0.Final-sources.jar; 赠送Maven依赖信息文件:xnio-nio-3.8.0.Final.pom; 包含翻译后的API...
赠送jar包:httpcore-nio-4.4.6.jar; 赠送原API文档:httpcore-nio-4.4.6-javadoc.jar; 赠送源代码:httpcore-nio-4.4.6-sources.jar; 赠送Maven依赖信息文件:httpcore-nio-4.4.6.pom; 包含翻译后的API文档:...
赠送jar包:httpcore-nio-4.4.14.jar; 赠送原API文档:httpcore-nio-4.4.14-javadoc.jar; 赠送源代码:httpcore-nio-4.4.14-sources.jar; 赠送Maven依赖信息文件:httpcore-nio-4.4.14.pom; 包含翻译后的API文档...
**JAVA-NIO程序设计完整实例** Java NIO(New IO)是Java 1.4引入的一个新特性,它为Java提供了非阻塞I/O操作的能力,使得Java在处理I/O时更加高效。NIO与传统的BIO(Blocking I/O)模型相比,其核心在于它允许程序...