NIO实例

Aug 14, 2016


NIO服务端客户端简单实现

  1. 客服端不断的向nio服务端发送消息、接收消息;
  2. 服务端不断的向nio服务端发送消息、接收消息;

NIO服务端

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;

public class NioService {
    
    private static final int BUF_SIZE=1024;
    private static final int PORT = 9070;
    private static final int TIMEOUT = 3000;

    public static void main(String[] args)
    {
        selector();
    }

    //处理新连接
    public static void handleAccept(SelectionKey key) throws IOException{
        ServerSocketChannel ssChannel = (ServerSocketChannel)key.channel();
        SocketChannel sc = ssChannel.accept();
        sc.configureBlocking(false);
        sc.register(key.selector(), SelectionKey.OP_READ,ByteBuffer.allocateDirect(BUF_SIZE));
    }

    //处理客户端发送过来的消息
    public static void handleRead(SelectionKey key) throws IOException{
        SocketChannel sc = (SocketChannel)key.channel();
        ByteBuffer buf = (ByteBuffer)key.attachment();
        long bytesRead = sc.read(buf);
        while(bytesRead>0){
            buf.flip();
            while(buf.hasRemaining()){
                System.out.print((char)buf.get());
            }
            System.out.println();
            buf.clear();
            bytesRead = sc.read(buf);
        }
        key.interestOps(key.interestOps()|SelectionKey.OP_WRITE);//此时注册写事件
        if(bytesRead == -1){
            sc.close();
        }
    }

    //向客户端发送消息
    public static int i = 0;
    public static void handleWrite(SelectionKey key) throws IOException{
        ByteBuffer buf = (ByteBuffer)key.attachment();
        SocketChannel sc = (SocketChannel) key.channel();
        String info = "I'm "+i+++"-th information from server";
        buf.clear();
        buf.put(info.getBytes());
        buf.flip();
        while(buf.hasRemaining()){
            sc.write(buf);
        }
        buf.clear();
        key.interestOps(key.interestOps()&~SelectionKey.OP_WRITE);//移除注册的写事件
    }

    public static void selector() {
        Selector selector = null;
        ServerSocketChannel ssc = null;
        try{
            selector = Selector.open();//打开Selector
            ssc= ServerSocketChannel.open();//打开ServerSocketChannel
            ssc.socket().bind(new InetSocketAddress(PORT));//Socket绑定端口
            ssc.configureBlocking(false);//非阻塞
            ssc.register(selector, SelectionKey.OP_ACCEPT);//向Selector注册Channel

            while(true){
                if(selector.select(TIMEOUT) == 0){
                    continue;
                }
                Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
                while(iter.hasNext()){
                    SelectionKey key = iter.next();
                    if(key.isAcceptable()){
                        handleAccept(key);
                    }
                    if(key.isReadable()){
                        handleRead(key);
                    }
                    if(key.isWritable() && key.isValid()){
                        handleWrite(key);
                    }
                    if(key.isConnectable()){
                        System.out.println("isConnectable = true");
                    }
                    iter.remove();
                }
            }

        }catch(IOException e){
            e.printStackTrace();
        }finally{
            try{
                if(selector!=null){
                    selector.close();
                }
                if(ssc!=null){
                    ssc.close();
                }
            }catch(IOException e){
                e.printStackTrace();
            }
        }
    }
}

NIO客户端

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.concurrent.TimeUnit;


public class NioClient {

    public static void main(String[] args) {
         ByteBuffer buffer = ByteBuffer.allocate(1024);
            SocketChannel socketChannel = null;
            try
            {
                socketChannel = SocketChannel.open();
                socketChannel.configureBlocking(false);
                socketChannel.connect(new InetSocketAddress("localhost",9070));

                if(socketChannel.finishConnect())
                {
                    int i=0;
                    while(true)
                    {
                        //客户端向服务端发送消息
                        TimeUnit.SECONDS.sleep(1);
                        String info = "I'm "+i+++"-th information from client";
                        buffer.clear();
                        buffer.put(info.getBytes());
                        buffer.flip();
                        while(buffer.hasRemaining()){
                            socketChannel.write(buffer);//发送消息
                        }
                        
                        //客户端从服务端接收消息
                        TimeUnit.SECONDS.sleep(1);
                        buffer.clear();
                        socketChannel.read(buffer);//接收消息
                        buffer.flip();
                        while(buffer.hasRemaining()){
                            System.out.print((char)buffer.get());
                        }
                        System.out.println();
                    }
                }
            }
            catch (IOException | InterruptedException e)
            {
                e.printStackTrace();
            }
            finally{
                try{
                    if(socketChannel!=null){
                        socketChannel.close();
                    }
                }catch(IOException e){
                    e.printStackTrace();
                }
            }
    }

}

NIO就绪处理之OP_WRITE

写就绪相对有一点特殊,一般来说,你不应该注册写事件。写操作的就绪条件为底层缓冲区有空闲空间,而写缓冲区绝大部分时间都是有空闲空间的,所以当你注册写事件后,写操作一直是就绪的,选择处理线程全占用整个CPU资源。所以,只有当你确实有数据要写时再注册写操作,并在写完以后马上取消注册。

  1. 在收到客户端消息后,注册写事件;

    key.interestOps(key.interestOps() SelectionKey.OP_WRITE);//此时注册写事件
  2. 服务端消息发送完毕,移除写事件。

    key.interestOps(key.interestOps()&~SelectionKey.OP_WRITE);//移除注册的写事件

NIO就绪处理之OP_WRITE


上一篇博客:java8 lambda & effectively final
下一篇博客:JDK1.7 Fork Join