java nio小结

根据网上的资料做些整理

Java NIO API详解

http://www.blogjava.net/19851985lili/articles/93524.html

这篇文章对nio的api讲解比较全,可以帮助在宏观上把握nio。

BIO 方式使得整个处理过程和连接是绑定的,只要连接建立,无论客户端是否有消息发送,都要进行等待处理,一定程度上浪费了服务器端的硬件资源,因此就有了NIO 方式。Java 对于 NIO 方式的支持是通过 Channel和 Selector 方式来实现,采用的方法为向 Channel注册感兴趣的事件,然后通过 Selector 来获取到发生了事件的 key,如发生了相应的事件,则进行相应的处理,否则则不做任何处理,是典型的Reactor 模式,按照这样的方式,就不用像 BIO 方式一样,即使在没有消息的情况下也需要占据一个线程来阻塞读取消息,从而提升服务器的使用效率, 为实现 TCP/IP+NIO 方式的系统间通讯, Java 提供了 SocketChannel和 ServerSocketChannel两个关键的类,网络 IO 的操作则改为通过ByteBuffer 来实现,具体的基于 java实现TCP/IP+NIO 方式的通讯的方法如下所示。

服务器端:

package com.flyoung;

import java.io.IOException;
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.util.Iterator;
import java.util.Set;
import java.nio.channels.SocketChannel;

public class NIOServer {
    /*标志数字*/
    private static int flag = 0;
    /*定义缓冲区大小*/
    private static int block = 4096;
    /*接收缓冲区*/
    private static ByteBuffer receiveBuffer = ByteBuffer.allocate(block);
    /*发送缓冲区*/
    private static ByteBuffer sendBuffer = ByteBuffer.allocate(block);
    /*定义Selector*/
    private Selector selector;
    
    public NIOServer(int port) throws IOException{
        //打开服务器套接字通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //服务器配置为非阻塞
        serverSocketChannel.configureBlocking(false);
        //检索与此服务器套接字通道关联的套接字
        ServerSocket serverSocket = serverSocketChannel.socket();
        //进行服务的绑定
        serverSocket.bind(new InetSocketAddress(port));
        //通过open()方法找到Selector
        selector = Selector.open();
        //注册到selector
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("Server Start -----8888:");
    }
    //监听
    public void listen() throws IOException{
        while(true){
            //监控所有注册的 channel ,当其中有注册的 IO 操作可以进行时,该函数返回,并将对应的 SelectionKey 加入 selected-key set
            selector.select();
            //Selected-key set 代表了所有通过 select() 方法监测到可以进行 IO 操作的 channel ,这个集合可以通过 selectedKeys() 拿到
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while(iterator.hasNext()){
                SelectionKey selectionKey = iterator.next();
                handleKey(selectionKey);
                iterator.remove();
            }
        }
        
    }
    //处理请求
    public void handleKey(SelectionKey selectionKey) throws IOException{
        //接受请求
        ServerSocketChannel serverSocketChannel = null;
        SocketChannel socketChannel = null;
        String receiveText;
        String sendText;
        int count;
        //测试此键的通道是否准备好接受新的套接字连接
        if(selectionKey.isAcceptable()){
            //返回创建此键的通道
            serverSocketChannel = (ServerSocketChannel)selectionKey.channel();
            //接受客户端建立连接的请求,并返回 SocketChannel 对象
            socketChannel = serverSocketChannel.accept();
            //配置为非阻塞
            socketChannel.configureBlocking(false);
            //注册到selector
            socketChannel.register(selector, SelectionKey.OP_READ);
        }else if(selectionKey.isReadable()){
            //返回为之创建此键的通道
            socketChannel = (SocketChannel)selectionKey.channel();
            //将缓冲区清空,以备下次读取
            receiveBuffer.clear();
            //将发送来的数据读取到缓冲区
            
            count = socketChannel.read(receiveBuffer);
        
            
            if(count>0){
                receiveText = new String(receiveBuffer.array(),0,count);
                System.out.println("服务器端接受到的数据---"+receiveText);
                socketChannel.register(selector, SelectionKey.OP_WRITE);
            }
        }else if (selectionKey.isWritable()) {  
            //将缓冲区清空以备下次写入  
            sendBuffer.clear();  
            // 返回为之创建此键的通道。  
            socketChannel = (SocketChannel) selectionKey.channel();  
            sendText="message from server--" + flag++;  
            //向缓冲区中输入数据  
            sendBuffer.put(sendText.getBytes());  
             //将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位  
            sendBuffer.flip();  
            //输出到通道  
            socketChannel.write(sendBuffer);  
            System.out.println("服务器端向客户端发送数据--:"+sendText);  
            socketChannel.register(selector, SelectionKey.OP_READ);  
        }  
        
    }
    public static void main(String[] args) throws IOException {
        int port = 8888; 
        NIOServer server = new NIOServer(port);
        server.listen();
    }

}

客户端

package com.flyoung;

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.SocketChannel;
import java.util.Set;

public class NIOClient {
    /*标识数字*/  
    private static int flag = 0;  
    /*缓冲区大小*/  
    private static int BLOCK = 4096;  
    /*接受数据缓冲区*/  
    private static ByteBuffer sendBuffer = ByteBuffer.allocate(BLOCK);  
    /*发送数据缓冲区*/  
    private static ByteBuffer receiveBuffer = ByteBuffer.allocate(BLOCK);  
    /*服务器端地址*/  
    private final static InetSocketAddress SERVER_ADDRESS = new InetSocketAddress(  
            "localhost", 8888);  
  
    public static void main(String[] args) throws IOException {  
        // 打开socket通道  
        SocketChannel clientChannel = SocketChannel.open();  
        // 设置为非阻塞方式  
        clientChannel.configureBlocking(false);  
        // 打开选择器  
        Selector selector = Selector.open();  
        // 注册连接服务端socket动作  
        clientChannel.register(selector, SelectionKey.OP_CONNECT);  
        // 连接  
        clientChannel.connect(SERVER_ADDRESS);  
    
        SocketChannel socketChannel;
        Set<SelectionKey> selectionKeys;    
        String receiveText;  
        String sendText;  
        int count=0;  
  
        while (true) {  
            //选择一组键,其相应的通道已为 I/O 操作准备就绪。  
            //监控所有注册的 channel ,当其中有注册的 IO 操作可以进行时,该函数返回,并将对应的 SelectionKey 加入 selected-key set 
            selector.select();  
            //返回此选择器的已选择键集。  
            selectionKeys = selector.selectedKeys();  
            //System.out.println(selectionKeys.size());  
            for(SelectionKey selectionKey:selectionKeys){ 
                //判断是否为建立连接的事件
                if (selectionKey.isConnectable()) {  
                    System.out.println("client connect");  
                    socketChannel = (SocketChannel) selectionKey.channel();  //
                    // 判断此通道上是否正在进行连接操作。  
                    // 完成套接字通道的连接过程。  
                    if (socketChannel.isConnectionPending()) { 
                        //完成连接的建立(TCP三次握手)
                        socketChannel.finishConnect();  
                        System.out.println("完成连接!");  
                        sendBuffer.clear();  
                        sendBuffer.put("Hello,Server".getBytes());  
                        sendBuffer.flip();  
                        socketChannel.write(sendBuffer);  
                    }  
                    socketChannel.register(selector, SelectionKey.OP_READ);  
                } else if (selectionKey.isReadable()) {  
                    socketChannel = (SocketChannel) selectionKey.channel();  
                    //将缓冲区清空以备下次读取  
                    receiveBuffer.clear();  
                    //读取服务器发送来的数据到缓冲区中  
                    count=socketChannel.read(receiveBuffer);  
                    if(count>0){  
                        receiveText = new String( receiveBuffer.array(),0,count);  
                        System.out.println("客户端接受服务器端数据--:"+receiveText);  
                        socketChannel.register(selector, SelectionKey.OP_WRITE);  
                    }  
  
                } else if (selectionKey.isWritable()) {  
                    sendBuffer.clear();  
                    socketChannel = (SocketChannel) selectionKey.channel();  
                    sendText = "message from client--" + (flag++);  
                    sendBuffer.put(sendText.getBytes());  
                     //将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位  
                    sendBuffer.flip();  
                    socketChannel.write(sendBuffer);  
                    System.out.println("客户端向服务器端发送数据--:"+sendText);  
                    socketChannel.register(selector, SelectionKey.OP_READ);  
                }  
            }  
            selectionKeys.clear();  
        }  
    }  
}
小结:之前对Selector注册事件和SocketChannel有点小困惑。SocketChannel就像一根水管,当监听到写事件时,就往管道写数据;当监听到读事件时,就从管道读出数据。


阅读更多
个人分类: Java
想对作者说点什么? 我来说一句

java NIOjava并发编程的书籍

2011年11月13日 3.87MB 下载

JavaNIO chm帮助文档

2017年10月09日 163KB 下载

JAVA NIO 实现网络编程

2011年09月27日 188KB 下载

java NIO文档

2017年12月20日 1.81MB 下载

java nio 包读取超大数据文件

2010年12月19日 26KB 下载

java NIO 中文版

2012年03月06日 2.12MB 下载

java nio 读文件

2011年12月21日 901B 下载

没有更多推荐了,返回首页

不良信息举报

java nio小结

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭