※ ChatGPTを利用し、要約された質問です(原文:Socket + XML)
SocketとXMLのプログラムにおける問題と解決方法
このQ&Aのポイント
SocketとXMLを使用したプログラムにおいて、サーバ側のInputStreamを引数とするXMLパースメソッドがwaitしてしまい、コネクションを終了させる方法を知りたい。
クライアント側のOutputStreamを閉じると、クライアントのソケット自体も閉じてしまうため、一度のコネクションで送受信を終了させたい。
現在はBufferedReaderを利用してXML文章を文字列に落としてからStringReaderを使用してXMLパースを行っているが、よりスマートな方法を探している。
・クライアントはサーバへXML形式のクエリを送信し、
・サーバからXML形式のデータを受け取る
という単純なプログラムを実装しています。
しかしサーバ側の、InputStreamを引数とするXMLパースメソッドでwaitしてしまって困っています。
かといって、クライアント側のOutputStreamを閉じると、クライアントのソケット自体も閉じてしまいます。
なるべく一度のコネクションで送受信を終了させたいのですが、よい方法はないでしょうか?
一応、BufferedReaderを利用してXML文章を文字列に落としてから
StringReaderをbuilder.parseの引数に与えることで解決出来てはいるのですがスマートではない気がしまして。
[Server側]
ServerSocket server_sock = new ServerSocket(12345);
Socket sock = server_sock.accept();
/* XMLパーサビルダ生成 */
DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = fact.newDocumentBuilder();
/* ドキュメント docIn の解析 */
Document docIn = builder.parse(sock.getInputStream());//ここから動かない(clientがoutputを終了するまで待っている?)
(略)
/* 結果ドキュメント docOut の構築*/
Document docOut = builder.newDocument();
(略)
/* docOut 送信 */
TransformerFactory tfactory = TransformerFactory.newInstance();
Transformer transformer = tfactory.newTransformer();
transformer.transform(new DOMSource(docOut), new StreamResult(sock.getOutputStream()));
sock.getInputStream().close();
sock.getOutputStream().close();
sock.close();
[Client側]
Socket sock = new Socket("localhost", 12345);
/* XMLパーサビルダ生成 */
DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = fact.newDocumentBuilder();
/* クエリドキュメント docOut の構築*/
Document docOut = builder.newDocument();
(略)
/* docOut 送信 */
TransformerFactory tfactory = TransformerFactory.newInstance();
Transformer transformer = tfactory.newTransformer();
transformer.transform(new DOMSource(docOut), new StreamResult(sock.getOutputStream()));
sock.getOutputStream().flush();
//sock.getOutputSream().close();//これにすると、socketが閉じてしまう
/* 結果ドキュメント docIn の解析 */
Document docIn = builder.parse(sock.getInputStream());//サーバから結果が送信されないので、クライアントはここで停止
(略)
お礼
デバッグで確認できています。 ソケットプログラミングに詳しくなかったのですが、ソケットを閉じない限りInputStreamのストリームの終端が見えないのなら、そもそもパーサに直接ソケットのInputStreamを渡すのはよくない気がしてきました。 なんらかのデータ解析を行ってから、文字列をパーサに渡すべきでしょうか。(HTTPプロトコルのように) たとえば送信時にXML文章の終了記号として "\r\n\0\r\n"を付加し、サーバ側のXMLパーサにInputStream()を直接渡さずにBufferedReaderを利用してその"\0"を検知させることで思ったとおりの送受信が行われます。、 [サーバ] BufferedReader br = new BufferedReader(new InputStreamReader(sock.getInputStream())); String str = ""; for(String line; !(line = br.readLine()).equals("\0");) str += line; /* XMLパーサビルダ生成 */ DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = fact.newDocumentBuilder(); /* ドキュメント docIn の解析 */ Document docIn = builder.parse(new InputSource(new StringReader(str))); (略) /* 結果ドキュメント docOut の構築*/ Document docOut = builder.newDocument(); (略) /* docOut 送信 */ TransformerFactory tfactory = TransformerFactory.newInstance(); Transformer transformer = tfactory.newTransformer(); transformer.transform(new DOMSource(docOut), new StreamResult(sock.getOutputStream())); sock.close(); [クライアント] (略) /* docOut 送信 */ TransformerFactory tfactory = TransformerFactory.newInstance(); Transformer transformer = tfactory.newTransformer(); transformer.transform(new DOMSource(docOut), new StreamResult(sock.getOutputStream())); sock.getOutputStream().write("\r\n\0\r\n".getBytes()); sock.getOutputStream().flush(); /* 結果ドキュメント docIn の解析 */ Document docIn = builder.parse(sock.getInputStream());//サーバ側がsocketをclose()するのでInputStream()の終端も見える (略)