【博学谷学习记录】超强总结,用心分享 | NIO网络编程

#博学谷IT技术支持#

前言

本章节包含NIO客户端服务端以及选择器的使用。


一、NIO概述

1. BIO与NIO区别

BIO:Blocking IO,阻塞型IO

NIO:Non-blocking IO,非阻塞型IO

阻塞IO的弊端:在等待客户端的过程中什么事也做不了。

非阻塞IO的好处:不需要一直等待,当一切就绪了再去做。

2. NIO三大模块

  • 缓冲区ByteBuffer:用来存储数据
  • 通道:用来建立连接和传输数据
  • 选择器Selector:监视通道状态

二、NIO通道应用

1. 客户端

  1. 打开通道:SocketChannel.open();
  2. 指定IP和端口:socketChannel.connect(ip,port);
  3. 写出数据:ByteBuffer.wrap();
  4. 读取服务器写回的数据:ByteBuffer.allocate(1024);
  5. 释放资源:socketChannel.close();
public class Clinet {
    public static void main(String[] args) throws IOException {
        SocketChannel socketChannel = SocketChannel.open();

        socketChannel.connect(new InetSocketAddress("127.0.0.1",10000));

        ByteBuffer byteBuffer1 = ByteBuffer.wrap("吃俺老孙一棒棒".getBytes());
        socketChannel.write(byteBuffer1);

        System.out.println("数据已经写给服务器");

        ByteBuffer byteBuffer2 = ByteBuffer.allocate(1024);
        int len;
        while((len = socketChannel.read(byteBuffer2)) != -1){
            System.out.println("客户端接收回写数据");
            byteBuffer2.flip();
            System.out.println(new String(byteBuffer2.array(),0,len));
            byteBuffer2.clear();
        }
        socketChannel.close();
    }
}

2. 服务端

  1. 打开服务端通道:ServerSocketChannel.open();
  2. 绑定端口:serverSocketChannel.bind(new InetSocketAddress(port));
  3. 通道设置为非阻塞:serverSocketChannel.configureBlocking(false);
  4. 如果有客户端来连接,在服务端通道内部创建一个客户端延伸通道。
  5. 获取客户端数据并放在byteBuffer中
  6. 给客户端回写数据:ByteBuffer.wrap();
  7. 释放资源:socketChannel.close();
public class Sever {
    public static void main(String[] args) throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        serverSocketChannel.bind(new InetSocketAddress(10000));

        serverSocketChannel.configureBlocking(false);

        while(true){
            SocketChannel socketChannel = serverSocketChannel.accept();
            if(socketChannel != null){
                System.out.println("此时有客户端来连接了");
              	// 将服务端内部获取的客户端通道设置为非阻塞的
                socketChannel.configureBlocking(false);
                //获取客户端传递过来的数据,并把数据放在byteBuffer1这个缓冲区中
                ByteBuffer byteBuffer1 = ByteBuffer.allocate(1024);
                //socketChannel.read(byteBuffer1);
                int len;
                //针对于缓冲区来讲
                    //如果 从添加数据 ----> 获取数据 flip
                    //如果 从获取数据 ----> 添加数据 clear
                while((len = socketChannel.read(byteBuffer1)) > 0){
                    System.out.println("服务端接收发送数据");
                    byteBuffer1.flip();
                    System.out.println(new String(byteBuffer1.array(),0,len));
                    byteBuffer1.clear();
                }

                System.out.println("接收数据完毕,准备开始往客户端回写数据");

                ByteBuffer byteBuffer2 = ByteBuffer.wrap("哎哟,真疼啊!!!".getBytes());
                socketChannel.write(byteBuffer2);

                socketChannel.close();
            }
        }
    }
}

三、NIO选择器

  • 选择器对象:Selector
  • 绑定的key:SelectionKey
  • 能使用选择器的通道:SelectableChannel,即SocketChannel和ServerSocketChannel

1. 改写服务端

  1. 打开服务端通道
  2. 绑定端口
  3. 通道设置为非阻塞
  4. 打开一个选择器
  5. 将选择器绑定服务端通道,并监视服务端是否准备好
  6. 如果有客户端连接,遍历所有服务端通道,在准备好的服务端通道内部创建客户端延伸通道
  7. 如果客户端数据传过来了,遍历所有延伸通道,准备好的通道去接收数据
public class Server {
    public static void main(String[] args) throws IOException {
        //1.打开服务端通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //2.让这个通道绑定一个端口
        serverSocketChannel.bind(new InetSocketAddress(10000));
        //3.设置通道为非阻塞
        serverSocketChannel.configureBlocking(false);
        //4.打开一个选择器
        //Selector --- 选择器
//        SelectionKey --- 绑定通道后返回那个令牌
  //      SelectableChannel --- 可以使用选择器的通道
        Selector selector = Selector.open();
        //5.绑定选择器和服务端通道
        serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);

        while(true){
            System.out.println("11");
            //选择器会监视客户端通道的状态.
            //6.返回值就表示此时有多少个客户端来连接.
            int count = selector.select();
            System.out.println("222");
            if(count != 0){
                System.out.println("有客户端来连接了");
                //7.会遍历所有的服务端通道.看谁准备好了,谁准备好了,就让谁去连接.
                //获取所有服务端通道的令牌,并将它们都放到一个集合中,将集合返回.
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while(iterator.hasNext()){
                    //selectionKey 依次表示每一个服务端通道的令牌
                    SelectionKey selectionKey = iterator.next();
                    if(selectionKey.isAcceptable()){
                        //可以通过令牌来获取到了一个已经就绪的服务端通道
                        ServerSocketChannel ssc = (ServerSocketChannel) selectionKey.channel();
                        //客户端的延伸通道
                        SocketChannel socketChannel = ssc.accept();
                        //将客户端延伸通道设置为非阻塞的
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector,SelectionKey.OP_READ);
                        //当客户端来连接的时候,所有的步骤已经全部执行完毕.
                    }else if(selectionKey.isReadable()){
                        //当前通道已经做好了读取的准备(延伸通道)
                        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                        ByteBuffer byteBuffer1 = ByteBuffer.allocate(1024);
                        //socketChannel.read(byteBuffer1);
                        int len;
                        while((len = socketChannel.read(byteBuffer1)) > 0){
                            byteBuffer1.flip();
                            System.out.println(new String(byteBuffer1.array(),0,len));
                            byteBuffer1.clear();
                        }
                        //给客户端的回写数据
                        socketChannel.write(ByteBuffer.wrap("哎哟喂好疼啊!!!".getBytes()));
                        socketChannel.close();
                    }
                    iterator.remove();
                }
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值