Netty--使用TCP协议传输文件
简介:
用于将文件通过TCP协议传输到另一台机器,两台机器需要通过网络互联。
实现:
使用Netty进行文件传输,服务端读取文件并将文件拆分为多个数据块发送,接收端接收数据块,并按顺序将数据写入文件。
工程结构:
Maven配置:
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.15.Final</version>
</dependency> <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
传输对象:type为数据块类型,index为数据块的序列,length为数据块的大小,data为要传输的数据。
public class TransData { private TypeEnum type; private int index; private int length; private ByteBuf data; public TransData() {
} public TransData(TypeEnum type, ByteBuf data) {
this.type = type;
this.data = data;
} public TypeEnum getType() {
return type;
} public void setType(TypeEnum type) {
this.type = type;
} public int getIndex() {
return index;
} public void setIndex(int index) {
this.index = index;
} public int getLength() {
return length;
} public void setLength(int length) {
this.length = length;
} public ByteBuf getData() {
return data;
} public void setData(ByteBuf data) {
this.data = data;
} @Override
public String toString() {
return "TransData{" +
"type=" + type +
", index=" + index +
", length=" + length +
'}';
}
}
---
类型枚举:
public enum TypeEnum { UNKNOW(0), CMD(1), MSG(2), DATA(3), BEGIN(4), END(5); short value; TypeEnum(int value) {
this.value = (short) value;
} public static TypeEnum get(short s) {
for (TypeEnum e : TypeEnum.values()) {
if (e.value == s) {
return e;
}
}
return UNKNOW;
} public short value() {
return this.value;
} }
---
解码器,从数据块还原Java对象
public class Decode extends ReplayingDecoder<Void> { @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
TransData data = new TransData();
data.setType(TypeEnum.get(in.readShort()));
data.setIndex(in.readInt());
data.setLength(in.readInt());
data.setData(in.readBytes(data.getLength()));
out.add(data);
}
}
---
编码器:
public class Encode extends MessageToByteEncoder<TransData> { @Override
protected void encode(ChannelHandlerContext ctx, TransData msg, ByteBuf out) throws Exception {
out.writeShort(msg.getType().value())
.writeInt(msg.getIndex())
.writeInt(msg.getData().readableBytes())
.writeBytes(msg.getData());
}
}
---
数据接收器
public class Receiver { private SortedQueue queue = new SortedQueue();
private String dstPath = System.getProperty("user.dir") + File.separator + "received";
private String fileName;
private long receivedSize = 0;
private long totalSize = 0;
private int chunkIndex = 0;
private FileOutputStream out;
private FileChannel ch;
private long t1;
private int process = 0; public Receiver(TransData data) {
init(Tool.getMsg(data));
} public void init(String msg) {
String[] ss = msg.split("/:/");
fileName = ss[0].trim();
totalSize = Long.valueOf(ss[1].trim());
//new File(dstPath).mkdirs();
File f = new File(dstPath + File.separator + fileName);
if (f.exists()) {
f.delete();
}
try {
out = new FileOutputStream(f);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
ch = out.getChannel();
queue.clear();
System.out.println("receive begin: " + fileName + " " + Tool.size(totalSize));
new MyThread().start();
t1 = System.currentTimeMillis();
} public void receiver(TransData data) {
queue.put(data);
} public void end() throws IOException {
long cost = Math.round((System.currentTimeMillis() - t1) / 1000f);
System.out.println("receive over: " + fileName + " Cost Time: " + cost + "s");
if (out != null) {
out.close();
out = null;
ch = null;
}
fileName = null;
chunkIndex = 0;
totalSize = 0;
receivedSize = 0;
process = 0;
queue.clear();
} private void printProcess() {
int ps = (int) (receivedSize * 100 / totalSize);
if (ps != process) {
this.process = ps;
System.out.print(process + "% ");
if (this.process % 10 == 0 || process >= 100) {
System.out.println();
}
}
} private class MyThread extends Thread {
public void run() {
try {
while (true) {
TransData data = queue.offer(chunkIndex++);
if (data.getType() == TypeEnum.END) {
end();
break;
}
ByteBuf bfn = data.getData();
receivedSize += data.getLength();
ByteBuffer bf = bfn.nioBuffer();
ch.write(bf);
printProcess();
bfn.release();
}
} catch (IOException e) {
e.printStackTrace();
}
}
} }
---
数据发送器,异步发送,每次传输任务实例化一个发送器对象。
public class Sender implements Runnable { String path;
String name;
File f;
FileInputStream in;
FileChannel ch;
ByteBuffer bf;
int index = 0;
Channel channel;
private long t0; public Sender(String path, String name, Channel c) {
this.path = path;
this.name = name;
this.channel = c;
} @Override
public void run() {
begin(path);
send();
} public void begin(String path) {
f = new File(path + File.separator + name);
if (!f.exists() || !f.isFile() || !f.canRead()) {
Tool.sendMsg(channel, "file can not read.");
} try {
in = new FileInputStream(f);
ch = in.getChannel();
bf = ByteBuffer.allocate(20480);
} catch (FileNotFoundException e) {
e.printStackTrace();
} t0 = System.currentTimeMillis();
System.out.println("send begin: " + name + " " + Tool.size(f.length()));
Tool.sendBegin(channel, name + "/:/" + f.length());
} public void send() {
if (in == null) {
return;
}
try {
while (ch.read(bf) != -1) {
while (!channel.isWritable()) {
TimeUnit.MILLISECONDS.sleep(5);
}
bf.flip();
Tool.sendData(channel, bf, index);
index++;
bf.clear();
}
Tool.sendEnd(channel, index); long cost = Math.round((System.currentTimeMillis() - t0) / 1000f);
System.out.println("send over: " + f.getName() + " Cost Time: " + cost + "s");
clear();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} private void clear() throws IOException {
in.close();
bf.clear();
f = null;
in = null;
index = 0;
} }
---
发送器线程池:
public class SenderThreadPool { private static ExecutorService exe = Executors.newFixedThreadPool(10); public static void exe(Runnable run) {
exe.execute(run);
} }
---
有序的数据队列,缓存接收到的数据,并按序号排序。
/*
按序列取出队列中的元素,如果序列缺失则阻塞
*/
public class SortedQueue { final Lock lock = new ReentrantLock();
final Condition canTake = lock.newCondition();
private final AtomicInteger count = new AtomicInteger();
private LinkedList<TransData> list = new LinkedList<TransData>(); public void put(TransData node) {
lock.lock();
boolean p = false;
for (int i = 0; i < list.size(); i++) {
if (node.getIndex() < list.get(i).getIndex()) {
list.add(i, node);
p = true;
break;
}
}
if (p == false) {
list.add(node);
}
count.incrementAndGet();
canTake.signal();
lock.unlock();
} public TransData offer(int index) {
lock.lock();
try {
while (list.isEmpty() || list.get(0).getIndex() != index) {
canTake.await();
}
count.getAndDecrement();
return list.pop();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return null;
} public int size() {
return count.get();
} public void clear() {
list.clear();
count.set(0);
}
}
---
服务端Handler:
public class ServerHandler extends SimpleChannelInboundHandler<TransData> { private String cpath = System.getProperty("user.dir"); @Override
protected void channelRead0(ChannelHandlerContext ctx, TransData data) throws Exception {
handle(ctx, data);
} private void handle(ChannelHandlerContext ctx, TransData data) throws Exception {
if (data.getType() == TypeEnum.CMD) {
String cmd = Tool.getMsg(data);
if (cmd.equalsIgnoreCase("ls")) {
ls(ctx.channel());
} else if (cmd.startsWith("cd ")) {
cd(ctx.channel(), cmd);
} else if (cmd.startsWith("get ")) {
String name = cmd.substring(4);
Sender sender = new Sender(cpath, name, ctx.channel());
SenderThreadPool.exe(sender);
} else if (cmd.equalsIgnoreCase("pwd")) {
Tool.sendMsg(ctx.channel(), "now at: " + cpath);
} else {
Tool.sendMsg(ctx.channel(), "unknow command!");
}
}
} private void ls(Channel channel) {
int k = 0;
StringBuilder sb = new StringBuilder();
File file = new File(cpath);
for (File f : file.listFiles()) {
if (f.isDirectory()) {
sb.append(k);
sb.append("/:/");
sb.append("目录");
sb.append("/:/");
sb.append(f.getName());
sb.append("\n");
k++;
}
}
for (File f : file.listFiles()) {
if (f.isFile()) {
sb.append(k);
sb.append("/:/");
sb.append(Tool.size(f.length()));
sb.append("/:/");
sb.append(f.getName());
sb.append("\n");
k++;
}
}
Tool.sendMsg(channel, "ls " + sb.toString());
} private void cd(Channel channel, String cmd) {
String dir = cmd.substring(3).trim();
if (dir.equals("..")) {
File f = new File(cpath);
f = f.getParentFile();
cpath = f.getAbsolutePath();
Tool.sendMsg(channel, "new path " + cpath);
ls(channel);
} else {
String path1 = cpath + File.separator + dir;
File f1 = new File(path1);
if (f1.exists()) {
cpath = path1;
Tool.sendMsg(channel, "new path " + cpath);
ls(channel);
} else {
Tool.sendMsg(channel, "error, path not found");
}
}
} }
---
客户端Handler:
public class ClientHandler extends SimpleChannelInboundHandler<TransData> { private static Map<Integer, String> map = new HashMap(); Receiver receiver; public static String getName(int i) {
return map.get(Integer.valueOf(i));
} @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Tool.sendCmd(ctx.channel(), "pwd");
Tool.sendCmd(ctx.channel(), "ls");
} @Override
protected void channelRead0(ChannelHandlerContext ctx, TransData data) throws Exception {
TypeEnum type = data.getType();
if (type == TypeEnum.MSG) {
String msg = Tool.getMsg(data);
if (msg.startsWith("ls ")) {
praseLs(msg);
} else if (msg.startsWith("msg ")) {
System.out.println(msg.substring(4));
} else {
System.out.println(msg);
}
} else if (type == TypeEnum.DATA || type == TypeEnum.END) {
receiver.receiver(data);
} else if (type == TypeEnum.BEGIN) {
receiver = new Receiver(data);
} else {
System.out.println(Tool.getMsg(data));
}
} private void praseLs(String msg) {
map.clear();
String ss = msg.substring(3).trim();
String[] paths = ss.split("\n");
for (String p : paths) {
p = p.trim();
String[] dd = p.split("/:/");
if (dd.length == 3) {
System.out.println(dd[0] + " " + dd[1] + " " + dd[2]);
map.put(Integer.valueOf(dd[0].trim()), dd[2].trim());
}
}
} }
---
客户端启动:
public class TransClient { private static TransClient client = new TransClient();
private String ip;
private int port;
private Channel channel = null;
private Thread t = new ClientThread(); private TransClient() {
} public static TransClient instance() {
return client;
} public void start(String ip, int port) {
if (t.isAlive()) {
return;
}
this.ip = ip;
this.port = port;
t.start();
} public void readCmd() {
Scanner sc = new Scanner(System.in);
while (sc.hasNextLine()) {
String cmd = sc.nextLine().trim();
if (cmd.equalsIgnoreCase("exit")) {
channel.closeFuture();
return;
} else if (cmd.startsWith("get ")) {
int i = Integer.valueOf(cmd.substring(4).trim());
cmd = "get " + ClientHandler.getName(i);
} else if (cmd.startsWith("cd ")) {
String p = cmd;
p = p.substring(3).trim();
if (!p.equals("..")) {
try {
int i = Integer.valueOf(p);
cmd = "cd " + ClientHandler.getName(i);
} catch (Exception e) {
}
}
}
Tool.sendCmd(channel, cmd);
}
} private class ClientThread extends Thread {
@Override
public void run() {
Bootstrap bootstrap = new Bootstrap();
EventLoopGroup group = new NioEventLoopGroup();
try {
bootstrap.group(group).channel(NioSocketChannel.class);
bootstrap.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
ch.pipeline().addLast(new Decode());
ch.pipeline().addLast(new Encode());
pipeline.addLast(new ClientHandler());
}
});
bootstrap.option(ChannelOption.SO_KEEPALIVE, true); channel = bootstrap.connect(ip, port).sync().channel();
System.out.println("Trans Client connect to " + ip + ":" + port);
channel.closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
System.out.println("Trans Client stoped.");
}
}
} }
---
服务端启动:
public class TransServer { private int port;
private static TransServer server = new TransServer();
private Thread t = new ServerThread(); private TransServer() {
} public static TransServer instance() {
return server;
} public void start(int port) {
this.port = port;
t.start();
} private class ServerThread extends Thread {
@Override
public void run() {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(1);
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new Decode());
ch.pipeline().addLast(new Encode());
ch.pipeline().addLast(new ServerHandler());
}
});
ChannelFuture future = bootstrap.bind(port).sync();
System.out.println("Trans Server started, port: " + port);
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("Trans Server shuting down");
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
} }
---
工具类,发送消息和数据。
public class Tool { public static final Charset CHARSET = Charset.forName("UTF8"); public static void sendMsg(Channel ch, String msg) {
ByteBuffer bf = CHARSET.encode(msg);
ByteBuf bfn = Unpooled.copiedBuffer(bf);
TransData d = new TransData(TypeEnum.MSG, bfn);
ch.writeAndFlush(d);
} public static void sendCmd(Channel ch, String msg) {
ByteBuffer bf = CHARSET.encode(msg);
ByteBuf bfn = Unpooled.copiedBuffer(bf);
TransData d = new TransData(TypeEnum.CMD, bfn);
ch.writeAndFlush(d);
} public static String getMsg(TransData data) {
CharBuffer cb = CHARSET.decode(data.getData().nioBuffer());
return cb.toString().trim();
} public static void sendBegin(Channel ch, String msg) {
ByteBuffer bf = CHARSET.encode(msg);
ByteBuf bfn = Unpooled.copiedBuffer(bf);
TransData d = new TransData(TypeEnum.BEGIN, bfn);
ch.writeAndFlush(d);
} public static void sendData(Channel ch, ByteBuffer bf, int index) {
TransData data = new TransData();
data.setType(TypeEnum.DATA);
ByteBuf bfn = Unpooled.copiedBuffer(bf);
data.setData(bfn);
data.setIndex(index);
ch.writeAndFlush(data);
} public static void sendEnd(Channel ch, int index) {
TransData data = new TransData(TypeEnum.END, Unpooled.EMPTY_BUFFER);
data.setIndex(index);
ch.writeAndFlush(data);
} public static String size(long num) {
long m = 1 << 20;
if (num / m == 0) {
return (num / 1024) + "KB";
}
return num / m + "MB";
} }
---
配置读取类:
public class ConfigTool { private static final String CONFIG_PATH = System.getProperty("user.dir") + File.separator + "config" + File.separator + "app.properties"; private static Properties ppt = new Properties(); static {
try {
ppt.load(new FileInputStream(CONFIG_PATH));
} catch (IOException e) {
e.printStackTrace();
}
} public static void reload() {
ppt.clear();
try {
ppt.load(new FileInputStream(CONFIG_PATH));
} catch (IOException e) {
e.printStackTrace();
}
} public static String getValue(String key) {
key = key.trim();
if (ppt.containsKey(key)) {
String value = ppt.getProperty(key).trim();
if ("".equals(value)) {
return null;
}
return value;
}
return null;
} public static int getInt(String key) {
String s = getValue(key);
return Integer.valueOf(s);
} }
---
启动类:
public class CmdApp { public static void main(String[] args) { String mode = ConfigTool.getValue("mode");
String ip = ConfigTool.getValue("server.ip");
int port = ConfigTool.getInt("server.port"); if (mode == null) {
System.out.println("error");
} else if (mode.equals("server")) {
TransServer.instance().start(port);
} else if (mode.equals("client")) {
TransClient.instance().start(ip, port);
TransClient.instance().readCmd();
} else if (mode.equals("both")) {
TransServer.instance().start(port);
TransClient.instance().start(ip, port);
TransClient.instance().readCmd();
} }
}
---
end
Netty--使用TCP协议传输文件的更多相关文章
- TCP协议传输大文件读取时候的问题
TCP协议传输大文件读取时候的问题 大文件传不完的bug 我们在定义的时候定义服务端每次文件读取大小为10240, 客户端每次接受大小为10240 我们想当然的认为客户端每次读取大小就是10240而把 ...
- 用c++开发基于tcp协议的文件上传功能
用c++开发基于tcp协议的文件上传功能 2005我正在一家游戏公司做程序员,当时一直在看<Windows网络编程> 这本书,把里面提到的每种IO模型都试了一次,强烈推荐学习网络编程的同学 ...
- Py-解决粘包现象,tcp实现并发,tcp实现传输文件的程序,校验思路,线程与进程
黏包现象 TCP粘包就是指发送方发送的若干包数据到达接收方时粘成了一包,从接收缓冲区来看,后一包数据的头紧接着前一包数据的尾,出现粘包的原因是多方面的,可能是来自发送方,也可能是来自接收方TCP接收到 ...
- 如何确保TCP协议传输稳定可靠?
TCP,控制传输协议,它充分实现了数据传输时的各种控制功能:针对发送端发出的数据包确认应答信号ACK:针对数据包丢失或者出现定时器超时的重发机制:针对数据包到达接收端主机顺序乱掉的顺序控制:针对高效传 ...
- tcp协议传输方法&粘包问题
socket实现客户端和服务端 tcp协议可以用socket模块实现服务端可客户端的交互 # 服务端 import socket #生成一个socket对象 soc = socket.socket(s ...
- java 26 - 6 网络编程之 TCP协议 传输思路 以及 代码
TCP传输 Socket和ServerSocket 建立客户端和服务器 建立连接后,通过Socket中的IO流进行数据的传输 关闭socket 同样,客户端与服务器是两个独立的应用程序 TCP协议发送 ...
- 开发错误日志之FTP协议传输文件问题
从开发端用FTP协议向服务器(Linux系统)传输文件时,cat -A查询文件内容中行尾会有^M出现. 解决方案:改用SFTP协议上传文件.
- Linux C++ TCP Socket传输文件或图片实例
环境:Linux 语言:C++ 通信方式:TCP 下面用TCP协议编写一个简单的服务器.客户端,其中服务器端一直监听本机的6666号端口.如果收到连接请求,将接收请求并接收客户端发来的消息:客户端与服 ...
- socket套接字TCP协议传输-案例测试
术语: 套接字接口:socket,是一个IP地址和一个端口号的组合,套接字可以唯一标识整个Internet中的一个网络进程. TCP连接:一对套接字接口(一个用于接收,一个用于发送)可定义面向连接的协 ...
随机推荐
- 【zzuli-2259】matrix
题目描述 在麦克雷的面前有N个数,以及一个R*C的矩阵.现在他的任务是从N个数中取出 R*C 个,并填入这个矩阵中.矩阵每一行的法值为本行最大值与最小值的差,而整个矩阵的法值为每一行的法值的最大值.现 ...
- LeetCode OJ:Range Sum Query 2D - Immutable(区域和2D版本)
Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper lef ...
- php远程文件包含截断问题
今天在学习<白帽子讲web安全>一书是,提到一个php远程文件包含漏洞 可以从攻击者服务器中的一个写好的攻击脚本中远程执行命令 服务器中有漏洞的页面代码为: #test.php#error ...
- jquery表单验证插件 jquery.form.js-转
来自:http://www.cnblogs.com/luluping/archive/2009/04/15/1436177.html Form插件,支持Ajax,支持Ajax文件上传,功能强大,基本满 ...
- IR Cut Filter
IR cut filter,即红外截止滤光片,它放在于LENS与Sensor之间.因人眼与CMOS Sensor对各波长的响应不同,人眼看不到红外光但sensor会感应,因此需要IR cut filt ...
- d3.js(v5.7)树状图
一.新建画布 二.数据处理 三.绘制连接线 图示: 四.绘制节点.文字 图示: 五.总结 path元素:其实就是定义了绘图的坐标点,从哪开始,移动到哪,怎样移动(命令) 具体可百度(或许以后我会总结一 ...
- d3.js(v5.7)完整地画一个柱状图
一.首先定义画布大小以及绘画区域的位置(总不能顶着屏幕边沿画吧) 代码: 图示: 二.横.纵向坐标轴 代码: 图示: 三.添加矩形个文本以及上色 图示:
- java面试题大纲
跳槽时时刻刻都在发生,但是我建议大家跳槽之前,先想清楚为什么要跳槽.切不可跟风,看到同事一个个都走了,自己也盲目的开始面试起来(期间也没有准备充分),到底是因为技术原因(影响自己的发展,偏移自己规划的 ...
- 【剑指offer】判断一个序列是否是二叉搜索树的后序遍历,C++实现
原创文章,转载请注明出处! 本题牛客网地址 博客文章索引地址 博客文章中代码的github地址 1.题目 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则输出Yes,否则输出N ...
- LOJ2421 NOIP2015 信息传递 【tarjan求最小环】
LOJ2421 NOIP2015 信息传递 LINK 题目大意就是给你一个有向图,求最小环 有一个很奇妙的性质叫做每个点只有一条出边 然后我们考虑对每个强联通分量进行考虑 发现每个强联通分量内的边数一 ...