Channel是个啥?

Channel,顾名思义,它就是一个通道。NIO中的所有IO都是从 Channel 开始的。
Channel通道和流非常类似,主要有以下几点区别:
1、流是单向的,通道是双向的,可读可写。
2、流读写是阻塞的,通道可以异步读写。
3、流中的数据可以选择性的先读到缓存中,通道的数据总是要先读到一个缓存Buffer中,或从缓存Buffer中写入。
 

继承关系图

Channel有两种分类方式。一种是按同步Channel和异步Channel划分,还有一种是按功能划分。
后面我们会主要讲解Channel的几个重要实现,如下所示:
FileChannel: 从文件中读写数据
DatagramChannel: 通过UDP读写网络中的
SocketChannel: 通过TCP读写网络中的,一般是客户端实现
ServerSocketChannel: 允许我们监听TCP链接请求,每个请求会创建会一个SocketChannel,一般是服务器实现
 
 

接口方法

public interface Channel extends Closeable {

    // 判断Channel的开关状态
public boolean isOpen(); // 关闭此Channel
public void close() throws IOException; }

FileChanel

FileChannel是一个连接到文件的通道,可以通过文件通道读写文件。众所周知文件通道总是阻塞式的,因此FileChannel无法设置为非阻塞模式。
FileChannel中重要方法,read、write通过其实现类FileChannelImpl实现
    // 从这个通道读入一个字节序列到给定的缓冲区
public abstract int read(ByteBuffer dst) throws IOException; // 从这个通道读入指定开始位置和长度的字节序列到给定的缓冲区
public abstract long read(ByteBuffer[] dsts, int offset, int length)
throws IOException; // 从这个通道读入一个字节序列到给定的缓冲区
public final long read(ByteBuffer[] dsts) throws IOException {
return read(dsts, 0, dsts.length);
} // 从给定的缓冲区写入字节序列到这个通道
public abstract int write(ByteBuffer src) throws IOException; // 从给定缓冲区的子序列向该信道写入字节序列
public abstract long write(ByteBuffer[] srcs, int offset, int length)
throws IOException; // 从给定的缓冲区写入字节序列到这个通道
public final long write(ByteBuffer[] srcs) throws IOException {
return write(srcs, 0, srcs.length);
}

FileChannelImpl中read、write方法实现

// read 方法实现
public int read(ByteBuffer var1) throws IOException {
this.ensureOpen();
if (!this.readable) {
throw new NonReadableChannelException();
} else {
synchronized(this.positionLock) {
int var3 = 0;
int var4 = -1; try {
this.begin();
var4 = this.threads.add();
if (!this.isOpen()) {
byte var12 = 0;
return var12;
} else {
do {
var3 = IOUtil.read(this.fd, var1, -1L, this.nd);
} while(var3 == -3 && this.isOpen()); int var5 = IOStatus.normalize(var3);
return var5;
}
} finally {
this.threads.remove(var4);
this.end(var3 > 0); assert IOStatus.check(var3);
}
}
}
} // write方法实现
public int write(ByteBuffer var1) throws IOException {
this.ensureOpen();
if (!this.writable) {
throw new NonWritableChannelException();
} else {
synchronized(this.positionLock) {
int var3 = 0;
int var4 = -1; byte var5;
try {
this.begin();
var4 = this.threads.add();
if (this.isOpen()) {
do {
var3 = IOUtil.write(this.fd, var1, -1L, this.nd);
} while(var3 == -3 && this.isOpen()); int var12 = IOStatus.normalize(var3);
return var12;
}
var5 = 0;
} finally {
this.threads.remove(var4);
this.end(var3 > 0); assert IOStatus.check(var3);
}
return var5;
}
}
}

IOUtil中read、write实现:

//  read方法实现
static int read(FileDescriptor var0, ByteBuffer var1, long var2, NativeDispatcher var4) throws IOException {
if (var1.isReadOnly()) {
throw new IllegalArgumentException("Read-only buffer");
} else if (var1 instanceof DirectBuffer) {
return readIntoNativeBuffer(var0, var1, var2, var4);
} else {
// 申请一块和缓存同大小的ByteBuffer var5
ByteBuffer var5 = Util.getTemporaryDirectBuffer(var1.remaining()); int var7;
try {
// 读取数据到缓存,底层由NativeDispatcher的read实现。
int var6 = readIntoNativeBuffer(var0, var5, var2, var4);
var5.flip();
if (var6 > 0) {
// 把数据读取到var1(用户定义的缓存,在jvm中分配内存)
var1.put(var5);
} var7 = var6;
} finally {
Util.offerFirstTemporaryDirectBuffer(var5);
} return var7;
}
} // write 方法实现
static int write(FileDescriptor var0, ByteBuffer var1, long var2, NativeDispatcher var4) throws IOException {
if (var1 instanceof DirectBuffer) {
return writeFromNativeBuffer(var0, var1, var2, var4);
} else {
int var5 = var1.position();
int var6 = var1.limit();
assert var5 <= var6; int var7 = var5 <= var6 ? var6 - var5 : 0;
// 申请一块ByteBuffer,大小为byteBuffer中的limit - position
ByteBuffer var8 = Util.getTemporaryDirectBuffer(var7); int var10;
try {
// 复制byteBuffer中的数据
var8.put(var1);
var8.flip();
var1.position(var5); // 把数据写入到文件,底层由NativeDispatcher的write实现
int var9 = writeFromNativeBuffer(var0, var8, var2, var4);
if (var9 > 0) {
var1.position(var5 + var9);
}
var10 = var9;
} finally {
Util.offerFirstTemporaryDirectBuffer(var8);
}
return var10;
}
}
 

小结

1、文件通道不能直接创建,只能通过InputStream、OutputStream或RandomAccessFile等创建对应的文件通道
2、文件通道FileChannel从缓冲区中读取数据,使用read方法
3、文件通道FileChannel的read方法只能读ByteBuffer缓冲区
 

DatagramChannel

DatagramChannel,使用UDP协议来进行传输。由于不需要建立连接,其实没有客户端服务端的概念,为了便于理解,我们定义其中一端为客户端,一端为服务端

客户端

public static void main(String[] args) throws Exception {
// 打开DatagramChannel
DatagramChannel datagramChannel = DatagramChannel.open();
// 绑定一个端口发送数据
ByteBuffer byteBuffer = ByteBuffer.wrap("A".getBytes());
int byteSent = datagramChannel.send(byteBuffer, new InetSocketAddress("127.0.0.1", 8000));
System.out.println("Byte sent is: " + byteSent);
}

服务端

public static void main(String[] args) throws Exception {
// 打开DatagramChannel,绑定一个端口
DatagramChannel datagramChannel = DatagramChannel.open();
datagramChannel.socket().bind(new InetSocketAddress(8000)); while (true) {
// 接收数据并输出
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
datagramChannel.receive(byteBuffer);
byteBuffer.flip();
if(byteBuffer.hasRemaining()) {
System.out.print((char) byteBuffer.get());
}
}
}

ServerSocketChannel和SocketChannel

ServerSocketChannel是一个可以监听新进来的TCP连接的通道。ServerSocketChannel本身不具备传输数据的能力,而只是负责监听传入的连接和创建新的SocketChannel。
SocketChannel是一个连接到TCP网络套接字的通道。通常SocketChannel在客户端向服务器发起连接请求,每个SocketChannel对象创建时都关联一个对等的Socket对象。同样SocketChannel也可以运行在非阻塞模式下。
可以通过以下2种方式创建SocketChannel:
1、打开一个SocketChannel并连接到互联网上的某台服务器
2、一个新连接到达ServerSocketChannel时,会创建一个SocketChannel
 

服务端

public static void main(String[] args) throws Exception {
// 服务端首先打开ServerSocketChannel,然后绑定一个端口
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(8000)); // 服务端ServerSocketChannel收到连接请求时,返回一个SocketChannel对象
SocketChannel socketChannel = serverSocketChannel.accept();
while(true) {
// 把数据从channel中读出来,然后写入到buffer中然后打印
ByteBuffer buffer = ByteBuffer.allocate(128);
socketChannel.read(buffer);
buffer.flip();
if(buffer.hasRemaining()) {
System.out.println((char) buffer.get());
}
}
}

客户端

public static void main(String[] args) throws Exception {
// 客户端建立连接的过程,首先打开SocketChannel,然后连接到服务端
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8000)); //连接是否建立成功
boolean isConnect = socketChannel.isConnected(); while (true) {
// 通过buffer,向channel中写入数据
ByteBuffer buffer = ByteBuffer.allocate(128);
buffer.clear();
buffer.put(("A").getBytes());
buffer.flip();
socketChannel.write(buffer);
Thread.sleep(1000);
}
}

盘一盘 NIO (二)—— Channel解析的更多相关文章

  1. NIO(二):Channel通道

    一.Channel概述 channel(通道):进行IO的连接通道,为NIO的几个核心(Buffer,selector,channel)之一,相比于IO的stream具有较高的性能. IO 单向传输 ...

  2. U盘装系统系列二—-如何设置U盘启动

    老毛桃U盘启动制作好之后,如何设置U盘启动呢?我的是华硕的电脑,开机后按F2进入BIOS设置(不同主板可能不一样,比如有的是按向下键或者Del键,可以在网上查下看看),按Tab键选中Boot:按向下键 ...

  3. 二、Centos7—U盘启动盘制作

      1,准备一个8gU盘. 2.iso系统镜像文件. 3.下载UltraISO软件制作启动盘. 4.运行UltraISO软件 5.在UltraISO软件中打开刚下载的Centos系统安装文件 6.开始 ...

  4. Linux U盘 启动盘

    /****************************************************************************** * Linux U盘 启动盘 * 说明: ...

  5. windows下制作linux U盘启动盘或者安装优盘(转)

    windows下制作linux U盘启动盘或者安装优盘(转) Linux发行版排行榜:http://iso.linuxquestions.org/ [方案一]:UltraISO(不推荐,在Window ...

  6. 怎样用通用pe工具箱制作U盘启动盘

    U盘启动盘制作过程,随着网络的普及,电脑已经成为我们日常生活中的重要一环,最近自己重装了下电脑系统,无意中发现一个傻瓜式的U盘装系统方法,就把怎么制作通用pe工具箱u盘启动盘的经验拿出来跟大家分享下. ...

  7. 老毛桃U盘启动盘制作工具V20140501完美贡献版

    老毛桃U盘启动盘制作工具V20140501完美贡献版 下载地址:http://down.laomaotao.net:90/LaoMaoTao_V2014zhuangji.exe 老毛桃U盘装系统综合教 ...

  8. 重装系统之制作U盘启动盘

    准备: 1.需要一个大于4G的U盘. 2.一个原版系统. 3.制作U盘启动盘的工具—ultraliso. 一.一个大于4G的U盘 制作启动盘将会格式化U盘,记得做好备份. 二.一个原版系统 至于你要装 ...

  9. win10与centos7的双系统U盘安装(一:制作u盘启动盘)

    博主近来在学习linux系统,当然学习第一步自然是安装系统了,博主选择的是centos7,博主自己的电脑是联想的,系统是win10专业版,在历经数次失败后,博主成功使用u盘安装了win10和cento ...

  10. 重装window 7系统,从做一个u盘启动盘,到装系统,很不错

    老毛桃U盘启动盘制作工具是现在最流行的U盘装系统和维护电脑的专用工具,一是制作简单,几乎100%支持所有U盘一键制作为启动盘,不必顾虑以前量产U盘考虑专用工具的问题.二是制作后工具功能强大,支持GHO ...

随机推荐

  1. webService 的使用

    webService Web service是一个平台独立的,低耦合的,自包含的.基于可编程的web的应用程序,可使用开放的XML(标准通用标记语言下的一个子集)标准来描述.发布.发现.协调和配置这些 ...

  2. 使用RabbitMQ做数据接收和处理时,自动关闭

    场景:N个客户端向MQ里发送数据:服务器上有另一个控制台程序(假设叫ServerClient)来处理这里数据(往数据库保存).方向为Client * n→MQSERVER→ServerClient 问 ...

  3. codemirror使用

    JS使用 使用bower下载 bower i codemirror 引入样式文件 <link rel="stylesheet" type="text/css&quo ...

  4. 程序员的长安十二时辰:Java实现从Google oauth2.0认证调用谷歌内部api

    最近公司在做一个app购买的功能,主要思路就是客户在app上购买套餐以后,Google自动推送消息到Java后端,然后Java后端通过订单的token获取订单信息,保存到数据库. Java后端要获取订 ...

  5. 快速清理maven仓库中下载错误的文件

    有时候使用pom文件下载依赖文件的时候突然网络异常,可能会出现依赖文件出现破损,导致怎么都不能使用,也没有重新下载. 之前解决办法是找到出现破损的文件并删除,让其重新下载,但是这样效率很低,也很难找到 ...

  6. 初始Spring 文档 完整版

    初始Spring 1.Spring官网 spring.io 01. IOC(Inverse of Control)控制反转 02. AOP(Aspect Oritend Programming)面向切 ...

  7. element el-table resetfields() 不生效

    表单中的重置按钮不生效的问题,结合文档对照后,发现是没有为el-form-item设置prop字段 所以,想让resetfields()生效有2个前提: form要设置ref,且ref值要与 this ...

  8. TCP加速机制是如何加速的?

    一.什么是TCP加速?   TCP加速就是在高时延链路提高吞吐量的一系列解决方案.   二.为什么需要对TCP进行加速?   1.传统的TCP拥塞控制算法并不适用于高时延.高误码的链路. 2.随着we ...

  9. 模块购物商城和ATM机代码:

    http://outofmemory.cn/python/video/let-us-python/ python为程序员服务  快来加入群[python爬虫交流群](群号570070796),发现精彩 ...

  10. [系列] Go - chan 通道

    目录 概述 声明 chan 写入 chan 读取 chan 关闭 chan 示例 推荐阅读 概述 原来分享基础语法的时候,还未分享过 chan 通道,这次把它补上. chan 可以理解为队列,遵循先进 ...