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(); } } }