【转载】Java NIO学习
这篇文章介绍了NIO的基本概念:
http://www.iteye.com/magazines/132-Java-NIO
Java NIO提供了与标准IO不同的IO工作方式:
- Channels and Buffers(通道和缓冲区):标准的IO基于字节流和字符流进行操作的,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。
- Asynchronous IO(异步IO):Java NIO可以让你异步的使用IO,例如:当线程从通道读取数据到缓冲区时,线程还是可以进行其他事情。当数据被写入到缓冲区时,线程可以继续处理它。从缓冲区写入通道也类似。
- Selectors(选择器):Java NIO引入了选择器的概念,选择器用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个的线程可以监听多个数据通道。
基于这篇文章 http://blog.csdn.net/shirdrn/article/details/6263692
写了一个NIO Server和 Client的代码。其中也有一些需要注意的地方。
首先是在代码里面有一些写就绪的情况,这种情况有一些特殊:
一般来说,你不应该注册写事件。写操作的就绪条件为底层缓冲区有空闲空间,而写缓冲区绝大部分时间都是有空闲空间的,所以当你注册写事件后,写操作一直是就绪的,选择处理线程全占用整个CPU资源。所以,只有当你确实有数据要写时再注册写操作,并在写完以后马上取消注册。
下面代码里面可以看到,有一个处理写就绪的函数,是使用了SelectionKey的attachment来处理,并且根据attachment是否仍有数据,来选择使用 interestOps与否。查了一些资料,可能是因为intestOps不会清空attachment吧,需要再研究。
下面是server的代码:
package com.myapp.nio;import org.apache.log4j.LogManager; import org.apache.log4j.Logger;import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set;/*** Created by baidu on 16/11/17.*/ public class NioServer extends Thread{private static final Logger logger = LogManager.getLogger(NioServer.class);private InetSocketAddress inetSocketAddress;private Handler handler = new ServerHandler();public NioServer(String hostname, int port) {inetSocketAddress = new InetSocketAddress(hostname, port);}// 用Override校验继承合法性 @Overridepublic void run() {try {Selector selector = Selector.open(); // 打开选择器ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // 打开通道serverSocketChannel.configureBlocking(false); // 非阻塞 serverSocketChannel.socket().bind(inetSocketAddress);serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);logger.info("Server: socket server stated on port " + inetSocketAddress.getPort());while(true) {int nKeys = selector.select();if (nKeys > 0) {Set<SelectionKey> selectionKeySet = selector.selectedKeys();Iterator<SelectionKey> iterator = selectionKeySet.iterator();while (iterator.hasNext()) {SelectionKey selectionKey = iterator.next();// 以下两种写法是等价的//if ((selectionKey.readyOps() & SelectionKey.OP_ACCEPT) != 0) {if (selectionKey.isAcceptable()) {logger.info("Server: accepted");handler.handleAccept(selectionKey);}//else if ((selectionKey.readyOps() & SelectionKey.OP_READ) != 0) {else if (selectionKey.isReadable()) {logger.info("Server: readable");handler.handleRead(selectionKey);}//else if ((selectionKey.readyOps() & SelectionKey.OP_WRITE) != 0) {else if (selectionKey.isWritable()) {logger.info("Server: writable");handler.handleWrite(selectionKey);}// Is below necessary? iterator.remove();}}}} catch (IOException e) {e.printStackTrace();}}interface Handler {void handleAccept(SelectionKey selectionKey) throws IOException;void handleRead(SelectionKey selectionKey) throws IOException;void handleWrite(SelectionKey selectionKey) throws IOException;}class ServerHandler implements Handler {public void handleAccept(SelectionKey selectionKey) throws IOException {ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();SocketChannel socketChannel = serverSocketChannel.accept();logger.info("Server: accept client socket " + socketChannel);socketChannel.configureBlocking(false);socketChannel.register(selectionKey.selector(), SelectionKey.OP_READ);}public void handleRead(SelectionKey selectionKey) throws IOException {ByteBuffer byteBuffer = ByteBuffer.allocate(512);SocketChannel socketChannel = (SocketChannel) selectionKey.channel();// 改了原文中的一个小问题,重复循环忙等的问题while (true) {int readBytes = socketChannel.read(byteBuffer);if (readBytes > 0) {logger.info("Server: readBytes: " + readBytes + ", data: "+ new String(byteBuffer.array(), 0, readBytes));// 这个flip是一定需要的, 会把limit和position重置,这样才能重新读到数据 byteBuffer.flip();socketChannel.write(byteBuffer);}else {break;}}socketChannel.close();} // handle Write这一块,其实需要再多研究一下public void handleWrite(SelectionKey selectionKey) throws IOException {ByteBuffer byteBuffer = (ByteBuffer) selectionKey.attachment();byteBuffer.flip();SocketChannel socketChannel = (SocketChannel) selectionKey.channel();socketChannel.write(byteBuffer);if (byteBuffer.hasRemaining()) {selectionKey.interestOps(SelectionKey.OP_READ);}}}public static void main(String[] args) {String hostname = "localhost";int port = 8106;NioServer nioServer = new NioServer(hostname, port);nioServer.start();} }
然后启动之后,用telnet作为客户端来访问一下:
$ telnet 127.0.0.1 8106 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. hihihi hihihi Connection closed by foreign host.如果没有byteBuffer.flip() 这个函数,那么不会有字符串返回。
打包: Project Structure-> Artifacts-> + -> Create Module with dependacies -> extract to the target JAR -> MANIFEST.MF路径最后的src改成resources.
然后 build->build artifact,就能在out目录里面有一个jar包,运行 java -jar xxx.jar
然后开始写客户端的代码:
package com.myapp.nio;import org.apache.log4j.LogManager; import org.apache.log4j.Logger;import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel;/*** Created by baidu on 16/11/17.*/ public class NioClient {private static final Logger logger = LogManager.getLogger(NioClient.class);private InetSocketAddress inetSocketAddress;public NioClient(String hostname, int port) {inetSocketAddress = new InetSocketAddress(hostname, port);}public void send(String requestData) {try {SocketChannel socketChannel = SocketChannel.open(inetSocketAddress);socketChannel.configureBlocking(false);ByteBuffer byteBuffer = ByteBuffer.allocate(512);socketChannel.write(ByteBuffer.wrap(requestData.getBytes()));while(true) {byteBuffer.clear();int readBytes = socketChannel.read(byteBuffer);if (readBytes > 0) {byteBuffer.flip();String getStr = new String(byteBuffer.array(), 0, readBytes);logger.info("Client: bytes: " + readBytes +"data: " + getStr);System.out.printf("Get return str: %s", getStr);socketChannel.close();break;}}} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) {String hostname = "localhost";int port = 8106;String requestData = "HIHIHI here~~~";new NioClient(hostname, port).send(requestData);}}
启动运行之后,命令行会返回:
log4j:WARN No appenders could be found for logger (com.myapp.nio.NioClient). log4j:WARN Please initialize the log4j system properly. log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info. Get return str: HIHIHI here~~~ Process finished with exit code 0
开始没有加log4j的properties,所以没有写日志,要加上。
log4j.properties
#log4j.rootLogger=INFO,Console,File log4j.rootLogger=INFO,File#控制台日志 log4j.appender.Console=org.apache.log4j.ConsoleAppender log4j.appender.Console.Target=System.out log4j.appender.Console.layout=org.apache.log4j.PatternLayout log4j.appender.Console.layout.ConversionPattern=[%p][%t][%d{yyyy-MM-dd HH\:mm\:ss}][%C] - %m%n#普通文件日志 log4j.appender.File=org.apache.log4j.RollingFileAppender log4j.appender.File.File=logs/nio_client.log log4j.appender.File.MaxFileSize=10MB #输出日志,如果换成DEBUG表示输出DEBUG以上级别日志 log4j.appender.File.Threshold=ALL log4j.appender.File.layout=org.apache.log4j.PatternLayout log4j.appender.File.layout.ConversionPattern=[%p][%t][%d{yyyy-MM-dd HH\:mm\:ss}][%C] - %m%n log4j.appender.File.encoding=UTF-8
pom.xml也加上依赖:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.myapp.nio</groupId><artifactId>nioClient</artifactId><version>1.0-SNAPSHOT</version><properties><!-- log4j日志包版本号 --><log4j.version>1.2.17</log4j.version></properties><dependencies><!-- 添加日志相关jar包 --><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>${log4j.version}</version></dependency></dependencies> </project>
下次再写个线程池来处理看看。
(完)
转载于:https://www.cnblogs.com/charlesblc/p/6074057.html
【转载】Java NIO学习相关推荐
- Java NIO 学习笔记(三)----Selector
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO学习笔记之图解ByteBuffer
转载自 Java NIO学习笔记之图解ByteBuffer ByteBuffer前前后后看过好几次了,实际使用也用了一些,总觉得条理不够清晰. <程序员的思维修炼>一本书讲过,主动学习,要 ...
- Java NIO学习篇之直接缓冲区和非直接缓冲区
定义 以上是书深入理解java虚拟机对直接内存的描述.直接缓冲区用的就是直接内存. java nio字节缓冲区要么是直接的,要么是非直接的.如果为直接字节缓冲区,则java虚拟机会尽最大努力直接在此缓 ...
- Java NIO学习篇之通道FileChannel详解
定义: FileChannel是Java NIO对应于磁盘等存储设备文件操作的通道. 常用API详解: 获取FileChannel的API /** * 打开一个与文件的连接通道,用于进行文件操作. * ...
- Java NIO学习篇之缓冲区ByteBuffer详解
定义: ByteBuffer是Buffer的实现类之一,是一个通用的缓冲区,功能要比其他缓冲区子类多.支持直接内存.是一个抽象类.子类实现是HeapByteBuffer(非直接缓冲区子类),Direc ...
- Java NIO 学习笔记(五)----路径、文件和管道 Path/Files/Pipe
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO学习系列七:Path、Files、AsynchronousFileChannel
相对于标准Java IO中通过File来指向文件和目录,Java NIO中提供了更丰富的类来支持对文件和目录的操作,不仅仅支持更多操作,还支持诸如异步读写等特性,本文我们就来学习一些Java NIO提 ...
- Java NIO学习系列四:NIO和IO对比
前面的一些文章中我总结了一些Java IO和NIO相关的主要知识点,也是管中窥豹,IO类库已经功能很强大了,但是Java 为什么又要引入NIO,这是我一直不是很清楚的?前面也只是简单提及了一下:因为性 ...
- Java NIO学习系列三:Selector
前面的两篇文章中总结了Java NIO中的两大基础组件Buffer和Channel的相关知识点,在NIO中都是通过Channel和Buffer的协作来读写数据的,在这个基础上通过selector来协调 ...
最新文章
- mysql loop嵌套_MySQL中Nested-Loop Join算法小结
- Android SERVICE后台服务进程的自启动和保持
- 算法-Valid Anagram
- Android studio | From Zero To One ——XML文件中的单行注释与多行注释
- 中国剩余定理及其拓展
- 怎样用u盘linux安装ntp协议,电脑中怎么配置NTP服务
- r语言 分类变量 虚拟变量_R语言中的变量
- linux的基础知识——终端
- 如何对RTSP播放器做功能和性能评估
- Tuxedo基本参数配置说明
- 计算机基础思维导图_超级简单的实操示范,教你零基础带孩子玩转风靡全球的思维导图...
- app_offline.htm的作用
- new对象时,类名后加括号与不加括号的区别
- 以太坊虚拟机 EVM(3)交易流(FISCO BCOS为例)
- html5 显示圆形图片,《小白HTML5成长之路40》怎样显示圆角图片
- python加法程序结果图片_OpenCV-Python系列之图像上的算术运算
- struts2学到屎挫死-深入Struts2(2)--Action
- 同时处理知网、万方、维普数据库——CiteSpace、Ucinet、Vosviewer等
- unity摄像头实物识别_“千万别让女朋友擦倒车摄像头,太tm可怕了哈哈哈哈哈!”...
- 【菜鸟学开发系统】学生成绩管理系统(二)
热门文章
- 很久之前写的【成绩管理系统】的数据库
- imp-00058: 遇到 oracle 错误 12560,MP-00058: 遇到 ORACLE 错
- 【Oracle】ORA-38171: Insufficient privileges for SQL management object operation
- .NET Core开发的iNeuOS物联网平台部署树莓派(raspbian),从网关到云端整体解决方案。助力2019中国.NET峰会。
- 关于EXP-00056: 遇到 ORACLE 错误 1455 ORA-01455: 转换列溢出整数数据类型 EXP-00000: 导出终止失败 的问题解决方法整理
- centos用ifconfig不显示ip地址的解决方法
- python异常(概念、捕获、传递、抛出)
- 在多行中查找和替换vim中的字符串
- 相当于jQuery .hide()来设置可见性:隐藏
- 为什么在Java中收到NoClassDefFoundError?