传输会话简要
客户端发起一个文本请求给服务器端, 服务器端解析里面文本, 返回文件给客户端, 客户端解析文件
服务器端
因为示例文件比较小, 所以没有做分段传输, 而是直接一次性把整个文件byte[]都发给客户端了.
如果需要传输大文件, 则需要做粘包拆包, 参考另外一篇博文 Netty之粘包分包
需要三个ChannelPipeline
1 // 解析客户端发送的文本json 2 pipeline.addLast(new StringDecoder()); 3 // 二进制文件加密传输 4 pipeline.addLast(new ObjectEncoder()); 5 // 业务逻辑 6 pipeline.addLast(new FileServerHandler());
FileServerHandler业务逻辑
// 获取到客户端请求, 解析path, 返回二进制文件 JSONObject jo = new JSONObject(msg.toString()); if (StringUtils.isNotEmpty(jo.optString("path"))) { PDFContent pdf = new PDFContent(); byte[] content = com.fr.general.IOUtils.inputStream2Bytes(new FileInputStream(jo.optString("path"))); pdf.setContent(content); ctx.writeAndFlush(pdf); } else { System.out.println(jo.optString("res")); }
客户端
跟服务器端对应的三个ChannelPipeline
1 // 传输文本给服务器端 2 p.addLast(new StringEncoder()); 3 // 二进制文件获取解析 4 p.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(this 5 .getClass().getClassLoader()))); 6 // 客户端业务代码 7 p.addLast(new FileClientHandler());
FileClientHandler业务逻辑
1 @Override 2 public void channelActive(ChannelHandlerContext ctx) { 3 try { 4 // 将要获取的pdf路径发送给服务器端 5 JSONObject jo = JSONObject.create().put("path", "d:\\a.pdf"); 6 ctx.writeAndFlush(jo.toString()); 7 } catch (JSONException e) { 8 e.printStackTrace(); 9 } 10 } 11 12 @Override 13 public void channelRead(ChannelHandlerContext ctx, Object msg) { 14 PDFContent content = (PDFContent) msg; 15 // 从服务器端获取的二进制文件存到本地 16 String fileName = UUID.randomUUID().toString() + ".pdf"; 17 File file = new File("D:\\" + fileName); 18 try { 19 FileOutputStream out = new FileOutputStream(file); 20 IOUtils.copyBinaryTo(new ByteArrayInputStream(content.getContent()), out); 21 out.close(); 22 } catch (FileNotFoundException e) { 23 e.printStackTrace(); 24 } catch (IOException e) { 25 e.printStackTrace(); 26 } 27 try { 28 JSONObject jo = JSONObject.create().put("res", "Thank You, I Have The File!"); 29 ctx.writeAndFlush(jo.toString()); 30 ctx.close(); 31 } catch (JSONException e) { 32 e.printStackTrace(); 33 } 34 }
完整的代码如下
FileClient & FileClientHandler
1 package test; 2 3 import com.fr.general.IOUtils; 4 import com.fr.json.JSONException; 5 import com.fr.json.JSONObject; 6 import com.fr.stable.core.UUID; 7 import io.netty.bootstrap.Bootstrap; 8 import io.netty.channel.ChannelFuture; 9 import io.netty.channel.ChannelHandlerContext; 10 import io.netty.channel.ChannelInboundHandlerAdapter; 11 import io.netty.channel.ChannelInitializer; 12 import io.netty.channel.ChannelPipeline; 13 import io.netty.channel.EventLoopGroup; 14 import io.netty.channel.nio.NioEventLoopGroup; 15 import io.netty.channel.socket.SocketChannel; 16 import io.netty.channel.socket.nio.NioSocketChannel; 17 import io.netty.handler.codec.serialization.ClassResolvers; 18 import io.netty.handler.codec.serialization.ObjectDecoder; 19 import io.netty.handler.codec.string.StringEncoder; 20 21 import java.io.ByteArrayInputStream; 22 import java.io.File; 23 import java.io.FileNotFoundException; 24 import java.io.FileOutputStream; 25 import java.io.IOException; 26 27 public class FileClient { 28 29 public FileClient(){ 30 31 } 32 33 public void start() { 34 EventLoopGroup group = new NioEventLoopGroup(); 35 try { 36 Bootstrap bootstrap = new Bootstrap(); 37 bootstrap.group(group) 38 .channel(NioSocketChannel.class) 39 .handler(new ChannelInitializer<SocketChannel>() { 40 41 @Override 42 protected void initChannel(SocketChannel s) throws Exception { 43 ChannelPipeline p = s.pipeline(); 44 // 传输文本给服务器端 45 p.addLast(new StringEncoder()); 46 // 二进制文件获取解析 47 p.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(this 48 .getClass().getClassLoader()))); 49 // 客户端业务代码 50 p.addLast(new FileClientHandler()); 51 } 52 }); 53 ChannelFuture future = bootstrap.connect("localhost", 7766).sync(); 54 future.channel().closeFuture().sync(); 55 } catch (InterruptedException e) { 56 e.printStackTrace(); 57 } finally { 58 group.shutdownGracefully(); 59 } 60 } 61 62 public static void main(String[] args) throws InterruptedException { 63 new FileClient().start(); 64 } 65 66 private static class FileClientHandler extends ChannelInboundHandlerAdapter { 67 68 69 @Override 70 public void channelActive(ChannelHandlerContext ctx) { 71 try { 72 // 将要获取的pdf路径发送给服务器端 73 JSONObject jo = JSONObject.create().put("path", "d:\\a.pdf"); 74 ctx.writeAndFlush(jo.toString()); 75 } catch (JSONException e) { 76 e.printStackTrace(); 77 } 78 } 79 80 @Override 81 public void channelRead(ChannelHandlerContext ctx, Object msg) { 82 PDFContent content = (PDFContent) msg; 83 // 从服务器端获取的二进制文件存到本地 84 String fileName = UUID.randomUUID().toString() + ".pdf"; 85 File file = new File("D:\\" + fileName); 86 try { 87 FileOutputStream out = new FileOutputStream(file); 88 IOUtils.copyBinaryTo(new ByteArrayInputStream(content.getContent()), out); 89 out.close(); 90 } catch (FileNotFoundException e) { 91 e.printStackTrace(); 92 } catch (IOException e) { 93 e.printStackTrace(); 94 } 95 try { 96 JSONObject jo = JSONObject.create().put("res", "Thank You, I Have The File!"); 97 ctx.writeAndFlush(jo.toString()); 98 ctx.close(); 99 } catch (JSONException e) { 100 e.printStackTrace(); 101 } 102 } 103 104 @Override 105 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 106 cause.printStackTrace(); 107 ctx.close(); 108 } 109 } 110 }