1.Netty传输工具
This commit is contained in:
commit
00d1dea5be
|
@ -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
|
|
@ -0,0 +1,8 @@
|
||||||
|
# 默认忽略的文件
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# 基于编辑器的 HTTP 客户端请求
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
|
@ -0,0 +1,40 @@
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.xiaoliu</groupId>
|
||||||
|
<artifactId>FileTransfer</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>FileTransferClient</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>FileTransferClient</name>
|
||||||
|
<url>http://maven.apache.org</url>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<project.java.version>21</project.java.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.xiaoliu</groupId>
|
||||||
|
<artifactId>FileTransferCommon</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.formdev</groupId>
|
||||||
|
<artifactId>flatlaf</artifactId>
|
||||||
|
<version>3.4.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.formdev</groupId>
|
||||||
|
<artifactId>flatlaf</artifactId>
|
||||||
|
<version>3.4.1</version>
|
||||||
|
<classifier>windows-x86_64</classifier>
|
||||||
|
<type>dll</type>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -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<NioSocketChannel>() {
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<FilePacket> {
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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<LoginResponsePacket> {
|
||||||
|
@Override
|
||||||
|
protected void channelRead0(ChannelHandlerContext ctx, LoginResponsePacket packet) throws Exception {
|
||||||
|
System.out.println(new Date() + " " + packet.getId() + " " + packet.getName() + " 登陆成功");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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()));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.xiaoliu</groupId>
|
||||||
|
<artifactId>FileTransfer</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>FileTransferCommon</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>FileTransferCommon</name>
|
||||||
|
<url>http://maven.apache.org</url>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<project.java.version>21</project.java.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.11.0</version>
|
||||||
|
<configuration>
|
||||||
|
<source>${project.java.version}</source>
|
||||||
|
<target>${project.java.version}</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
|
@ -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<Byte, Class<? extends Packet>> 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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<ByteBuf, Object> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void encode(ChannelHandlerContext ctx, Object o, List<Object> 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<Object> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Object> list) throws Exception {
|
||||||
|
list.add(Codec.INSTANCE.decode(byteBuf));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.xiaoliu.protocol;
|
||||||
|
|
||||||
|
public abstract class Packet {
|
||||||
|
|
||||||
|
public abstract Byte getCommand();
|
||||||
|
|
||||||
|
}
|
|
@ -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> SESSION = AttributeKey.newInstance("session");
|
||||||
|
|
||||||
|
AttributeKey<LoginPacket> userAttr = AttributeKey.newInstance("user");
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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> T deSerilize(byte[] bytes, Class<T> clazz);
|
||||||
|
|
||||||
|
}
|
|
@ -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> T deSerilize(byte[] bytes, Class<T> clazz){
|
||||||
|
return JSON.parseObject(bytes, clazz);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.xiaoliu</groupId>
|
||||||
|
<artifactId>FileTransfer</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>FileTransferService</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>FileTransferService</name>
|
||||||
|
<url>http://maven.apache.org</url>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<project.java.version>21</project.java.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.xiaoliu</groupId>
|
||||||
|
<artifactId>FileTransferCommon</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<finalName>sane-service-v${project.version}</finalName>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.11.0</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-dependencies</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<finalName>${project.build.finalName}</finalName>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<mainClass>com.xiaoliu.Server</mainClass>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
<descriptorRefs>
|
||||||
|
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||||
|
</descriptorRefs>
|
||||||
|
<appendAssemblyId>false</appendAssemblyId>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>make-assembly</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>single</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>io.github.fvarrui</groupId>
|
||||||
|
<artifactId>javapackager</artifactId>
|
||||||
|
<version>1.7.5</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>package</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<!-- mandatory -->
|
||||||
|
<mainClass>com.xiaoliu.Server</mainClass>
|
||||||
|
<!-- optional -->
|
||||||
|
<bundleJre>true</bundleJre>
|
||||||
|
<generateInstaller>true</generateInstaller>
|
||||||
|
<administratorRequired>false</administratorRequired>
|
||||||
|
<platform>windows</platform>
|
||||||
|
<displayName>netty</displayName>
|
||||||
|
<description>服务端软件</description>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
|
@ -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<NioSocketChannel>() {
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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<String, Console> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<String, Channel> channelMap = SessionUtil.getNodeIdChannelMap();
|
||||||
|
for (Map.Entry<String, Channel> entry : channelMap.entrySet()) {
|
||||||
|
entry.getValue().writeAndFlush(filePacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<FilePacket> {
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<LoginPacket> {
|
||||||
|
@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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<String, Channel> 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<String, Channel> getLoginMap() {
|
||||||
|
return LOGIN_MAP;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<String, Channel> 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>com.xiaoliu</groupId>
|
||||||
|
<artifactId>FileTransfer</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
<name>FileTransfer</name>
|
||||||
|
<url>http://maven.apache.org</url>
|
||||||
|
<modules>
|
||||||
|
<module>FileTransferClient</module>
|
||||||
|
<module>FileTransferService</module>
|
||||||
|
<module>FileTransferCommon</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>3.8.1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.32</version>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-all</artifactId>
|
||||||
|
<version>4.1.110.Final</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>fastjson</artifactId>
|
||||||
|
<version>1.2.29</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<version>2.0.13</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-simple</artifactId>
|
||||||
|
<version>2.0.13</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
Loading…
Reference in New Issue