2015-01-07 2 views
0

소켓을 통해 서버에 연결하고 XML 메시지를 보내거나받는 Java 클라이언트에서 작업하고 있습니다. 나는 메시지의 스키마가 무엇인지를 안다. 현재 작동하고 있지만 지저분한 느낌이 들며 서버에서 여러 XML을 한꺼번에 처리하는 방법을 아직 테스트하지 않았습니다.소켓에서 XML을 읽는 방법을 개선하는 방법

private static BufferedReader socketIn; 
    private static PrintWriter socketOut; 
    private static final String SERVICES_FILE = "/etc/services"; 

    public static void main(String[] args) { 
    int port = getServicePortByName("Service1", "tcp"); 
    try { 
     logger.info("Creating socket port with ip: " + InetAddress.getLocalHost() + " and port: " + port); 
     Socket socket = new Socket(InetAddress.getLocalHost(), port); 
     socketOut = new PrintWriter(socket.getOutputStream(), true); 

     String message = createMessageXML("LOGIN"); 
     socketOut.println(message); 

     socketIn = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
     StringBuilder inputLine = new StringBuilder(); 
     String tmp; 

     while (true) { 
     //loop to keep listening/parsing 
     while(socketIn.ready()) { 
      tmp = socketIn.readLine(); 
      inputLine.append(tmp); 
     } 
     if(inputLine.length() > 0) { 
      parseReceivedMessage(inputLine.toString()); 
     } 
     inputLine.replace(0, inputLine.length(), ""); 
     } 

    } catch (UnknownHostException ex) { 
     Logger.getLogger(TestClient.class.getName()).log(Level.SEVERE, null, ex); 
    } catch (IOException ex) { 
     Logger.getLogger(TestClient.class.getName()).log(Level.SEVERE, null, ex); 
    } 
    } 

parseReceivedMessage 방법은 단지 문자열 취하고 문서 객체로 변환 : 여기서

코드이다. 내 질문은 거기에 더 나은 또는 클리너 StringBuilder를 사용하는 대신 할 수있는 방법은 무엇입니까? 원래는 InputStream을 DOM 빌더 구문 분석 메소드에 전달하려고했다. ByteArray 스트림을 사용하는 몇 가지 예제를 보았지만 그 중 일부 문제가있었습니다. 설명이 필요한 것이 있으면 알려주세요.

+0

구현하려는 프로토콜의 실제 형식은 무엇입니까? 이것은 분명히 제작 프로토콜이 아니기 때문에 메시지 형식에 대한 문서가 있습니까? –

답변

1

XML 메시지 주위에 프레이밍 프로토콜을 디자인하는 것이 좋습니다. 각 XML 메시지에 전송되는 바이트 수를 지정하는 길이 값을 접두사로 붙인 다음 XML 자체를 문자로가 아닌 바이트 단위로 보냅니다. 그런 다음 수신기는 길이를 읽은 다음 지정된 바이트 수를 읽고 XML 파서로 전달한 후 다음 길이를 읽는 식으로 계속 수행 할 수 있습니다. 예 :

private static DataInputStream socketIn; 
    private static DataOutputStream socketOut; 
    private static final String SERVICES_FILE = "/etc/services"; 

    public static void main(String[] args) { 
    int port = getServicePortByName("Service1", "tcp"); 
    try { 
     logger.info("Creating socket port with ip: " + InetAddress.getLocalHost() + " and port: " + port); 
     Socket socket = new Socket(InetAddress.getLocalHost(), port); 
     socketIn = new DataInputStream(new BufferedInputStream(socket.getInputStream())); 
     socketOut = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream())); 

     byte[] message = createMessageXML("LOGIN"); 
     socketOut.writeInt(message.length()); 
     socketOut.write(message, 0, message.length()); 

     int len = socketIn.readInt(); 
     if (len > 0) 
     { 
      message = new byte[len]; 
      socketIn.readFully(message); 
      parseReceivedMessage(message); 
     } 

    } catch (UnknownHostException ex) { 
     Logger.getLogger(TestClient.class.getName()).log(Level.SEVERE, null, ex); 
    } catch (IOException ex) { 
     Logger.getLogger(TestClient.class.getName()).log(Level.SEVERE, null, ex); 
    } 
    } 
+0

하지만 XML 메시지를 제어 할 권한이 없습니다. 그러나 메시지는 항상 동일한 형식을 따르며 동일한 길이입니다. 매번 고정 된 양의 바이트를 읽을 수 있을까요? – Seephor

+0

해당 정보는 귀하의 질문에 명시되어 있어야합니다. 이 경우 XML 길이가 가변적이며 메시지가 나중에 수정 될 수 있기 때문에 항상 동일한 길이의 메시지에 의존하지 않을 것입니다. 메시지는 어떻게 서로 구분됩니까? 귀하의 코드를 기반으로, 나는 XML 자체에 줄 바꿈이 없다고 가정하고 줄 바꿈이 루트 요소의 닫는 태그 다음에 나타나는 것은 맞습니까? 각 메시지에 프롤로그가 있습니까? 그렇다면 루트 요소의 시작 태그 앞에 줄 바꿈이 있습니다. –

+0

각 메시지 사이에 명확한 구분 기호가 없으면 원시 바이트를 지속적으로 롤링 버퍼로 읽어 들이고 각 메시지의 닫기 태그를 스캔 한 다음 처리 할 때 버퍼에서 전체 메시지를 제거해야합니다. 사실 명백한 구분 기호가 있더라도 할당 방법에 대한 버퍼를 대신 스캔하여 결국이 방법으로 이동해야 할 것입니다. –

1

ready()은 메시지 끝 부분에 대한 유효한 테스트가 아닙니다. 이를 제거하고 StringBuilderreadLine() 호출을 모두 호출하고 입력 스트림을 DOM 파서로 직접 전달하십시오.

+0

나는 그것을 시도했지만 .parse (inputstream) 호출 만 멈춘다. – Seephor

+0

그러면 완전한 XML 문서가 전송되지 않습니다. – EJP

+0

하지만 문자열을 스트림으로 변환하고 파서에 전달하면 제대로 작동합니다. 스트림이 닫힐 때까지 중단되기 때문입니까? – Seephor

관련 문제