commit 00d1dea5be8937701152c2e8c409beecdb2fa007 Author: JianGuo Date: Fri Jun 14 13:13:23 2024 +0800 1.Netty传输工具 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/FileTransferClient/pom.xml b/FileTransferClient/pom.xml new file mode 100644 index 0000000..cb375f5 --- /dev/null +++ b/FileTransferClient/pom.xml @@ -0,0 +1,40 @@ + + 4.0.0 + + com.xiaoliu + FileTransfer + 0.0.1-SNAPSHOT + + + FileTransferClient + jar + + FileTransferClient + http://maven.apache.org + + + UTF-8 + 21 + + + + + com.xiaoliu + FileTransferCommon + 0.0.1-SNAPSHOT + + + com.formdev + flatlaf + 3.4.1 + + + com.formdev + flatlaf + 3.4.1 + windows-x86_64 + dll + + + diff --git a/FileTransferClient/src/main/java/com/xiaoliu/Client.java b/FileTransferClient/src/main/java/com/xiaoliu/Client.java new file mode 100644 index 0000000..a72dd0e --- /dev/null +++ b/FileTransferClient/src/main/java/com/xiaoliu/Client.java @@ -0,0 +1,120 @@ +package com.xiaoliu; + +import com.xiaoliu.codec.DecodeHandler; +import com.xiaoliu.codec.EncodeHandler; +import com.xiaoliu.handler.FilePacketClientHandler; +import com.xiaoliu.handler.FileSendClientHandler; +import com.xiaoliu.handler.LoginResponseHandler; +import com.xiaoliu.protocol.FilePacket; +import com.xiaoliu.protocol.request.LoginPacket; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.*; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.stream.ChunkedWriteHandler; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.util.concurrent.TimeUnit; + +@Slf4j +public class Client { + + private static final String HOST = System.getProperty("host", "127.0.0.1"); + + private static final int PORT = Integer.parseInt(System.getProperty("port", "8080")); + + private static ChannelFuture future; + + private static NioEventLoopGroup group; + + public static void init() throws Exception{ + init(HOST, PORT); + } + + public static void init(String host, int port) throws Exception{ + + if(host == null || host.isEmpty()){ + host = HOST; + } + + if(port == 0){ + port = PORT; + } + + log.info("客户端启动开始..."); + + Bootstrap bootstrap = new Bootstrap(); + + group = new NioEventLoopGroup(2); + + bootstrap.group(group) + .channel(NioSocketChannel.class) + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000) + .option(ChannelOption.SO_KEEPALIVE, true) + .option(ChannelOption.TCP_NODELAY, true) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(NioSocketChannel channel) throws Exception { + ChannelPipeline pipeline = channel.pipeline(); + //pipeline.addLast(new FileReceiveClientHandler()); + pipeline.addLast(new FileSendClientHandler()); + pipeline.addLast(new DecodeHandler()); + pipeline.addLast(new EncodeHandler()); + pipeline.addLast(new ChunkedWriteHandler()); + pipeline.addLast(new LoginResponseHandler()); + pipeline.addLast(new FilePacketClientHandler()); + // pipeline.addLast(new MyClientHandler()); + } + }); + + future = bootstrap.connect(host, port).sync(); + if (future.isSuccess()) { + log.info("连接服务器成功"); + Channel channel = future.channel(); + joinCluster(channel); + + } else { + log.info("连接服务器失败"); + } + + future.channel().closeFuture().sync(); + } + + private static void joinCluster(Channel channel){ + LoginPacket loginPacket = new LoginPacket("node1"); + channel.writeAndFlush(loginPacket); + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + public static ChannelFuture getFuture() { + if(future == null){ + throw new RuntimeException("请先使用init方法,连接到服务器..."); + } + return future; + } + + public static void send(File file){ + ChannelFuture future1 = getFuture(); + send(future1.channel(), file); + } + + public static void send(Channel channel, File file) { + FilePacket filePacket = new FilePacket(file); + channel.writeAndFlush(filePacket); + } + + public static void shutdown(){ + if(future != null){ + future.channel().close(); + } + if(group != null){ + group.shutdownGracefully(10, 30, TimeUnit.SECONDS); + } + } +} + diff --git a/FileTransferClient/src/main/java/com/xiaoliu/console/SendFileConsole.java b/FileTransferClient/src/main/java/com/xiaoliu/console/SendFileConsole.java new file mode 100644 index 0000000..c1f2396 --- /dev/null +++ b/FileTransferClient/src/main/java/com/xiaoliu/console/SendFileConsole.java @@ -0,0 +1,26 @@ +package com.xiaoliu.console; + +import com.xiaoliu.protocol.FilePacket; +import io.netty.channel.Channel; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.util.Scanner; + +@Slf4j +public class SendFileConsole { + + public static void exec(Channel channel) { + Scanner sc = new Scanner(System.in); + log.info("请输入文件路径:"); + String path = sc.nextLine(); + File file = new File(path); + log.info("文件存在吗:{}", file.exists()); + + log.info("文件大小:{}", file.length()); + + FilePacket filePacket = new FilePacket(file); + channel.writeAndFlush(filePacket); + } + +} diff --git a/FileTransferClient/src/main/java/com/xiaoliu/handler/FilePacketClientHandler.java b/FileTransferClient/src/main/java/com/xiaoliu/handler/FilePacketClientHandler.java new file mode 100644 index 0000000..7850dac --- /dev/null +++ b/FileTransferClient/src/main/java/com/xiaoliu/handler/FilePacketClientHandler.java @@ -0,0 +1,24 @@ +package com.xiaoliu.handler; + +import com.xiaoliu.protocol.FilePacket; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.FileOutputStream; + +@Slf4j +public class FilePacketClientHandler extends SimpleChannelInboundHandler { + @Override + protected void channelRead0(ChannelHandlerContext ctx, FilePacket packet) throws Exception { + File file = packet.getFile(); + log.info("receive file from server: {}", file.getName()); + FileReceiveClientHandler.fileLength = file.length(); + FileReceiveClientHandler.outputStream = new FileOutputStream( + new File("./client-receive-" + file.getName()) + ); + packet.setACK(packet.getACK() + 1); + ctx.writeAndFlush(packet); + } +} diff --git a/FileTransferClient/src/main/java/com/xiaoliu/handler/FileReceiveClientHandler.java b/FileTransferClient/src/main/java/com/xiaoliu/handler/FileReceiveClientHandler.java new file mode 100644 index 0000000..d833766 --- /dev/null +++ b/FileTransferClient/src/main/java/com/xiaoliu/handler/FileReceiveClientHandler.java @@ -0,0 +1,47 @@ +package com.xiaoliu.handler; + +import com.xiaoliu.codec.Codec; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import lombok.extern.slf4j.Slf4j; + +import java.io.FileOutputStream; +import java.io.IOException; + +@Slf4j +public class FileReceiveClientHandler extends ChannelInboundHandlerAdapter { + + static FileOutputStream outputStream; + + static long fileLength; + + private static long readLength; + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + ByteBuf byteBuf = (ByteBuf) msg; + int type = byteBuf.getInt(0); + if (type != Codec.TYPE) { + readLength += byteBuf.readableBytes(); + writeToFile(byteBuf); + sendComplete(readLength); + } else { + super.channelRead(ctx, msg); + } + } + + private void writeToFile(ByteBuf byteBuf) throws IOException { + byte[] bytes = new byte[byteBuf.readableBytes()]; + byteBuf.readBytes(bytes); + outputStream.write(bytes); + byteBuf.release(); + } + + private void sendComplete(long readLength) throws IOException { + if (readLength >= fileLength) { + log.info("文件接收完成..."); + outputStream.close(); + } + } +} diff --git a/FileTransferClient/src/main/java/com/xiaoliu/handler/FileSendClientHandler.java b/FileTransferClient/src/main/java/com/xiaoliu/handler/FileSendClientHandler.java new file mode 100644 index 0000000..10329bb --- /dev/null +++ b/FileTransferClient/src/main/java/com/xiaoliu/handler/FileSendClientHandler.java @@ -0,0 +1,51 @@ +package com.xiaoliu.handler; + +import com.xiaoliu.codec.Codec; +import com.xiaoliu.protocol.FilePacket; +import com.xiaoliu.protocol.Packet; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.DefaultFileRegion; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; + +@Slf4j +@ChannelHandler.Sharable +public class FileSendClientHandler extends ChannelInboundHandlerAdapter { + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + ByteBuf byteBuf = (ByteBuf) msg; + int type = byteBuf.getInt(0); + if (type == Codec.TYPE) { + Packet packet = Codec.INSTANCE.decode(byteBuf); + if (packet instanceof FilePacket) { + FilePacket filePacket = (FilePacket) packet; + if (filePacket.getACK() != 0) { + writeAndFlushFileRegion(ctx, filePacket); + } else { + super.channelRead(ctx, packet); + } + } else { + super.channelRead(ctx, packet); + } + } else { + log.info("无法识别此类数据包"); + } + } + + private void writeAndFlushFileRegion(ChannelHandlerContext ctx, FilePacket packet) { + File file = packet.getFile(); + DefaultFileRegion fileRegion = new DefaultFileRegion(file, 0, file.length()); + ctx.writeAndFlush(fileRegion).addListener(future -> { + if (future.isSuccess()) { + log.info("{} 发送完成...", file.getName()); + } + }); + } + + +} diff --git a/FileTransferClient/src/main/java/com/xiaoliu/handler/LoginResponseHandler.java b/FileTransferClient/src/main/java/com/xiaoliu/handler/LoginResponseHandler.java new file mode 100644 index 0000000..169e37e --- /dev/null +++ b/FileTransferClient/src/main/java/com/xiaoliu/handler/LoginResponseHandler.java @@ -0,0 +1,17 @@ +package com.xiaoliu.handler; + +import com.xiaoliu.protocol.response.LoginResponsePacket; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; + +import java.util.Date; + +@ChannelHandler.Sharable +public class LoginResponseHandler extends SimpleChannelInboundHandler { + @Override + protected void channelRead0(ChannelHandlerContext ctx, LoginResponsePacket packet) throws Exception { + System.out.println(new Date() + " " + packet.getId() + " " + packet.getName() + " 登陆成功"); + } +} + diff --git a/FileTransferClient/src/main/java/com/xiaoliu/handler/MyClientHandler.java b/FileTransferClient/src/main/java/com/xiaoliu/handler/MyClientHandler.java new file mode 100644 index 0000000..1a18c25 --- /dev/null +++ b/FileTransferClient/src/main/java/com/xiaoliu/handler/MyClientHandler.java @@ -0,0 +1,30 @@ +package com.xiaoliu.handler; + +import com.xiaoliu.codec.Codec; +import com.xiaoliu.protocol.FilePacket; +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.stream.ChunkedFile; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; + +@Slf4j +public class MyClientHandler extends ChannelInboundHandlerAdapter { + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + log.info("msg : {}", msg.toString()); + ByteBuf byteBuf = (ByteBuf) msg; + FilePacket filePacket = (FilePacket) Codec.INSTANCE.decode(byteBuf); + File file = filePacket.getFile(); + log.info("prepared send: {}", file.getName()); + + Channel channel = ctx.channel(); + channel.writeAndFlush(new ChunkedFile(filePacket.getFile())); + // channel.writeAndFlush(new DefaultFileRegion(file, 0, file.length())); + + } +} diff --git a/FileTransferClient/src/main/java/com/xiaoliu/window/MainWindow.java b/FileTransferClient/src/main/java/com/xiaoliu/window/MainWindow.java new file mode 100644 index 0000000..30e79bb --- /dev/null +++ b/FileTransferClient/src/main/java/com/xiaoliu/window/MainWindow.java @@ -0,0 +1,185 @@ +package com.xiaoliu.window; + +import com.xiaoliu.Client; +import lombok.extern.slf4j.Slf4j; +import com.formdev.flatlaf.FlatDarculaLaf; + +import javax.swing.*; +import java.awt.*; +import java.io.File; + +@Slf4j +public class MainWindow extends JFrame { + + private static JTextArea logTextArea; + + private static JLabel statusLabel; + + public MainWindow() { + // 设置窗口标题 + setTitle("影像Netty客户端"); + + // 设置默认的关闭操作 + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + setVisible(true); + + // 设置窗口大小 + setSize(500, 400); + + // 居中显示窗口 + setLocationRelativeTo(null); + + //初始化状态组件 + initStatusLabel(); + + // 初始化UI组件 + initializeComponents(); + } + + private void initStatusLabel() { + statusLabel = new JLabel(); + statusLabel.setForeground(Color.RED); + statusLabel.setText("未连接"); + } + + public void setStatusLabel(String text, Color color ){ + if(statusLabel == null){ + initStatusLabel(); + } + statusLabel.setText(text); + statusLabel.setForeground(color); + } + + private void initializeComponents() { + // 在这里添加你的组件初始化代码 + // 创建一个JTextArea用于显示日志 + logTextArea = new JTextArea(); + logTextArea.setEditable(false); // 设置为不可编辑 + logTextArea.setEnabled(false); + + // 将JTextArea放入JScrollPane中以支持滚动 + JScrollPane scrollPane = new JScrollPane(logTextArea); + + // 将JScrollPane添加到窗口中 + getContentPane().add(scrollPane, BorderLayout.CENTER); + JLabel adress = new JLabel(); + adress.setText("地址:"); + JTextField textField = new JTextField(20); // 参数20指定了文本框的列数 + JButton sendButton = new JButton("连接"); + JButton stopButton = new JButton("断开"); + stopButton.setEnabled(false); + sendButton.addActionListener(e ->{ + String text = textField.getText(); + String[] split = text.split(":"); + if(split.length < 2){ + JOptionPane.showMessageDialog(this, "请输入正确地址", "输入错误", JOptionPane.ERROR_MESSAGE); + return; + } + String host = split[0]; + String port = split[1]; + setLogText("连接到: "+ host + ":" + port); + new Thread(() -> { + //新起一个县城去初始化netty + try { + sendButton.setEnabled(false); + setLogText("连接成功..."); + setStatusLabel("已连接", Color.GREEN); + stopButton.setEnabled(true); + Client.init(host, Integer.parseInt(port)); + } catch (Exception ex) { + setLogText("连接失败..."); + stopButton.setEnabled(false); + setStatusLabel("已连接", Color.RED); + throw new RuntimeException(ex); + } + }).start(); + }); + + JPanel northPanel = new JPanel(); + northPanel.add(adress); + northPanel.add(textField); + northPanel.add(sendButton); + northPanel.add(statusLabel, BorderLayout.WEST); + + getContentPane().add(northPanel, BorderLayout.NORTH); + + JPanel bottomPanel = new JPanel(); + + // 示例:添加一个按钮,点击时向文本区域添加日志信息 + JButton startButton = new JButton("发送"); + + + startButton.addActionListener(e -> { + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setCurrentDirectory(new java.io.File(System.getProperty("user.home") + "/Desktop")); + + // 显示文件选择对话框,返回值为用户操作的结果 + int result = fileChooser.showOpenDialog(this); + + // 如果用户点击了"打开"按钮 + if (result == JFileChooser.APPROVE_OPTION) { + // 获取选中的文件路径 + String selectedFilePath = fileChooser.getSelectedFile().getPath(); + File file = new File(selectedFilePath); + log.info("文件存在吗:{}", file.exists()); + log.info("文件大小:{}", file.length()); + new Thread(() -> { + //开一个新县城去泡发送程序 + setLogText("文件发送开始: "+selectedFilePath); + Client.send(file); + }).start(); + + } + }); + + stopButton.addActionListener(e -> { + setLogText("连接中止..."); + Client.shutdown(); + setStatusLabel("未连接", Color.RED); + sendButton.setEnabled(true); + }); + + + bottomPanel.add(startButton, BorderLayout.CENTER); + bottomPanel.add(stopButton, BorderLayout.CENTER); + getContentPane().add(bottomPanel, BorderLayout.SOUTH); + + } + + public static void main(String[] args) { + log.info("服务启动中..."); + try { + UIManager.setLookAndFeel(new FlatDarculaLaf()); + } catch (UnsupportedLookAndFeelException e) { + System.err.println("UnsupportedLookAndFeelException: " + e.getMessage()); + } + SwingUtilities.invokeLater(() -> new MainWindow().setVisible(true)); + } + + public JTextArea getLogTextArea(){ + if(logTextArea == null){ + initLogTextArea(); + } + return logTextArea; + } + + public void setLogText(String text){ + logTextArea.append(text + "\n"); + } + + public void initLogTextArea(){ + // 在这里添加你的组件初始化代码 + // 创建一个JTextArea用于显示日志 + logTextArea = new JTextArea(); + logTextArea.setEditable(false); // 设置为不可编辑 + logTextArea.setEnabled(false); + + // 将JTextArea放入JScrollPane中以支持滚动 + JScrollPane scrollPane = new JScrollPane(logTextArea); + + // 将JScrollPane添加到窗口中 + getContentPane().add(scrollPane, BorderLayout.CENTER); + } + +} diff --git a/FileTransferClient/src/main/resources/simplelogger.properties b/FileTransferClient/src/main/resources/simplelogger.properties new file mode 100644 index 0000000..ab4d629 --- /dev/null +++ b/FileTransferClient/src/main/resources/simplelogger.properties @@ -0,0 +1,14 @@ +# ???????? +org.slf4j.simpleLogger.defaultLogLevel=INFO +# ?????? class ????????????,?????? INFO +# org.slf4j.simpleLogger.log.com.baomidou.mybatisplus.generator=DEBUG +# ???? +org.slf4j.simpleLogger.showDateTime=true +# ???? +org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss +# ??? +# org.slf4j.simpleLogger.showThreadName=true +# ??? +org.slf4j.simpleLogger.showLogName=true +# ????? +#org.slf4j.simpleLogger.showShortLogName=false \ No newline at end of file diff --git a/FileTransferClient/src/test/java/com/xiaoliu/AppTest.java b/FileTransferClient/src/test/java/com/xiaoliu/AppTest.java new file mode 100644 index 0000000..974da25 --- /dev/null +++ b/FileTransferClient/src/test/java/com/xiaoliu/AppTest.java @@ -0,0 +1,38 @@ +package com.xiaoliu; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Unit test for simple App. + */ +public class AppTest + extends TestCase +{ + /** + * Create the test case + * + * @param testName name of the test case + */ + public AppTest( String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( AppTest.class ); + } + + /** + * Rigourous Test :-) + */ + public void testApp() + { + assertTrue( true ); + } +} diff --git a/FileTransferCommon/pom.xml b/FileTransferCommon/pom.xml new file mode 100644 index 0000000..b3ce6e4 --- /dev/null +++ b/FileTransferCommon/pom.xml @@ -0,0 +1,36 @@ + + 4.0.0 + + com.xiaoliu + FileTransfer + 0.0.1-SNAPSHOT + + + FileTransferCommon + jar + + FileTransferCommon + http://maven.apache.org + + + UTF-8 + 21 + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + ${project.java.version} + ${project.java.version} + + + + + diff --git a/FileTransferCommon/src/main/java/com/xiaoliu/codec/Codec.java b/FileTransferCommon/src/main/java/com/xiaoliu/codec/Codec.java new file mode 100644 index 0000000..0ac9713 --- /dev/null +++ b/FileTransferCommon/src/main/java/com/xiaoliu/codec/Codec.java @@ -0,0 +1,58 @@ +package com.xiaoliu.codec; + +import com.xiaoliu.protocol.FilePacket; +import com.xiaoliu.protocol.Packet; +import com.xiaoliu.protocol.request.LoginPacket; +import com.xiaoliu.protocol.response.LoginResponsePacket; +import com.xiaoliu.protocol.serilizer.Serilizer; +import io.netty.buffer.ByteBuf; + +import java.util.HashMap; +import java.util.Map; + +import static com.xiaoliu.protocol.command.Command.*; + + +public class Codec { + + public static final int TYPE = 0x12345678; + + private final Map> packetTypeMap; + + public static Codec INSTANCE = new Codec(); + + private Codec() { + packetTypeMap = new HashMap<>(); + packetTypeMap.put(FILE_PACKET, FilePacket.class); + packetTypeMap.put(LOGIN_PACKET_REQUEST, LoginPacket.class); + packetTypeMap.put(LOGIN_PACKET_RESPONSE, LoginResponsePacket.class); + } + + public void encode(ByteBuf byteBuf, Packet packet) { + byte[] bytes = Serilizer.DEFAULT.serilize(packet); + byteBuf.writeInt(TYPE); + byteBuf.writeByte(packet.getCommand()); + byteBuf.writeInt(bytes.length); + byteBuf.writeBytes(bytes); + // return byteBuf; + } + + public Packet decode(ByteBuf byteBuf) { + byteBuf.readInt(); + Byte command = byteBuf.readByte(); + int len = byteBuf.readInt(); + byte[] bytes = new byte[len]; + byteBuf.readBytes(bytes); + byteBuf.release(); + Class clazz = packetTypeMap.get(command); + if (clazz == null) { + throw new NullPointerException("解析失败,没有该类型的数据包"); + } + + return (Packet) Serilizer.DEFAULT.deSerilize(bytes, clazz); + + } + + + +} diff --git a/FileTransferCommon/src/main/java/com/xiaoliu/codec/CodecHandler.java b/FileTransferCommon/src/main/java/com/xiaoliu/codec/CodecHandler.java new file mode 100644 index 0000000..1d6ace0 --- /dev/null +++ b/FileTransferCommon/src/main/java/com/xiaoliu/codec/CodecHandler.java @@ -0,0 +1,39 @@ +package com.xiaoliu.codec; + +import com.xiaoliu.protocol.Packet; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageCodec; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +@Slf4j +@ChannelHandler.Sharable +public class CodecHandler extends MessageToMessageCodec { + + @Override + protected void encode(ChannelHandlerContext ctx, Object o, List list) throws Exception { + if (o instanceof Packet) { + ByteBuf byteBuf = ctx.channel().alloc().ioBuffer(); + Codec.INSTANCE.encode(byteBuf, (Packet) o); + list.add(byteBuf); + } else { + log.info("File ByteBuf need encode"); + // ctx.writeAndFlush(o); + } + } + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List list) throws Exception { + if (byteBuf.getInt(0) == Codec.TYPE) { + System.out.println("decode FilePacket"); + list.add(Codec.INSTANCE.decode(byteBuf)); + } else { + log.info("File ByteBuf need decode"); + // list.add(byteBuf); + ctx.fireChannelRead(byteBuf); + } + } +} diff --git a/FileTransferCommon/src/main/java/com/xiaoliu/codec/DecodeHandler.java b/FileTransferCommon/src/main/java/com/xiaoliu/codec/DecodeHandler.java new file mode 100644 index 0000000..95f41a4 --- /dev/null +++ b/FileTransferCommon/src/main/java/com/xiaoliu/codec/DecodeHandler.java @@ -0,0 +1,15 @@ +package com.xiaoliu.codec; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; + +import java.util.List; + + +public class DecodeHandler extends ByteToMessageDecoder { + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List list) throws Exception { + list.add(Codec.INSTANCE.decode(byteBuf)); + } +} diff --git a/FileTransferCommon/src/main/java/com/xiaoliu/codec/EncodeHandler.java b/FileTransferCommon/src/main/java/com/xiaoliu/codec/EncodeHandler.java new file mode 100644 index 0000000..c4616b7 --- /dev/null +++ b/FileTransferCommon/src/main/java/com/xiaoliu/codec/EncodeHandler.java @@ -0,0 +1,15 @@ +package com.xiaoliu.codec; + +import com.xiaoliu.protocol.Packet; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; + +@ChannelHandler.Sharable +public class EncodeHandler extends MessageToByteEncoder { + @Override + protected void encode(ChannelHandlerContext ctx, Object o, ByteBuf byteBuf) throws Exception { + Codec.INSTANCE.encode(byteBuf, (Packet) o); + } +} diff --git a/FileTransferCommon/src/main/java/com/xiaoliu/protocol/FilePacket.java b/FileTransferCommon/src/main/java/com/xiaoliu/protocol/FilePacket.java new file mode 100644 index 0000000..2af5968 --- /dev/null +++ b/FileTransferCommon/src/main/java/com/xiaoliu/protocol/FilePacket.java @@ -0,0 +1,47 @@ +package com.xiaoliu.protocol; + + +import java.io.File; + +import static com.xiaoliu.protocol.command.Command.FILE_PACKET; + + +public class FilePacket extends Packet { + + File file; + + int ACK; + + @Override + public Byte getCommand() { + return FILE_PACKET; + } + + public FilePacket() { + } + + public FilePacket(File file) { + this.file = file; + } + + public FilePacket(File file, int ACK) { + this.file = file; + this.ACK = ACK; + } + + public File getFile() { + return file; + } + + public void setFile(File file) { + this.file = file; + } + + public int getACK() { + return ACK; + } + + public void setACK(int ACK) { + this.ACK = ACK; + } +} diff --git a/FileTransferCommon/src/main/java/com/xiaoliu/protocol/Packet.java b/FileTransferCommon/src/main/java/com/xiaoliu/protocol/Packet.java new file mode 100644 index 0000000..8b8c18a --- /dev/null +++ b/FileTransferCommon/src/main/java/com/xiaoliu/protocol/Packet.java @@ -0,0 +1,7 @@ +package com.xiaoliu.protocol; + +public abstract class Packet { + + public abstract Byte getCommand(); + +} diff --git a/FileTransferCommon/src/main/java/com/xiaoliu/protocol/attribute/Attributes.java b/FileTransferCommon/src/main/java/com/xiaoliu/protocol/attribute/Attributes.java new file mode 100644 index 0000000..ea49e43 --- /dev/null +++ b/FileTransferCommon/src/main/java/com/xiaoliu/protocol/attribute/Attributes.java @@ -0,0 +1,13 @@ +package com.xiaoliu.protocol.attribute; + +import com.xiaoliu.protocol.request.LoginPacket; +import com.xiaoliu.protocol.session.Session; +import io.netty.util.AttributeKey; + +public interface Attributes { + + AttributeKey SESSION = AttributeKey.newInstance("session"); + + AttributeKey userAttr = AttributeKey.newInstance("user"); + +} diff --git a/FileTransferCommon/src/main/java/com/xiaoliu/protocol/command/Command.java b/FileTransferCommon/src/main/java/com/xiaoliu/protocol/command/Command.java new file mode 100644 index 0000000..89db599 --- /dev/null +++ b/FileTransferCommon/src/main/java/com/xiaoliu/protocol/command/Command.java @@ -0,0 +1,11 @@ +package com.xiaoliu.protocol.command; + +public interface Command { + + Byte FILE_PACKET = 1; + + Byte LOGIN_PACKET_REQUEST = 2; + + Byte LOGIN_PACKET_RESPONSE = 3; + +} diff --git a/FileTransferCommon/src/main/java/com/xiaoliu/protocol/request/LoginPacket.java b/FileTransferCommon/src/main/java/com/xiaoliu/protocol/request/LoginPacket.java new file mode 100644 index 0000000..ec1d2e1 --- /dev/null +++ b/FileTransferCommon/src/main/java/com/xiaoliu/protocol/request/LoginPacket.java @@ -0,0 +1,46 @@ +package com.xiaoliu.protocol.request; + +import com.xiaoliu.protocol.Packet; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +import java.io.FileOutputStream; + +import static com.xiaoliu.protocol.command.Command.LOGIN_PACKET_REQUEST; + +@EqualsAndHashCode(callSuper = true) +@Data +public class LoginPacket extends Packet { + + String name; + + String id; + + String fileName; + + long fileLength; + + long readLength; + + FileOutputStream fileOutputStream; + + boolean exec = false; + + @Override + public Byte getCommand() { + return LOGIN_PACKET_REQUEST; + } + + public LoginPacket() { + } + + public LoginPacket(String name) { + this.name = name; + } + + public LoginPacket(String name, String id) { + this.name = name; + this.id = id; + } +} diff --git a/FileTransferCommon/src/main/java/com/xiaoliu/protocol/response/LoginResponsePacket.java b/FileTransferCommon/src/main/java/com/xiaoliu/protocol/response/LoginResponsePacket.java new file mode 100644 index 0000000..4bce660 --- /dev/null +++ b/FileTransferCommon/src/main/java/com/xiaoliu/protocol/response/LoginResponsePacket.java @@ -0,0 +1,39 @@ +package com.xiaoliu.protocol.response; + +import com.xiaoliu.protocol.Packet; +import static com.xiaoliu.protocol.command.Command.LOGIN_PACKET_RESPONSE; + +public class LoginResponsePacket extends Packet { + + String id; + String name; + + public LoginResponsePacket() { + } + + public LoginResponsePacket(String id, String name) { + this.id = id; + this.name = name; + } + + @Override + public Byte getCommand() { + return LOGIN_PACKET_RESPONSE; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/FileTransferCommon/src/main/java/com/xiaoliu/protocol/serilizer/Serilizer.java b/FileTransferCommon/src/main/java/com/xiaoliu/protocol/serilizer/Serilizer.java new file mode 100644 index 0000000..5d67e3f --- /dev/null +++ b/FileTransferCommon/src/main/java/com/xiaoliu/protocol/serilizer/Serilizer.java @@ -0,0 +1,14 @@ +package com.xiaoliu.protocol.serilizer; + + +import com.xiaoliu.protocol.serilizer.impl.JSONSerilizer; + +public interface Serilizer { + + Serilizer DEFAULT = new JSONSerilizer(); + + byte[] serilize(Object object); + + T deSerilize(byte[] bytes, Class clazz); + +} diff --git a/FileTransferCommon/src/main/java/com/xiaoliu/protocol/serilizer/impl/JSONSerilizer.java b/FileTransferCommon/src/main/java/com/xiaoliu/protocol/serilizer/impl/JSONSerilizer.java new file mode 100644 index 0000000..1d62b1d --- /dev/null +++ b/FileTransferCommon/src/main/java/com/xiaoliu/protocol/serilizer/impl/JSONSerilizer.java @@ -0,0 +1,18 @@ +package com.xiaoliu.protocol.serilizer.impl; + + +import com.alibaba.fastjson.JSON; +import com.xiaoliu.protocol.serilizer.Serilizer; + +public class JSONSerilizer implements Serilizer { + + @Override + public byte[] serilize(Object object){ + return JSON.toJSONBytes(object); + } + + @Override + public T deSerilize(byte[] bytes, Class clazz){ + return JSON.parseObject(bytes, clazz); + } +} diff --git a/FileTransferCommon/src/main/java/com/xiaoliu/protocol/session/Session.java b/FileTransferCommon/src/main/java/com/xiaoliu/protocol/session/Session.java new file mode 100644 index 0000000..80fc2f9 --- /dev/null +++ b/FileTransferCommon/src/main/java/com/xiaoliu/protocol/session/Session.java @@ -0,0 +1,38 @@ +package com.xiaoliu.protocol.session; + +public class Session { + + String nodeId; + String nodeName; + + public Session() { + } + + public Session(String nodeId, String nodeName) { + this.nodeId = nodeId; + this.nodeName = nodeName; + } + + public String getNodeId() { + return nodeId; + } + + public void setNodeId(String nodeId) { + this.nodeId = nodeId; + } + + public String getNodeName() { + return nodeName; + } + + public void setNodeName(String nodeName) { + this.nodeName = nodeName; + } + + @Override + public String toString() { + return "Session{" + + "nodeName='" + nodeName + "-" + nodeId + '\'' + + '}'; + } +} diff --git a/FileTransferCommon/src/main/resources/simplelogger.properties b/FileTransferCommon/src/main/resources/simplelogger.properties new file mode 100644 index 0000000..ab4d629 --- /dev/null +++ b/FileTransferCommon/src/main/resources/simplelogger.properties @@ -0,0 +1,14 @@ +# ???????? +org.slf4j.simpleLogger.defaultLogLevel=INFO +# ?????? class ????????????,?????? INFO +# org.slf4j.simpleLogger.log.com.baomidou.mybatisplus.generator=DEBUG +# ???? +org.slf4j.simpleLogger.showDateTime=true +# ???? +org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss +# ??? +# org.slf4j.simpleLogger.showThreadName=true +# ??? +org.slf4j.simpleLogger.showLogName=true +# ????? +#org.slf4j.simpleLogger.showShortLogName=false \ No newline at end of file diff --git a/FileTransferCommon/src/test/java/com/xiaoliu/AppTest.java b/FileTransferCommon/src/test/java/com/xiaoliu/AppTest.java new file mode 100644 index 0000000..974da25 --- /dev/null +++ b/FileTransferCommon/src/test/java/com/xiaoliu/AppTest.java @@ -0,0 +1,38 @@ +package com.xiaoliu; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Unit test for simple App. + */ +public class AppTest + extends TestCase +{ + /** + * Create the test case + * + * @param testName name of the test case + */ + public AppTest( String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( AppTest.class ); + } + + /** + * Rigourous Test :-) + */ + public void testApp() + { + assertTrue( true ); + } +} diff --git a/FileTransferService/pom.xml b/FileTransferService/pom.xml new file mode 100644 index 0000000..51bfe22 --- /dev/null +++ b/FileTransferService/pom.xml @@ -0,0 +1,102 @@ + + 4.0.0 + + com.xiaoliu + FileTransfer + 0.0.1-SNAPSHOT + + + FileTransferService + jar + + FileTransferService + http://maven.apache.org + + + UTF-8 + 21 + + + + + com.xiaoliu + FileTransferCommon + 0.0.1-SNAPSHOT + + + + + sane-service-v${project.version} + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + ${project.build.finalName} + + + com.xiaoliu.Server + + + + jar-with-dependencies + + false + + + + make-assembly + package + + single + + + + + + io.github.fvarrui + javapackager + 1.7.5 + + + package + + package + + + + com.xiaoliu.Server + + true + true + false + windows + netty + 服务端软件 + + + + + + + diff --git a/FileTransferService/src/main/java/com/xiaoliu/Server.java b/FileTransferService/src/main/java/com/xiaoliu/Server.java new file mode 100644 index 0000000..f764c7b --- /dev/null +++ b/FileTransferService/src/main/java/com/xiaoliu/Server.java @@ -0,0 +1,74 @@ +package com.xiaoliu; + +import com.xiaoliu.codec.DecodeHandler; +import com.xiaoliu.codec.EncodeHandler; +import com.xiaoliu.console.ConsoleManager; +import com.xiaoliu.console.impl.SendFileConsole; +import com.xiaoliu.handler.FilePacketServerHandler; +import com.xiaoliu.handler.FileReceiveServerHandler; +import com.xiaoliu.handler.FileSendServerHandler; +import com.xiaoliu.handler.JoinClusterRequestHandler; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.*; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import lombok.extern.slf4j.Slf4j; + +import java.util.Scanner; + +@Slf4j +public class Server { + + private static final int PORT = Integer.parseInt(System.getProperty("port", "8080")); + + public static void main(String[] args) throws InterruptedException { + init(); + } + + public static void init() throws InterruptedException { + init(PORT); + } + + public static void init(int port) throws InterruptedException { + + if(port == 0){ + port = 8080; + } + + log.info("服务启动开始..."); + + ServerBootstrap bootstrap = new ServerBootstrap(); + + EventLoopGroup boss = new NioEventLoopGroup(2); + EventLoopGroup worker = new NioEventLoopGroup(10); + + bootstrap.group(boss, worker) + .channel(NioServerSocketChannel.class) + .option(ChannelOption.SO_BACKLOG, 1024) + .option(ChannelOption.TCP_NODELAY, true) + .childHandler(new ChannelInitializer() { + @Override + protected void initChannel(NioSocketChannel channel) { + ChannelPipeline pipeline = channel.pipeline(); + pipeline.addLast(new FileReceiveServerHandler()); + pipeline.addLast(new FileSendServerHandler()); + pipeline.addLast(new DecodeHandler()); + pipeline.addLast(new EncodeHandler()); + pipeline.addLast(new JoinClusterRequestHandler()); + pipeline.addLast(new FilePacketServerHandler()); + // pipeline.addLast("handler", new MyServerHandler()); + } + }); + + ChannelFuture future = bootstrap.bind(port).sync(); + if (future.isSuccess()) { + log.info("端口绑定成功"); + } else { + log.info("端口绑定失败"); + } + + future.channel().closeFuture().sync(); + } + +} diff --git a/FileTransferService/src/main/java/com/xiaoliu/console/Console.java b/FileTransferService/src/main/java/com/xiaoliu/console/Console.java new file mode 100644 index 0000000..316480d --- /dev/null +++ b/FileTransferService/src/main/java/com/xiaoliu/console/Console.java @@ -0,0 +1,11 @@ +package com.xiaoliu.console; + +import io.netty.channel.Channel; + +import java.util.Scanner; + +public interface Console { + + void exec(Channel channel, Scanner scanner); + +} diff --git a/FileTransferService/src/main/java/com/xiaoliu/console/ConsoleManager.java b/FileTransferService/src/main/java/com/xiaoliu/console/ConsoleManager.java new file mode 100644 index 0000000..76724c8 --- /dev/null +++ b/FileTransferService/src/main/java/com/xiaoliu/console/ConsoleManager.java @@ -0,0 +1,33 @@ +package com.xiaoliu.console; + +import com.xiaoliu.console.impl.SendFileConsole; +import io.netty.channel.Channel; +import lombok.extern.slf4j.Slf4j; + +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; + +@Slf4j +public class ConsoleManager implements Console { + + private Map consoleMap; + + public ConsoleManager() { + consoleMap = new HashMap<>(); + consoleMap.put("sendFile", new SendFileConsole()); + } + + @Override + public void exec(Channel channel, Scanner scanner) { + String consoleType = scanner.nextLine(); + Console console = consoleMap.get(consoleType); + if (console != null) { + console.exec(channel, scanner); + } else { + log.info("无法识别指令:{}", consoleType); + } + + } + +} diff --git a/FileTransferService/src/main/java/com/xiaoliu/console/impl/SendFileConsole.java b/FileTransferService/src/main/java/com/xiaoliu/console/impl/SendFileConsole.java new file mode 100644 index 0000000..1ee4cb7 --- /dev/null +++ b/FileTransferService/src/main/java/com/xiaoliu/console/impl/SendFileConsole.java @@ -0,0 +1,28 @@ +package com.xiaoliu.console.impl; + +import com.xiaoliu.console.Console; +import com.xiaoliu.protocol.FilePacket; +import com.xiaoliu.util.SessionUtil; +import io.netty.channel.Channel; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.util.Map; +import java.util.Scanner; + +@Slf4j +public class SendFileConsole implements Console { + @Override + public void exec(Channel channel, Scanner scanner) { + log.info("请输入文件路径:"); + String path = scanner.nextLine(); + + File file = new File(path); + FilePacket filePacket = new FilePacket(file); + + Map channelMap = SessionUtil.getNodeIdChannelMap(); + for (Map.Entry entry : channelMap.entrySet()) { + entry.getValue().writeAndFlush(filePacket); + } + } +} diff --git a/FileTransferService/src/main/java/com/xiaoliu/handler/FilePacketServerHandler.java b/FileTransferService/src/main/java/com/xiaoliu/handler/FilePacketServerHandler.java new file mode 100644 index 0000000..85fdf7a --- /dev/null +++ b/FileTransferService/src/main/java/com/xiaoliu/handler/FilePacketServerHandler.java @@ -0,0 +1,37 @@ +package com.xiaoliu.handler; + +import com.xiaoliu.protocol.FilePacket; +import com.xiaoliu.protocol.request.LoginPacket; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.FileOutputStream; + +import static com.xiaoliu.protocol.attribute.Attributes.userAttr; + +@Slf4j +@ChannelHandler.Sharable +public class FilePacketServerHandler extends SimpleChannelInboundHandler { + @Override + protected void channelRead0(ChannelHandlerContext ctx, FilePacket packet) throws Exception { + File file = packet.getFile(); + log.info("receive file from client: {}", file.getName()); + LoginPacket loginPacket = ctx.channel().attr(userAttr).get(); + if(loginPacket.isExec()){ + return; + } + loginPacket.setExec(true); + loginPacket.setFileName(file.getName()); + loginPacket.setFileLength(file.length()); + loginPacket.setFileOutputStream(new FileOutputStream(new File("./server-receive-" + file.getName()))); +// FileReceiveServerHandler.fileLength = file.length(); +// FileReceiveServerHandler.outputStream = new FileOutputStream( +// new File("./server-receive-" + file.getName()) +// ); + packet.setACK(packet.getACK() + 1); + ctx.writeAndFlush(packet); + } +} diff --git a/FileTransferService/src/main/java/com/xiaoliu/handler/FileReceiveServerHandler.java b/FileTransferService/src/main/java/com/xiaoliu/handler/FileReceiveServerHandler.java new file mode 100644 index 0000000..2fafd11 --- /dev/null +++ b/FileTransferService/src/main/java/com/xiaoliu/handler/FileReceiveServerHandler.java @@ -0,0 +1,62 @@ +package com.xiaoliu.handler; + +import com.xiaoliu.codec.Codec; +import com.xiaoliu.protocol.attribute.Attributes; +import com.xiaoliu.protocol.request.LoginPacket; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import lombok.extern.slf4j.Slf4j; + +import java.io.FileOutputStream; +import java.io.IOException; + +@Slf4j +public class FileReceiveServerHandler extends ChannelInboundHandlerAdapter { + + static FileOutputStream outputStream; + + static long fileLength; + + private static long readLength; + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + LoginPacket loginPacket = ctx.channel().attr(Attributes.userAttr).get(); + + ByteBuf byteBuf = (ByteBuf) msg; + int type = byteBuf.getInt(0); + if (type != Codec.TYPE) { + loginPacket.setReadLength(loginPacket.getReadLength()+byteBuf.readableBytes()); + //readLength += byteBuf.readableBytes(); + writeToFile(byteBuf, loginPacket.getFileOutputStream()); + sendComplete(loginPacket); + } else { + super.channelRead(ctx, msg); + } + } + + private void writeToFile(ByteBuf byteBuf, FileOutputStream outputStream) throws IOException { + byte[] bytes = new byte[byteBuf.readableBytes()]; + byteBuf.readBytes(bytes); + outputStream.write(bytes); + byteBuf.release(); + } + + private void sendComplete(long readLength) throws IOException { + if (readLength >= fileLength) { + log.info("文件接收完成....."); + outputStream.close(); + } + } + + private void sendComplete(LoginPacket loginPacket) throws IOException { + if (loginPacket.getReadLength() >= loginPacket.getFileLength()) { + log.info("文件接收完成..."); + loginPacket.setExec(false); + loginPacket.setReadLength(0); + loginPacket.getFileOutputStream().close(); + } + } + +} diff --git a/FileTransferService/src/main/java/com/xiaoliu/handler/FileSendServerHandler.java b/FileTransferService/src/main/java/com/xiaoliu/handler/FileSendServerHandler.java new file mode 100644 index 0000000..ff9a504 --- /dev/null +++ b/FileTransferService/src/main/java/com/xiaoliu/handler/FileSendServerHandler.java @@ -0,0 +1,45 @@ +package com.xiaoliu.handler; + +import com.xiaoliu.codec.Codec; +import com.xiaoliu.protocol.FilePacket; +import com.xiaoliu.protocol.Packet; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.DefaultFileRegion; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; + +@Slf4j +public class FileSendServerHandler extends ChannelInboundHandlerAdapter { + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + ByteBuf byteBuf = (ByteBuf) msg; + int type = byteBuf.getInt(0); + if (type == Codec.TYPE) { + Packet packet = Codec.INSTANCE.decode(byteBuf); + if (packet instanceof FilePacket) { + FilePacket filePacket = (FilePacket) packet; + if (filePacket.getACK() != 0) { + writeAndFlushFileRegion(ctx, filePacket); + } else { + super.channelRead(ctx, packet); + } + } else { + super.channelRead(ctx, packet); + } + } + } + + private void writeAndFlushFileRegion(ChannelHandlerContext ctx, FilePacket packet) { + File file = packet.getFile(); + DefaultFileRegion fileRegion = new DefaultFileRegion(file, 0, file.length()); + ctx.writeAndFlush(fileRegion).addListener(future -> { + if (future.isSuccess()) { + log.info("{} 发送完成...", file.getName()); + } + }); + } + +} diff --git a/FileTransferService/src/main/java/com/xiaoliu/handler/JoinClusterRequestHandler.java b/FileTransferService/src/main/java/com/xiaoliu/handler/JoinClusterRequestHandler.java new file mode 100644 index 0000000..b261aa5 --- /dev/null +++ b/FileTransferService/src/main/java/com/xiaoliu/handler/JoinClusterRequestHandler.java @@ -0,0 +1,32 @@ +package com.xiaoliu.handler; + +import com.xiaoliu.protocol.request.LoginPacket; +import com.xiaoliu.protocol.response.LoginResponsePacket; +import com.xiaoliu.protocol.session.Session; +import com.xiaoliu.util.IDUtil; +import com.xiaoliu.util.LoginUtil; +import com.xiaoliu.util.SessionUtil; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import lombok.extern.slf4j.Slf4j; + +import java.util.Date; + +@Slf4j +@ChannelHandler.Sharable +public class JoinClusterRequestHandler extends SimpleChannelInboundHandler { + @Override + protected void channelRead0(ChannelHandlerContext ctx, LoginPacket loginPacket) { + String id = IDUtil.randomId(); + loginPacket.setId(id); + log.info("{} [{}-{}]加入集群", new Date(), loginPacket.getName(), id); + LoginUtil.bindUser(loginPacket, ctx.channel()); + ctx.writeAndFlush(new LoginResponsePacket(id, loginPacket.getName())); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + LoginUtil.unBind(ctx.channel()); + } +} diff --git a/FileTransferService/src/main/java/com/xiaoliu/handler/MyServerHandler.java b/FileTransferService/src/main/java/com/xiaoliu/handler/MyServerHandler.java new file mode 100644 index 0000000..e145294 --- /dev/null +++ b/FileTransferService/src/main/java/com/xiaoliu/handler/MyServerHandler.java @@ -0,0 +1,48 @@ +package com.xiaoliu.handler; + +import com.xiaoliu.codec.Codec; +import com.xiaoliu.protocol.FilePacket; +import com.xiaoliu.protocol.Packet; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; + +@Slf4j +public class MyServerHandler extends ChannelInboundHandlerAdapter { + + public static File file; + public static FileOutputStream outputStream; + + public MyServerHandler() throws FileNotFoundException { + + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + System.out.println(msg); + ByteBuf byteBuf = (ByteBuf) msg; + int type = byteBuf.getInt(0); + if (type != Codec.TYPE) { + byte[] bytes = new byte[byteBuf.readableBytes()]; + byteBuf.readBytes(bytes); + outputStream.write(bytes); + byteBuf.release(); + } else { + Packet packet = Codec.INSTANCE.decode(byteBuf); + if (packet instanceof FilePacket) { + FilePacket filePacket = (FilePacket) packet; + outputStream = new FileOutputStream("./receive-" + filePacket.getFile().getName()); + + ByteBuf byteBuf1 = ctx.channel().alloc().ioBuffer(); + Codec.INSTANCE.encode(byteBuf1, filePacket); + ctx.channel().writeAndFlush(byteBuf1); + } + } + + } +} diff --git a/FileTransferService/src/main/java/com/xiaoliu/util/IDUtil.java b/FileTransferService/src/main/java/com/xiaoliu/util/IDUtil.java new file mode 100644 index 0000000..7515a2c --- /dev/null +++ b/FileTransferService/src/main/java/com/xiaoliu/util/IDUtil.java @@ -0,0 +1,11 @@ +package com.xiaoliu.util; + +import java.util.UUID; + +public class IDUtil { + + public static String randomId() { + return UUID.randomUUID().toString().split("-")[0]; + } + +} diff --git a/FileTransferService/src/main/java/com/xiaoliu/util/LoginUtil.java b/FileTransferService/src/main/java/com/xiaoliu/util/LoginUtil.java new file mode 100644 index 0000000..2938749 --- /dev/null +++ b/FileTransferService/src/main/java/com/xiaoliu/util/LoginUtil.java @@ -0,0 +1,43 @@ +package com.xiaoliu.util; + +import com.xiaoliu.protocol.attribute.Attributes; +import com.xiaoliu.protocol.request.LoginPacket; +import com.xiaoliu.protocol.session.Session; +import io.netty.channel.Channel; +import lombok.extern.slf4j.Slf4j; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +public class LoginUtil { + + private static final Map LOGIN_MAP = new HashMap<>(); + + public static void bindUser(LoginPacket login, Channel channel) { + LOGIN_MAP.put(login.getId(), channel); + channel.attr(Attributes.userAttr).set(login); + } + + public static void unBind(Channel channel) { + if (hasLogin(channel)) { + LoginPacket login = getLogin(channel); + LOGIN_MAP.remove(login.getId()); + channel.attr(Attributes.userAttr).set(null); + log.info("{} {}退出集群", new Date(), login.getId()); + } + } + + private static boolean hasLogin(Channel channel) { + return channel.hasAttr(Attributes.userAttr); + } + + private static LoginPacket getLogin(Channel channel) { + return channel.attr(Attributes.userAttr).get(); + } + + public static Map getLoginMap() { + return LOGIN_MAP; + } +} diff --git a/FileTransferService/src/main/java/com/xiaoliu/util/SessionUtil.java b/FileTransferService/src/main/java/com/xiaoliu/util/SessionUtil.java new file mode 100644 index 0000000..6005061 --- /dev/null +++ b/FileTransferService/src/main/java/com/xiaoliu/util/SessionUtil.java @@ -0,0 +1,42 @@ +package com.xiaoliu.util; + +import com.xiaoliu.protocol.attribute.Attributes; +import com.xiaoliu.protocol.session.Session; +import io.netty.channel.Channel; +import lombok.extern.slf4j.Slf4j; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +public class SessionUtil { + + private static final Map NODE_ID_CHANNEL_MAP = new HashMap<>(); + + public static void bindSession(Session session, Channel channel) { + NODE_ID_CHANNEL_MAP.put(session.getNodeId(), channel); + channel.attr(Attributes.SESSION).set(session); + } + + public static void unBindSession(Channel channel) { + if (hasLogin(channel)) { + Session session = getSession(channel); + NODE_ID_CHANNEL_MAP.remove(session.getNodeId()); + channel.attr(Attributes.SESSION).set(null); + log.info("{} {}退出集群", new Date(), session); + } + } + + private static boolean hasLogin(Channel channel) { + return channel.hasAttr(Attributes.SESSION); + } + + private static Session getSession(Channel channel) { + return channel.attr(Attributes.SESSION).get(); + } + + public static Map getNodeIdChannelMap() { + return NODE_ID_CHANNEL_MAP; + } +} diff --git a/FileTransferService/src/main/resources/simplelogger.properties b/FileTransferService/src/main/resources/simplelogger.properties new file mode 100644 index 0000000..ab4d629 --- /dev/null +++ b/FileTransferService/src/main/resources/simplelogger.properties @@ -0,0 +1,14 @@ +# ???????? +org.slf4j.simpleLogger.defaultLogLevel=INFO +# ?????? class ????????????,?????? INFO +# org.slf4j.simpleLogger.log.com.baomidou.mybatisplus.generator=DEBUG +# ???? +org.slf4j.simpleLogger.showDateTime=true +# ???? +org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss +# ??? +# org.slf4j.simpleLogger.showThreadName=true +# ??? +org.slf4j.simpleLogger.showLogName=true +# ????? +#org.slf4j.simpleLogger.showShortLogName=false \ No newline at end of file diff --git a/FileTransferService/src/test/java/com/xiaoliu/AppTest.java b/FileTransferService/src/test/java/com/xiaoliu/AppTest.java new file mode 100644 index 0000000..974da25 --- /dev/null +++ b/FileTransferService/src/test/java/com/xiaoliu/AppTest.java @@ -0,0 +1,38 @@ +package com.xiaoliu; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Unit test for simple App. + */ +public class AppTest + extends TestCase +{ + /** + * Create the test case + * + * @param testName name of the test case + */ + public AppTest( String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( AppTest.class ); + } + + /** + * Rigourous Test :-) + */ + public void testApp() + { + assertTrue( true ); + } +} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..edbf23e --- /dev/null +++ b/pom.xml @@ -0,0 +1,56 @@ + + 4.0.0 + + com.xiaoliu + FileTransfer + 0.0.1-SNAPSHOT + pom + + FileTransfer + http://maven.apache.org + + FileTransferClient + FileTransferService + FileTransferCommon + + + + UTF-8 + + + + + junit + junit + 3.8.1 + test + + + org.projectlombok + lombok + 1.18.32 + true + + + io.netty + netty-all + 4.1.110.Final + + + com.alibaba + fastjson + 1.2.29 + + + org.slf4j + slf4j-api + 2.0.13 + + + org.slf4j + slf4j-simple + 2.0.13 + + +