[java] 자바 비동기 IO를 사용한 채팅 서버 구현하기

이번 포스트에서는 자바에서 비동기 IO를 사용하여 간단한 채팅 서버를 구현하는 방법에 대해 알아보겠습니다. 비동기 IO를 사용하면 서버가 동시에 여러 클라이언트의 요청에 대응할 수 있어서 확장성과 성능을 향상시킬 수 있습니다.

필요한 기술 요구사항

단계별로 진행해보기

  1. 서버 소켓 생성하기: 서버 소켓을 생성하여 클라이언트의 연결 요청을 받을 준비를 합니다.
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;

public class ChatServer {
    private ServerSocketChannel serverSocketChannel;

    public void startServer(int port) {
        try {
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.bind(new InetSocketAddress(port));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void acceptClient() {
        while (true) {
            try {
                SocketChannel clientSocketChannel = serverSocketChannel.accept();
                // 클라이언트 연결 처리 로직 구현
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        ChatServer chatServer = new ChatServer();
        chatServer.startServer(9090);
        chatServer.acceptClient();
    }
}
  1. 클라이언트 연결 처리하기: 클라이언트가 서버에 연결되었을 때 발생하는 이벤트를 처리하는 로직을 구현합니다.
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class ChatServer {
    // ...

    public void acceptClient() {
        while (true) {
            try {
                SocketChannel clientSocketChannel = serverSocketChannel.accept();
                clientSocketChannel.configureBlocking(false);

                Selector selector = Selector.open();
                clientSocketChannel.register(selector, SelectionKey.OP_READ);

                while (true) {
                    selector.select();
                    Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();

                    while (keyIterator.hasNext()) {
                        SelectionKey key = keyIterator.next();
                        keyIterator.remove();

                        if (key.isReadable()) {
                            SocketChannel channel = (SocketChannel) key.channel();
                            ByteBuffer buffer = ByteBuffer.allocate(1024);
                            int bytesRead = channel.read(buffer);

                            if (bytesRead > 0) {
                                buffer.flip();
                                byte[] data = new byte[buffer.remaining()];
                                buffer.get(data);

                                String message = new String(data, StandardCharsets.UTF_8);
                                System.out.println("Received message: " + message);

                                // 클라이언트로부터 받은 메시지 처리 로직 구현
                            }
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    // ...
}
  1. 클라이언트로부터 받은 메시지 처리하기: 클라이언트로부터 받은 메시지에 대한 처리 로직을 구현합니다. 이 예제에서는 간단하게 받은 메시지를 그대로 콘솔에 출력하는 것으로 구현했습니다.
// ...

if (bytesRead > 0) {
    buffer.flip();
    byte[] data = new byte[buffer.remaining()];
    buffer.get(data);

    String message = new String(data, StandardCharsets.UTF_8);
    System.out.println("Received message: " + message);

    // 클라이언트로부터 받은 메시지 처리 로직
    // ...

    // 클라이언트에게 응답하기
    String responseMessage = "Message received!";
    byte[] responseBytes = responseMessage.getBytes(StandardCharsets.UTF_8);
    ByteBuffer responseBuffer = ByteBuffer.wrap(responseBytes);
    channel.write(responseBuffer);
}
  1. 코드 실행하기: ChatServer 클래스의 main 메서드를 실행하여 서버를 시작합니다.
public static void main(String[] args) {
    ChatServer chatServer = new ChatServer();
    chatServer.startServer(9090);
    chatServer.acceptClient();
}

결론

이렇게 자바에서 비동기 IO를 사용하여 간단한 채팅 서버를 구현하는 방법에 대해 알아보았습니다. 비동기 IO를 사용하면 동시에 여러 클라이언트와 효율적으로 통신할 수 있으며, 확장성과 성능을 향상시킬 수 있습니다.