メソッド間で渡すJavaソケット
-
21-12-2019 - |
質問
私はJavaソケットプログラミングを始めたばかりで、ソケットに関する文献を読んでいます ここに.以下のコードは、私が取った教科書のサンプルで、バグを見つけるように求められています。私はバグを見ていませんが、文献と比較してください。Socket、bufferedreader、およびprintwriterの作成は正しいように見え、try-catchブロックにも囲まれています。Try-catchブロックでも適切に"close()"されています。これらをprocess()に渡すときにエラーがありますか?任意の助けがいただければ幸いです。
import java.net.*;
import java.io.*;
class main{
public void process(PrintWriter out, BufferedReader in, Socket echoSocket){
//....
}
public void processData() {
Socket echoSocket;
PrintWriter out;
BufferedReader in;
try{
echoSocket = new Socket("server.company.com", 8081);
out = new PrintWriter(echoSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(echoSocket.getInputStream()));
}
catch (Exception e) {
System.err.println("Exception has occured");
return;
}
process(out, in, echoSocket);
try {
out.close();
in.close();
echoSocket.close();
}
catch(IOException e) {
System.err.println("IOException has occurred.");
}
}
}
解決
タイプミスにもかかわらず、実際の"バグ"が何であるかを推測することしかできませんが、このコードにはエラー処理に問題があります。具体的には、資源の処分において。
リソースについての議論
リソースとは何ですか?
基本的には :基礎となるOSレベルのリソースに依存する任意のJavaオブジェクト。主に :IOリソース(入力ストリームと出力ストリーム、チャネル)、ソケット。しかし、もっと重要なのは :あなたが使用している"もの"が持っている場合 close
, dispsose
, shutdown
またはそのようなもののいずれか、それは確かに内部的にリソースを保持します。
いくつかの例外があります(特に ByteArrayInputStream
リソースを保持しませんが、メモリを保持します)が、これらは実装の詳細です :あなたが彼らのインターフェースに固執するならば(そしてあなたがすべきである、これは「契約」です)、すべてのストリームは閉じられるべきです。
Java7以降では、Java APIのこれらのオブジェクトのほとんどは、次のように実装されています。 AutoCloseable
インターフェイスですが、多くのサードパーティは必ずしもこれをコードに移植していません(他の理由で移植できない人もいます)。
私の会社のコードレビュー担当者の一人として :私は読んで停止し、私はすぐに私はへの安全な呼び出しが表示されないように、任意のコードを拒否します close
リソースのメソッド。Secureとは、finally節の中で実行されることが保証されていることを意味します。
リソースについての経験則
任意の プログラムによって取得されたリソースは、次の場所で解放される必要があります finally
節(いくつかは追加しても :それ自身の)。
リソースの典型的なライフサイクルは何ですか まあ:
- あなたはそれを得る
- あなたはそれを使用します
- あなたはそれを解放します
あなたのコードでは、それは
ResourceObject myObject = null;
try {
myObject = getResource();
processResource(myObject);
} finally {
if(myObject != null) {
try {
myObject.close();
} catch (Exception e) {
// Usually there is nothing one can do but log
}
}
}
Java7以降では、resourceオブジェクトが実装している場合 AutoCloseable
あなたはそれを書く新しい方法を持っています、それは「リソースで試す」と呼ばれています。
try(ResourceObject myObject = getResource()) {
process(myObject);
}
あなたはfinallyが表示されませんが、それはそこにあります、コンパイラはその場合にあなたのためにfinally節を書きます。
複数のリソースはどうですか?
まあ :複数のリソース、複数のfinallys。アイデアは、異なるfinally句で失敗の原因を分離することです。ファイルをコピーしたいとします。..
public void myCopy() throws IOException {
InputStream source = null;
try {
source = new FileInputStream("yourInputFile");
// If anything bad happens, I have a finally clause that protects this now
OutputStream destination = null;
try {
destination = new FileOutputStream("yourOurputFile"); // If fails, my Input will be closed thanks to its own finally
performCopy(source, destination); // If this fail, my destination will also be closed thanks to its own finally
} finally {
if(destination!=null) { try { destination.close(); } catch (Exception e) {/* log*/ }}
}
} finally {
if(source!=null) { try { source.close(); } catch (Exception e) {/* log*/ }}
}
}
または、Java7の構文では、より短い(免責事項)を持っています :私は今Java7を持っていないので、これがコンパイルされているかどうかを実際に確認することはできません) :
try(
InputStream input = new FileInputStream("in");
OutputStream output = new FileOutputStream("out")) {
performCopy(input, output);
} catch(IOException e) {
// You still have to deal with it of course.
}
これはとても定型文です!
はい、そうです。それが私たちが図書館を持っている理由です。そのようなコードを書くべきではないと主張することができます。Commons IOのような標準的で正常に動作するライブラリを使用するか、そのユーティリティメソッドのいずれかを使用します。または、次のような新しいJDKメソッド Files
API、およびこれがどのように動作するかを参照してください。
コモンズIOには便利なものがあります IOUtils.closeQuietly()
ストリームを閉じるためのメソッドのスイート。
リソースの落とし穴を試してみてください
「リソースを試してみる」呼び出しには、それよりも少し深くなる影響があります。これらが含まれます::Finally句で発生する例外を除いて何かをしたい場合はどうすればよいですか?それを、中に発生したであろう例外と区別するにはどうすればよいですか performCopy
?別のケースは次のとおりです :ここで何が起こるか :
try(Reader reader = new InputStreamReader(new FileInputStream("in"), "an encoding that is not supported")) {
// Whatever
}
それは起こる UnsupportedEncodingException
が投げられるが、 後 ザ- FileInputStream
が発生します。しかし、として FileInputStream
はtry節の対象ではなく、閉じられません。ファイル記述子リークがあります。それを1000回試してみると、JVMはファイルを開くことができなくなり、OSは「開いているファイルの最大数を超えました」と表示します(ulimit
一般的にUNIXではそれを行います)
あなたのソケットに戻る
だからあなたのリソースは何ですか?
まず、ソケットjavadocが(javadoc)と言っているので、あなたのソケットインスタンスである真のリソースが1つしかないことに気付くことができます。:
* <p> Closing this socket will also close the socket's
* {@link java.io.InputStream InputStream} and
* {@link java.io.OutputStream OutputStream}.
したがって、入力ストリームと出力ストリームはソケットに接続されており、これで十分です。
あなたのコードの何が問題になっていますか
コメントを追加する1つの元のコード:
try{
echoSocket = new Socket("server.company.com", 8081);
out = new PrintWriter(echoSocket.getOutputStream(), true); // This can throw IOException
in = new BufferedReader(new InputStreamReader(echoSocket.getInputStream())); // Ditto
}
catch (Exception e) {
// If an exception was thrown getting any of the streams, we get there
System.err.println("Exception has occured");
// And you return without closing the socket. It's bad !
return;
}
// Let's assume everything worked, no exception.
process(out, in, echoSocket); // This may throw an exception (timeout, socket closed by peer, ...)
// that is uncaught (no catch clause). Your socket will be left unclosed as a result.
try {
out.close(); // This can fail
in.close(); // This too
echoSocket.close(); // And this too - although nothing you can do about it
}
catch(IOException e) {
// if out.close fails, we get here, and in.close and socket.close
// never got a chance to be called. You may be leaking resources
System.err.println("IOException has occurred.");
}
安全な実装
Socket echoSocket = null;
try {
// open socket,
echoSocket = new Socket("server.company.com", 8081); // protected by finally
out = new PrintWriter(echoSocket.getOutputStream(), true); // protected
in = new BufferedReader(new InputStreamReader(echoSocket.getInputStream())); // protected
process(out, in, echoSocket); // Still protected
} catch (Exception e) {
// Your current error handling
} finally {
// Anyway, this close will be called if needs be.
if(echoSocket != null) {
try { echoSocket.close(); } catch (Exception e) { /* log */}
// See javadoc, this has closed the in & out streams too.
}
}
他のヒント
public void process(){PrintWriter out, BufferedReader in, Socket echoSocket){
.
は
であるべきですpublic void process(PrintWriter out, BufferedReader in, Socket echoSocket){
.
それ以外の場合はすべてが私に問題ないようです
これを試してみてください私はあなたが1セミコロン
を逃したと思うpublic void processData() {
Socket echoSocket;
PrintWriter out;
BufferedReader in;
try{
echoSocket = new Socket("localhost", 8080);
out = new PrintWriter(echoSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(echoSocket.getInputStream()));
}
catch (IOException e) {
System.err.println("Exception has occured");
return;
}
process(out, in, echoSocket);
try {
out.close();
in.close();
echoSocket.close();
}
catch(IOException e) {
System.err.println("IOException has occurred.");
}
}
public void process (PrintWriter out, BufferedReader in, Socket echoSocket)
{
}
.