基于Java AIO --回调函数CompletionHandler 与 Future

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.channels.FileChannel;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

/**

 * @desc 基于Java AIO ---client

 **/
public class Client implements CompletionHandler<Integer,Integer>
{

   private static Logger log = LoggerFactory.getLogger(Client.class);
   // 异步 Socket 通道
   private AsynchronousSocketChannel asc;
   // 读写共用的缓冲区
   private ByteBuffer buffer = ByteBuffer.allocate(1024);
   // 当前要读取的文件输入流
   private FileInputStream fis;
   // 当前要读取的文件通道
   private FileChannel fc;
   // 待发送文件的最大编号
   private int maxFileNo = 2;

   public void start() throws IOException, ExecutionException, InterruptedException
   {
      // 创建 Socket 通道
      asc = AsynchronousSocketChannel.open();
      // 建立连接,用 Future 模式等待连接的建立
      Future<Void> ft = asc.connect(new InetSocketAddress("127.0.0.1",9000));
      ft.get();
      // 从第 0 个文件开始发送,也可以将文件集合的 iterator 作为参数
      this.send(0);
   }

   public void send(Integer i) throws IOException
   {
      // 此方法会反复被调用。发送新文件前,需要先确保上一个文件已经被关闭
      StreamUtil.close(fis);
      StreamUtil.close(fc);
      // 读取当前文件,参数 i 为当前要读取的文件编号
      String fn = String.format("G:/%d.txt",i);
      fis = new FileInputStream(new File(fn));
      fc = fis.getChannel();
      // 首次读取的时候,读取文件大小,并作为前 8 字节发送
      buffer.clear();
      // 要发送的字节数为文件大小 + 文件长度变量(Long 类型)所占的 8 字节
      buffer.putLong(fc.size() + 8);
      // 将文件内容读取到 buffer 。
      fc.read(buffer);
      // 将 buffer 的 limit 设置为当前 position (有效字节数),position 设置为 0
      buffer.flip();
      log.info("Write first buffer of file {}",fn);
      // 异步发送
      asc.write(buffer,i,this);
   }
   @Override
   public void completed(Integer result, Integer attachment)
   {
      if( result <= 0 )
      {
         log.info("No written data now. Quit");
         return;
      }
      log.info("Written {} bytes",result);
      try
      {
         // 上次发送完可能没发完,需要继续发送
         if(buffer.hasRemaining())
         {
            asc.write(buffer ,attachment,this);
            return;
         }
         buffer.clear();
         // 将文件内容读取到缓冲区
         if(fc.read(buffer)>0)
         {
            buffer.flip();
            asc.write(buffer ,attachment,this);
         }else
         {
            // 读不到则说明已经遇到文件尾,表示发送完成。这时可以读取服务器端的响应
            result = asc.read(buffer).get();
                log.info("Read response {} bytes ",result);
                buffer.flip();
            // 发送下一个文件
            if(attachment < maxFileNo)
            {
               this.send( attachment + 1);
            }else
            {
               // 关闭输出流,告诉服务端,数据已经发送完成
               asc.shutdownOutput();
               // 通知主线程继续执行(退出)
               synchronized (this)
               {
                       this.notify();
               }
            }
         }
      }
      catch (Exception e)
      {
         log.error("Error on send file",e);
         this.close();
      }

   }

   @Override
   public void failed(Throwable exc, Integer attachment)
   {
      // 遇到异常,直接关闭
      this.close();
      SocketAddress sa = null;
      try
      {
         sa = asc.getRemoteAddress();
      }
      catch (IOException e)
      {
         log.error("Error on getRemoteAddress",e);
      }
      log.error("Error on read from {}",sa , exc);
   }

   public void close()
   {
      StreamUtil.close(fc);
      StreamUtil.close(fis);
      StreamUtil.close(asc);
   }

   public static void main(String[] args)
   {
      Client client = new Client();
      try
      {
         client.start();
         // 阻塞主线程,等待 Client 完成传输并唤醒自己
         synchronized (client)
         {
            client.wait();
         }
      }
      catch (Exception e)
      {
         log.error("Error on run client",e);
      }finally
      {
         client.close();
      }
      System.out.println("bye");
   }
}

----------------

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

/**

 * @desc 基于Java AIO ---Server

 **/
public class Server implements CompletionHandler<AsynchronousSocketChannel,Object>
{
   private static final Logger log = LoggerFactory.getLogger(Server.class);
   private AsynchronousServerSocketChannel assc = null;
   public void start() throws IOException
   {
      //创建服务端 Socket ,开始监听
       assc = AsynchronousServerSocketChannel.open()
            .bind(new InetSocketAddress(9000));
       // 接收新连接,操作系统会在有新连接请求的时候,调用 this 的 completed()
      assc.accept(null,this);
   }
   @Override
   public void completed(AsynchronousSocketChannel asc, Object attachment)
   {
      // 继续受理一个连接
      assc.accept(null,this);
      // 为当前通道准备一个 Read 对象,此 Read 对象拥有一个 buffer , 并持有当前通道的引用
      ReadCH reader = new ReadCH(asc);
      asc.read(reader.getBuffer(),0,reader);
   }

   @Override
   public void failed(Throwable exc, Object attachment)
   {
      this.close();
      log.error("Error on accept connection",exc);
   }

   public void close()
   {
      StreamUtil.close(assc);
   }

   public static void main(String[] args)
   {
      BufferedReader br = null;
      Server server = new Server();
      try
      {
         server.start();
         String cmd = null;
         System.out.println("Enter 'exit' to exit");
         br = new BufferedReader(new InputStreamReader(System.in));
         while( (cmd = br.readLine()) != null)
         {
            if("exit".equalsIgnoreCase(cmd))
            {
               break;
            }
         }

      }
      catch (Exception e)
      {
         log.error("Error on run server",e);
      }finally
      {
         StreamUtil.close(br);
         server.close();
      }
      System.out.println("bye");
   }
}

---------------

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.channels.FileChannel;

/**

 * @desc 基于Java AIO -- Server 端的处理对象

 **/
public class ReadCH implements CompletionHandler<Integer , Integer>
{
    private static final Logger log = LoggerFactory.getLogger(ReadCH.class);
   private AsynchronousSocketChannel asc;
   private ByteBuffer buffer = ByteBuffer.allocate(1024);
   // 当前这笔数据的总字节数
   private long total;
   // 当前这笔数据已接收到的字节数
   private long received;
   private FileOutputStream fos;
   private FileChannel fc;

   public ReadCH(AsynchronousSocketChannel asc)
   {
      this.asc = asc;
   }

   public ByteBuffer getBuffer()
   {
      return buffer;
   }

   @Override
   public void completed(Integer result, Integer attachment)
   {
      if(result <= 0)
      {
         // 当客户端调用 Socket 通道的 shutdownOutput 时会进到这里
         log.info("No more incoming data now . Quit");
         return;
      }
      received += result;
      log.info("Read {}/{}/{} bytes",result,received,total);
      try
      {
         buffer.flip();
         // fc 为空,表示是一个新文件
         if(fc == null)
         {
            //首 8 个字节记录了要传输的字节总数(包括这个8个字节)
            total = buffer.getLong();
            // 打开文件通道,准备写入
            fc = fos.getChannel();
         }
         // 写入(从第 9 个字节开始)
         fc.write(buffer);
         buffer.clear();
         if( received < total)
         {
            // 没接收完就继续 read
            asc.read(buffer,attachment,this);
         }else
         {
            // 接收完则发回响应,依次是应发字节总数、已接收成功的字节数
            buffer.putLong(total);
            buffer.putLong(received);
            buffer.flip();
            //write 后等待 I/O 完成
            result = asc.write(buffer).get();
            log.info("W    ritten response {} bytes",result);
            // 重设 reader ,准备在此通道上的下一次读取
            this.reset();
            // 读取下一个文件的数据
            this.asc.read(buffer,attachment + 1 , this);
         }

      }
      catch (Exception e)
      {
         log.error("Error on receive file",e);
         this.close();
      }

   }

   @Override
   public void failed(Throwable exc, Integer attachment)
   {
      // 遇到异常,直接关闭
      this.close();
      log.error("Error on read from{}",exc);
      this.close();
   }


   public void reset()
   {
      StreamUtil.close(fos);
      fos = null;
      StreamUtil.close(fc);
      fc = null;
      buffer.clear();
      total = 0 ;
      received = 0 ;
   }

   public void close()
   {
      this.reset();
      StreamUtil.close(asc);
   }
}

---------------------------------------------

import java.io.Closeable;
import java.io.IOException;

/**
 * @desc 关闭I/O流的工具类
 **/
public class StreamUtil
{
    public static void close(Closeable p){
        if( p == null){
            return ;
        }
        try {
            p.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小达人Fighting

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

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

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

打赏作者

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

抵扣说明:

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

余额充值