NIO基础代码例子
来源:互联网 发布:在淘宝买手机要看什么 编辑:程序博客网 时间:2024/06/06 03:19
自己的一个NIO的小例子,代码:
public class HelloServer { private Selector selector; private ByteBuffer byteBuffer = ByteBuffer.allocate(1024); private String name; public HelloServer() throws IOException { selector = Selector.open(); ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().bind(new InetSocketAddress(8888)); serverSocketChannel.configureBlocking(false); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); } public Selector getSelector() { return selector; } public void setSelector(Selector selector) { this.selector = selector; } // 开始监听 public void listen() { try { for (;;) { // 选择已经准备好的通道,返回通道数,这个值是上次调用select()后到现在这个阶段 //已经准备好的通道 int i = selector.select(); Iterator iter = selector.selectedKeys().iterator(); if (iter.hasNext()) { SelectionKey selectionKey = (SelectionKey) iter.next(); // 处理完一个就需要删除一个SelectionKey 不然的话,一直堆积,cpu100% iter.remove(); process(selectionKey); } } } catch (IOException e) { e.printStackTrace(); } } public void process(SelectionKey selectionKey) throws IOException { // 准备好接受新的套接字连接 这个只有ServerSocketChannel绑定的SelectionKey才有效 if (selectionKey.isAcceptable()) { ServerSocketChannel server = (ServerSocketChannel) selectionKey .channel(); SocketChannel channel = server.accept(); // 设置非阻塞模式 channel.configureBlocking(false); // 得到client的socket后可以使其在OP_READ状态,下个时段的selector.select()中就是selectionKey.isReadable()的了,可以执行下面这个分支了 channel.register(selector, SelectionKey.OP_READ); System.out.println("accept:"+selectionKey.interestOps()+" "+selectionKey.readyOps()); } else if (selectionKey.isReadable()) { // 可读,下面就是client读数据 SocketChannel channel = (SocketChannel) selectionKey.channel(); // int count = 0; int count = channel.read(byteBuffer); if (count > 0) { byte[] bbb = new byte[1024]; byteBuffer.flip(); bbb = byteBuffer.array(); name = new String(bbb); System.out.println(name + "aaa"); byteBuffer.clear(); } // channel.close();这个可以关闭,可以不关闭,一般需要关闭(count==0),不然client的socket堆积的太多 // channel.register(selector, SelectionKey.OP_WRITE); // 可以在注册感兴趣的时间,这个是往客户端写,下个select() // 时段就可以往client里面写了 System.out.println("isReadable:"+selectionKey.interestOps()+" "+selectionKey.readyOps()); } else if (selectionKey.isWritable()) { System.out.println("isWritable:"+selectionKey.interestOps()); SocketChannel channel = (SocketChannel) selectionKey.channel(); byteBuffer.reset(); byteBuffer.put(name == null ? "acb".getBytes() : name.getBytes()); } } public static void main(String[] args) { int port = 8888; try { HelloServer server = new HelloServer(); System.out.println("listening on " + port); server.listen(); } catch (IOException e) { e.printStackTrace(); } } }
public class HelloClient { static InetSocketAddress ip = new InetSocketAddress("localhost", 8888); static ByteBuffer byteBuffer = ByteBuffer.allocate(1024); static class Message implements Runnable { protected String name; String msg = ""; public Message(String index) { this.name = index; } public void run() { try { long start = System.currentTimeMillis(); // 打开Socket通道 SocketChannel client = SocketChannel.open(); // 设置为非阻塞模式 client.configureBlocking(false); // 打开选择器 Selector selector = Selector.open(); // 注册连接服务端socket动作 client.register(selector, SelectionKey.OP_CONNECT); // 连接 client.connect(ip); // 分配内存 ByteBuffer buffer = ByteBuffer.allocate(1); int total = 0; _FOR: for (;;) { selector.select(); Iterator iter = selector.selectedKeys().iterator(); while (iter.hasNext()) { SelectionKey key = (SelectionKey) iter.next(); iter.remove(); if (key.isConnectable()) { SocketChannel channel = (SocketChannel) key .channel(); if (channel.isConnectionPending()) channel.finishConnect(); byte [] aaa = name.getBytes(); channel.write(buffer.wrap(aaa)); //写完可以监听读,server的响应就可以在下个select()时段检测到了 channel.register(selector, SelectionKey.OP_READ); } else if (key.isReadable()) { SocketChannel channel = (SocketChannel) key .channel(); int count = channel.read(buffer); if (count > 0) { total += count; buffer.flip(); while (buffer.remaining() > 0) { byte b = buffer.get(); msg += (char) b; } buffer.clear(); } else { client.close(); break _FOR; } } } } double last = (System.currentTimeMillis() - start) * 1.0 / 1000; System.out.println(msg + "used time :" + last + "s."); msg = ""; } catch (IOException e) { e.printStackTrace(); } } } public static void main(String[] args) throws IOException { String names[] = new String[10]; for (int index = 0; index < 10; index++) { names[index] = "jeff[" + index + "]"; new Thread(new Message(names[index])).start(); } } }
基本代码解释都有,需要注意的是:
1 selector的selectedKeys()方法返回的是上次select()方法调用后到现在为止的放入到就绪集里面的SelectionKey
,这个跟SelectionKey的interest和ready集合是没有关系的
2.用完的SelectionKey必须从就绪集里面删除,不然的话会有堆积的
3.在调用SelectableChannel.register方法时,就是channel和selector进行关联的时候,此时SelectionKey的interest和ready集合是一致的
当调用selector的select()方法,查看api知道需要做的事情如下:
对应步骤2我的理解是:对应一个selectionKey,如果就绪集合里面有的话,只是把这个key的状态和集合里面这个key已有的状态或操作,集合没有的话就直接set进去.
下面看NioReceiver的代码:
NioReceiver实现了Runnable接口,肯定会在线程中被执行,我们看他的run()方法:
调用了listen()方法,看代码:
前面是设置了一些标志位,接下来调用了events()方法,执行NioReplicationTask的里面的一些任务的一个列表。
每一个event都是Runnable,比较怪的是在events()方法中,执行的是runnable的run()的方法,也不是线程的执行方 法.
接着是socketTimeouts()方法调用,关闭一些已经超时的channel,接着就是根据SelectionKey的ready集合进行操作,包括注册channel,读channel的数据,之后如果NioReceiver不需要监听,进行一些资源的关闭和回收
看下readDataFromSocket()方法的定义:
protected void readDataFromSocket(SelectionKey key) throws Exception { NioReplicationTask task = (NioReplicationTask) getTaskPool().getRxTask(); if (task == null) { // No threads/tasks available, do nothing, the selection // loop will keep calling this method until a // thread becomes available, the thread pool itself has a waiting mechanism // so we will not wait here. if (log.isDebugEnabled()) log.debug("No TcpReplicationThread available"); } else { // invoking this wakes up the worker thread then returns //add task to thread pool task.serviceChannel(key); getExecutor().execute(task); } }
首先从一个资源池中拿到NioReplicationTask,有可能资源池没有空闲资源,此时返回null,那么这次读就丢失了,不过没有关系,readDataFromSocket()方法是在一个循环中,会一直调用这个方法,只是这次的数据丢失了,如果返回不为空,则执行这个Task.
比较经典的是:在这个Task执行前,传入的SelectionKey的OP_READ事件被屏蔽,在这个方法中数据读完后,重新开时这个事件,这里不是很明白,暂且认为,这样子SelectionKey带的数据不会被下一次的数据所污染,因为下一次通道发的数据在下一次select的时候是不会在SelectionKey的interest集合里面的,也就不可能在ready集合里面了。
最后面只是发送一个ACK的应答数据.
相比自己写的例子和tomcat的例子,差距在细节,包括异常的处理,各种临界状态的处理,线程的同步和阻塞唤醒。写一个主体常常很容易,但是各个细节的考虑却是功力的体现
- NIO基础代码例子
- BIO, NIO和 AIO的代码例子
- NIO例子
- Java NIO框架Netty(二)netty5例子,代码详解
- NIO基础
- NIO基础
- NIO基础
- NIO基础
- NIO一些例子
- NIO例子《二》
- NIO 简单交互例子
- Java NIO 简单例子
- java nio 使用例子
- Java nio几个例子
- java7 nio 例子
- Java Nio简单例子
- Java NIO简单例子
- NIO的简单例子
- 重新生成或者删除出错的IDOC
- Java中 初始化的先后顺序?
- 11_自定义样式style
- 变量与延迟变量
- 色 彩 RGB 值 对 照 表
- NIO基础代码例子
- Form Grid
- Oracle 11g 并行DML
- Java 相关优秀书籍推荐(尚学堂推荐)
- 开博.
- 删除IDOC
- 收藏的Android非常好用的组件或者框架。
- ALE 后台配置
- 大数据架构:flume-ng+Kafka+Storm+HDFS 实时系统组合