javaNIO学习笔记(二)
javaNIO 学习笔记(二)
参考文档:http://tutorials.jenkov.com/java-nio/channels.html Java NIO Channel
和传统的IO中的 InputStream 、OutputStream 相比,主要区别就是传统IO中的stream是单向的。但是在NIO中的channel 是可读可写的。并且读写是可以异步进行的。另外channel 都是从buffer 中读取或者写入数据的
上一篇学习笔记中的主要实现的管道类型 FileChannel 从文件和文件读取数据 DatagramChannel 可以通过UDP在网络上读写数据。 SocketChannel 可以通过TCP在网络上读写数据。 ServerSocketChannel 允许您侦听传入的TCP连接,就像web服务器一样。为每个传入连接创建SocketChannel。
这4个类在idea中可以查看相应的UML图 FileChannel
先看下面的程序
读取文件数据: public class FileChannelPrc { public static void main(String[] args) throws IOException { // 创建一个rw模式的随机文件 RandomAccessFile randomAccessFile =new RandomAccessFile("D:nioFile.txt", "rw"); // 获取fileChinnel FileChannel fileChannel = randomAccessFile.getChannel(); // 设定缓冲区大小48 ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = fileChannel.read(buf); while(bytesRead !=-1){ System.out.println(" Read " + bytesRead); buf.flip(); while(buf.hasRemaining()){ System.out.print((char) buf.get()); } buf.clear(); bytesRead = fileChannel.read(buf); } randomAccessFile.close(); } } // 运行结果: Hi, I am jimmy.
上面程序要正确执行需要,先在本地对应文件夹中创建相应文件。我在文件中写入的是 Hi, I am jimmy. 这里就可以看到读取出来并且打印了。
然后我们将文件内容修改为 Hi, I am jimmy. I am learning Java NIO. Hope I can hold it. (大于48字符)。在运行程序,此时返回值
Read 48
Hi, I am jimmy. I am learning Java NIO. Hope I
Read 13
can hold it.
通过上面的测试我们大概可以猜测。 // 通过fileChannel将数据读取到缓冲区,返回值是缓冲区读取的字节数 int bytesRead = fileChannel.read(buf); // 判断缓冲区是否还有数据 buf.hasRemaining() // 获取当前缓冲区的第一个元素 buf.get() // 切换缓冲区读写模式 buf.flip(); // 清空缓存区 buf.clear();
我们现在先只来看 channel 相关的猜测。改造程序(改造部分如下)// 设定缓冲区大小 ByteBuffer buf = ByteBuffer.allocate(48); ByteBuffer buf2 = ByteBuffer.allocate(48); int bytesRead = fileChannel.read(buf); int bytesRead2 = fileChannel.read(buf); System.out.println("bytesRead" + bytesRead + " bytesRead2" + bytesRead2);
此时走debug或者查看打印语句就可以看出来, bytesRead = 16 bytesRead2 = -1 (文件信息Hi, I am jimmy. )。那么这样就可以得出一个结论
fileChannel.read(buf);会将 channel 的数据flush 到缓冲区,而不是复制。
下面是程序则是使用fileChannel写信息到文件中 public class FileChannelPrc { public static void main(String[] args) throws IOException { ... // 写文件 // ByteBuffer buf2 = ByteBuffer.allocate(48); String newData = "do not worry, be happy 时间:" + System.currentTimeMillis(); buf.clear(); buf.put(newData.getBytes()); while(buf.hasRemaining()) { buf.flip(); fileChannel.write(buf); } fileChannel.close(); randomAccessFile.close(); } }
查看文件,文件文本显示
Hi, I am jimmy. do not worry, be happy 时间:1591970658685
看这样的结果可以看出来, fileChannel 写数据的时候是在文件结尾处增加写入数据,而非覆盖。
那么根据上面的例子我们先简单地总结下 fileChannel 简单使用方法。获取fileChannel的实例(注意 public abstract class FileChannel 这是个抽象类),获得方式
可以使用 RandomAccessFile 的实例来获取读取数据的方法使用 read 将内容读取到ByteBuffer ,当然这里也涉及ByteBuffer 的使用,这个后面在学习总结写过程呢则是使用 write 方法向文件写入ByteBuffer 的信息。当然最后和传统IO一样,需要调用 close 来关闭fileChannel
接下来稍微看下 fileChannel 的源码(这里也算是写一个备忘录,以后遇到相关问题的时候可以回来看下,或许会有帮助)// ---- read方法 ------- public abstract int read(ByteBuffer dst) throws IOException; // 从指定位置开始读取数据 public abstract int read(ByteBuffer dst, long position) throws IOException; public final long read(ByteBuffer[] dsts) throws IOException { return read(dsts, 0, dsts.length); } public abstract long read(ByteBuffer[] dsts, int offset, int length) throws IOException; // ------ write方法 ------ public abstract int write(ByteBuffer src) throws IOException; public abstract int write(ByteBuffer src, long position) throws IOException; public final long write(ByteBuffer[] srcs) throws IOException { return write(srcs, 0, srcs.length); } public abstract long write(ByteBuffer[] srcs, int offset, int length) throws IOException; // ---- open方法 ------ @since 1.7 这个方法可以获取fileChannel public static FileChannel open(Path path, OpenOption... options) throws IOException { Set set = new HashSet(options.length); Collections.addAll(set, options); return open(path, set, NO_ATTRIBUTES); } public static FileChannel open(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException { FileSystemProvider provider = path.getFileSystem().provider(); return provider.newFileChannel(path, options, attrs); } // --- position -- // 获取一个新的fileChannel public abstract FileChannel position(long newPosition) throws IOException; // 返回buffer读取的位置 public abstract long position() throws IOException; // --- force ---- // 强制将channel的数据写入,根据metaData决定是否写入本地磁盘存储 public abstract void force(boolean metaData) throws IOException; // Returns the current size of this channel"s file. 返回当前通道中的文件大小 public abstract long size() throws IOException; // 截取指定长度的数据,从开始进行截取长度为 size的 public abstract FileChannel truncate(long size) throws IOException;
当然还有一些其他方法,这些等以后涉及使用的时候在补充吧。这里附上测试的时候的代码(非全部,有的API直接可使用idea的片段执行查看结果。) public class FileChannelPrc { public static void main(String[] args) throws IOException { // 创建一个rw模式的随机文件 RandomAccessFile randomAccessFile =new RandomAccessFile("D:nioFile.txt", "rw"); // 获取fileChinnel FileChannel fileChannel = randomAccessFile.getChannel(); // fileChannel.position(); FileChannel fileChannel2 = fileChannel.truncate(10); // 设定缓冲区大小 ByteBuffer buf = ByteBuffer.allocate(48); // int bytesRead = fileChannel.read(buf,10L); int bytesRead = fileChannel2.read(buf); // ByteBuffer buf2 = ByteBuffer.allocate(48); // int bytesRead = fileChannel.read(buf); /*int bytesRead2 = fileChannel.read(buf); System.out.println("bytesRead" + bytesRead + " bytesRead2" + bytesRead2);*/ while(bytesRead !=-1){ System.out.println("Read " + bytesRead); buf.flip(); while(buf.hasRemaining()){ System.out.print((char) buf.get()); } buf.clear(); // bytesRead = fileChannel.read(buf); bytesRead = fileChannel2.read(buf); } // 写文件 // ByteBuffer buf2 = ByteBuffer.allocate(48); /* String newData = "do not worry, be happy 时间:" + System.currentTimeMillis(); buf.clear(); buf.put(newData.getBytes()); while(buf.hasRemaining()) { buf.flip(); fileChannel.write(buf); }*/ fileChannel.close(); randomAccessFile.close(); } } SocketChannel 和 ServerSocketChannel
socketChannel 是一个用来处理TCP网络套接字的通道。有两种方式来获取SocketChannel 使用open()可以直接获取一个实例 通过 ServerSocketChannel 来获取SocketChannel socketChannel = SocketChannel.open();
ServerSocketChannel 类似于标准Java网络中的ServerSocket 一样,是一个可以侦听传入TCP连接的通道.ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
no talk,show code package jniolearn.channel; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; /** * @Author: jimmy * @Date: 2020/6/13 15:03 * @Description: * * 理解为server */ public class ServerSocketChannelPrc { public static void main(String[] args) throws IOException { // 获取实例 ServerSocketChannel server = ServerSocketChannel.open(); // 绑定ip和端口 server.socket().bind(new InetSocketAddress("127.0.0.1",2999)); //设置为非阻塞模式 server.configureBlocking(false); // 获取 SocketChannel while(true){ SocketChannel serverChannel = server.accept(); if(serverChannel != null) { ByteBuffer buf = ByteBuffer.allocate(512); int byteRead = serverChannel.read(buf); StringBuilder req = new StringBuilder(); buf.flip(); while(buf.hasRemaining()){ req.append((char) buf.get()); } buf.clear(); System.out.println("客户端发来消息" + req); buf.put("hi,this is jimmy. be happy ".getBytes()); buf.flip(); serverChannel.write(buf); // serverChannel.close(); } } } } package jniolearn.channel; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; /** * @Author: jimmy * @Date: 2020/6/13 12:04 * @Description: * * 可以理解为client */ public class SocketChannelPrc { /** * 为了方便,这里直接将异常抛出,实际开发要进行异常捕捉 * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // 获取 SocketChannel 的实例 SocketChannel client = SocketChannel.open(); /** * 连接服务端,这里面传入的参数对象为 SocketAddress * 查看源码可以找到一个实现类 InetSocketAddress 构造方法 public InetSocketAddress(String hostname, int port) * 看到这个构造就很熟悉了, * * 连接服务 127.0.0.1 和 2999 端口 */ client.connect(new InetSocketAddress("127.0.0.1",2999)); // 有channel就需要有缓冲区buffer ByteBuffer buf = ByteBuffer.allocate(512); buf.put("hi, jimmy,this is client.".getBytes()); // 切换读写模式 buf.flip(); // 写数据去服务器 client.write(buf); // 清空buffer,此时相当于将buffer重置为读模式 buf.clear(); int byteRead = client.read(buf); buf.flip(); StringBuilder stringBuffer=new StringBuilder(); while (buf.hasRemaining()){ stringBuffer.append((char) buf.get()); } buf.clear(); System.out.println("从服务端接收到的数据:"+stringBuffer); // 关闭 socketChannel client.close(); } }
先启动服务端 ServerSocketChannel ,然后U启动SocketChannel .返回结果如下:客户端发来消息hi, jimmy,this is client. 从服务端接收到的数据:hi,this is jimmy. be happy
整体看来和 fileChannel 差不太多,基本都是要先有一个打开的channel (有一个方法是isOpen()可以获取是否打开)。然后使用相应的缓冲区来将数据从通道写入缓冲区或者从缓冲区读取数据。(关于buffer相关的方法后面在学习)。不过要注意一些特别的地方SocketChannel``ServerSocketChannel 的一个方法configureBlocking() 这个方法表示这个SocketChannel``ServerSocketChannel 是阻塞还是非阻塞的,具体使用方式等学习了selector 的时候在进行补充。当然上面程序是不完善的,接受放缓冲区不够大的时候会报错。具体可以改下代码看看。DatagramChannel
DatagramChannel 类似于java 网络编程的DatagramSocket类;使用UDP进行网络传输
同样的可以使用open方法获取一个实例 package jniolearn.channel; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; /** * @Author: jimmy * @Date: 2020/6/13 17:32 * @Description: * server */ public class DatagramChannelPrcS { public static void main(String[] args) throws IOException { DatagramChannel s = DatagramChannel.open(); s.bind(new InetSocketAddress("127.0.0.1",3999)); ByteBuffer buf = ByteBuffer.allocate(48); s.receive(buf); buf.clear(); while(buf.hasRemaining()){ System.out.print((char)buf.get()); } } } package jniolearn.channel; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; /** * @Author: jimmy * @Date: 2020/6/13 17:26 * @Description: * client */ public class DatagramChannelPrc { public static void main(String[] args) throws IOException { DatagramChannel channel = DatagramChannel.open(); // 绑定端口 // channel.bind(new InetSocketAddress(3999)); ByteBuffer buf = ByteBuffer.allocate(48); buf.put("datagrameChannel info".getBytes()); // datagrameChannel不需要使用这个方法 // channel.write(buf); buf.flip(); int a = channel.send(buf,new InetSocketAddress("127.0.0.1",3999)); channel.close(); } }
使用起来比较简单,服务端可以直接使用bind方法绑定端口、ip。客户端从缓冲区直接将数据发哦是那个至服务器。是否发送成功或者成功被接收到是没有保证的;发送消息通过send方法发出,改方法返回一个int值,表示成功发送的字节数:
那么这学习留下一个尾巴就是关于阻塞模式和非阻塞模式的区别以及应用。这个在学习完其他类之后在进行补充学习
助听器一定要双耳配同款的吗?对听力有障碍的人,选配助听器一事,在我陪父亲去专卖店,选配6年中的体会,一开始仅配一个耳背式助听器,但随着老人年龄的增长,听力逐步减弱,我认为双耳都配戴,听力效果显得更好。助听器随
戴助听器会不会听到吵的声音会头晕?导致头晕的可能性1。初次佩戴,出现不适应的现象。2。助听器的性能比较简单,对噪音处理不好,嘈杂环境吵得头晕。3。调试的问题,增益给的过大,也会吵得头晕现在助听器都是根据听力和需求精
火星着陆遭国外质疑,祝融号传回着陆影像,为何数据接收这么久?他质疑或者是相信,跟我们没关系,只要你别跟着瞎起哄就行祝融号登陆火星的前两天并没有与天问一号轨道器建立数据链路,而是采用直传地球的方式,传输速率最高16Bs。主要是回传工程参数。现
想买一部流畅,护眼的手机,求推荐,5000以下的?5000元这个价位卡得很死,苹果新款就不用想了,甚至连华为Mate40Pro小米11Ultra这样顶级的安卓旗舰也买不到。不过可以考虑以下几款手机第一款vivoX60Pro这应该是
新买的手机上面的原厂膜你们会不会撕掉?现在大部分手机都会贴上原厂膜了,最开始是OPPOvivo带头,华为在经历了疏油层门之后也给手机贴了原厂膜。就连一向比较抠门的小米,这两年的新机也都贴上了出厂膜。对于这层膜,我个人的
为什么京东卖的小米手机比小米官网还便宜?不请自来,还望勿怪。因为经常在京东上买购买手机的缘故,所以对渠道这方面的问题还是比较了解的,所以就让我来为大家解答一下吧。京东是小米手机的授权经销商,拿货的量大,而且小米方面在网上
那些催收人是怎么搞到欠款人的新号码的?01hr网络渠道找到的现在的网络媒体太发达了,每个人都有自己常用的网络社交工具。很多人都喜欢分享自己工作和生活中的点点滴滴,QQ空间朋友圈微博抖音等等社交网络工具中经常会更新我们的
今日头条各种游戏广告都是轻松提现真的假的?首先,我们不能说是假的,为了验证是不是假的,我一口气下载了好几十个这类app,从二月到五月,总计4个月共收入276。24元说到这里,大家可能觉得这有点少,4个月确实少,但这只是平时
烟花燃放产生的温度能不能消灭空气中的新冠病毒?新冠病毒最适合低温下生存,遇到高温活力变小或自取灭亡。这是经过科研人员,近一年来的摸索研究经过科学论断出来的真理。所以专家们才提醒人们,尽量不要接触那么多冷冻食品。特别是海产品和肉
为何任正非宁可自损八百,也要围剿李一男?西方资本的渗入李一男对华为有多重要呢?当年他与郑宝用开发的万门机所使用的准SDH技术,至今仍然是国际通信技术的最高标准。有人说一个李一男,半部华为史,其实更确切地说一个李一男,半部中国通信史。李
为什么中国所有手机厂商不一致启用鸿蒙操作系统来抵制谷歌?随着商务部最新出台的阻断法实施,国内各手机产品在中国境内继续使用安卓系统是要申请得到批准才行的,不过我认为商务部不会同意继续使用安卓,这样三星小米等手机在中国境内销售只能安装自研操