【问题标题】:Java Sockets passing between methods在方法之间传递的 Java 套接字
【发布时间】:2014-09-22 16:07:27
【问题描述】:

我刚开始学习 Java 套接字编程,我一直在阅读有关套接字的文献here。下面的代码是我学习的教科书中的一个示例,它要求我找到错误。尽管我没有看到任何错误,但与文献进行比较。 socket、bufferedreader 和 printwriter 的创建似乎是正确的,它们也被包围在一个 try-catch 块中。它们也在 try-catch 块中正确地“关闭()”。将这些传递给 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.");
    }
  }
}

【问题讨论】:

  • 教科书是否给出了代码行为不正确的示例,还是只是说“有一个错误,找到它”?
  • @ajb - 不,只是对套接字如何工作的总结,作为“查找错误”的旁白。
  • 不正确的异常处理算不算,还是您专门查看套接字/io“错误”?
  • 好的,查看getOutputStream 的javadoc,它说当输出流关闭时,套接字也关闭了,所以echoSocket.close() 可能是不必要的并且可能会抛出异常,但我没有不知道...再看一遍,关闭PrintWriter 似乎并没有关闭流。
  • @user1615559 :在异常处理中添加了我的 cmets 作为答案。也许不是您要找的那个,但仍然是真实的。

标签: java sockets bufferedreader printwriter


【解决方案1】:
public void process(){PrintWriter out, BufferedReader in, Socket echoSocket){

应该是

public void process(PrintWriter out, BufferedReader in, Socket echoSocket){

否则我觉得一切都很好

【讨论】:

  • 虽然这是真的,但我想知道一本关于套接字的书的作者是否说“找到错误”然后在示例代码中放入语法错误。
  • 这是我复制代码时的用户错误。我很抱歉,但肯定是一个错误。
【解决方案2】:

试试这个,我想你错过了一个分号

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)
{

}

【讨论】:

    【解决方案3】:

    尽管存在拼写错误,但只能猜测实际的“错误”是什么,但此代码在错误处理方面存在问题。特别是在资源的处置方面。

    关于资源的讨论

    什么是资源?

    基本上:任何依赖底层操作系统级别资源的 Java 对象。主要是:IO 资源(输入和输出流、通道)、套接字。但更重要的是:如果你使用的“东西”有closedispsoseshutdown 或任何类似的,它肯定会在内部保留资源。
    有一些例外(特别是 ByteArrayInputStream 不包含任何资源但内存),但这些是实现细节:如果你坚持他们的接口(你应该,这是一个“合同”),每个流都应该关闭。
    从 Java 7 开始,Java API 中的这些对象中的大多数都实现了AutoCloseable 接口,但许多第 3 方未必将其移植到他们的代码中(也许有些不能因为其他原因)。

    作为我公司的代码审查员之一:一旦我没有看到对资源的​​close 方法的安全调用,我就会停止阅读并拒绝任何代码。我所说的安全是指在 finally 子句中,保证会被执行。

    关于资源的经验法则

    您的程序获得的任何资源都应该在finally 子句中释放(有些甚至添加 : 自己的)。

    资源的典型生命周期是什么 嗯:

    1. 你得到它
    2. 你用它
    3. 你放开它

    在您的代码中,即

    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
            }
        }
    }
    

    从 Java 7 开始,如果资源对象实现了AutoCloseable你有一种新的写法,它被称为“资源尝试”。

    try(ResourceObject myObject = getResource()) {
        process(myObject);
    }
    

    你看不到 finally,但它就在那里,在这种情况下编译器会为你编写 finally 子句。

    多个资源呢?

    嗯:多个资源,多个 finally。这个想法是在不同的 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*/ }}
        }
    }
    

    或者,使用 Java 7 语法,我们有更短的(免责声明:我现在没有 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,看看它是如何工作的。

    Commons IO 有一套方便的IOUtils.closeQuietly() 方法用于关闭流。

    尝试资源陷阱

    “尝试使用资源”调用的后果比这更深一些。其中包括:如果我想对 finally 子句中出现的异常做一些事情怎么办?我如何将其与performCopy 期间发生的异常区分开来? 另一种情况是:这里发生了什么:

    try(Reader reader = new InputStreamReader(new FileInputStream("in"), "an encoding that is not supported")) {
      // Whatever
    }
    

    碰巧UnsupportedEncodingException 被抛出,但之后 FileInputStream 被实例化。但由于FileInputStream 不是try 子句的主题,它不会被关闭。你有一个文件描述符泄漏。尝试一千次,您的 JVM 将无法再打开文件,您的操作系统会告诉您“超出打开文件的最大数量”(ulimit 通常在 UNIX 中这样做)

    回到你的插座

    那么你的资源是什么?

    首先,我们可以注意到您只有一个真正的资源,即您的 Socket 实例,因为 Socket javadoc 说 (javadoc):

     * <p> Closing this socket will also close the socket's
     * {@link java.io.InputStream InputStream} and
     * {@link java.io.OutputStream OutputStream}.
    

    所以你的输入和输出流被绑定到你的 Socket,这就足够了。

    你的代码有什么问题

    将 cmets 添加到您的原始代码中:

    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.
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-04
      • 2013-02-12
      • 1970-01-01
      • 2018-06-29
      • 2013-03-27
      • 1970-01-01
      相关资源
      最近更新 更多