网络编程之NIO

非阻塞的NIO

1缓冲区

客户端

public class NIOClient {
    public static void main(String[] args) throws IOException {
        //1.创建通道对象并打开
        SocketChannel socketChannel = SocketChannel.open();

        //2.指定IP和端口号
        socketChannel.connect(new InetSocketAddress("127.0.0.1",10000));

        //3.写出数据
        ByteBuffer byteBuffer = ByteBuffer.wrap("一点寒芒先到".getBytes());
        socketChannel.write(byteBuffer);

        //4.关流
        socketChannel.close();
    }
}

服务端

public class NIOServer {
    public static void main(String[] args) throws IOException {
//        1.创建打开服务端通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//        2.绑定对应的端口号
        serverSocketChannel.bind(new InetSocketAddress(10000));
//        3.通道默认是阻塞的,需要设置为非阻塞
            //如果传递true 表示通道设置为阻塞通道...默认值
            //如果传递false 表示通道设置为非阻塞通道
        serverSocketChannel.configureBlocking(false);
//        4.此时没有门卫大爷,所以需要经常看一下有没有连接发过来没?
        while (true) {
//        5.如果有客户端来连接了,则在服务端通道内部,再创建一个客户端通道
            //设置了通道为非阻塞
            //所以在调用方法的时候,如果有客户端来连接,那么会创建一个SocketChannel对象
            //如果在调用方法的时候,没有客户端来连接,那么他会返回一个null
            SocketChannel socketChannel = serverSocketChannel.accept();
            //System.out.println(socketChannel);
            if(socketChannel != null){
//        6.客户端将缓冲区通过通道传递给服务端,就到了这个延伸通道socketChannel里面
//        7.服务端创建一个空的缓冲区装数据并输出
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                //获取传递过来的数据,并把他们放到byteBuffer缓冲区中.
                //返回值:
                    //正数: 表示本次读到的有效字节个数.
                    //0   : 表示本次没有读到
                    //-1  : 表示读完了
                int len = socketChannel.read(byteBuffer);
                System.out.println(new String(byteBuffer.array(),0,len));
              //8.关闭
                socketChannel.close();
            }
        }
    }
}

2通道

客户端

public class Clinet {
    public static void main(String[] args) throws IOException {
        // 1.创建打开通道
        SocketChannel socketChannel = SocketChannel.open();
        // 2.指定IP和端口号
        socketChannel.connect(new InetSocketAddress("127.0.0.1",10000));
        // 3.写出数据
        ByteBuffer byteBuffer1 = ByteBuffer.wrap("吃俺老孙一棒棒".getBytes());
        socketChannel.write(byteBuffer1);
  		// 写入结束标记
        socketChannel.shutdownOutput();

        System.out.println("数据已经写给服务器");
        // 4.读取服务器写回的数据
        ByteBuffer byteBuffer2 = ByteBuffer.allocate(1024);
        int len;
        while((len = socketChannel.read(byteBuffer2)) != -1){
            byteBuffer2.flip();
            System.out.println(new String(byteBuffer2.array(),0,len));
            byteBuffer2.clear();
        }
        // 5.释放资源
        socketChannel.close();
    }
}

服务端

public class Sever {
    public static void main(String[] args) throws IOException {
        // 1,打开一个服务端通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 2,绑定对应的端口号
        serverSocketChannel.bind(new InetSocketAddress(10000));
        // 3,通道默认是阻塞的,需要设置为非阻塞
        serverSocketChannel.configureBlocking(false);
        // 4,此时没有门卫大爷,所以需要经常看一下有没有连接发过来没?
        while(true){
            //  5,如果有客户端来连接了,则在服务端通道内部,再创建一个客户端通道,相当于是客户端通道的延伸
            SocketChannel socketChannel = serverSocketChannel.accept();
            if(socketChannel != null){
                System.out.println("此时有客户端来连接了");
                // 6,获取客户端传递过来的数据,并把数据放在byteBuffer1这个缓冲区中
                ByteBuffer byteBuffer1 = ByteBuffer.allocate(1024);
                //socketChannel.read(byteBuffer1);
                int len;
                //针对于缓冲区来讲
                //如果 从添加数据 ----> 获取数据 flip
                //如果 从获取数据 ----> 添加数据 clear
                while((len = socketChannel.read(byteBuffer1)) != -1){
                    byteBuffer1.flip();
                    System.out.println(new String(byteBuffer1.array(),0,len));
                    byteBuffer1.clear();
                }

                System.out.println("接收数据完毕,准备开始往客户端回写数据");
                // 7,给客户端回写数据
                ByteBuffer byteBuffer2 = ByteBuffer.wrap("哎哟,真疼啊!!!".getBytes());
                socketChannel.write(byteBuffer2);
                // 8,释放资源
                socketChannel.close();
            }
        }
    }
}

3选择器

客户端

	public static void main(String[] args) throws IOException {
		//创建并打开通道
        SocketChannel socketChannel = SocketChannel.open();
		//指定ip和端口
        socketChannel.connect(new InetSocketAddress("192.168.1.39",9999));
		//写出数据
        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();
    }

服务端


	public static void main(String[] args) throws Exception {
        //1:建立服务器 的通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(9999));
        //设置非阻塞连接
        serverSocketChannel.configureBlocking(false);

        //2:为了同同时处理多个客户端的连接
        //          加上选择器
        //    1:获得一个独立的选择器
        Selector selector = Selector.open();
        //    2:将当前的服务器 通道 注册上该选择器,并设置该通道的状态
        //                  状态有三种
        //                      可接受
        //                      可读取数据
        //                      可写出数据
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        //正式开始 通过选择器 操作每一个客户端的数据输入
        //      所有的数据输入和输出 通过选择器

        while (true) {
            //:1获得选择器中 的处理的 客户端 个数
            int count = selector.select();
            //      只有当前有客户才处理
            if (count != 0) {
                // SelectionKey  ----> 就是一个客户端连接后 , 选择器给他分配一个 令牌 ,该令牌描述 该通道的状态
                Set<SelectionKey> keys = selector.selectedKeys();
                // 循环使用每一个令牌 , 其实就是循环操作每个 客户端通道
                Iterator<SelectionKey> iterator = keys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    // 获得 当前令牌的状态 , 根据不同的状态 执行不同的代码
                    //             如果是第一次进入 去选择器登记和注册
                    if (key.isAcceptable()) {  // 一个客户端 最先是在次 先执行,后续才能 读或者写
                        //  获得服务端通道
                        ServerSocketChannel server = (ServerSocketChannel) key.channel();
                        //  通过服务端 获得一个连接的客户端通道
                        SocketChannel client = server.accept();
                        //将客户端的通道设置为非阻塞
                        client.configureBlocking(false);
                        //修改当前选择器中 令牌的状态
                        //      如果填写是  SelectionKey.OP_READ 那么此客户端下一次进来时就是 进入 else if(key.isReadable())
                        //      如果填写是  SelectionKey.OP_WRITE 那么此客户端下一次进来时就是 进入 else if(key.isWritable())
                        client.register(selector, SelectionKey.OP_READ);

                        //              如果是读取数据的状态
                    } else if (key.isReadable()) {
                        //需要读取 客户端中的数据
                        SocketChannel client = (SocketChannel) key.channel();

                        //缓冲区获得数据
                        ByteBuffer buffer = ByteBuffer.allocate(8192);
                        while (true) {
                            buffer.clear();
                            //获得通道中的数据 写入缓冲中
                            int len = client.read(buffer);
                            if (len == -1) {
                                //说明 客户端发送全部结束
                                // 如果我想告诉 客户端  我接受了全部数据 --->我就是想给客户端发送一个消息
                                //  将当前客户端的状态修改为 可写
                                client.register(selector, SelectionKey.OP_WRITE);
                                break;
                            } else if (len == 0) {
                                // 通道连接是正常 ,但是数据不来了
                                break;
                            } else {
                                buffer.flip();
                                //将数据继续解析 ---> 我们先默认发送的字符串
                                String msg = new String(buffer.array(), 0, len);
                                String ip = client.getRemoteAddress().toString();
                                System.out.println(ip + " ---> " + msg);
                            }
                        }

                        //              如果是写出数据的状态
                    } else if (key.isWritable()) {
                        SocketChannel client = (SocketChannel) key.channel();
                        ByteBuffer buffer = ByteBuffer.wrap("服务端接受数据完毕 ,你可以正常退出了".getBytes());
                        client.write(buffer);

//                        client.close();
//                        //可以将次通道关闭
//                        iterator.remove();
                    }
                }

            }
        }


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zxy_spure

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值