[java] 자바 비동기 IO를 사용하여 다중 클라이언트 접속 처리하기

이 글에서는 자바의 비동기 IO를 사용하여 다중 클라이언트 접속을 처리하는 방법에 대해 알아보겠습니다.

비동기 IO란?

비동기 IO(Asynchronous Input/Output)는 입출력 작업을 동기적으로 처리하지 않고, 비동기적으로 처리하는 방법입니다. 이를 통해 하나의 스레드로 여러 개의 클라이언트 접속을 처리할 수 있어서 다중 클라이언트를 효율적으로 처리할 수 있습니다.

NIO(Non-blocking IO)와 NIO.2

자바에서는 비동기 IO를 구현하기 위해 NIO(Non-blocking IO)와 NIO.2를 제공합니다. NIO는 자바 1.4부터 도입되었고, NIO.2는 자바 1.7부터 도입되었습니다. NIO.2는 NIO보다 더 유연하고 편리하게 비동기 IO를 사용할 수 있도록 개선되었습니다.

다중 클라이언트 접속 처리 예제

아래는 다중 클라이언트 접속을 처리하는 간단한 예제입니다.

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

public class MultiClientServer {

    private final static int PORT = 3000;

    public static void main(String[] args) throws Exception {
        AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open();
        serverChannel.bind(new InetSocketAddress(PORT));

        System.out.println("서버가 시작되었습니다.");

        serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
            @Override
            public void completed(AsynchronousSocketChannel clientChannel, Void attachment) {
                serverChannel.accept(null, this);

                System.out.println("클라이언트 접속");

                ByteBuffer buffer = ByteBuffer.allocate(1024);
                clientChannel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
                    @Override
                    public void completed(Integer result, ByteBuffer buffer) {
                        if (result == -1) {
                            try {
                                clientChannel.close();
                                System.out.println("클라이언트 연결 종료");
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            return;
                        }

                        buffer.flip();
                        byte[] bytes = new byte[buffer.remaining()];
                        buffer.get(bytes);
                        String message = new String(bytes);

                        System.out.println("메시지 수신: " + message);

                        // 클라이언트에게 응답
                        ByteBuffer responseBuffer = ByteBuffer.wrap("응답 메시지".getBytes());
                        clientChannel.write(responseBuffer, null, new CompletionHandler<Integer, Void>() {
                            @Override
                            public void completed(Integer result, Void attachment) {
                                // 처리 완료 후 다시 읽기
                                buffer.clear();
                                clientChannel.read(buffer, buffer, this);
                            }

                            @Override
                            public void failed(Throwable exc, Void attachment) {
                                exc.printStackTrace();
                            }
                        });
                    }

                    @Override
                    public void failed(Throwable exc, ByteBuffer buffer) {
                        exc.printStackTrace();
                    }
                });
            }

            @Override
            public void failed(Throwable exc, Void attachment) {
                exc.printStackTrace();
            }
        });

        // 서버가 종료되지 않고 유지되도록 대기
        Thread.sleep(Long.MAX_VALUE);
    }
}

위 예제는 비동기 서버를 구현하는데 사용되는 AsynchronousServerSocketChannel과 클라이언트와의 통신에 사용되는 AsynchronousSocketChannel을 활용합니다. 클라이언트가 접속하면 completed 메서드가 호출되고, 클라이언트로부터의 메시지를 수신한 후 응답 메시지를 전송합니다.

이 예제는 단순한 동작을 설명하기 위해 간단하게 작성되었으며, 실제 서비스에서는 예외 처리, 스레드 풀 사용 등 추가적인 구현이 필요할 수 있습니다.

결론

자바의 비동기 IO를 사용하여 다중 클라이언트 접속을 처리할 수 있습니다. 비동기 IO를 활용하면 단일 스레드로 다중 클라이언트를 처리할 수 있어서 서버의 효율성을 높일 수 있습니다. NIO와 NIO.2를 적절히 활용하여 비동기 IO를 사용해보세요.

참고 자료