Java IO 模型及示例

重要概念

同步和异步

概念 含义
同步 IO 时,Java 发起 IO 的线程自己处理 IO 读写,因此需要等待 IO 操作完成或者轮询地去查看 IO 操作是否就绪
异步 IO 时,Java 发起 IO 的线程将 IO 读写委托给操作系统,并将数据缓冲区地址和大小传给操作系统,当 IO 完成时 Java 线程会得到通知

阻塞和非阻塞

概念 含义
阻塞 使用阻塞 IO 时,Java 发起 IO 的线程会一直阻塞到读写完成才返回
非阻塞 使用非阻塞 IO 时,不管是否能读写,发起 IO 的线程会马上得到一个返回值,进而继续执行其他代码

1. BIO(同步阻塞IO)

1.1 BIO 的处理流程

BIO 的流程通常是服务端启动一个ServerSocket,然后在客户端启动Socket来连接服务端进行通信,默认情况下服务端需要对每个请求建立一个线程进行处理,而客户端发送请求后线程会等待获得响应后才继续执行

在这里插入图片描述
Blocking IO的特点就是在IO执行的两个阶段,等待数据和拷贝数据,都被阻塞了。大部分的socket接口都是阻塞型的,而所谓阻塞型接口是指系统调用(一般是IO接口)不返回调用结果并让当前线程一直阻塞,直到该系统调用获得结果或者超时出错时才返回

1.2 使用示例

1.2.1 客户端

public class Client {
    //默认的端口号
    private static int DEFAULT_SERVER_PORT = 10086;

    private static String DEFAULT_SERVER_IP = "127.0.0.1";

    private static String QUIT_MSG = "q";

    private static Socket socket;

    public static void start() {
        try {
            socket = new Socket(DEFAULT_SERVER_IP, DEFAULT_SERVER_PORT);
            System.out.println("Client: start, port " + DEFAULT_SERVER_PORT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static boolean send(String exp) {
        if (QUIT_MSG.equals(exp)) {
            System.out.println("Client: quit");
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return false;
        }
        System.out.println("Client: send " + exp);
        InputStreamReader inputStreamReader;
        BufferedReader in;
        PrintWriter out;
        try {
            inputStreamReader = new InputStreamReader(socket.getInputStream());
            in = new BufferedReader(inputStreamReader);
            out = new PrintWriter(socket.getOutputStream(), true);

            out.println(exp);
            System.out.println("Client: receive " + in.readLine());
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

}

1.2.2 服务端

  1. 服务器类

    public class Server {
    
     //默认的端口号
     private static int DEFAULT_PORT = 10086;
     private static ServerSocket server;
    
     public static void start() {
         start(DEFAULT_PORT);
     }
    
     public synchronized static void start(int port) {
         if (server != null) return;
         new Thread(() -> {
             try {
                 // 通过构造函数创建ServerSocket,如果端口合法且空闲,服务端就监听成功
                 server = new ServerSocket(port);
                 System.out.println("Server: start, port " + port);
                 // 无限循环监听客户端连接,如果没有客户端接入,将阻塞在accept操作上
                 while (true) {
                     Socket socket = server.accept();
                     //当有新的客户端接入时,会执行下面的代码,创建一个新的线程处理这条 Socket 连接
                     new Thread(new ServerHandler(socket)).start();
                 }
             } catch (IOException e) {
                 e.printStackTrace();
             } finally {
                 //一些必要的清理工作
                 if (server != null) {
                     System.out.println("Server: close");
                     try {
                         server.close();
                     } catch (IOException e) {
                         e.printStackTrace();
                     }
                     server = null;
                 }
             }
         }, "Server").start();
     }
    }
    
  2. 服务器处理者

    public class ServerHandler implements Runnable {
     private Socket socket;
    
     public ServerHandler(Socket socket) {
         this.socket = socket;
     }
    
     @Override
     public void run() {
    
         InputStreamReader inputStreamReader;
         BufferedReader in;
         PrintWriter out;
         try {
             inputStreamReader = new InputStreamReader(socket.getInputStream());
             in = new BufferedReader(inputStreamReader);
             out = new PrintWriter(socket.getOutputStream(), true);
             String exp;
             while (null != (exp = in.readLine())) {
                 // 通过BufferedReader读取一行,如果已经读到输入流尾部或者对端关闭,返回null,退出循环
                 System.out.println("Server: receive " + exp);
                 String result = "Server: send " + exp;
                 out.println(result);
             }
             System.out.println("Server: handler quit");
         } catch (Exception e) {
             e.printStackTrace();
         } finally {
             try {
                 if (null != socket)
                     socket.close();
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
     }
    }
    

1.2.3 测试类

  public class Test {

   public static void main(String[] args) throws InterruptedException {

       Server.start();
       //避免客户端先于服务器启动前执行代码
       Thread.sleep(100);

       //运行客户端
      Client.start();
      while (Client.send(new Scanner(System.in).nextLine()));
   }
  }

2. NIO(同步非阻塞IO)

NIO本身是基于事件驱动思想来完成的,主要解决的是BIO的大并发问题:

在使用同步 I/O 的网络应用中,如果同时处理多个客户端请求,就必须使用多线程来处理,也就是将每一个客户端请求分配给一个线程来单独处理。这样做会带来一个问题,由于每创建一个线程就要为这个线程分配一定的内存空间,而操作系统本身资源是有限的,这样就对线程的总数构成了一定的限制。如果客户端的请求过多,服务端程序可能会因为不堪重负而拒绝客户端的请求,甚至服务器可能会因此而瘫痪。
NIO 解决了线程创建过多的问题,不过在 NIO 的处理方式中,当一个请求进来,开启线程进行处理可能会等待后端应用的资源(JDBC连接等),其实这个线程就被阻塞了,当并发量上来时还是会有和 BIO 一样的问题

NIO 基于 Reactor,当socket 有流可读或可写入 socket 时,操作系统会相应的通知应用程序进行处理,应用再将流读取到缓冲区或写入操作系统。 也就是说,这个时候已经不是一个连接就要对应一个处理线程了,而是有效的请求,对应一个线程,当连接没有数据时,是没有工作线程来处理的

BIO 与 NIO 比较重要的不同点:

  1. 使用 BIO 的时候往往会引入多线程,每个连接对应一个单独的线程;而 NIO 线程处理的都是有效连接(数据就绪),且一个线程可以分管处理多个连接上的就绪数据,节省线程资源开销
  2. BIO 面向流,NIO 面向缓冲区

2.1 NIO 的处理流程

当用户线程发起 I/O 请求后,会将 socket 连接及关注事件注册到服务端的selector(多路复用器,os级别线程)上,selector 循环遍历注册其上的 socket 连接,看是否有其关注事件就绪,如果连接有数据就绪后,就通知应用程序建立线程进行数据读写,也就是实际的读写操作是由应用程序完成的,而不是操作系统

在这里插入图片描述

2.2 使用示例

2.2.1 客户端

  1. 客户端

    public class Client {
    
     private static String DEFAULT_HOST = "127.0.0.1";
    
     private static int DEFAULT_PORT = 10086;
    
     private static String QUIT_MSG = "q";
    
     private static ClientHandler clientHandle;
    
     public static void start() {
         start(DEFAULT_HOST, DEFAULT_PORT);
     }
    
     public static synchronized void start(String ip, int port) {
         if (clientHandle != null) {
             return;
         }
         clientHandle = new ClientHandler(ip, port);
         new Thread(clientHandle, "Client").start();
     }
    
     //向服务器发送消息
     public static boolean sendMsg(String msg) throws Exception {
         if (QUIT_MSG.equals(msg)) {
             System.out.println("Client: exit");
             clientHandle.stop();
             return false;
         }
         clientHandle.sendMsg(msg);
         return true;
     }
    }
    
  2. 客户端处理者

    public class ClientHandler implements Runnable {
    
     private String host;
     private int port;
     private Selector selector;
     private SocketChannel socketChannel;
     private volatile boolean started;
    
     public ClientHandler(String ip, int port) {
         this.host = ip;
         this.port = port;
         try {
             //创建选择器
             selector = Selector.open();
             //打开监听通道
             socketChannel = SocketChannel.open();
             //如果为 true,则此通道将被置于阻塞模式;如果为 false,则此通道将被置于非阻塞模式
             socketChannel.configureBlocking(false);
             started = true;
             System.out.println("Client: start, port " + port);
         } catch (IOException e) {
             e.printStackTrace();
             System.exit(1);
         }
     }
    
     public void stop() {
         started = false;
     }
    
     @Override
     public void run() {
         try {
             doConnect();
         } catch (IOException e) {
             e.printStackTrace();
             System.exit(1);
         }
         //循环遍历selector
         while (started) {
             try {
                 //无论是否有读写事件发生,selector每隔1s被唤醒一次
                 selector.select(1000);
                 Set<SelectionKey> keys = selector.selectedKeys();
                 Iterator<SelectionKey> it = keys.iterator();
                 SelectionKey key;
                 while (it.hasNext()) {
                     key = it.next();
                     it.remove();
                     try {
                         handleInput(key);
                     } catch (Exception e) {
                         if (key != null) {
                             key.cancel();
                             if (key.channel() != null) {
                                 key.channel().close();
                             }
                         }
                     }
                 }
             } catch (Exception e) {
                 e.printStackTrace();
                 System.exit(1);
             }
         }
         //selector关闭后会自动释放里面管理的资源
         if (selector != null)
             try {
                 selector.close();
             } catch (Exception e) {
                 e.printStackTrace();
             }
     }
    
     private void handleInput(SelectionKey key) throws IOException {
         if (key.isValid()) {
             SocketChannel sc = (SocketChannel) key.channel();
             if (key.isConnectable()) {
                 if (sc.finishConnect()) {
                     System.out.println("Client: connect to server");
                 } else {
                     System.exit(1);
                 }
             }
             //读消息
             if (key.isReadable()) {
                 //创建ByteBuffer,并开辟一个1M的缓冲区
                 ByteBuffer buffer = ByteBuffer.allocate(1024);
                 //读取请求码流,返回读取到的字节数
                 int readBytes = sc.read(buffer);
                 //读取到字节,对字节进行编解码
                 if (readBytes > 0) {
                     //将缓冲区当前的limit设置为position=0,用于后续对缓冲区的读取操作
                     buffer.flip();
                     //根据缓冲区可读字节数创建字节数组
                     byte[] bytes = new byte[buffer.remaining()];
                     //将缓冲区可读字节数组复制到新建的数组中
                     buffer.get(bytes);
                     String result = new String(bytes, StandardCharsets.UTF_8);
                     System.out.println("Client: receive " + result);
                 } else if (readBytes < 0) {
                     //链路已经关闭,释放资源
                     key.cancel();
                     sc.close();
                 }
             }
         }
     }
    
     //异步发送消息
     private void doWrite(SocketChannel channel, String request) throws IOException {
         //将消息编码为字节数组
         byte[] bytes = request.getBytes();
         //根据数组容量创建ByteBuffer
         ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
         //将字节数组复制到缓冲区
         writeBuffer.put(bytes);
         //flip操作
         writeBuffer.flip();
         //发送缓冲区的字节数组
         channel.write(writeBuffer);
     }
    
     private void doConnect() throws IOException {
         if (socketChannel.connect(new InetSocketAddress(host, port))) {
             System.out.println("Client: already connect to Server");
         } else {
             socketChannel.register(selector, SelectionKey.OP_CONNECT);
         }
     }
    
     public void sendMsg(String msg) throws Exception {
         socketChannel.register(selector, SelectionKey.OP_READ);
         doWrite(socketChannel, msg);
     }
    }
    

2.2.2 服务端

  1. 服务器

    public class Server {
     private static int DEFAULT_PORT = 10086;
     private static ServerHandler serverHandle;
    
     public static void start() {
         start(DEFAULT_PORT);
     }
    
     public static synchronized void start(int port) {
         if (serverHandle != null) {
             return;
         }
         serverHandle = new ServerHandler(port);
         new Thread(serverHandle, "Server").start();
     }
    }
    
  2. 服务端处理者

    public class ServerHandler implements Runnable {
     private Selector selector;
     private ServerSocketChannel serverChannel;
     private volatile boolean started;
    
     /**
      * 构造方法
      *
      * @param port 指定要监听的端口号
      */
     public ServerHandler (int port) {
         try {
             //创建选择器
             selector = Selector.open();
             //打开监听通道
             serverChannel = ServerSocketChannel.open();
             //如果为 true,则此通道将被置于阻塞模式;如果为 false,则此通道将被置于非阻塞模式
             serverChannel.configureBlocking(false);
             //绑定端口 backlog设为1024
             serverChannel.socket().bind(new InetSocketAddress(port), 1024);
             //监听客户端连接请求
             serverChannel.register(selector, SelectionKey.OP_ACCEPT);
             //标记服务器已开启
             started = true;
             System.out.println("Server: start, port " + port);
         } catch (IOException e) {
             e.printStackTrace();
             System.exit(1);
         }
     }
    
     public void stop() {
         started = false;
     }
    
     @Override
     public void run() {
         //循环遍历selector
         while (started) {
             try {
                 //无论是否有读写事件发生,selector每隔1s被唤醒一次
                 selector.select(1000);
                 Set<SelectionKey> keys = selector.selectedKeys();
                 Iterator<SelectionKey> it = keys.iterator();
                 SelectionKey key;
                 while (it.hasNext()) {
                     key = it.next();
                     it.remove();
                     try {
                         handleInput(key);
                     } catch (Exception e) {
                         if (key != null) {
                             key.cancel();
                             if (key.channel() != null) {
                                 key.channel().close();
                             }
                         }
                     }
                 }
             } catch (Throwable t) {
                 t.printStackTrace();
             }
         }
         //selector关闭后会自动释放里面管理的资源
         if (selector != null)
             try {
                 selector.close();
             } catch (Exception e) {
                 e.printStackTrace();
             }
     }
    
     private void handleInput(SelectionKey key) throws IOException {
         if (key.isValid()) {
             //处理新接入的请求消息
             if (key.isAcceptable()) {
                 ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                 //通过ServerSocketChannel的accept创建SocketChannel实例
                 //完成该操作意味着完成TCP三次握手,TCP物理链路正式建立
                 SocketChannel sc = ssc.accept();
                 //设置为非阻塞的
                 sc.configureBlocking(false);
                 //注册处理读就绪事件
                 sc.register(selector, SelectionKey.OP_READ);
             }
             // 读就绪事件触发读消息
             if (key.isReadable()) {
                 SocketChannel sc = (SocketChannel) key.channel();
                 //创建ByteBuffer,并开辟一个1M的缓冲区
                 ByteBuffer buffer = ByteBuffer.allocate(1024);
                 //读取请求码流,返回读取到的字节数
                 int readBytes = sc.read(buffer);
                 //读取到字节,对字节进行编解码
                 if (readBytes > 0) {
                     //将缓冲区当前的limit设置为position=0,用于后续对缓冲区的读取操作
                     buffer.flip();
                     //根据缓冲区可读字节数创建字节数组
                     byte[] bytes = new byte[buffer.remaining()];
                     //将缓冲区可读字节数组复制到新建的数组中
                     buffer.get(bytes);
                     String exp = new String(bytes, StandardCharsets.UTF_8);
                     System.out.println("Server: receive " + exp);
                     //处理数据
                     String result;
                     try {
                         result = "Server: send " + exp;
                     } catch (Exception e) {
                         result = "Error: " + e.getMessage();
                     }
                     //发送应答消息
                     doWrite(sc, result);
                 } else if (readBytes < 0) {
                     //链路已经关闭,释放资源
                     key.cancel();
                     sc.close();
                 }
             }
         }
     }
    
     //异步发送应答消息
     private void doWrite(SocketChannel channel, String response) throws IOException {
         //将消息编码为字节数组
         byte[] bytes = response.getBytes();
         //根据数组容量创建ByteBuffer
         ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
         //将字节数组复制到缓冲区
         writeBuffer.put(bytes);
         //flip操作
         writeBuffer.flip();
         //发送缓冲区的字节数组
         channel.write(writeBuffer);
     }
    }
    

2.2.3 测试类

public class Test {

    public static void main(String[] args) throws InterruptedException {

        Server.start();
        //避免客户端先于服务器启动前执行代码
        Thread.sleep(100);

        //运行客户端
       Client.start();
       while (Client.send(new Scanner(System.in).nextLine()));

    }
}

3. AIO(异步非阻塞IO)

3.1 AIO 的处理流程

NIO不同,AIO 基于 Proactor,进行读写操作时,只须直接调用 APIread/write方法。这两种方法都是异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,也会主动通知应用程序。 即可以理解为,read/write方法都是异步的,完成后会主动调用数据接收方的回调函数

在这里插入图片描述

3.2 使用示例

3.2.1 客户端

  1. 客户端

    public class Client {
     private static String DEFAULT_HOST = "127.0.0.1";
    
     private static int DEFAULT_PORT = 10086;
    
     private static String QUIT_MSG = "q";
    
     private static AsyncClientHandler clientHandle;
    
     public static void start() {
         start(DEFAULT_HOST, DEFAULT_PORT);
     }
    
     public static synchronized void start(String ip, int port) {
         if (clientHandle != null) {
             return;
         }
         clientHandle = new AsyncClientHandler(ip, port);
         new Thread(clientHandle, "Client").start();
     }
    
     //向服务器发送消息
     public static boolean sendMsg(String msg) {
         if (QUIT_MSG.equals(msg)) {
             clientHandle.stop();
             return false;
         }
         clientHandle.sendMsg(msg);
         return true;
     }
    }
    
  2. 客户端处理者

    public class AsyncClientHandler implements CompletionHandler<Void, AsyncClientHandler>, Runnable {
     private AsynchronousSocketChannel clientChannel;
     private String host;
     private int port;
     private CountDownLatch latch;
    
     public AsyncClientHandler(String host, int port) {
         this.host = host;
         this.port = port;
         try {
             //创建异步的客户端通道
             clientChannel = AsynchronousSocketChannel.open();
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
    
     @Override
     public void run() {
         //创建CountDownLatch等待
         latch = new CountDownLatch(1);
         //发起异步连接操作,回调接口就是这个类本身,如果连接成功会回调completed方法
         clientChannel.connect(new InetSocketAddress(host, port), this, this);
         try {
             latch.await();
         } catch (InterruptedException e1) {
             e1.printStackTrace();
         }
         try {
             clientChannel.close();
             System.out.println("Client: close");
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
    
     public void stop() {
         latch.countDown();
     }
    
     //连接服务器成功
     //意味着TCP三次握手完成
     @Override
     public void completed(Void result, AsyncClientHandler attachment) {
         System.out.println("Client: connect to server");
     }
    
     //连接服务器失败
     @Override
     public void failed(Throwable exc, AsyncClientHandler attachment) {
         System.err.println("Client: connect to server fail");
         exc.printStackTrace();
         try {
             clientChannel.close();
             latch.countDown();
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
    
     //向服务器发送消息
     public void sendMsg(String msg) {
         byte[] req = msg.getBytes();
         ByteBuffer writeBuffer = ByteBuffer.allocate(req.length);
         writeBuffer.put(req);
         writeBuffer.flip();
         //异步写
         clientChannel.write(writeBuffer, writeBuffer, new ClientWriteHandler(clientChannel, latch));
     }
    }
    
  3. 客户端写处理

    public class ClientWriteHandler implements CompletionHandler<Integer, ByteBuffer> {
     private AsynchronousSocketChannel clientChannel;
     private CountDownLatch latch;
    
     public ClientWriteHandler(AsynchronousSocketChannel clientChannel, CountDownLatch latch) {
         this.clientChannel = clientChannel;
         this.latch = latch;
     }
    
     @Override
     public void completed(Integer result, ByteBuffer buffer) {
         //完成全部数据的写入
         if (buffer.hasRemaining()) {
             clientChannel.write(buffer, buffer, this);
         } else {
             //读取数据
             ByteBuffer readBuffer = ByteBuffer.allocate(1024);
             clientChannel.read(readBuffer, readBuffer, new ClientReadHandler(clientChannel, latch));
         }
     }
    
     @Override
     public void failed(Throwable exc, ByteBuffer attachment) {
         System.err.println("Client: send fail");
         try {
             clientChannel.close();
         } catch (IOException e) {
             e.printStackTrace();
         } finally {
             latch.countDown();
         }
     }
    }
    
  4. 客户端读处理

    public class ClientReadHandler implements CompletionHandler<Integer, ByteBuffer> {
     private AsynchronousSocketChannel clientChannel;
     private CountDownLatch latch;
    
     public ClientReadHandler(AsynchronousSocketChannel clientChannel, CountDownLatch latch) {
         this.clientChannel = clientChannel;
         this.latch = latch;
     }
    
     @Override
     public void completed(Integer result, ByteBuffer buffer) {
         buffer.flip();
         byte[] bytes = new byte[buffer.remaining()];
         buffer.get(bytes);
         String body;
         body = new String(bytes, StandardCharsets.UTF_8);
         System.out.println("Client: receive " + body);
     }
    
     @Override
     public void failed(Throwable exc, ByteBuffer attachment) {
         System.err.println("Client: receive fail");
         try {
             clientChannel.close();
         } catch (IOException e) {
             e.printStackTrace();
         } finally {
             latch.countDown();
         }
     }
    }
    

3.2.2 服务端

  1. 服务器

    public class Server {
    
     private static int DEFAULT_PORT = 10086;
     private static AsyncServerHandler SERVER_HANDLER;
    
     public static void start() {
         start(DEFAULT_PORT);
     }
    
     public static synchronized void start(int port) {
         if (SERVER_HANDLER != null) {
             System.out.println("Server: already start");
             return;
         }
         SERVER_HANDLER = new AsyncServerHandler(port);
         new Thread(SERVER_HANDLER, "Server").start();
     }
    }
    
  2. 服务端处理者

    public class AsyncServerHandler implements Runnable {
     public CountDownLatch latch;
     public AsynchronousServerSocketChannel serverSocketChannel;
    
     public AsyncServerHandler(int port) {
         try {
             //创建服务端通道
             serverSocketChannel = AsynchronousServerSocketChannel.open();
             //绑定端口
             serverSocketChannel.bind(new InetSocketAddress(port));
             System.out.println("Server: start, port " + port);
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
    
     @Override
     public void run() {
         // CountDownLatch 初始化
         // 作用:在完成一组正在执行的操作之前,允许当前的线程一直阻塞,防止服务端执行完成后退出, 也可以使用 while(true) + sleep
         latch = new CountDownLatch(1);
         //用于接收客户端的连接
         serverSocketChannel.accept(this, new AcceptHandler());
         try {
             latch.await();
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
     }
    }
    
  3. 服务端接受连接处理

    public class AcceptHandler implements CompletionHandler<AsynchronousSocketChannel, AsyncServerHandler> {
    
     private static int CLIENT_COUNT = 0;
    
     @Override
     public void completed(AsynchronousSocketChannel channel, AsyncServerHandler attachment) {
         //继续接受其他客户端的请求
         CLIENT_COUNT++;
         System.out.println("Server: client num " + CLIENT_COUNT);
         attachment.serverSocketChannel.accept(attachment, this);
         //创建新的Buffer
         ByteBuffer buffer = ByteBuffer.allocate(1024);
         //异步读  第三个参数为接收消息回调的业务Handler
         channel.read(buffer, buffer, new ReadHandler(channel));
     }
    
     @Override
     public void failed(Throwable exc, AsyncServerHandler serverHandler) {
         exc.printStackTrace();
         serverHandler.latch.countDown();
     }
    }
    
  4. 服务端读处理

    public class ReadHandler implements CompletionHandler<Integer, ByteBuffer> {
     //用于读取半包消息和发送应答
     private AsynchronousSocketChannel channel;
    
     public ReadHandler(AsynchronousSocketChannel channel) {
         this.channel = channel;
    
     }
    
     //读取到消息后的处理
     @Override
     public void completed(Integer result, ByteBuffer buffer) {
         //flip操作
         buffer.flip();
         byte[] message = new byte[buffer.remaining()];
         buffer.get(message);
         String expr = new String(message, StandardCharsets.UTF_8);
         System.out.println("Server: receive " + expr);
         String resultMsg;
         try {
             resultMsg = "Server: send " + expr;
         } catch (Exception e) {
             resultMsg = "Error:" + e.getMessage();
         }
         //向客户端发送消息
         doWrite(resultMsg);
     }
    
     //发送消息
     private void doWrite(String result) {
         byte[] bytes = result.getBytes();
         ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
         writeBuffer.put(bytes);
         writeBuffer.flip();
         //异步写数据 参数与前面的read一样
         channel.write(writeBuffer, writeBuffer, new WriteHandler(channel));
     }
    
     @Override
     public void failed(Throwable exc, ByteBuffer attachment) {
         try {
             this.channel.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
    }
    
  5. 服务端写处理

    public class WriteHandler implements CompletionHandler<Integer, ByteBuffer> {
    
     private AsynchronousSocketChannel channel;
    
     public WriteHandler(AsynchronousSocketChannel channel) {
         this.channel = channel;
     }
    
     @Override
     public void completed(Integer result, ByteBuffer buffer) {
         //如果没有发送完,就继续发送直到完成
         if (buffer.hasRemaining()) {
             channel.write(buffer, buffer, this);
         } else {
             //发送完毕,准备读取数据
             ByteBuffer readBuffer = ByteBuffer.allocate(1024);
             //异步读  第三个参数为接收消息回调的业务 Handler
             channel.read(readBuffer, readBuffer, new ReadHandler(channel));
         }
     }
    
     @Override
     public void failed(Throwable exc, ByteBuffer attachment) {
         try {
             channel.close();
         } catch (IOException ignored) {
         }
     }
    }
    

3.2.3 测试类

public class Test {

    public static void main(String[] args) throws Exception {
        //运行服务器
        Server.start();
        //避免客户端先于服务器启动前执行代码
        Thread.sleep(100);
        //运行客户端
        Client.start();
        System.out.println("please input:");
        Scanner scanner = new Scanner(System.in);
        while (Client.sendMsg(scanner.nextLine()));
        scanner.close();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章