最近给内部做了一个NIO的分享,是基于JKD1.6的JDK的,由于我不喜欢写PPT,所以就只写了一个DEMO,现在把代码拿出来分享一下,关于NIO的使用方法,以及如何扩展都在代码的注释里面写着的,希望对需要的同学有帮助。
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
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.Date;
import java.util.Iterator;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author coffee
* mail:coffee_hc@163.com
*/
public class NIoTest {
private static Logger logger = LoggerFactory.getLogger(NIoTest.class);
private Selector acceptSelector;
private Selector rwSelector;
private BlockingQueue<SocketChannel> waitRegeditChannel = new LinkedBlockingQueue<SocketChannel>();
public static void main(String[] args) {
NIoTest ns = new NIoTest();
ns.start();
}
public void start() {
InetSocketAddress localAddress = new InetSocketAddress("127.0.0.1", 8888);
ServerSocketChannel serverSocketChannel;
try {
acceptSelector = Selector.open();
rwSelector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
// 非堵塞
serverSocketChannel.configureBlocking(false);
ServerSocket socket = serverSocketChannel.socket();
// 端口不复用
socket.setReuseAddress(false);
socket.setSoTimeout(60000);
socket.setReceiveBufferSize(1024);
socket.bind(localAddress);
serverSocketChannel.register(acceptSelector, SelectionKey.OP_ACCEPT);
Executor e = Executors.newFixedThreadPool(2);// 这里可以不用线程池
e.execute(new Accept());
e.execute(new RWThread());
} catch (IOException e) {
e.printStackTrace();
}
}
public class Accept implements Runnable {
@Override
public void run() {
while (true) {
try {
int count = acceptSelector.select(500);
// logger.debug("accept");
if (count > 0) {
Iterator<SelectionKey> keys = acceptSelector.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = keys.next();
// 一定要删除
keys.remove();
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
// 接受了才能获取连接的通道
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
// 取消以下注释代码,会导致通道在选择器中注册的时候与选择器在选择的时候争抢互斥锁,很难被注册进去。
// logger.debug("开始注册连接");
// socketChannel.register(rwSelector,
// SelectionKey.OP_READ);
// logger.debug("结束注册连接");
waitRegeditChannel.put(socketChannel);
// 当然,可以建立一个选择器池,并发处理接受的连接,具体如何实现自己扩展
rwSelector.wakeup();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private class RWThread implements Runnable {
/*
* (non-Javadoc)
*
* @see java.lang.Thread#run()
*/
@Override
public void run() {
while (true) {
try {
while (!waitRegeditChannel.isEmpty()) {
SocketChannel socketChannel = waitRegeditChannel.poll();
socketChannel.register(rwSelector, SelectionKey.OP_READ);// 此处需要改造
logger.debug("注册了一个连接:" + socketChannel.socket());
}
int count = rwSelector.select(1000);
// logger.debug("rw");
if (count > 0) {
Iterator<SelectionKey> keys = rwSelector.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = keys.next();
keys.remove();
// 此处可以扩展为将数据放到线程池中处理,这样可以提高数据的吞吐量,但是要注意内存的处理
processKey(key);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void processKey(SelectionKey key) {
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer bb = ByteBuffer.allocate(1024);
int count;
try {
// 此处加断点以后可以明显看到,OS底层的TCP会缓存数据,read的时候将会一次性读出来。
count = socketChannel.read(bb);
if (count < 0) {
// 已经读到流的结尾,或连接异常,需要关闭连接
socketChannel.close();
// 注意key.cancel()是在下次select()的时候才会被清理
key.cancel();
return;
}
} catch (IOException e) {
e.printStackTrace();
}
// buffer的使用一定要好好看看API,buffer的熟练使用对NIO编程很重要
bb.flip();
int limit = bb.limit();
byte[] tmpbytes = new byte[limit];
bb.get(tmpbytes);
logger.debug("接受信息为:" + new String(tmpbytes));
if (!isCache(key, tmpbytes)) {
byte[] bytes = (byte[]) key.attachment();
String requestStr = new String(bytes);
logger.debug("请求字符串:" + requestStr);
bb.clear();
if (requestStr.equals("gettime")) {
bb.put(new Date().toString().getBytes());
key.attach(new byte[0]);
} else if (requestStr.endsWith("clear")) {
key.attach(new byte[0]);
try {
bb.put("缓存已清理".getBytes("GB2312"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else {
try {
bb.put("不能识别的请求".getBytes("GB2312"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
bb.flip();
try {
socketChannel.write(bb);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private boolean isCache(SelectionKey key, byte[] tmpbytes) {
Object obj = key.attachment();
byte[] bytes;
if (obj != null) {
bytes = (byte[]) obj;
} else {
bytes = new byte[0];
}
int sumLength = bytes.length + tmpbytes.length;
ByteBuffer bb = ByteBuffer.allocate(sumLength);
bb.put(bytes);
bb.put(tmpbytes);
bb.flip();
tmpbytes = bb.array();
if (tmpbytes[sumLength - 1] == 10) {
tmpbytes = new byte[sumLength - 2];
bb.get(tmpbytes);
key.attach(tmpbytes);
return false;
} else {
key.attach(tmpbytes);
return true;
}
}
}
}
分享到:
相关推荐
NIO,全称New Input/Output,是从Java 1.4版本开始引入的一个新特性,它与传统的IO模型( Blocking I/O)相比,提供了更高效的数据传输方式。传统的IO基于流(Stream)和缓冲区(Buffer)操作,而NIO的核心在于通道...
而"工具"可能是指博主介绍了一些辅助开发NIO应用的库或框架,例如Netty,它是一个基于NIO的高性能、异步事件驱动的网络应用程序框架,常用于创建服务器和客户端的网络应用。 压缩包中的"dsapp"可能是某种数据服务...
- **分散/聚合读取**:可以在单次操作中将数据从一个Channel分散到多个Buffers,或将数据从多个Buffers聚集到一个Channel。 ### Redis #### 概述 Redis (REmote DIctionary Server) 是一个开源的键值存储系统,...
【标题】"原创nio socket mina+javascript+flash实现commet长连接网页聊天室"揭示了一个基于Java NIO(Non-blocking I/O)的Socket通信框架Mina与JavaScript、Flash技术结合,实现COMET(Comet是使服务器向浏览器推...
文件操作是IO的一个重要子集,主要关注对本地文件系统的操作,包括创建、读取、写入、删除文件或目录。Java中的java.io.File类是进行文件操作的基础,它提供了丰富的静态方法和实例方法来管理文件和目录。 NIO是...
* 高性能:NIO、内部协议、Reactor线程模型。 * 高可靠:得到了成千上百的生产系统检验。 Netty设计 * 针对多种传输类型的统一API - 阻塞和非阻塞 * 基于稳定和可扩展的事件模型,允许清晰的分离关注点 * 高可定制...
QCon是一个关注实践者经验分享的国际软件开发会议,因此分享内容很可能会集中在Netty在实际应用中的经验教训、最佳实践和深入讨论。 描述部分说明了这份PPT资料对于学习者来说非常有帮助,暗示了Norman Maurer的...
封装允许我们将数据和操作数据的方法打包到一个类中,保护数据安全;继承使得类可以基于已有类创建,从而实现代码复用;多态则允许我们设计灵活的接口,提高程序的扩展性。在实践中,理解并运用接口、抽象类以及访问...
3. **javaZlfx516.zip**:这是一个Java相关的压缩文件,可能是包含了一系列的Java源代码、教程文档、项目案例或者是用于教学的练习题目。由于标题提及的是“管理系统”,我们可以推测这部分内容可能涉及到Java在构建...
Apache Tomcat 是一个功能非常强大且完善的开源项目,具有悠久的发展历史,从 WebLogic 到 JBoss,再到 Tomcat/Jetty,逐步轻量化的演变过程。Tomcat 社区作为一个活跃的开源社区,已经存在了 25 年之久,且这个社区...
在学习Netty时,"成长语录一.png" 这个文件可能是一个鼓励或指导性的图片,它可能包含了一些关于学习过程中的心得、经验分享或者是开发者的座右铭,尽管它不是直接的技术资料,但可以作为学习过程中的心理支持。...
本文基于一份2021年的面试经验分享文档,该文档总结了一位求职者成功获得华为、字节跳动、腾讯、京东、网易、滴滴等六家知名互联网公司实习offer的经历。这份经验分享不仅包括面试流程、面试题目,还涉及到了面试...
本资源摘要信息为阿里云面试经验分享,涵盖了Java多线程、JVM相关、Java扩展篇、Spring相关、中间件篇、数据库篇、Redis和其他相关知识点。该资源将为读者提供详细的IT知识点总结,旨在帮助读者更好地理解和掌握相关...
Netty是基于Java NIO的一个高性能的网络通信框架,它可以用于快速开发可维护的高性能协议服务器与客户端。Netty提供了事件驱动、非阻塞的API来简化网络编程,并且内置了大量的协议支持(如HTTP、FTP、SMTP、...
在业界新闻板块,斯坦福大学发布了一个免费在线文本分析工具etcML,该工具利用机器学习引擎进行文本分析,并能估计文本的情感倾向,这标志着机器学习技术在文本分析领域的进一步普及和便利化。另外,Parse公司发布了...
Pudn.com是一个知名的编程资源分享网站,所以这个文件可能是从该网站下载的源代码的出处记录。 2. "networks-code":这是一个可能包含多个源代码文件的目录或文件。如果它是一个目录,那么里面可能有不同语言(如C...
iteye是一个知名的中国IT社区,博主们经常在这里分享技术经验和代码示例。 标签“源码”意味着我们将讨论的是实际的编程代码,而“工具”可能指的是一种用于实现目录监视功能的实用程序或者库。在实际操作中,...
例如,使用`ACTION_PICK`可以启动一个文件选择器,`ACTION_SEND`可以分享文件。复制和移动文件需要利用`FileChannel`进行流式传输。 安全是开发文件管理器时不可忽视的一环。在Android 6.0(API级别23)及以上,...