【转载】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校验继承合法性
@Override
public 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 > ) {
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();
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
// 改了原文中的一个小问题,重复循环忙等的问题
while (true) {
int readBytes = socketChannel.read(byteBuffer);
if (readBytes > ) {
logger.info("Server: readBytes: " + readBytes + ", data: "
+ new String(byteBuffer.array(), , 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 = ;
NioServer nioServer = new NioServer(hostname, port);
nioServer.start();
}
}
然后启动之后,用telnet作为客户端来访问一下:
$ telnet 127.0.0.1
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();
socketChannel.write(ByteBuffer.wrap(requestData.getBytes()));
while(true) {
byteBuffer.clear();
int readBytes = socketChannel.read(byteBuffer);
if (readBytes > ) {
byteBuffer.flip();
String getStr = new String(byteBuffer.array(), , 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 = ; 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
开始没有加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-
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.</modelVersion> <groupId>com.myapp.nio</groupId>
<artifactId>nioClient</artifactId>
<version>1.0-SNAPSHOT</version> <properties>
<!-- log4j日志包版本号 -->
<log4j.version>1.2.</log4j.version>
</properties> <dependencies>
<!-- 添加日志相关jar包 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
</project>
下次再写个线程池来处理看看。
(完)
【转载】Java NIO学习的更多相关文章
- Java NIO学习与记录(八): Reactor两种多线程模型的实现
Reactor两种多线程模型的实现 注:本篇文章例子基于上一篇进行:Java NIO学习与记录(七): Reactor单线程模型的实现 紧接着上篇Reactor单线程模型的例子来,假设Handler的 ...
- Java NIO学习笔记
Java NIO学习笔记 一 基本概念 IO 是主存和外部设备 ( 硬盘.终端和网络等 ) 拷贝数据的过程. IO 是操作系统的底层功能实现,底层通过 I/O 指令进行完成. 所有语言运行时系统提供执 ...
- 零拷贝详解 Java NIO学习笔记四(零拷贝详解)
转 https://blog.csdn.net/u013096088/article/details/79122671 Java NIO学习笔记四(零拷贝详解) 2018年01月21日 20:20:5 ...
- Java NIO 学习笔记(七)----NIO/IO 的对比和总结
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO 学习笔记(六)----异步文件通道 AsynchronousFileChannel
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO 学习笔记(五)----路径、文件和管道 Path/Files/Pipe
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO 学习笔记(四)----文件通道和网络通道
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO 学习笔记(三)----Selector
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO 学习笔记(二)----聚集和分散,通道到通道
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO 学习笔记(一)----概述,Channel/Buffer
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
随机推荐
- HDU 1533 二分图最小权匹配 Going Home
带权二分图匹配,把距离当做权值,因为是最小匹配,所以把距离的相反数当做权值求最大匹配. 最后再把答案取一下反即可. #include <iostream> #include <cst ...
- 初学-BeautifulSoup爬取豆瓣页面
# -*- coding: utf-8 -*-import osimport urllibimport urllib2from bs4 import BeautifulSoup headers = { ...
- 让 PHP COOKIE 立即生效(不用刷新就可以使用)
<?php function set_my_cookie($, $path = '', $domain = '') { $_COOKIE[$var] = $value; setcookie($v ...
- HDU - 1705 Count the grid
昨天吉老师讲了皮克定理 皮克定理用于计算点阵中顶点在格点上的多边形面积.对于一个顶点全部在格点上的多边形来说,它的面积计算有如下特点: 如果用a表示位于多边形内部的格点数,b表示位于多边形边界上的格点 ...
- springmvc始终跳转至首页,不报404错误
本篇博客特别补充:2017-3-4 9:42,经过分析和测试,本篇博客的解决方案只是碰巧,暂时的解决了问题.在后续的运行中,又出现了同样的毛病.经过日志跟踪,发现了端倪,下篇博客深入的剖析!本篇博客, ...
- hdu6035[dfs+思维] 2017多校1
/*hdu6035[dfs+思维] 2017多校1*/ //合并色块, 妙啊妙啊 #include<bits/stdc++.h> using namespace std; ; const ...
- 【Luogu】P1411树(树形高精DP)
题目链接 我貌似又做了一道高精题呢(笑) 这题的DP方程很好想,设f[i][j]表示i为根的子树,i所在联通块大小为j的最大值,然后乱搞 但是要高精,那么搞是得要高精除的 所以考虑f[i][j]是除以 ...
- 国外12家值得注意的SD-WAN厂商
国外12家值得注意的SD-WAN厂商 来源 https://www.sdnlab.com/18611.html 在网络行业,围绕软件定义网络的炒作似乎已经殆尽,但业界普遍对软件定义广域网(SD-WAN ...
- HDU——1846Brave Game(巴什博弈)
Brave Game Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total ...
- 洛谷P4175 - [CTSC2008]网络管理
Portal Description 给出一棵\(n(n\leq8\times10^4)\)个点的带点权的树,进行\(m(m\leq8\times10^4)\)次操作,操作有两种: 修改一个点的点权. ...