这篇文章介绍了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学习相关推荐

  1. Java NIO 学习笔记(三)----Selector

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  2. Java NIO学习笔记之图解ByteBuffer

    转载自 Java NIO学习笔记之图解ByteBuffer ByteBuffer前前后后看过好几次了,实际使用也用了一些,总觉得条理不够清晰. <程序员的思维修炼>一本书讲过,主动学习,要 ...

  3. Java NIO学习篇之直接缓冲区和非直接缓冲区

    定义 以上是书深入理解java虚拟机对直接内存的描述.直接缓冲区用的就是直接内存. java nio字节缓冲区要么是直接的,要么是非直接的.如果为直接字节缓冲区,则java虚拟机会尽最大努力直接在此缓 ...

  4. Java NIO学习篇之通道FileChannel详解

    定义: FileChannel是Java NIO对应于磁盘等存储设备文件操作的通道. 常用API详解: 获取FileChannel的API /** * 打开一个与文件的连接通道,用于进行文件操作. * ...

  5. Java NIO学习篇之缓冲区ByteBuffer详解

    定义: ByteBuffer是Buffer的实现类之一,是一个通用的缓冲区,功能要比其他缓冲区子类多.支持直接内存.是一个抽象类.子类实现是HeapByteBuffer(非直接缓冲区子类),Direc ...

  6. Java NIO 学习笔记(五)----路径、文件和管道 Path/Files/Pipe

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  7. Java NIO学习系列七:Path、Files、AsynchronousFileChannel

    相对于标准Java IO中通过File来指向文件和目录,Java NIO中提供了更丰富的类来支持对文件和目录的操作,不仅仅支持更多操作,还支持诸如异步读写等特性,本文我们就来学习一些Java NIO提 ...

  8. Java NIO学习系列四:NIO和IO对比

    前面的一些文章中我总结了一些Java IO和NIO相关的主要知识点,也是管中窥豹,IO类库已经功能很强大了,但是Java 为什么又要引入NIO,这是我一直不是很清楚的?前面也只是简单提及了一下:因为性 ...

  9. Java NIO学习系列三:Selector

    前面的两篇文章中总结了Java NIO中的两大基础组件Buffer和Channel的相关知识点,在NIO中都是通过Channel和Buffer的协作来读写数据的,在这个基础上通过selector来协调 ...

最新文章

  1. mysql loop嵌套_MySQL中Nested-Loop Join算法小结
  2. Android SERVICE后台服务进程的自启动和保持
  3. 算法-Valid Anagram
  4. Android studio | From Zero To One ——XML文件中的单行注释与多行注释
  5. 中国剩余定理及其拓展
  6. 怎样用u盘linux安装ntp协议,电脑中怎么配置NTP服务
  7. r语言 分类变量 虚拟变量_R语言中的变量
  8. linux的基础知识——终端
  9. 如何对RTSP播放器做功能和性能评估
  10. Tuxedo基本参数配置说明
  11. 计算机基础思维导图_超级简单的实操示范,教你零基础带孩子玩转风靡全球的思维导图...
  12. app_offline.htm的作用
  13. new对象时,类名后加括号与不加括号的区别
  14. 以太坊虚拟机 EVM(3)交易流(FISCO BCOS为例)
  15. html5 显示圆形图片,《小白HTML5成长之路40》怎样显示圆角图片
  16. python加法程序结果图片_OpenCV-Python系列之图像上的算术运算
  17. struts2学到屎挫死-深入Struts2(2)--Action
  18. 同时处理知网、万方、维普数据库——CiteSpace、Ucinet、Vosviewer等
  19. unity摄像头实物识别_“千万别让女朋友擦倒车摄像头,太tm可怕了哈哈哈哈哈!”...
  20. 【菜鸟学开发系统】学生成绩管理系统(二)

热门文章

  1. 很久之前写的【成绩管理系统】的数据库
  2. imp-00058: 遇到 oracle 错误 12560,MP-00058: 遇到 ORACLE 错
  3. 【Oracle】ORA-38171: Insufficient privileges for SQL management object operation
  4. .NET Core开发的iNeuOS物联网平台部署树莓派(raspbian),从网关到云端整体解决方案。助力2019中国.NET峰会。
  5. 关于EXP-00056: 遇到 ORACLE 错误 1455 ORA-01455: 转换列溢出整数数据类型 EXP-00000: 导出终止失败 的问题解决方法整理
  6. centos用ifconfig不显示ip地址的解决方法
  7. python异常(概念、捕获、传递、抛出)
  8. 在多行中查找和替换vim中的字符串
  9. 相当于jQuery .hide()来设置可见性:隐藏
  10. 为什么在Java中收到NoClassDefFoundError?