TCP通信的 BIO(同步阻塞),NIO(同步非阻塞),AIO(异步非阻塞)三种通信方式

时间:2022-02-09作者:klpeng分类:Java技术浏览:526评论:0

Channel(通道)

ServerSocketChannel和SocketChannel创建连接

同步阻塞实现

/*
    实现同步非阻塞的服务器
    相关的类:
        java.nio.channels.ServerSocketChannel:针对面向流的侦听套接字的可选择通道。
    获取对象的方式:
        static ServerSocketChannel open() 打开服务器套接字通道。
    成员方法:
        ServerSocketChannel bind(SocketAddress local) 给服务器绑定端口号
        SocketChannel accept() 监听客户端的请求。
        SelectableChannel configureBlocking(boolean block) 设置阻塞模式,true:阻塞; false:非阻塞
    实现步骤:
        1.获取ServerSocketChannel对象
        2.使用ServerSocketChannel对象中的bind绑定指定的端口号
        3.使用ServerSocketChannel对象中的accept方法监听客户端的请求
 */
public class TCPServer {
    public static void main(String[] args) throws IOException {
        //1.获取ServerSocketChannel对象
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //2.使用ServerSocketChannel对象中的bind绑定指定的端口号
        serverSocketChannel.bind(new InetSocketAddress(8888));
        //3.使用ServerSocketChannel对象中的accept方法监听客户端的请求
        System.out.println("服务器等待客户端连接...");
        SocketChannel socketChannel = serverSocketChannel.accept();
        System.out.println("有客户端连接服务器...");
    }
}
/*
    实现同步非阻塞的客户端
    相关的类:
        java.nio.channels.SocketChannel
    获取对象的方法:
        static SocketChannel open() 打开套接字通道。
    成员方法:
        boolean connect(SocketAddress remote) 根据服务器的ip地址和端口号连接服务器
        configureBlocking​(boolean block)设置阻塞模式,true:阻塞; false:非阻塞
    实现步骤:
        1.获取SocketChannel对象
        2.使用SocketChannel对象中的方法connect根据服务器的ip地址和端口号连接服务器
 */
public class TCPClient {
    public static void main(String[] args) throws IOException {
        //1.获取SocketChannel对象
        SocketChannel socketChannel = SocketChannel.open();
        //2.使用SocketChannel对象中的方法connect根据服务器的ip地址和端口号连接服务器
        socketChannel.connect(new InetSocketAddress("127.0.0.1",8888));
    }
}

服务器轮询监听客户端

public class TCPServer {
    public static void main(String[] args) throws IOException, InterruptedException {
        //1.获取ServerSocketChannel对象
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //2.使用ServerSocketChannel对象中的bind绑定指定的端口号
        serverSocketChannel.bind(new InetSocketAddress(8888));
        //3.使用ServerSocketChannel对象中的accept方法监听客户端的请求
        //设置服务器非阻塞模式
        serverSocketChannel.configureBlocking(false);
        //轮询监听客户端
        while (true){
            System.out.println("服务器等待客户端连接...");
            SocketChannel socketChannel = serverSocketChannel.accept();//默认阻塞
            //对象SocketChannel进行判断,没有客户端连接返回null
            if(socketChannel!=null){
                System.out.println("有客户端连接服务器...");
                break;//结束轮询
            }else{
                System.out.println("没有客户端连接服务器,休息2秒,干点其他事,继续轮询...");
                Thread.sleep(2000);
            }
        }
    }
}

执行结果

服务器等待客户端连接...
没有客户端连接服务器,休息2,继续轮询...
服务器等待客户端连接...
没有客户端连接服务器,休息2,继续轮询...
服务器等待客户端连接...
有客户端连接服务器...

客户端实现同步非阻塞

/*
    实现同步非阻塞的客户端
    相关的类:
        java.nio.channels.SocketChannel
    获取对象的方法:
        static SocketChannel open() 打开套接字通道。
    成员方法:
        boolean connect(SocketAddress remote) 根据服务器的ip地址和端口号连接服务器
        configureBlocking(boolean block)设置阻塞模式,true:阻塞; false:非阻塞
    实现步骤:
        1.获取SocketChannel对象
        2.使用SocketChannel对象中的方法connect根据服务器的ip地址和端口号连接服务器
    注意:
        客户端使用connect连接服务器
            客户端设置为阻塞模式,会多次尝试连接,连接成功connect方法返回true连接不成功抛出连接异常
            客户端设置为非阻塞模式,只会连接一次服务器,connect方法返回false
 */
public class TCPClient {
    public static void main(String[] args) throws InterruptedException {
        //阻塞轮询连接服务器
        while (true){
            try{
                //1.获取SocketChannel对象
                SocketChannel socketChannel = SocketChannel.open();
                socketChannel.configureBlocking(true);//默认true:阻塞
                //socketChannel.configureBlocking(false);//false:非阻塞
                //2.使用SocketChannel对象中的方法connect根据服务器的ip地址和端口号连接服务器
                boolean b = socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888));
                System.out.println(b);
                System.out.println("连接服务器成功,结束轮询...");
                break;//结束轮询
            }catch (Exception e){
                System.out.println("连接服务器失败,休息2秒,干点别的...");
                Thread.sleep(2000);
            }
        }
    }
}

ServerSocketChannel和SocketChannel收发信息

/*
    客户端给服务器发送数据,读取服务器回写的数据
        int write(ByteBuffer src)  给服务器发送数据
        int read(ByteBuffer dst)  读取服务器发送的数据
 */
public class TCPClient {
    public static void main(String[] args) throws IOException, InterruptedException {
        //轮询连接服务器
        while (true){
            try {
                //1.使用SocketChannel中的方法open获取SocketChannel的子类对象
                SocketChannel socketChannel = SocketChannel.open();

                //设置客户端的阻塞模式
                socketChannel.configureBlocking(true);
                //socketChannel.configureBlocking(false);

                //2.使用SocketChannel对象中的方法connect根据服务器的ip地址和端口号连接服务器
                boolean b = socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888));
                //System.out.println(b);

                //int write(ByteBuffer src)  给服务器发送数据
                ByteBuffer buffer = ByteBuffer.wrap("你好服务器".getBytes());
                System.out.println("容量:"+buffer.capacity());
                System.out.println("索引:"+buffer.position());
                System.out.println("限定:"+buffer.limit());
                socketChannel.write(buffer);

                //int read(ByteBuffer dst)  读取服务器发送的数据
                ByteBuffer buffer2 = ByteBuffer.allocate(1024);
                int len = socketChannel.read(buffer2);
                buffer2.flip();//缩小limit的范围
                String msg = new String(buffer2.array(), 0, buffer2.limit());
                System.out.println("客户端收到服务器发送的数据:"+msg);
                System.out.println("连接服务器成功,结束轮询...");
                socketChannel.close();
                break;
            } catch (IOException e) {
                System.out.println("连接服务器失败休息2秒,干点别的,继续轮询...");
                Thread.sleep(2000);
            }
        }
    }
}
/*
    服务器接收客户端发送的数据,给客户端回写数据
        int read(ByteBuffer dst)  读取客户端发送的数据
        int write(ByteBuffer src)  给客户端发送(回写)数据
 */
public class TCPServer {
    public static void main(String[] args) throws IOException, InterruptedException {
        //1.使用ServerSocketChannel中的方法open获取一个ServerSocketChannel的子类对象
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //2.使用ServerSocketChannel对象中的bind方法绑定指定的端口号
        serverSocketChannel.bind(new InetSocketAddress(8888));
        //3.使用ServerSocketChannel对象中的accept方法监听客户端的请求

        //设置服务器的非阻塞模式
        serverSocketChannel.configureBlocking(false);

        //轮询监听客户端
        while (true){
            System.out.println("服务器等待客户端连接....");
            SocketChannel socketChannel = serverSocketChannel.accept();
            //对SocketChannel进行判断:accept没有获取到客户端对象默认值null
            if(socketChannel!=null){
                System.out.println("有客户端连接服务器....");

                //int read(ByteBuffer dst)  读取客户端发送的数据
                ByteBuffer buffer1 = ByteBuffer.allocate(1024);
                int len = socketChannel.read(buffer1);
                buffer1.flip();//缩小limit的范围
                String msg = new String(buffer1.array(), 0, buffer1.limit());
                System.out.println("服务器读取客户端发送的数据是:"+msg);

                //int write(ByteBuffer src)  给客户端发送(回写)数据
                socketChannel.write(ByteBuffer.wrap("收到谢谢".getBytes()));

                break;//跳出循环,结束轮询
            }else{
                System.out.println("没有客户端连接服务器,休息2秒,干点其他事情,继续轮询...");
                Thread.sleep(2000);
            }
        }

        //释放资源
        serverSocketChannel.close();
    }
}

Selector(选择器)

多路复用概念

选择器Selector是NIO中的重要技术之一。它与SelectableChannel联合使用实现了非阻塞的多路复用。使用它可以节省CPU资源,提高程序的运行效率。

"多路"是指:服务器端同时监听多个“端口”的情况。每个端口都要监听多个客户端的连接。

  • 服务器端的非多路复用效果
    TCP通信的 BIO(同步阻塞),NIO(同步非阻塞),AIO(异步非阻塞)三种通信方式
    如果不使用“多路复用”,服务器端需要开很多线程处理每个端口的请求。如果在高并发环境下,造成系统性能下降。
  • 服务器端的多路复用效果
    TCP通信的 BIO(同步阻塞),NIO(同步非阻塞),AIO(异步非阻塞)三种通信方式
    使用了多路复用,只需要一个线程就可以处理多个通道,降低内存占用率,减少CPU切换时间,在高并发、高频段业务环境下有非常重要的优势

选择器Selector——服务器端实现多路注册

/*
    选择器Selector_服务器端实现多路注册
    java.nio.channels.Selector:SelectableChannel对象的多路复用器。
        获取Selector对象的方式:
            static Selector open​() 打开选择器。
    注册Channel到Selector:ServerSocketChannel中的方法
        SelectionKey register(Selector,SelectionKey.OP_READ);
        参数:
            SelectionKey:意思是在通过Selector监听Channel时对什么事件感兴趣
            接收就绪--常量:SelectionKey.OP_ACCEPT      (ServerSocketChannel在注册时只能使用此项)
    实现步骤:
    1.创建3个ServerSocketChannel对象
    2.分别给3个ServerSocketChannel对象绑定不同的端口号
    3.设置3个ServerSocketChannel对象为非阻塞
    4.获取Selector对象
    5.把3个ServerSocketChannel对象注册到Selector对象
 */
public class TCPServer {
    public static void main(String[] args) throws IOException {
        //1.创建3个ServerSocketChannel对象
        ServerSocketChannel channel = ServerSocketChannel.open();
        ServerSocketChannel channe2 = ServerSocketChannel.open();
        ServerSocketChannel channe3 = ServerSocketChannel.open();
        //2.分别给3个ServerSocketChannel对象绑定不同的端口号
        channel.bind(new InetSocketAddress(7777));
        channe2.bind(new InetSocketAddress(8889));
        channe3.bind(new InetSocketAddress(9999));
        //3.设置3个ServerSocketChannel对象为非阻塞
        channel.configureBlocking(false);
        channe2.configureBlocking(false);
        channe3.configureBlocking(false);
        //4.获取Selector对象
        Selector selector = Selector.open();
        //5.把3个ServerSocketChannel对象注册到Selector对象
        channel.register(selector, SelectionKey.OP_ACCEPT);
        channe2.register(selector, SelectionKey.OP_ACCEPT);
        channe3.register(selector, SelectionKey.OP_ACCEPT);
    }
}

选择器Selector——常用方法

- Selector的keys()方法**
  - 此方法返回一个Set<SelectionKey>集合,表示:已注册通道的集合。每个已注册通道封装为一个SelectionKey对象。
- **Selector的selectedKeys()方法**
  - 此方法返回一个Set<SelectionKey>集合,表示:当前已连接的通道的集合。每个已连接通道同一封装为一个SelectionKey对象。
- **Selector的select()方法**
  - 此方法会阻塞,直到至少有1个客户端连接。
  - 此方法会返回一个int值,表示有几个客户端连接了服务器。
public class TCPServer {
    public static void main(String[] args) throws IOException, InterruptedException {
        //1.创建3个ServerSocketChannel对象
        ServerSocketChannel channel = ServerSocketChannel.open();
        ServerSocketChannel channe2 = ServerSocketChannel.open();
        ServerSocketChannel channe3 = ServerSocketChannel.open();
        //2.分别给3个ServerSocketChannel对象绑定不同的端口号
        channel.bind(new InetSocketAddress(7777));
        channe2.bind(new InetSocketAddress(8888));
        channe3.bind(new InetSocketAddress(9999));
        //3.设置3个ServerSocketChannel对象为非阻塞
        channel.configureBlocking(false);
        channe2.configureBlocking(false);
        channe3.configureBlocking(false);
        //4.获取Selector对象
        Selector selector = Selector.open();
        //5.把3个ServerSocketChannel对象注册到Selector对象
        channel.register(selector, SelectionKey.OP_ACCEPT);
        channe2.register(selector, SelectionKey.OP_ACCEPT);
        channe3.register(selector, SelectionKey.OP_ACCEPT);

        //循环接收客户端的连接
        while (true){
            //Selector的select()方法:获取连接客户端的数量,没有获取到客户端的连接对象,此方法将阻塞
            int count = selector.select();
            System.out.println("连接客户端的数量:"+count);

            //Selector的keys()方法:获取已经注册的通道集合
            Set<SelectionKey> keys = selector.keys();
            System.out.println("已经注册的通道的数量:"+keys.size());

            //Selector的selectedKeys()方法:获取已经连接的通道集合
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            System.out.println("已经连接的通道的数量:"+selectionKeys.size());

            //获取完一个客户端的连接,睡眠2秒
            Thread.sleep(2000);
        }
    }
}
/*
    创建两个线程,连接服务器
 */
public class TCPClient {
    public static void main(String[] args) {
        //启动两个线程,分别连接7777和8888端口
        new Thread(()->{
            while (true) {
                try (SocketChannel socket = SocketChannel.open()) {
                    System.out.println("7777客户端连接服务器......");
                    socket.connect(new InetSocketAddress("localhost", 7777));
                    System.out.println("7777客户端连接成功....");
                    break;
                } catch (IOException e) {
                    System.out.println("7777异常重连");
                }
            }
        }).start();

        new Thread(()->{
            while (true) {
                try (SocketChannel socket = SocketChannel.open()) {
                    System.out.println("8888客户端连接服务器......");
                    socket.connect(new InetSocketAddress("localhost", 8888));
                    System.out.println("8888客户端连接成功....");
                    break;
                } catch (IOException e) {
                    System.out.println("8888异常重连");
                }
            }
        }).start();
    }
}

选择器Selector——多路信息接收

/*
    使用选择器Selector:服务器端实现多路复用
    java.nio.channels.Selector:SelectableChannel 对象的多路复用器。
    获取Selector对象的方式:
        static Selector open() 打开一个选择器。
    注册Channel到Selector中:ServerSocketChannel中的方法:
        SelectionKey register(Selector sel, SelectionKey.OP_ACCEPT)
        参数:
            SelectionKey:在通过Selector监听Channel时对什么事件感兴趣
            SelectionKey.OP_ACCEPT:常量-->ServerSocketChanne在注册时只能使用此项
    实现步骤:
        1.创建3个ServerSocketChannel对象
        2.分别给3个ServerSocketChannel对象绑定不同的端口号
        3.设置3个ServerSocketChannel对象为非阻塞
        4.获取Selector对象
        5.使用3个ServerSocketChannel对象中的方法register注册到Selector对象上
 */
public class TCPServer {
    public static void main(String[] args) throws IOException, InterruptedException {
        //1.创建3个ServerSocketChannel对象
        ServerSocketChannel channel1 = ServerSocketChannel.open();
        ServerSocketChannel channel2 = ServerSocketChannel.open();
        ServerSocketChannel channel3 = ServerSocketChannel.open();
        //2.分别给3个ServerSocketChannel对象绑定不同的端口号
        channel1.bind(new InetSocketAddress(7777));
        channel2.bind(new InetSocketAddress(8888));
        channel3.bind(new InetSocketAddress(9999));
        //3.设置3个ServerSocketChannel对象为非阻塞
        channel1.configureBlocking(false);
        channel2.configureBlocking(false);
        channel3.configureBlocking(false);
        //4.获取Selector对象
        Selector selector = Selector.open();
        //5.使用3个ServerSocketChannel对象中的方法register注册到Selector对象上
        channel1.register(selector, SelectionKey.OP_ACCEPT);
        channel2.register(selector, SelectionKey.OP_ACCEPT);
        channel3.register(selector, SelectionKey.OP_ACCEPT);


        //循环接收客户端的连接
        while (true){
            System.out.println("服务器等待客户端连接...");
            //使用Selector中的方法select:获取连接客户端的数量,没有客户端连接服务器,此方法将阻塞
            int count = selector.select();
            System.out.println("连接服务器的客户端数量是:"+count);

            //使用Selector中的方法selectedKeys:获取客户端已经连接到通道(ServerSocketChannel)集合
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            System.out.println("已经连接到通道的数量:"+selectionKeys.size());

            //处理Selector监听到的事件,遍历获取已经注册的Set,获取每一个SelectionKey对象
            Iterator<SelectionKey> it = selectionKeys.iterator();
            while (it.hasNext()){
                SelectionKey selectionKey = it.next();
                //获取SelectionKey中封装的ServerSocketChannel对象
                ServerSocketChannel channel = (ServerSocketChannel)selectionKey.channel();
                //获取此通道监听的端口号
                System.out.println("当前通道监听到的端口号:"+channel.getLocalAddress());
                //处理accept事件
                SocketChannel socketChannel = channel.accept();
                //读取SocketChannel发送的数据
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                int len = socketChannel.read(buffer);
                buffer.flip();//缩小limit范围
                String msg = new String(buffer.array(), 0, buffer.limit());
                System.out.println("服务器接收到客户端的数据是:"+msg);

                //处理完SelectonKey监听到的事件,要在Set集合中溢出已经处理完的SelectionKey
                it.remove();
            }
            //获取完每一个客户端的连接,睡眠2秒
            Thread.sleep(2000);
        }
    }
}
/*
    创建线程,分别创建客户端对象,连接不同的服务器
 */
public class TCPClient {
    public static void main(String[] args) throws InterruptedException {
        //启动两个线程,分别连接端口号是7777和8888的通道
        new Thread(()->{
            //轮询连接服务器
            while (true){
                try {
                    SocketChannel socketChannel = SocketChannel.open();
                    System.out.println("客户端开始连接服务器的7777端口...");
                    socketChannel.connect(new InetSocketAddress("localhost",7777));
                    System.out.println("客户始连接服务器的7777端口成功...");
                    //给服务器发送数据
                    socketChannel.write(ByteBuffer.wrap("你好服务器,我是连接7777端口号的客户端!".getBytes()));
                    break;//结束轮询
                } catch (IOException e) {
                    System.out.println("客户端重新连接7777端口...");
                    System.out.println("----------------7777-----------------");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                }
            }
        }).start();

        new Thread(()->{
            //轮询连接服务器
            while (true){
                try {
                    SocketChannel socketChannel = SocketChannel.open();
                    System.out.println("客户端开始连接服务器的8888端口...");
                    socketChannel.connect(new InetSocketAddress("localhost",8888));
                    System.out.println("客户始连接服务器的8888端口成功...");
                    //给服务器发送数据
                    socketChannel.write(ByteBuffer.wrap("你好服务器,我是连接8888端口号的客户端!".getBytes()));
                    break;//结束轮询
                } catch (IOException e) {
                    System.out.println("客户端重新连接8888端口...");
                    System.out.println("----------------7777-----------------");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                }
            }
        }).start();

        Thread.sleep(10000);//等待服务器读取完数据
    }
}

NIO2—AIO(异步,非阻塞)

AIO概述

JDK7新增的:AsynchronousIO:异步、非阻塞IO

AIO异步非阻塞连接

/*
    创建AIO的服务器端:
    相关的类:
        java.nio.channels.AsynchronousServerSocketChannel:用于面向流的侦听套接字的异步通道。
    获取对象的方法:
        static AsynchronousServerSocketChannel open() 打开异步服务器套接字通道。
    成员方法:
        void accept(A attachment, CompletionHandler<AsynchronousSocketChannel,? super A> handler)
        参数:
            A attachment:附件,可以传递null
            CompletionHandler handler:事件处理类,用于处理accept方法监听到的事件
            CompletionHandler也叫回调函数,客户端请求服务器之后,会自动执行CompletionHandler接口中的方法
            CompletionHandler接口中的方法:
                void completed(V result, A attachment);客户端连接成功执行的方法
                void failed(Throwable exc, A attachment);客户端连接失败执行的方法
 */
public class TCPServer {
    public static void main(String[] args) throws IOException {
        //创建异步非阻塞服务器AsynchronousServerSocketChannel对象
        AsynchronousServerSocketChannel channel = AsynchronousServerSocketChannel.open();
        //给AsynchronousServerSocketChannel对象绑定指定的端口号
        channel.bind(new InetSocketAddress(8888));
        System.out.println("accept方法开始执行....");
        //使用AsynchronousServerSocketChannel对象中的方法accept监听请求的客户端对象
        channel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            //客户端连接成功执行的方法
            @Override
            public void completed(AsynchronousSocketChannel result, Object attachment) {
                System.out.println("有客户端连接成功!");
            }
            //客户端连接失败执行的方法
            @Override
            public void failed(Throwable exc, Object attachment) {
                System.out.println("有客户端连接失败!");
            }
        });
        System.out.println("accept方法执行结束....");
        while (true){}
    }
}
/*
    创建AIO的客户端:
    和客户端想关的类:
        java.nio.channels.AsynchronousSocketChannel:用于面向流的连接插座的异步通道。
    获取对象的方法:
        static AsynchronousSocketChannel open() 打开异步套接字通道。
    成员方法:
        abstract Future<Void> connect(SocketAddress remote) 连接服务器的方法,参数传递服务器的ip地址和端口号
    使用步骤:
        1.创建异步非阻塞AsynchronousSocketChannel客户端
        2.使用AsynchronousSocketChannel客户端对象中的方法connect连接服务器
 */
public class TCPClient {
    public static void main(String[] args) throws IOException {
        //1.创建异步非阻塞AsynchronousSocketChannel客户端
        AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
        //2.使用AsynchronousSocketChannel客户端对象中的方法connect连接服务器
        socketChannel.connect(new InetSocketAddress("127.0.0.1",8888));
    }
}

AIO异步连接:同步非阻塞读写

public class TCPServer {
    public static void main(String[] args) throws IOException {
        //创建异步非阻塞服务器AsynchronousServerSocketChannel对象
        AsynchronousServerSocketChannel channel = AsynchronousServerSocketChannel.open();
        //给AsynchronousServerSocketChannel对象绑定指定的端口号
        channel.bind(new InetSocketAddress(8888));
        System.out.println("accept方法开始执行....");
        //使用AsynchronousServerSocketChannel对象中的方法accept监听请求的客户端对象
        channel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            //客户端连接成功执行的方法
            @Override
            public void completed(AsynchronousSocketChannel result, Object attachment) {
                System.out.println("有客户端连接成功!");
                //获取客户端中发送的数据
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                Future<Integer> future = result.read(buffer);//同步非阻塞的read方法
                Integer len = null;
                try {
                    len = future.get();//读取客户端中发送的信息
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
                String msg = new String(buffer.array(),0,len);
                System.out.println("服务器读取到客户端的信息:"+msg);
            }
            //客户端连接失败执行的方法
            @Override
            public void failed(Throwable exc, Object attachment) {
                System.out.println("有客户端连接失败!");
            }
        });
        System.out.println("accept方法执行结束....");
        while (true){}
    }
}
/*
    创建AIO的客户端:
    和客户端相关的类:
        java.nio.channels.AsynchronousSocketChannel:用于面向流的连接插座的异步通道。
    获取对象的方法:
        static AsynchronousSocketChannel open​() 打开异步套接字通道。
    成员方法:
        Future<Void> connect​(SocketAddress remote) 连接此频道。
        Future<Integer> read​(ByteBuffer dst) 从该通道读取到给定缓冲区的字节序列。
        Future<Integer> write​(ByteBuffer src) 从给定的缓冲区向该通道写入一个字节序列。
 */
public class TCPClient {
    public static void main(String[] args) throws IOException {
        //创建异步非阻塞客户端AsynchronousSocketChannel对象
        AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
        //使用AsynchronousSocketChannel对象中的方法connect连接服务器
        socketChannel.connect(new InetSocketAddress("127.0.0.1",8888));
        //连接服务器成功,给服务器发送数据
        ByteBuffer buffer = ByteBuffer.wrap("你好服务器".getBytes());
        socketChannel.write(buffer);
    }
}

AIO异步连接:异步非阻塞读写

/*
    创建AIO(异步,非阻塞)的服务器端:
    相关的类:
        是JDK1.7之后出现的
        java.nio.channels.AsynchronousServerSocketChannel:用于面向流的侦听套接字的异步通道。
    获取对象的方法:
        static AsynchronousServerSocketChannel open​() 打开异步服务器套接字通道。
    成员方法:
        AsynchronousServerSocketChannel bind​(SocketAddress local) 给服务器绑定指定的端口号
        void accept​(A attachment, CompletionHandler<AsynchronousSocketChannel,? super A> handler) 接受连接。
        参数:
           A attachment:附件,可以传递null
           CompletionHandler  handler:事件处理相关的类,用于处理accept方法监听到的事件
           CompletionHandler也叫回调函数,客户端请求服务器之后,会自动执行CompletionHandler接口中的方法
       CompletionHandler接口中的常用方法:用于消除异步I / O操作结果的处理程序。
            void completed​(V result, A attachment) 客户端连接成功执行的方法
            void failed​(Throwable exc, A attachment) 客户端连接失败执行的方法
    实现步骤:
        1.创建异步非阻塞AsynchronousServerSocketChannel服务器对象
        2.使用AsynchronousServerSocketChannel对象中的方法bind绑定指定的端口号
        3.使用AsynchronousServerSocketChannel对象中的方法accept监听客户端请求
 */
public class TCPServer {
    public static void main(String[] args) throws IOException {
        //1.创建异步非阻塞AsynchronousServerSocketChannel服务器对象
        AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
        //2.使用AsynchronousServerSocketChannel对象中的方法bind绑定指定的端口号
        serverSocketChannel.bind(new InetSocketAddress(8888));
        //3.使用AsynchronousServerSocketChannel对象中的方法accept监听客户端请求
        serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            //客户端连接成功执行的方法
            @Override
            public void completed(AsynchronousSocketChannel result, Object attachment) {
                System.out.println("有客户端连接成功!");
                //获取客户端发送的数据
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                //Future<Integer> future = result.read(buffer);//是一个阻塞的方法,没有读取到客户端发送的数据,会阻塞

                /*
                    void read​(ByteBuffer dst, long timeout, TimeUnit unit, A attachment, CompletionHandler<Integer,? super A> handler) 是一个非阻塞的方法
                    参数
                        ByteBuffer dst - 要传输字节的缓冲区
                        long timeout - 完成I / O操作的最长时间
                        TimeUnit unit - timeout参数的时间单位
                        attachment - 要附加到I / O操作的对象; 可以是null
                        CompletionHandler handler - 消费结果的处理程序,回调函数
                            客户端给服务器发送数据之后,就会执行回调函数中的方法
                 */
                result.read(buffer, 10, TimeUnit.SECONDS, null, new CompletionHandler<Integer, Object>() {
                    @Override
                    public void completed(Integer result, Object attachment) {
                        System.out.println("读取客户端发送数据成功,执行的方法");
                        buffer.flip();//缩小limit的范围
                        String msg = new String(buffer.array(), 0, buffer.limit());
                        System.out.println("服务器读取到客户端发送的信息:"+msg);
                    }

                    @Override
                    public void failed(Throwable exc, Object attachment) {
                        System.out.println("读取客户端发送数据失败,执行的方法");
                    }
                });
                System.out.println("readu读取数据的方法执行完毕!");
            }

            //客户端连接失败执行的方法
            @Override
            public void failed(Throwable exc, Object attachment) {
                System.out.println("有客户端连接失败!");
            }
        });
        System.out.println("accept方法执行结束...");
        while (true){}
    }
}
/*
    创建AIO的客户端:
    和客户端想关的类:
        java.nio.channels.AsynchronousSocketChannel:用于面向流的连接插座的异步通道。
    获取对象的方法:
        static AsynchronousSocketChannel open​() 打开异步套接字通道。
    成员方法:
        abstract Future<Void> connect​(SocketAddress remote) 连接服务器的方法,参数传递服务器的ip地址和端口号
        Future<Integer> read​(ByteBuffer dst) 从该通道读取到给定缓冲区的字节序列
        Future<Integer> write​(ByteBuffer src) 从给定的缓冲区向该通道写入一个字节序列。
    使用步骤:
        1.创建异步非阻塞AsynchronousSocketChannel客户端
        2.使用AsynchronousSocketChannel客户端对象中的方法connect连接服务器
 */
public class TCPClient {
    public static void main(String[] args) throws IOException, InterruptedException {
        //1.创建异步非阻塞AsynchronousSocketChannel客户端
        AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
        //2.使用AsynchronousSocketChannel客户端对象中的方法connect连接服务器
        socketChannel.connect(new InetSocketAddress("127.0.0.1",8888));
        //连接服务器成功,给服务器发送数据
        Thread.sleep(1000*11);
        ByteBuffer buffer = ByteBuffer.wrap("你好服务器".getBytes());
        socketChannel.write(buffer);
    }
}
打赏
文章版权声明:除非注明,否则均为彭超的博客原创文章,转载或复制请以超链接形式并注明出处。
相关推荐

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

猜你喜欢