2016-11-07 3 views
0

스프링 통합에는 TcpInboundGateway 및 ByteArrayStxEtxSerializer가있어 TCP 포트를 통해 오는 데이터를 처리합니다.일회용 소켓 연결 닫기 = false 스프링 통합 TCP 서버

ByteArrayStxEtxSerializer는 TCP 서버가 클라이언트에서 보낸 모든 데이터를 읽고 나서 처리해야하는 경우 잘 작동합니다. (요청 및 응답 모델) 단일 요청 = false를 사용하여 여러 요청을 동일한 연결에서 처리 할 수 ​​있습니다.

예를 들어 클라이언트가 0x02AAPL0x03을 보내는 경우 Server는 AAPL 가격을 보낼 수 있습니다.

클라이언트가 0x02AAPL0x030x02GOOG0x03을 보내는 경우 My TCP Server가 작동합니다. 그것은 AAPL과 GOOG 가격의 가격을 보냅니다.

때때로 클라이언트는 EOT (0x04)를 보낼 수 있습니다. 클라이언트가 EOT를 보내면 소켓 연결을 닫고 싶습니다.

예 : 클라이언트 요청은 0x02AAPL0x030x02GOOG0x03 0x020x040x03이 될 수 있습니다. 참고 EOT가 마지막 패킷에 들어 왔습니다.

ByteArrayStxEtxSerializer 디시리얼라이저는 클라이언트가 보낸 바이트를 읽도록 사용자 정의 할 수 있습니다.

은 deserializer 소켓 연결을 닫기 좋은 곳입니까? 만약 아니라면 스프링 통합 프레임 워크가 소켓 연결을 닫도록 통보되어야 하는가?

도와주세요.

<int-ip:tcp-connection-factory id="crLfServer" 
     type="server" 
     port="${availableServerSocket}" 
     single-use="false" 
     so-timeout="10000" 
     using-nio="false" 
     serializer="connectionSerializeDeserialize" 
     deserializer="connectionSerializeDeserialize" 
     so-linger="2000"/> 

    <bean id="connectionSerializeDeserialize" class="org.springframework.integration.ip.tcp.serializer.ByteArrayStxEtxSerializer"/> 

    <int-ip:tcp-inbound-gateway id="gatewayCrLf" 
     connection-factory="crLfServer" 
     request-channel="serverBytes2StringChannel" 
     error-channel="errorChannel" 
     reply-timeout="10000"/> <!-- reply-timeout works on inbound-gateway --> 

    <int:channel id="toSA" /> 

    <int:service-activator input-channel="toSA" 
     ref="myService" 
     method="prepare"/> 

    <int:object-to-string-transformer id="serverBytes2String" 
     input-channel="serverBytes2StringChannel" 
     output-channel="toSA"/> 

    <int:transformer id="errorHandler" 
     input-channel="errorChannel" 
     expression="payload.failedMessage.payload + ':' + payload.cause.message"/> 

UPDATE : 추가 던져 새로운 SoftEndOfStreamException ("스트림이 닫혀") 직렬 작품의 스트림을 닫습니다 그리고 내가 가의 EventListener의 로그 항목을 CLOSED 볼 수 있습니다

여기 내 스프링 구성입니다. 서버가 연결을 닫으면 클라이언트에서 java.io.InputStream.read()를 -1로 수신 할 것으로 예상됩니다. 그러나 클라이언트는

java.net.SocketTimeoutException: Read timed out 
    at java.net.SocketInputStream.socketRead0(Native Method) 
    at java.net.SocketInputStream.read(SocketInputStream.java:129) 
    at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264) 
    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306) 
    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158) 
    at sun.nio.cs.StreamDecoder.read0(StreamDecoder.java:107) 
    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:93) 
    at java.io.InputStreamReader.read(InputStreamReader.java:151) 

는 서버 측에서 연결을 종료하고 클라이언트에 전파하기 위해 다른 있나요 받고있다?

감사합니다.

는 디시리얼라이저, 그냥 입력 스트림을 소켓에 액세스 할 수없는 당신에게

답변

1

감사; 닫는 것이 효과적 일지 모르지만 로그에 많은 소음이 올 것입니다.

가장 좋은 해결책은 SoftEndOfStreamException을 던집니다. 소켓을 닫아야하고 모든 것이 정리되어야한다는 신호입니다.

/감지 닫기를 기록하기위한 청취자를 추가합니다

편집 ...

@SpringBootApplication 
public class So40471456Application { 

    public static void main(String[] args) throws Exception { 
     ConfigurableApplicationContext context = SpringApplication.run(So40471456Application.class, args); 
     Socket socket = SocketFactory.getDefault().createSocket("localhost", 1234); 
     socket.getOutputStream().write("foo\r\n".getBytes()); 
     socket.close(); 
     Thread.sleep(10000); 
     context.close(); 
    } 

    @Bean 
    public EventListener eventListener() { 
     return new EventListener(); 
    } 

    @Bean 
    public TcpNetServerConnectionFactory server() { 
     return new TcpNetServerConnectionFactory(1234); 
    } 

    @Bean 
    public TcpReceivingChannelAdapter inbound() { 
     TcpReceivingChannelAdapter adapter = new TcpReceivingChannelAdapter(); 
     adapter.setConnectionFactory(server()); 
     adapter.setOutputChannelName("foo"); 
     return adapter; 
    } 

    @ServiceActivator(inputChannel = "foo") 
    public void syso(byte[] in) { 
     System.out.println(new String(in)); 
    } 

    public static class EventListener implements ApplicationListener<TcpConnectionCloseEvent> { 

     private final Log logger = LogFactory.getLog(getClass()); 

     @Override 
     public void onApplicationEvent(TcpConnectionCloseEvent event) { 
      logger.info(event); 
     } 

    } 

} 

XML로, 당신의 리스너 클래스의 <bean/>을 추가합니다.

결과 :

foo 
2016-11-07 16:52:04.133 INFO 29536 --- [pool-1-thread-2] c.e.So40471456Application$EventListener : TcpConnectionCloseEvent 
[source=org[email protected]118a7548], 
[factory=server, connectionId=localhost:50347:1234:b9fcfaa9-e92c-487f-be59-1ed7ebd9312e] 
**CLOSED** 

EDIT2 나를 위해 예상대로 작동

...

@SpringBootApplication 
public class So40471456Application { 

    public static void main(String[] args) throws Exception { 
     ConfigurableApplicationContext context = SpringApplication.run(So40471456Application.class, args); 
     Socket socket = SocketFactory.getDefault().createSocket("localhost", 1234); 
     socket.getOutputStream().write("foo\r\n".getBytes()); 
     try { 
      System.out.println("\n\n\n" + socket.getInputStream().read() + "\n\n\n"); 
      context.getBean(EventListener.class).latch.await(10, TimeUnit.SECONDS); 
     } 
     finally { 
      socket.close(); 
      context.close(); 
     } 
    } 

    @Bean 
    public EventListener eventListener() { 
     return new EventListener(); 
    } 

    @Bean 
    public TcpNetServerConnectionFactory server() { 
     TcpNetServerConnectionFactory server = new TcpNetServerConnectionFactory(1234); 
     server.setDeserializer(is -> { 
      throw new SoftEndOfStreamException(); 
     }); 
     return server; 
    } 

    @Bean 
    public TcpReceivingChannelAdapter inbound() { 
     TcpReceivingChannelAdapter adapter = new TcpReceivingChannelAdapter(); 
     adapter.setConnectionFactory(server()); 
     adapter.setOutputChannelName("foo"); 
     return adapter; 
    } 

    public static class EventListener implements ApplicationListener<TcpConnectionCloseEvent> { 

     private final Log logger = LogFactory.getLog(getClass()); 

     private final CountDownLatch latch = new CountDownLatch(1); 

     @Override 
     public void onApplicationEvent(TcpConnectionCloseEvent event) { 
      logger.info(event); 
      latch.countDown(); 
     } 

    } 

} 

결과 :

좋은 소리
2016-11-08 08:27:25.964 INFO 86147 --- [   main] com.example2.So40471456Application  : Started So40471456Application in 1.195 seconds (JVM running for 1.764) 



-1 



2016-11-08 08:27:25.972 INFO 86147 --- [pool-1-thread-2] c.e.So40471456Application$EventListener : TcpConnectionCloseEvent [source=org[email protected]fee3774], [factory=server, connectionId=localhost:54984:1234:f79a6826-0336-4823-8844-67054903a094] **CLOSED** 
+0

. 연결을 닫을 때 프레임 워크가 로그합니까? 로그에서 닫는 시점을보고 싶을 때 (threadId를 사용하는 것이 좋습니다.) – kevin

+0

"SoftEndOfStreamException"에 대한 로그가 "정상"닫기로 간주되기 때문에 로그가 없습니다. 그러나 'ApplicationListener'를 추가 할 수 있습니다. 이것은 디시리얼라이저를 호출하는 동일한 스레드에서 호출됩니다. 예를 들어 답을 편집했습니다. –

+0

ApplicationListener는 매우 유용합니다. 고마워. – kevin