天天看點

NIO 初體驗遇到的坑記錄一下

服務端代碼:

坑: 因為寫事件在selector中會一直會true, 是以會一直向client寫東西,是以要在寫事件完成後,取消寫事件。

package com.nio.yubo;

import java.io.IOException;

import java.net.InetSocketAddress;

import java.net.ServerSocket;

import java.nio.ByteBuffer;

import java.nio.channels.*;

import java.util.Iterator;

import java.util.Set;

public class NioServer {

    public NioServer(int port) {

        this.port = port;

    }

    private int port;

    private Selector selector;    

    private ServerSocketChannel serverCh;

    private Selector getSelector() throws IOException

    {

        selector = Selector.open();

        serverCh = ServerSocketChannel.open();

        serverCh.configureBlocking(false);

        ServerSocket socket = serverCh.socket();

        InetSocketAddress address = new InetSocketAddress(port);

        socket.bind(address);

        serverCh.register(selector, SelectionKey.OP_ACCEPT);

        return selector;

    }

    public void listen()

    {

        System.out.println("Start to listen on port:" + this.port);

        while(true) 

        {

            try {

                selector.select();

                Set<SelectionKey> keys = selector.selectedKeys();

                Iterator<SelectionKey> it = keys.iterator();

                while(it.hasNext()) {

                    SelectionKey key = it.next();

                    it.remove();

                    process(key);

                }

            } catch (IOException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

        }

    }

    public  void process(SelectionKey key) {

        if(key.isAcceptable())

        {

            ServerSocketChannel server = (ServerSocketChannel) key.channel();

            SocketChannel channel = null;

            try {

                channel = server.accept();

            } catch (IOException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

            try {

                channel.configureBlocking(false);

            } catch (IOException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

            try {

                channel.register(selector, SelectionKey.OP_READ);

            } catch (ClosedChannelException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

        }

        else if(key.isReadable()) {

            //fix me

            SocketChannel channel = (SocketChannel)key.channel();

            ByteBuffer buffer = ByteBuffer.allocate(1024);

            try {

                int len = channel.read(buffer);

                if(len > 0) {

                    buffer.flip();

                    System.out.println("Content len is:" + len + "pos is: " + buffer.position() + "limit:" + buffer.limit() + "ca:" +buffer.capacity());

                    String content = new String(buffer.array());

                    System.out.println("Content in readable is:" + content);                    

                    SelectionKey skey = channel.register(selector, SelectionKey.OP_WRITE);

                    skey.attach(content); // 為一下次寫事件的key 準備資料

                }

                else

                {

                    channel.close();

                }

            } catch (IOException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

            buffer.clear();            

        }

        else if (key.isWritable()) {

            SocketChannel channel = (SocketChannel)key.channel();

            String content = (String)key.attachment();

            ByteBuffer block = ByteBuffer.wrap( ("hello:" + content ).getBytes());

            try {

                channel.write(block);

            } catch (IOException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

            finally

            {

                key.cancel(); //  這裡是取消寫事件

            }

        }

    }

    public static void main(String[] args) {

        // TODO Auto-generated method stub

        NioServer myServer = new NioServer(9999);

        try {

            myServer.getSelector();

            myServer.listen();

        } catch (IOException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

        }

    }

}

用戶端代碼:

坑:

在把内容寫到buffer 後,在發送到channel 前,要把buffer flip() 一下,不然position, limit的值會不對,發到server的東西是空的。

package com.nio.yubo;

import java.io.IOException;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.Selector;

import java.nio.channels.SocketChannel;

public class NioClient {

    Selector selector = null;

    SocketChannel channel = null;

    public void connect()

    {

        try {

            channel = (SocketChannel)SocketChannel.open();

            channel.connect(new InetSocketAddress( "127.0.0.1", 9999));

            ByteBuffer buffer = ByteBuffer.allocate(1024);

            buffer.put("my name is tom".getBytes());

            buffer.flip(); // 這裡一定要記得恢複positon到0

            if(buffer.hasRemaining())

            {

                System.out.println("Client send: " + new String(buffer.array()));

                channel.write(buffer);

            }

            buffer.clear();

            channel.read(buffer);

            buffer.flip();

            if(buffer.hasRemaining())

            {

                System.out.println(new String(buffer.array()));

            }

            buffer.clear();

            channel.close();

        } catch (IOException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

        }

    }

    public static void main(String[] args) {

        // TODO Auto-generated method stub

        new NioClient().connect();

    }

}