Spring

[Spring] Netty 서버 데이터 끊기는 문제 해결

lakelight 2022. 8. 16. 16:48
728x90
반응형

이미지 출처: https://docs.jboss.org/netty/3.1/guide/html/architecture.html

 

 

문제 인식
 

[Spring] Netty 서버 구현과 문제 발생

Netty 를 스프링 에서 구현해보았습니다. 클라이언트는 테스트 프로그램을 이용하였습니다. 테스트 프로그램을 통해 데이터를 보내고 서버에서 그 데이터를 받는 테스트를 해보았습니다. Netty Ser

lakelight.tistory.com

 

Netty Server로 Client에서 많은 데이터들을 보냈을 때
긴 데이터는 끊겨서 넘어오는 문제가 발생하였습니다.
찾아보던 중 비슷한 문제를 해결한 사례를 발견하여 적용시켜 보았습니다.

 

[NettySocketServerHandler.java]

package twim.netty.server;

import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
import org.json.JSONObject;

import java.util.StringTokenizer;

@ChannelHandler.Sharable //안전하게 데이터를 처리하도록 하는 어노테이션
public class NettySocketServerHandler extends ChannelInboundHandlerAdapter {

    private String message = "";

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg){

        // ByteBuf: 사용자 정의 버퍼 형식으로 확장할 수 있습니다.
        // 순차적인 두가지 포인트 변수를 제공하여 읽기 쓰기 전환 없이 사용가능합니다.
        // ChannelHandlerContext 는 다음 ChannelHandler에게 이벤트를 넘기거나
        // 동적으로 ChannelPipeline 을 변경할 수 있습니다.

        // 들어오는 데이터를 받아서 message에 이어 붙입니다.
        message += (String) msg;
    }

    //데이터 다음으로 넘어가서 출력되는 문제 해결 참고
    //https://groups.google.com/g/netty-ko/c/IcRU-Qoaw7w

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception{
        // 모든 데이터를 읽었을 때 message를 파싱하여 데이터를 빼냅니다.
        StringTokenizer st = new StringTokenizer(message, "}");
        int count = st.countTokens();

        for (int i = 0; i < count; i++) {
            System.out.println("Data: " + st.nextToken() + "}");
        }

        ctx.writeAndFlush(message);

        //모든 데이터를 출력한 후 message 초기화 해줍니다.
        message = "";
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause){
        cause.printStackTrace();
        ctx.close();
    }
}
channelRead에서 읽을 msg가 있는 경우 message라는 변수에 String을 저장하였습니다.
channelReadComplete를 통해 channelRead가 끝났을 때 실행되는 함수를 오버라이드하였고, 클라이언트에서 넘어온 데이터를 출력하는 로직을 구현하였습니다. 한번에 넘어온 데이터들은 StringTokenizer를 사용하여 나눴고, 포맷에 맞춰서 출력해주었습니다.
출력을 해준 이후에는 message에 남아있던 내용을 초기화해주고 다음 msg를 기다립니다.

 

 

결론

위와 같이 조치한 결과 누락되는 데이터나, 잘린 데이터가 출력되지 않고,
모든 데이터들이 정상적으로 출력되었습니다.
클라이언트 프로그램을 통해 500개의 데이터들을 보냈는데
콘솔 창에 모든 데이터들이 출력되는 것을 확인할 수 있었습니다.

 

출력 화면
Data: SERVER:READY
Data: 0036A9901FFFFFFFFFFFFFFFA{0:0}
Data: 0036A9901FFFFFFFFFFFFFFFA{0:0}
Data: 0036A9901FFFFFFFFFFFFFFFA{0:0}
Data: 0036A9901FFFFFFFFFFFFFFFA{0:0}
Data: 0036A9901FFFFFFFFFFFFFFFA{0:0}
Data: 0036A9901FFFFFFFFFFFFFFFA{0:0}
Data: 0036A9901FFFFFFFFFFFFFFFA{0:0}
Data: 0052A9914FFFFFFFFFFFFFFFE{0:0,1:1,2:2,3:3,4.4}
Data: 0052A9914FFFFFFFFFFFFFFFE{0:0,1:1,2:2,3:3,4.4}
Data: 0052A9914FFFFFFFFFFFFFFFE{0:0,1:1,2:2,3:3,4.4}
Data: 0052A9914FFFFFFFFFFFFFFFE{0:0,1:1,2:2,3:3,4.4}
Data: 0036A9901FFFFFFFFFFFFFFFA{0:0}
Data: 0040A9914FFFFFFFFFFFFFFFB{0:0,1:1}
Data: 0044A9914FFFFFFFFFFFFFFFC{0:0,1:1,2:2}
Data: 0044A9914FFFFFFFFFFFFFFFC{0:0,1:1,2:2}
Data: 0036A9901FFFFFFFFFFFFFFFA{0:0}
Data: 0040A9914FFFFFFFFFFFFFFFB{0:0,1:1}
Data: 0044A9914FFFFFFFFFFFFFFFC{0:0,1:1,2:2}
Data: 0036A9901FFFFFFFFFFFFFFFA{0:0}
Data: 0040A9914FFFFFFFFFFFFFFFB{0:0,1:1}
Data: 0044A9914FFFFFFFFFFFFFFFC{0:0,1:1,2:2}
...
...
...
Data: 0036A9901FFFFFFFFFFFFFFFA{0:0}
Data: 0040A9914FFFFFFFFFFFFFFFB{0:0,1:1}
Data: 0044A9914FFFFFFFFFFFFFFFC{0:0,1:1,2:2}
Data: 0036A9901FFFFFFFFFFFFFFFA{0:0}
Data: 0040A9914FFFFFFFFFFFFFFFB{0:0,1:1}
Data: 0044A9914FFFFFFFFFFFFFFFC{0:0,1:1,2:2}
Data: 0036A9901FFFFFFFFFFFFFFFA{0:0}
Data: 0040A9914FFFFFFFFFFFFFFFB{0:0,1:1}
Data: 0044A9914FFFFFFFFFFFFFFFC{0:0,1:1,2:2}
Data: 0036A9901FFFFFFFFFFFFFFFA{0:0}
Data: 0040A9914FFFFFFFFFFFFFFFB{0:0,1:1}
Data: 0052A9914FFFFFFFFFFFFFFFE{0:0,1:1,2:2,3:3,4.4}
Data: 0052A9914FFFFFFFFFFFFFFFE{0:0,1:1,2:2,3:3,4.4}
Data: 0052A9914FFFFFFFFFFFFFFFE{0:0,1:1,2:2,3:3,4.4}
Data: 0052A9914FFFFFFFFFFFFFFFE{0:0,1:1,2:2,3:3,4.4}
Data: 0052A9914FFFFFFFFFFFFFFFE{0:0,1:1,2:2,3:3,4.4}
Data: 0052A9914FFFFFFFFFFFFFFFE{0:0,1:1,2:2,3:3,4.4}
Data: 0052A9914FFFFFFFFFFFFFFFE{0:0,1:1,2:2,3:3,4.4}
Data: 0052A9914FFFFFFFFFFFFFFFE{0:0,1:1,2:2,3:3,4.4}
Data: 0052A9914FFFFFFFFFFFFFFFE{0:0,1:1,2:2,3:3,4.4}
Data: 0052A9914FFFFFFFFFFFFFFFE{0:0,1:1,2:2,3:3,4.4}
Data: 0052A9914FFFFFFFFFFFFFFFE{0:0,1:1,2:2,3:3,4.4}
Data: 0052A9914FFFFFFFFFFFFFFFE{0:0,1:1,2:2,3:3,4.4}
Data: 0052A9914FFFFFFFFFFFFFFFE{0:0,1:1,2:2,3:3,4.4}
Data: 0052A9914FFFFFFFFFFFFFFFE{0:0,1:1,2:2,3:3,4.4}
Data: 0052A9914FFFFFFFFFFFFFFFE{0:0,1:1,2:2,3:3,4.4}
Data: 0052A9914FFFFFFFFFFFFFFFE{0:0,1:1,2:2,3:3,4.4}
destroy socket

Process finished with exit code 130

 

전체 코드 Git Link

 

GitHub - hooyn/NettyServer: 트윔 신입 교육 - Netty라이브러리를 사용한 서버 구축

트윔 신입 교육 - Netty라이브러리를 사용한 서버 구축. Contribute to hooyn/NettyServer development by creating an account on GitHub.

github.com

 

 

[참고]

1. 서버로 전송된 패킷을 디코더로 온전히 복원하고 싶습니다. 잘려서 전송된 패킷 복원 관련

728x90
반응형

'Spring' 카테고리의 다른 글

[Spring] Server-Client 간 암, 복호화 구현  (0) 2022.08.18
[Spring] JWT Token 인증 구현  (0) 2022.08.18
[Spring] Netty 서버 구현과 문제 발생  (0) 2022.08.16
[Spring] Netty 개념  (0) 2022.08.11
[Spring] 관습적인 추상화  (2) 2022.08.09