Java NIO应用实例

同步非阻塞IO (NIO

  • NIO是基于事件驱动思想的,实现上通常采用Reactor(http://en.wikipedia.org/wiki/Reactor_pattern)模式,从程序角度而言,当发起IO的读或写操作时,是非阻塞的;当socket有流可读或可写入socket时,操作系统会相应的通知引用程序进行处理,应用再将流读取到缓冲区或写入操作系统。
  • 对于网络IO而言,主要有连接建立、流读取及流写入三种事件、linux2.6以后的版本使用epoll(http://lse.sourceforge.net/epoll/index.html)方式实现NIO。
  • select/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。它的基本原理就是select/epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。
  • 当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。用select的优势在于它可以同时处理多个connection。

server:

  1 package org.windwant.nio;
  2
  3 import java.io.IOException;
  4 import java.net.InetSocketAddress;
  5 import java.net.ServerSocket;
  6 import java.nio.ByteBuffer;
  7 import java.nio.channels.SelectionKey;
  8 import java.nio.channels.Selector;
  9 import java.nio.channels.ServerSocketChannel;
 10 import java.nio.channels.SocketChannel;
 11 import java.util.Iterator;
 12 import java.util.Set;
 13
 14 /**
 15  * ServerSocketChannel
 16  */
 17 public class NIOServer {
 18     /*标识数字*/
 19     private int flag = 0;
 20     /*缓冲区大小*/
 21     private int BLOCK = 2048;
 22     /*接受数据缓冲区*/
 23     private ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);
 24     /*发送数据缓冲区*/
 25     private ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);
 26     private Selector selector;
 27
 28     public NIOServer(int port) throws IOException {
 29         // 打开服务器套接字通道
 30
 31         ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
 32         // 服务器配置为非阻塞
 33
 34         serverSocketChannel.configureBlocking(false);
 35         // 检索与此通道关联的服务器套接字
 36
 37         ServerSocket serverSocket = serverSocketChannel.socket();
 38         // 进行服务的绑定
 39
 40         serverSocket.bind(new InetSocketAddress(port));
 41         // 通过open()方法找到Selector
 42
 43         selector = Selector.open();
 44         // 注册到selector,等待连接
 45
 46         serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
 47         System.out.println("Server Start----8888:");
 48     }
 49
 50
 51     // 监听
 52
 53     private void listen() throws IOException {
 54         while (true) {
 55             // 选择一组键,并且相应的通道已经打开
 56
 57             selector.select();
 58             // 返回此选择器的已选择键集。
 59
 60             Set<SelectionKey> selectionKeys = selector.selectedKeys();
 61             Iterator<SelectionKey> iterator = selectionKeys.iterator();
 62             while (iterator.hasNext()) {
 63                 SelectionKey selectionKey = iterator.next();
 64                 iterator.remove();
 65                 handleKey(selectionKey);
 66             }
 67         }
 68     }
 69
 70     // 处理请求
 71
 72     private void handleKey(SelectionKey selectionKey) throws IOException {
 73         // 接受请求
 74
 75         ServerSocketChannel server = null;
 76         SocketChannel client = null;
 77         String receiveText;
 78         String sendText;
 79         int count=0;
 80         // 测试此键的通道是否已准备好接受新的套接字连接。
 81
 82         if (selectionKey.isAcceptable()) {
 83             // 返回为之创建此键的通道。
 84
 85             server = (ServerSocketChannel) selectionKey.channel();
 86             // 接受到此通道套接字的连接。
 87
 88             // 此方法返回的套接字通道(如果有)将处于阻塞模式。
 89
 90             client = server.accept();
 91             // 配置为非阻塞
 92
 93             client.configureBlocking(false);
 94             // 注册到selector,等待连接
 95
 96             client.register(selector, SelectionKey.OP_READ);
 97         } else if (selectionKey.isReadable()) {
 98             // 返回为之创建此键的通道。
 99
100             client = (SocketChannel) selectionKey.channel();
101             //将缓冲区清空以备下次读取
102
103             receivebuffer.clear();
104             //读取服务器发送来的数据到缓冲区中
105
106             count = client.read(receivebuffer);
107             if (count > 0) {
108                 receiveText = new String( receivebuffer.array(),0,count);
109                 System.out.println("服务器端接受客户端数据--:"+receiveText);
110                 client.register(selector, SelectionKey.OP_WRITE);
111             }
112         } else if (selectionKey.isWritable()) {
113             //将缓冲区清空以备下次写入
114
115             sendbuffer.clear();
116             // 返回为之创建此键的通道。
117
118             client = (SocketChannel) selectionKey.channel();
119
120
121             sendText = "<h1>message from server: this is the test message!</h1>";
122
123 //            sendText="message from server--" + flag++;
124             //向缓冲区中输入数据
125
126             sendbuffer.put(sendText.getBytes());
127             //将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位
128
129             sendbuffer.flip();
130             //输出到通道
131
132             client.write(sendbuffer);
133             System.out.println("服务器端向客户端发送数据--:"+sendText);
134             client.register(selector, SelectionKey.OP_READ);
135         }
136     }
137
138     /**
139      * @param args
140      * @throws IOException
141      */
142     public static void main(String[] args) throws IOException {
143         int port = 8888;
144         NIOServer server = new NIOServer(port);
145         server.listen();
146     }
147 }

client:

  1 package org.windwant.nio;
  2
  3 import java.io.IOException;
  4 import java.net.InetSocketAddress;
  5 import java.nio.ByteBuffer;
  6 import java.nio.channels.SelectionKey;
  7 import java.nio.channels.Selector;
  8 import java.nio.channels.SocketChannel;
  9 import java.util.Iterator;
 10 import java.util.Set;
 11
 12 /**
 13  * SocketChannel
 14  */
 15 public class NIOClient {
 16     /*标识数字*/
 17     private static int flag = 0;
 18     /*缓冲区大小*/
 19     private static int BLOCK = 4096;
 20     /*接受数据缓冲区*/
 21     private static ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);
 22     /*发送数据缓冲区*/
 23     private static ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);
 24     /*服务器端地址*/
 25     private final static InetSocketAddress SERVER_ADDRESS = new InetSocketAddress(
 26             "localhost", 8888);
 27
 28     public static void main(String[] args) throws IOException {
 29         // 打开socket通道
 30
 31         SocketChannel socketChannel = SocketChannel.open();
 32         // 设置为非阻塞方式
 33
 34         socketChannel.configureBlocking(false);
 35         // 打开选择器
 36
 37         Selector selector = Selector.open();
 38         // 注册连接服务端socket动作
 39
 40         socketChannel.register(selector, SelectionKey.OP_CONNECT);
 41         // 连接
 42
 43         socketChannel.connect(SERVER_ADDRESS);
 44         // 分配缓冲区大小内存
 45
 46
 47         Set<SelectionKey> selectionKeys;
 48         Iterator<SelectionKey> iterator;
 49         SelectionKey selectionKey;
 50         SocketChannel client;
 51         String receiveText;
 52         String sendText;
 53         int count=0;
 54
 55         while (true) {
 56             //选择一组键,其相应的通道已为 I/O 操作准备就绪。
 57
 58             //此方法执行处于阻塞模式的选择操作。
 59
 60             selector.select();
 61             //返回此选择器的已选择键集。
 62
 63             selectionKeys = selector.selectedKeys();
 64             //System.out.println(selectionKeys.size());
 65
 66             iterator = selectionKeys.iterator();
 67             while (iterator.hasNext()) {
 68                 selectionKey = iterator.next();
 69                 if (selectionKey.isConnectable()) {
 70                     System.out.println("client connect");
 71                     client = (SocketChannel) selectionKey.channel();
 72                     // 判断此通道上是否正在进行连接操作。
 73
 74                     // 完成套接字通道的连接过程。
 75
 76                     if (client.isConnectionPending()) {
 77                         client.finishConnect();
 78                         System.out.println("完成连接!");
 79                         sendbuffer.clear();
 80                         sendbuffer.put("Hello,Server".getBytes());
 81                         sendbuffer.flip();
 82                         client.write(sendbuffer);
 83                     }
 84                     client.register(selector, SelectionKey.OP_READ);
 85                 } else if (selectionKey.isReadable()) {
 86                     client = (SocketChannel) selectionKey.channel();
 87                     //将缓冲区清空以备下次读取
 88
 89                     receivebuffer.clear();
 90                     //读取服务器发送来的数据到缓冲区中
 91
 92                     count=client.read(receivebuffer);
 93                     if(count>0){
 94                         receiveText = new String( receivebuffer.array(),0,count);
 95                         System.out.println("客户端接受服务器端数据--:"+receiveText);
 96                         client.register(selector, SelectionKey.OP_WRITE);
 97                     }
 98
 99                 } else if (selectionKey.isWritable()) {
100                     sendbuffer.clear();
101                     client = (SocketChannel) selectionKey.channel();
102                     sendText = "message from client--" + (flag++);
103                     sendbuffer.put(sendText.getBytes());
104                     //将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位
105
106                     sendbuffer.flip();
107                     client.write(sendbuffer);
108                     System.out.println("客户端向服务器端发送数据--:"+sendText);
109                     client.register(selector, SelectionKey.OP_READ);
110                 }
111             }
112             selectionKeys.clear();
113         }
114     }
115 }

项目地址:https://github.com/windwant/nio-test

时间: 2024-09-30 19:16:41

Java NIO应用实例的相关文章

Java NIO原理及实例

Java NIO是在jdk1.4开始使用的,它既可以说成“新I/O”,也可以说成非阻塞式I/O.下面是java NIO的工作原理: 1. 由一个专门的线程来处理所有的 IO 事件,并负责分发. 2. 事件驱动机制:事件到的时候触发,而不是同步的去监视事件. 3. 线程通讯:线程之间通过 wait,notify 等方式通讯.保证每次上下文切换都是有意义的.减少无谓的线程切换. (1)Java NIO的服务端只需启动一个专门的线程来处理所有的 IO 事件,这种通信模型是怎么实现的呢? java NI

java NIO socket 通信实例

java Nio 通信与Bio通信主要不同点: 1.Nio中的单个channel即可支持读操作也可以支持写操作,而bio中读操作要用inputstream,写操作要outputstream. 2.nio 采用byteBuffer 作为内存缓存区,向channel里写或者度操作,bio基本是用byte[] 3.nio采用 selector组件轮询读取就绪channel 服务端demo代码: package com.my.socket3; import java.io.ByteArrayOutput

Java nio socket与as3 socket(粘包解码)连接的应用实例

对Java nio socket与as3 socket连接的简单应用 <ignore_js_op>Java nio socket与as3 socket连接的应用实例.rar (9.61 KB, 下载次数: 1691) 这个从基本的弄起太复杂了,我弄个了mina与flash通信的,通信数据模式是dataLength+data(数据长度+数据内容),对于socket字节流通信,我习惯用这种方式,方便粘包解码处理.其中已包含所需的jar包,客户端代码须另自写,不可沿用.flash socket发送的

Java NIO(六) Selector

Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件.这样,一个单独的线程可以管理多个channel,从而管理多个网络连接. 下面是本文所涉及到的主题列表: 为什么使用Selector? Selector的创建 向Selector注册通道 SelectionKey 通过Selector选择通道 wakeUp() close() 完整的示例 为什么使用Selector? 仅用单个线程来处理多个Channels的好处是,只需要更少的

Java NIO (二) 缓冲区(Buffer)

缓冲区(Buffer):一个用于特定基本数据类型的容器,由 java.nio 包定义的,所有缓冲区都是 Buffer 抽象类的子类. Java NIO 中的Buffer 主要用于和NIO中的通道(Channel)进行交互, 数据从通道(Channel)读入缓冲区(Buffer)或者从缓冲区(Buffer)写入通道(Channel).如下,我画的一个简图,Chanenl直接和数据源或者目的位置接触,Buffer作为中介这,从一个Channel中读取数据,然后将数据写入另一个Channel中. Bu

Java NIO 系列教程(转)

原文中说了最重要的3个概念,Channel 通道Buffer 缓冲区Selector 选择器其中Channel对应以前的流,Buffer不是什么新东西,Selector是因为nio可以使用异步的非堵塞模式才加入的东西.以前的流总是堵塞的,一个线程只要对它进行操作,其它操作就会被堵塞,也就相当于水管没有阀门,你伸手接水的时候,不管水到了没有,你就都只能耗在接水(流)上.nio的Channel的加入,相当于增加了水龙头(有阀门),虽然一个时刻也只能接一个水管的水,但依赖轮换策略,在水量不大的时候,各

Java NIO之缓冲

一.前言 在笔者打算学习Netty框架时,发现很有必要先学习NIO,因此便有了本博文,首先介绍的是NIO中的缓冲. 二.缓冲 2.1 层次结构图 除了布尔类型外,其他基本类型都有相对应的缓冲区类,其继承关系层次图如下. 其中,Buffer是所有类的父类,Buffer中也规定了所有缓冲区的共同行为. 2.2 缓冲区基础 缓冲区是包在一个对象内的基本数据元素数组,其有四个重要属性 容量( Capacity):缓冲区能够容纳的数据元素的最大数量,容量在缓冲区创建时被设定,并且永远不能被改变. 上界(L

Java NIO try-with-resources [ 转载 ]

Java NIO try-with-resources [ 转载 ] @原文链接 http://blog.csdn.net/jackiehff/article/details/17765909     try-with-resources语句是一个声明一个或多个资源的 try 语句.一个资源作为一个对象,必须在程序结束之后随之关闭. try-with-resources语句确保在语句的最后每个资源都被关闭 .任何实现了 java.lang.AutoCloseable的对象, 包括所有实现了 ja

Java NIO FileChannel

Java NIO中的FileChannel是一个连接到文件的通道.可以通过文件通道读写文件. FileChannel无法设置为非阻塞模式,它总是运行在阻塞模式下. 打开FileChannel 在使用FileChannel之前,必须先打开它.但是,我们无法直接打开一个FileChannel,需要通过使用一个InputStream.OutputStream或RandomAccessFile来获取一个FileChannel实例.下面是通过RandomAccessFile打开FileChannel的示例