Netty 서버에서 클라이언트로 메시지 전송 구현

Netty 기반 서버-클라이언트 통신 구조에서 서버 측 메시지 전송을 구현하는 방법을 설명합니다. WebSocket 프로토콜을 활용한 채팅 시스템 예제로 핵심 구성 요소를 다룹니다.

Netty 서버 기본 구조

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class NettyServer {
    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    private ServerBootstrap bootstrap;
    
    public NettyServer() {
        bossGroup = new NioEventLoopGroup();
        workerGroup = new NioEventLoopGroup();
        bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, workerGroup)
                 .channel(NioServerSocketChannel.class)
                 .childHandler(new ServerInitializer());
    }
    
    public void start(int port) {
        try {
            ChannelFuture future = bootstrap.bind(port).sync();
            future.channel().closeFuture().sync();
        } finally {
            shutdown();
        }
    }
    
    private void shutdown() {
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }
}

채널 파이프라인 초기화

import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;

public class ServerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) {
        ch.pipeline()
          .addLast(new HttpServerCodec())
          .addLast(new HttpObjectAggregator(65536))
          .addLast(new WebSocketServerProtocolHandler("/ws"))
          .addLast(new ConnectionMonitor())
          .addLast(new MessageHandler());
    }
}

연결 상태 모니터링

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;

public class ConnectionMonitor extends ChannelInboundHandlerAdapter {
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
        if (evt instanceof IdleStateEvent) {
            IdleState state = ((IdleStateEvent) evt).state();
            if (state == IdleState.ALL_IDLE) {
                ctx.channel().close();
            }
        }
    }
}

메시지 핸들링 로직

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;

public class MessageHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    public static final ChannelGroup activeChannels = 
        new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        activeChannels.add(ctx.channel());
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame frame) {
        String payload = frame.text();
        String receiverId = parseReceiverId(payload);
        
        Channel target = ClientRegistry.getChannel(receiverId);
        if (target != null && activeChannels.contains(target)) {
            target.writeAndFlush(new TextWebSocketFrame(payload));
        }
    }

    private String parseReceiverId(String payload) {
        // JSON 파싱 로직 구현
        return extractIdFromJson(payload);
    }
}

클라이언트 채널 레지스트리

import io.netty.channel.Channel;
import java.util.concurrent.ConcurrentHashMap;

public class ClientRegistry {
    private static final ConcurrentHashMap<String, Channel> clients = 
        new ConcurrentHashMap<>();

    public static void register(String clientId, Channel channel) {
        clients.put(clientId, channel);
    }

    public static Channel getChannel(String clientId) {
        return clients.get(clientId);
    }
}

메시지 데이터 구조

public class MessageData {
    private String senderId;
    private String recipientId;
    private String content;
    
    // Getter/Setter 생략
}

public class MessageWrapper {
    private int messageType;
    private MessageData payload;
    
    // Getter/Setter 생략
}

태그: Netty java websocket 서버통신 채팅서버

6월 7일 17:56에 게시됨