[java] 자바 네티 (Java Netty)를 사용하여 네트워크 파일 전송을 구현하는 방법은?

네트워크 파일 전송은 자바 네티를 사용하여 간단하게 구현할 수 있습니다. 자바 네티는 자바로 빠르고 안정적인 네트워크 애플리케이션을 구축하기 위해 디자인된 프레임워크입니다. 이를 사용하여 파일을 전송하는 방법을 알아보겠습니다.

1. 프로젝트 설정

먼저, 자바 네티를 사용하기 위해 프로젝트에 아래의 Maven 의존성을 추가해야 합니다.

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.52.Final</version>
</dependency>

2. 서버 구현

파일을 전송할 서버를 구현해야 합니다. 아래는 간단한 예시 코드입니다.

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.stream.ChunkedWriteHandler;

public class FileServer {

    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) {
                        ch.pipeline().addLast(new ChunkedWriteHandler());
                        ch.pipeline().addLast(new FileServerHandler());
                    }
                });

            ChannelFuture future = bootstrap.bind(8080).sync();
            future.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

위의 예시 코드에서는 NioEventLoopGroup으로 이벤트 루프 그룹을 생성하고, ServerBootstrap을 사용하여 서버를 구성합니다. 핸들러로는 ChunkedWriteHandlerFileServerHandler를 추가하여 파일 전송을 처리합니다.

3. 클라이언트 구현

파일을 전송하는 클라이언트도 구현해야 합니다. 아래는 클라이언트의 예시 코드입니다.

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;

import java.io.File;
import java.io.RandomAccessFile;

public class FileClient {

    public static void main(String[] args) throws Exception {
        String fileName = "path/to/file.txt";
        File file = new File(fileName);
        RandomAccessFile raf = new RandomAccessFile(file, "r");

        EventLoopGroup group = new NioEventLoopGroup();

        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) {
                        ch.pipeline().addLast(new LineBasedFrameDecoder(8192));
                        ch.pipeline().addLast(new StringDecoder());
                        ch.pipeline().addLast(new FileClientHandler(raf));
                    }
                });

            ChannelFuture future = bootstrap.connect("localhost", 8080).sync();
            future.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
            raf.close();
        }
    }
}

위의 예시 코드에서는 NioEventLoopGroup으로 이벤트 루프 그룹을 생성하고, Bootstrap을 사용하여 클라이언트를 구성합니다. 핸들러로는 LineBasedFrameDecoder, StringDecoder, 그리고 FileClientHandler를 추가하여 파일 전송을 처리합니다.

4. 핸들러 구현

서버 및 클라이언트에서 사용되는 핸들러들을 구현해야 합니다. 아래는 간단한 FileServerHandlerFileClientHandler 예시 코드입니다.

FileServerHandler:

import io.netty.buffer.ByteBuf;
import io.netty.channel.*;

import java.io.FileOutputStream;

public class FileServerHandler extends ChannelInboundHandlerAdapter {
    private static final String FILE_PATH = "path/to/save/directory/";

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;

        try (FileOutputStream fos = new FileOutputStream(FILE_PATH)) {
            while (byteBuf.isReadable()) {
                byteBuf.readBytes(fos, byteBuf.readableBytes());
            }

            ctx.writeAndFlush("File received successfully");
        } finally {
            byteBuf.release();
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

FileClientHandler:

import io.netty.buffer.Unpooled;
import io.netty.channel.*;

import java.io.RandomAccessFile;

public class FileClientHandler extends ChannelInboundHandlerAdapter {
    private final RandomAccessFile raf;
    private long transferredBytes = 0;

    public FileClientHandler(RandomAccessFile raf) {
        this.raf = raf;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(raf.length() + "\n");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof String) {
            long length = Long.parseLong((String) msg);
            if (length == transferredBytes) {
                ctx.close();
                System.out.println("File transferred successfully");
            } else {
                ctx.writeAndFlush(raf.getChannel().map(FileChannel.MapMode.READ_ONLY, transferredBytes, length - transferredBytes));
            }
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        transferredBytes += raf.length();
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

5. 실행

위의 서버와 클라이언트 코드를 각각 실행하여 파일 전송을 테스트할 수 있습니다.

자바 네티를 사용하여 네트워크 파일 전송을 구현하는 방법을 알아보았습니다. 자세한 내용은 자바 네티 공식 문서를 참고하시기 바랍니다.

참고 문서: Java Netty 공식 문서