2017-10-06 82 views
0

Java 응용 프로그램에서 netty.io (4.0.4)를 사용하여 외부 하드웨어 드라이버와 통신하기 위해 TCP 클라이언트를 구현합니다. 이 하드웨어의 요구 사항 중 하나는 클라이언트가 30 초마다 KEEP_ALIVE (하트 비트) 메시지를 보내지 만 하드웨어가이 과열에 응답하지 않는다는 것입니다. 내 문제는 연결이 갑자기 끊어진 경우 (예 : 네트워크 케이블이 빠져 있음) 클라이언트가이를 인식하지 못하고 작업 시간 초과 예외가 발생하기 전에 KEEP_ALIVE 메시지를 훨씬 오래 (약 5-10 분) 보냅니다. 즉, 클라이언트 측에서는 아직 연결되어 있는지 여부를 알 수있는 방법이 없습니다. 이깨진 네트워크가 감지되기 ​​전에 netty 클라이언트가 매우 오래 걸림

// bootstrap setup 
bootstrap = new Bootstrap().group(group) 
      .channel(NioSocketChannel.class) 
      .option(ChannelOption.SO_KEEPALIVE, true) 
      .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000) 
      .remoteAddress(ip, port) 
      .handler(tcpChannelInitializer); 


// part of the pipeline responsible for keep alive messages 
    pipeline.addLast("idleStateHandler", new IdleStateHandler(0, 0, 30, TimeUnit.SECONDS)); 
    pipeline.addLast("keepAliveHandler", keepAliveMessageHandler); 
클라이언트가 살아 메시지를 계속 보내고 이후 기대

을하는 데 도움이, 그리고 그 메시지가 다른 쪽 끝에서 수신되지 않는 경우 아래

내 부트 스트랩 설치의 조각이다, 누락 확인을 표시해야 훨씬 이전에 연결 문제가 있습니까? 2

피곤 명시 적으로 아래의 코드를 사용하여 전달 살아 메시지를 계속 보장하는 KeepAliveMessageHandler

public class KeepAliveMessageHandler extends ChannelDuplexHandler 
{ 

    private static final Logger LOGGER = getLogger(KeepAliveMessageHandler.class); 

    private static final String KEEP_ALIVE_MESSAGE = ""; 


    @Override 
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception 
    { 
     if (!(evt instanceof IdleStateEvent)) { 
      return; 
     } 

     IdleStateEvent e = (IdleStateEvent) evt; 
     Channel channel = ctx.channel(); 

     if (e.state() == IdleState.ALL_IDLE) { 
      LOGGER.info("Sending KEEP_ALIVE_MESSAGE"); 
      channel.writeAndFlush(KEEP_ALIVE_MESSAGE); 
     } 
    } 
} 

편집에서

편집

코드

@Override 
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception 
    { 
     if (!(evt instanceof IdleStateEvent)) { 
      return; 
     } 

     IdleStateEvent e = (IdleStateEvent) evt; 
     Channel channel = ctx.channel(); 

     if (e.state() == IdleState.ALL_IDLE) { 
      LOGGER.info("Sending KEEP_ALIVE_MESSAGE"); 
      channel.writeAndFlush(KEEP_ALIVE_MESSAGE).addListener(future -> { 

       if (!future.isSuccess()) { 
        LOGGER.error("KEEP_ALIVE message write error"); 
        channel.close(); 
       } 
      }); 
     } 
    } 

이것은 또한 작동하지 않습니다. : this answer에 따르면이 동작은 의미가 있지만 필자는 쓰기가 "진짜"성공인지 파악할 수있는 방법이 있기를 바라고 있습니다. 하드웨어를 가지고 있으면 듣기가 불가능합니다.

+1

는 아마 여기에 대한 답을 살펴? https://stackoverflow.com/questions/21358800/tcp-keep-alive-to-determine-if-client-disconnected-in-netty –

+0

그 링크를 주셔서 감사합니다. 질문하기 전에 저것을 보았습니다. 문제는 해결책은 다음과 같습니다. a. 네트워크 케이블이 연결되어 있지 않으므로 채널을 정상적으로 닫을 수 없습니다. b. ReadTimeoutHandler를 구현하는 것은 하드웨어가 그다지 말하지 않기 때문에 작동하지 않을 수 있습니다. 이렇게하면 너무 자주 트리거됩니다./(질문에서 말하는 것은 응용 프로그램 수준이 아닌 TCP 레이어입니다.) 이해가 되니? 아마 내가 원한 것은 TCP에 의해서조차 가능하지 않으며, 그것은 질문의 일부분이다. – codeCruncher

+0

몇 분 후에 '연결 재설정'또는 '소프트웨어로 인해 연결이 중단됨'을 기대합니다. 청취 신호를 보낼 때 올바르게 송신 오류를 감지하고 있습니까? – EJP

답변

0

당신은 TCP 연결 유지

.option(ChannelOption.SO_KEEPALIVE, true) 

를 활성화 그러나 코드에서 나는 30 초 속도로 전송하는 킵 얼라이브을 보장 어떤 조각을 볼 수 없습니다.

연결이로 인해 종료 된 경우 TCP Keepalive time-out과 다른 호스트는 결국 이전 연결에 대한 패킷을 보내고 th e 호스트는 연결을 종료 한 RST 플래그가 설정된 패킷을 보내면 이전 연결이 더 이상 활성 상태가 아닌 다른 호스트에 신호를 보냅니다. 이렇게하면 다른 호스트가 연결 종료를 종결시켜 새 연결을 설정할 수 있습니다.

일반적으로 유휴 TCP 연결에서 45 초 또는 60 초마다 TCP Keepalive가 보내지고 세 번째 ACK가 누락 된 후 연결이 끊어집니다. 호스트에 따라 다릅니다 (예 : 기본적으로 Windows PC는 7200000ms (2 시간) 후에 첫 번째 TCP Keepalive 패킷을 보낸 다음 Keepalive 패킷에 대한 응답이없는 경우 연결을 끊는 5ms의 Keepalive를 1000ms 간격으로 보냅니다.

(촬영 형태 http://ltxfaq.custhelp.com/app/answers/detail/a_id/1512/~/tcp-keepalives-explained_

나는

pipeline.addLast("idleStateHandler", new IdleStateHandler(0, 0, 30, TimeUnit.SECONDS)); 
pipeline.addLast("keepAliveHandler", keepAliveMessageHandler); 

상호 활동 및 keepAliveMessageHandler에 30 초마다이 경우 측면을 제거하는 패킷을 전송합니다 유휴 이벤트를 트리거 이니까 이해한다.

은 OS 버퍼에 쓸 때 성공한 것으로 간주됩니다. 외부 장치에서 일부 응답 (distruption 발생하지 않습니다 일)
있을 것이다 명령을 보내기

  1. 하지만 가정 것 :

    당신의 조건 하에서 만 2 optios이 보인다 당신의 경우에는 불가능합니다.

  2. 기본 TCP 드라이버 설정 변경
    TCP keepalive의 기본 OS 설정은 많은 양의 응용 프로그램과 연결을 지원하기 위해 시스템 리소스를 보존하는 것에 관한 것입니다. 전용 시스템을 가지고 있다면보다 적극적인 TCP 검사 구성을 설정할 수 있습니다. 다음은 Linux 커널을 조정하는 방법에 대한 링크입니다.
    솔루션은 일반 설치와 마찬가지로 VM 및 Docker 컨테이너에서도 작동해야합니다.

주제에 대한 일반 정보 : https://blog.stephencleary.com/2009/05/detection-of-half-open-dropped.html

+0

내가 게시물을 살펴 봤는데, 내가 후속 의견에서 말했듯이, 솔루션은 작동하지 않을 것이다. (나는 그것을 시도했다. 읽기가 없다면 예외를 던져서 반드시 연결을 의미하지는 않는다. 죽은 것입니다. 그것은 내가 원하는 것이 아닙니다.) 또한 원래 질문에 내 KeepAliveHandler에 대한 코드를 추가했습니다. 도움을 주려는 노력에 감사드립니다. – codeCruncher

+0

이제는 다른 이야기입니다. 나는 당신을위한 업데이 트를했습니다.도움이되지 않는다면 보내기 타임 아웃, 재시도 횟수 및 KEEP_ALIVE_MESSAGE가 정확히 무엇인지에 대한 정보를 추가하십시오. –

+0

그래서 나는이처럼 writeAndFlush() 호출 후 반환되는 ChannelFuture 처리를 시도했다! channel.writeAndFlush (KEEP_ALIVE_MESSAGE) .addListener (미래 -> { 경우 (future.isSuccess()) { LOGGER .error ("KEEP_ALIVE message write error"); channel.close(); } }); 하지만이 기능이 작동하지 않으면 if 블록이 실행되지 않습니다! 필자는 데이터가 IO 버퍼에 기록 될 때 성공을 말했고, 반대쪽 끝에서 받아 들일 때가 아니라는 것을 읽었다. – codeCruncher