nio selector简单使用

Server端代码:

public class ServerSocket {

    public static final int PORT=9888;

    public static void main(String[] args){
        try {
            ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
            serverSocketChannel.socket().bind(new InetSocketAddress(PORT));
            Selector selector= Selector.open();
            serverSocketChannel.configureBlocking(false); //设置非阻塞
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            while(true){
                int n=selector.select();
                if(n==0){
                    continue;
                }
                Iterator<SelectionKey> it=selector.selectedKeys().iterator();
                while(it.hasNext()){
                    try {
                        SelectionKey key=it.next();
                        it.remove();
                        if(key.isAcceptable()){
                            ServerSocketChannel cl=(ServerSocketChannel)key.channel();
                            SocketChannel sc=cl.accept();  //接收到连接请求后,设置客户端channel为异步
                            sc.configureBlocking(false);
                            sc.register(selector,SelectionKey.OP_READ);
                        }
                        if(key.isConnectable()){
                            System.out.println("server channel is receive client connection");
                        }
                        if(key.isReadable()){
                            System.out.println("server channel isReadable enter");
                            ByteBuffer bf=ByteBuffer.allocate(1024);
                            SocketChannel clientChannel=(SocketChannel) key.channel();

                            boolean needShutdownChannel=readAndPrint(bf,clientChannel);
                            if(needShutdownChannel){
                                key.cancel();
                                clientChannel.close();
                                continue;
                            }

                            writeToClient(bf, clientChannel);
                        }
                        if(key.isWritable()){
                            System.out.println("enter isWritable ");
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void writeToClient(ByteBuffer bf, SocketChannel clientChannel) throws IOException {
        bf.put(("receiver client msg,return hello world,"+new Date()).getBytes());
        bf.flip();
        while(bf.hasRemaining()){
            clientChannel.write(bf);
        }
        bf.clear();
    }

    /**
     * 读取数据并且打印出来,这里有个问题,当客户端关闭连接后,主方法会一直进入key.isReadable,进入死循环,
     * 这里我自己判断,如果是第一次就读不到字节,关闭channel,
     * @param bf
     * @param clientChannel
     * @return
     * @throws IOException
     */
    private static boolean readAndPrint(ByteBuffer bf, SocketChannel clientChannel) throws IOException {
        try {
            int index=0;
            int n=-1;
            while((n=clientChannel.read(bf))>0){
                index++;
                bf.flip();
                while(bf.hasRemaining()){
                    byte[] b=new byte[bf.remaining()];//字节数组的长度要跟bf的长度一样,否则会抛异常
                    while(bf.hasRemaining()){
                        bf.get(b);
                    }
                    System.out.println(new String(b));
                }
                bf.clear();
            }

            if(n<=0 && index==0){
               return true;
            }
            return false;
        } catch (IOException e) {
            e.printStackTrace();
            return true;
        }

    }
}


client端代码:

public class ClientSocket {

    public static int serverPort=9888;

    public static void main(String[] args) throws InterruptedException {
        try {
            SocketChannel socketChannel=SocketChannel.open();
            Selector selector= Selector.open();
            socketChannel.configureBlocking(false);
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
            socketChannel.connect(new InetSocketAddress("192.168.3.27",serverPort));
            while(true){
                int n=selector.select();
                if(n==0){
                    continue;
                }
                Iterator<SelectionKey> it=selector.selectedKeys().iterator();
                while (it.hasNext()){
                    SelectionKey key=it.next();
                    it.remove();
                    if(key.isConnectable()){
                        ByteBuffer bf=ByteBuffer.allocate(1024);
                        SocketChannel clientChannel=(SocketChannel) key.channel();
                        //finishConnection校验是否连接成功,同步会阻塞直到成功或者抛出异常
                        //异步模式,会直接返回true|false
                        while(!clientChannel.finishConnect()){
                            Thread.sleep(100);
                        }
                        write(clientChannel, bf);
                        clientChannel.register(selector,SelectionKey.OP_READ);
                    }
                    if(key.isReadable()){
                        //获取channel
                        SocketChannel clientChannel=(SocketChannel) key.channel();
                        //初始化字节
                        ByteBuffer bf=ByteBuffer.allocate(1024);

                        readAndPrint(clientChannel,bf);

                        clientChannel.close(); //关闭channel
                        return;
                    }
                    if(key.isWritable()){
                        System.out.println("client isWritable enter");
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void write(SocketChannel clientChannel, ByteBuffer bf) throws IOException {
        bf.put(("hello server,"+new Date()).getBytes());
        bf.flip();
        while(bf.hasRemaining()){
            clientChannel.write(bf);
        }
        bf.clear();
    }

    private static void readAndPrint(SocketChannel clientChannel, ByteBuffer bf) throws IOException {
        while(clientChannel.read(bf)>0){
            bf.flip();
            while(bf.hasRemaining()){
                byte[] b=new byte[bf.remaining()];
                while(bf.hasRemaining()){
                    bf.get(b);
                }
                System.out.println(new String(b));
            }
            bf.clear();
        }

    }
}


这里有几个坑,在注释里面有的:

1 想把接收到的bytebuffer打印出来,定义的 byte[]数组接收,数组大小要跟bf.remaining()大小相等,要这么写 byte[] b=new byte[bf.remaining()];

2 客户端关闭连接后,server端 方法会一直进入 isReadable代码块

if(key.isReadable()){ }

只是这时候从ByteBuffer读到的数据长度是0 ,不是 -1

3客户端在 isConnectioned方法里面发送数据会抛异常,要先执行

clientChannel.finishConnect() 

但是看注释发现这个方法在 异步模式不代表一定会等待连接建立,连接没成功可能返回false,当然,也可能我英语不好,翻译不准确,

所以我只能按照下面的方式这么写

  1.  while(!clientChannel.finishConnect()){  
  2.         Thread.sleep(100);  
  3.  }  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值