手写一个NIO传输实例

java socket编程大体有三个技术阶段,大致区别在于:

  • bio(java1) 阻塞同步 流式传输
  • nio(java 1.4) 非阻塞同步块传输 适应于轻量级连接 如聊天  项目上基本用框架:Netty、Mina
  • aio(java1.7) 异步非阻塞 适用于重量级 如相册服务

这里重点说一下NIO,实现它主要是由三个组件:1.selector(能够进行多路复用,一个线程可处理多个channel,极大减少线程数,读写线程数量推荐使用cpu线程数,充分利用cpu资源,减少线程切换带来的损失) 2.channel 3.buffer(相比较)。这三个组件都值得去查阅一番。NIO使用这三个组件的工作流程大致如下:

 

 

接下来直接上代码:

/**
 * @author wwd
 * @data 2019/10/30
 * @project MuiltiDataImport
 **/
public class NioTest {
    private static Charset charset = Charset.forName("utf-8");

    public static void main(String[] args) throws IOException {
        ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(4);
        //创建一个selector
        Selector selector = Selector.open();
        //创建channel 通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //绑定9200端口
        serverSocketChannel.bind(new InetSocketAddress(9200));
        //接下来需要serverSocketChannel绑定到selector
        //指定非阻塞的方式
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        //一个线程负责选择就绪的channel  这里的话就是主线程循环
        while (true) {
            //阻塞选择就绪的事件,select()可中断的
            int readyChannelcount = selector.select();
            if(readyChannelcount ==0){
                continue;
            }
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            //整个遍历器
            Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
            while (keyIterator.hasNext()){
                SelectionKey key = keyIterator.next();
                //连接进来了
                if(key.isAcceptable()){
                    System.out.println("连接进来了");
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    //注册到selector,用它来监测 注意ServerSocketChannel  和  SocketChannel是两个层次
                    socketChannel.configureBlocking(false);
                    //监测可读取
                    socketChannel.register(selector,SelectionKey.OP_READ);

                }else if(key.isReadable()){
                    System.out.println("数据发过来了,可以去读取了");
                    //交到线程池中去处理吧
                    newFixedThreadPool.submit(new SocketProcess(key));
                    //值得注意的是 这里由于使用的是线程池  可能处理的不及时(线程就绪等待等等),需要及时取消掉改
                    key.cancel();
                }else if(key.isWritable()){
                    System.out.println("数据可以发送出去了");
                }else if(key.isConnectable()){
                    System.out.println("我已经联通了其他的服务器了");

                }
                //处理完了就去掉
                keyIterator.remove();
            }


        }

    }
    //处理类
    static class SocketProcess implements  Runnable{
        SelectionKey key;
        public SocketProcess(SelectionKey key){
            super();
            this.key =key;
        }

        @Override
        public void run() {
            SocketChannel channel = (SocketChannel)key.channel();
            //读数据
            //1.创建buffer
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            try {
                //读取到buffer的数量 为-1 读完
                int readBuffer = channel.read(buffer);
                while (readBuffer!=-1){
                    //转换buffer模式,从写模式转换到读模式  读取其中的值 使得buffer可向外输出
                    //具体转换position和limit值
                    buffer.flip();
                    //看buffer缓冲还有没有东西了
                    while(buffer.hasRemaining()){
                        CharsetDecoder charsetDecoder = charset.newDecoder();
                        CharBuffer decode = charsetDecoder.decode(buffer);
                        System.out.println( decode.toString());
                       /* System.out.println((char)buffer.get());*/
                    }
                    //清除缓存  还有一个buffer.compact()整理已读的 未读到的提前
                    buffer.clear();
                    readBuffer=channel.read(buffer);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                channel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }


        }
    }
}

最后可以使用telnet命令进行简单的测试。结果如下:

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值