服务器代码:
package com.caohao.jiami.learn1.nettytest;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* 简单的文件服务器
* 要实现的功能
* 1。将文件上传到文件服务器
* 2。将文件服务器中的文件下载到本地
* 3。查看文件服务器上的文件树形存储结构
*
*/
public class test3 {
public static String BASEPATH = "/Users/caohao/IdeaProjects/myftptest/";
private EventLoopGroup parentGroup = new NioEventLoopGroup();
private EventLoopGroup childGroup = new NioEventLoopGroup();
private ServerBootstrap bootstrap;
private ChannelFuture future;
public void start(int port){
try {
bootstrap = new ServerBootstrap();
bootstrap.group(parentGroup,childGroup);
bootstrap.handler(new LoggingHandler());
bootstrap.childHandler(new channeliniternized());
bootstrap.channel(NioServerSocketChannel.class);
future= bootstrap.bind(port).sync();
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
System.err.println("启动失败");
} finally {
parentGroup.shutdownGracefully();
childGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
test3 test3 = new test3();
test3.start(9010);
}
}
/**
* inboundhandler:
* 1.解决沾包问题,通过在消息头加入文件字节流长度的int值,每次先读取4个字节来判断文件长度
* 2。接受byte流将其传入fileoutputstream并存入本地
*/
class channeliniternized extends ChannelInitializer<SocketChannel>{
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new IdleStateHandler(10,10,0, TimeUnit.SECONDS));
pipeline.addLast(new test3bytetobytedecoder());
pipeline.addLast(new test3handler());
}
}
class test3bytetobytedecoder extends ByteToMessageDecoder{
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
in.markReaderIndex();
int i = in.readInt();
System.out.println("our request is "+i);
if (i==0){//请求下载文件
in.markReaderIndex();
int filenamelength = in.readInt();
byte[] bytes = new byte[filenamelength];
System.out.println("request files name length is "+filenamelength);
if (in.readableBytes()>=filenamelength){
in.readBytes(bytes);
String filename = new String(bytes);
System.out.println("target file name is "+filename);
FileInputStream inputStream = new FileInputStream(test3.BASEPATH+filename);
ByteBuf buffer = Unpooled.buffer();
int c;
while ((c = inputStream.read())!=-1){
buffer.writeInt(c);
}
int readableBytes = buffer.readableBytes();
byte[] file = new byte[readableBytes];
System.out.println("out tranfor file length is "+readableBytes);
buffer.readBytes(file);
ctx.channel().write(readableBytes);
ctx.channel().writeAndFlush(file);
out.add("filedownload".getBytes());
}else {
bytes = null;
in.resetReaderIndex();
}
}else {//请求文件上传
System.out.println("储存为文件头中的长度信息为"+i);
byte[] bytes = new byte[i];
if (in.readableBytes()>=i){
in.readBytes(bytes);
out.add(bytes);
}else {
bytes = null;
in.resetReaderIndex();
}
}
}
}
class test3handler extends SimpleChannelInboundHandler<byte[]>{
private int count = 0;
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("一个文件连接到FTP服务器");
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent){
IdleStateEvent event = (IdleStateEvent) evt;
if (IdleState.READER_IDLE.equals(event.state())){
if (count >= 3){
ctx.channel().close();
}else {
System.err.println("一次连接超时");
count+=1;
}
}
}
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, byte[] msg) throws Exception {
if (new String(msg).equals("filedownload")){
System.out.println("filedownload");
}else {
String filename = null;
filename = UUID.randomUUID().toString()+ new Date().toString();
System.out.println("新加入的文件名字设定为"+filename+".ico");
ctx.channel().writeAndFlush("新加入的文件名字设定为"+filename+".ico");
FileOutputStream outputStream = new FileOutputStream(test3.BASEPATH+filename+".ico");
System.out.println("file length is "+msg.length);
outputStream.write(msg);
outputStream.flush();
outputStream.close();
}
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("一个连接离开FTP服务器");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.err.println("文件传输过程中发生错误,中断传输");
ctx.channel().close();
}
}
连接服务器的API代码:
package com.caohao.jiami.learn1.nettytest;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.epoll.EpollSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.List;
public class test3connecttool {
NioEventLoopGroup group = new NioEventLoopGroup(1);
ChannelFuture connect = null;
public void connect(){
try {
Bootstrap bootstrap = new Bootstrap();
connect = bootstrap.group(group).channel(NioSocketChannel.class).handler(new test3clintinitelaizer()).connect("127.0.0.1", 9010).sync();
} catch (Exception e) {
System.err.println("连接失败");
group.shutdownGracefully();
}
}
public void stop(){
try {
connect.channel().closeFuture().sync();
} catch (InterruptedException e) {
System.out.println("err");
}finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) {
test3connecttool test3connecttool = new test3connecttool();
test3connecttool.connect();
// test3connecttool.sendmessage("/Users/caohao/test/httpserver/favicon.ico");测试通过
test3connecttool.downloadmessage("4fe9cff2-a4f2-4285-86a3-7ff03ed2e589Fri May 29 13:33:20 CST 2020.ico");
test3connecttool.stop();
}
public void sendmessage(String filepath){
ByteBuf buffer = Unpooled.buffer();
byte[] filepathBytes = filepath.getBytes();
buffer.writeInt(1);
buffer.writeBytes(filepathBytes);
connect.channel().writeAndFlush(buffer);
}
public void downloadmessage(String filename){
ByteBuf buffer = Unpooled.buffer();
byte[] filepathBytes = filename.getBytes();
buffer.writeInt(2);
buffer.writeBytes(filepathBytes);
connect.channel().writeAndFlush(buffer);
}
}
class test3clintinitelaizer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new test3bytetobytedecoder());
pipeline.addLast(new test3inboundhandler());
pipeline.addLast(new test3clienthandler());
}
}
class test3clienthandler extends MessageToByteEncoder<ByteBuf>{
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception {
//读取一个int如果是1 就是文件上传,如果是2就是文件下载
if (msg.readInt()==1) {
FileInputStream inputStream = null;
try {
int l = msg.readableBytes();
byte[] message = new byte[l];
msg.readBytes(message);
String s = new String(message);
System.out.println("our target file is "+s);
inputStream = new FileInputStream(s);
ByteBuf byteBuf = Unpooled.buffer();
int c ;
while ((c=inputStream.read())!=-1){
byteBuf.writeByte(c);
}
int len = byteBuf.readableBytes();
byte[] body = new byte[len];
byteBuf.readBytes(body);
System.out.println("file length is "+len);
out.writeInt(len);
out.writeBytes(body);
} catch (IOException e) {
System.err.println("文件读取失败");
} finally {
try {
inputStream.close();
} catch (IOException e) {
System.err.println("文件流关闭失败");
}
}
} else {
out.writeInt(0);//0代表文件下载
int l = msg.readableBytes();
byte[] filename = new byte[l];
msg.readBytes(filename);
String s = new String(filename);
System.out.println("我们请求下载文件"+s);
out.writeInt(filename.length);
out.writeBytes(filename);
}
}
}
class test3clientbytetomessagedecoder extends ByteToMessageDecoder{
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
in.markReaderIndex();
int len = in.readInt();
System.out.println("our reponse file length is "+len);
if (in.readableBytes()>=len){
byte[] body = new byte[len];
in.readBytes(body);
FileOutputStream outputStream = null;
try {
outputStream = new FileOutputStream("/Users/caohao/IdeaProjects/myftptest/testico.ico");
outputStream.write(body);
out.add("file download success");
} catch (IOException e) {
e.printStackTrace();
System.err.println("file download failed");
out.add("file download failed");
} finally {
outputStream.close();
}
}else {
in.resetReaderIndex();
}
}
}
class test3inboundhandler extends SimpleChannelInboundHandler<String>{
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println(msg);
}
}