클라이언트와 netty 서버간 connetion 유지가 안됩니다.

101 views
Skip to first unread message

류중욱

unread,
Nov 29, 2023, 6:06:31 AM11/29/23
to Netty Korean User Group
안녕하세요. 
netty 사용한지 얼마 안된 뉴비인데요.


NTRIP이라는 위치 관련 위성의 정보를 받는 Client 기능의 Spring boot Application과
그 정보를 저장 가공하고 주기적으로 접속한 클라이언트들에게 전송하는 Caster를 동시에 만들고 있습니다.

Caster가 Spring boot에서 netty를 사용하여 클라이언트 접속을 받도록 구현하였는데요.
MessageToMessageDecoder를 상속받은 MessageHandler의 decode에서 로그인한 client에 대해 아래와 같이
주기적으로 데이터를 보내도록 하고 있습니다.

while(ctx.channel().isActive()){

// 최신 데이터를 가져오기
RtcmMessage rtcmMessage = new RtcmMessage(clientInfo, dBManager);
if(rtcmMessage.getSize() > 0){
ctx.writeAndFlush(rtcmMessage);  
}else{
// 이런 경우 encode가 호출되지 않으므로 여기서 content release
rtcmMessage.getContent().release();
}

try {
Thread.sleep(fetchInterval);
} catch (InterruptedException ie) {
// Restore interrupted state...
Thread.currentThread().interrupt();
}



문제는 Client가 다른 해외 Ntrip Caster 서버에 붙었을 때는 연결이 유지되면서 주기적(주로 1초 간격)으로 데이터를 잘 받는데 
제가 만든 Ntrip Caster에서는 데이터를 한번 받고는 연결이 끊어집니다.

이렇게 보면 제가 만든 Caster 쪽에서 연결을 끊는게 아닌가 생각되는데
로그를 찍어보면 그렇지가 않는것 같습니다.
위의 MessageHandler의 channelInactive에서 로그를 찍어보면 한참(30초 정도)후에 로그가 찍히거든요.


Client 쪽에서는 HttpURLConnection을 사용하여 connention을 맺고
아래와 같이 connention으로 부터 InputStream을 얻어 

try (InputStream is = connection.getInputStream()) {
for (int r = fillUp(is); r >= 0; r = fillUp(is)) {
//버퍼에 받은 데이터 처리
}
} catch (Exception exception) {
log.error(mountPoint + " Exception : ", exception);
}

아래와 같은 fillup이라는 메소드에서 InputStream으로 부터 데이터를 읽어서
버퍼에 채워넣고 읽은 데이터 크기가  0이상이면 계속 for루프를 돌면서 버퍼의 데이터를 처리하도록 되어있습니다.

private int fillUp(final InputStream is) throws IOException {
final int max = bufferMaxWrite();
if (max == 0) {
throw new OrekitInternalError(null);
}

byte[] bytesRead = new byte[max];

int r = is.read(bytesRead);

if (r >= 0) {
System.arraycopy(bytesRead, 0, buffer, writeIndex, r);
writeIndex = (writeIndex + r) % BUFFER_SIZE;
}

return r;
}

그런데 해외 Ntrip Caster 서버에 연결을 했을때는 로그를 찍어보니 fillup의
int r = is.read(bytesRead); 
에서 데이터가 올때까지 기다려서 r이 항상 0보다 큰 것을 확인하였습니다.

제가 만든 Ntrip Caster에 연결을 했을때는 로그를 찍어보니 fillup의
int r = is.read(bytesRead); 
에서 첫번째는 데이터를 받지만 그 다음에는 바로 r이 -1이 되버리면서 for 루프를 빠져나갑니다.


왜 그런걸까요 ㅠㅠ

keep-alive 관련이라는 얘기를 들어서 클라이언트 header의
connetion : close로 되어 있는것을 keep-alive로 바꿔도 보고
이것 저것 해보았지만 마찬가지더라구요.

둘다 윈도우 vscode에서 로컬로 돌리고 있는데 
그럼 방화벽이나 네트워크도 문제 없는 것 아닌가요? 
제가 뭔가 놓치고 있는게 있을까요? 

조언 부탁드리겠습니다.
감사합니다.



권석훈

unread,
Nov 29, 2023, 8:29:26 PM11/29/23
to nett...@googlegroups.com
MessageToMessageDecoder 같은  Decoder 는 받은 패킷을 자바객체로 변경하는 클래스입니다.  이 클래스에서 데이터 전송을 하지 않습니다.
데이터 전송을 할 용도로 만든 MessageHandler 는 Decoder가 아닌 SimpleChannelInboundHandler 같은 클래스를 상속받아 구현하시면 됩니다. 

그리고 SimpleChannelInboundHandler  상속받으면 구현할 수 있는 channelActive 메소드에서 주기적으로 클라이언트에서 메세지를 전송하도록 하면 될 듯 싶습니다. 
( ScheduledExecutorService 클래스를 이용하여 매초마다 전송하게 작성하시면 되겠네요.) 





2023년 11월 29일 (수) 오후 8:06, 류중욱 <ncr...@gmail.com>님이 작성:
--
이 메일은 Google 그룹스 'Netty Korean User Group' 그룹에 가입한 분들에게 전송되는 메시지입니다.
이 그룹에서 탈퇴하고 더 이상 이메일을 받지 않으려면 netty-ko+u...@googlegroups.com에 이메일을 보내세요.
웹에서 이 토론을 보려면 https://groups.google.com/d/msgid/netty-ko/9ca972dd-6e02-4dd2-b2b7-ca4bf808cb86n%40googlegroups.com을(를) 방문하세요.

JooSing

unread,
Nov 29, 2023, 9:42:22 PM11/29/23
to Netty Korean User Group
안녕하세요. 

전체 코드가 올라온게 아니라, 확실하지는 않지만 우선 decoder 함수에서 작성하신 코드에서 무한 루프를 도는 부분이 문제가 되지 않았을까 추측해 봅니다. 

네티는 기본적으로 채널 별로 I/O 쓰레드 하나를 할당받아 사용합니다. 그래서 하나의 I/O 쓰레드가 네트워크로부터의 저수준 입출력 및 채널 파이프라인에서 발생하는 모든 작업 요청을 루프를 돌면서 처리합니다. 따라서 특정 핸들러(작성자 님의 decoder 처럼)에서 Sleep 하면서 무한 루프를 돌면 해당 핸들러에 채널 I/O 쓰레드가 블록킹 되어 있게 되고, 파이프라인에 추가된 다른 핸들러는 실행되지 못하며, 네트워크로부터의 저수준 입출력 또한 수행될 수 없게 됩니다. 

만약 위와 같은 상황에서 채널 파이프라인에 IdleStateHandler 같은 것을 추가하시고 채널이 Idle인 경우 연결을 끊는 코드를 작성한 부분이 있다면 말씀하신 상황이 발생할 수 있을 것 같습니다.

위 가정이 틀리더라도 decoder 구현은 수정이 필요한 부분인 것 같아요. 주기적으로 처리해야하는 동작이 있다면 핸들러에서 루프를 도는 대신 채널 EventLoop의 scheduleAtFixedRate() 기능을 사용하거나, 아니면 채널 파이프라인 외부에서 전용 쓰레드를 사용하는 등의 방법을 고려해 보아야 할 것 같아요. 

2023년 11월 29일 수요일 오후 8시 6분 31초 UTC+9에 ncr...@gmail.com님이 작성:
Reply all
Reply to author
Forward
0 new messages