简体   繁体   English

方法之间传递的Java套接字

[英]Java Sockets passing between methods

I am just starting out with Java Socket Programming, and I have been reading literature on sockets here . 我刚开始使用Java套接字编程,并且已经在这里阅读有关套接字的文献。 The below code is a sample from a textbook I've taken which asks me to find the bug. 以下代码是我取材的一本教科书中的示例,要求我查找错误。 Comparing with the literature though I am not seeing any bugs. 与文献相比,尽管我没有发现任何错误。 The creation of the socket, bufferedreader, and printwriter seem correct, and they are surrounded in a try-catch block as well. 套接字,bufferedreader和printwriter的创建似乎是正确的,并且它们也都包含在try-catch块中。 The are properly "close()"ed in a try-catch block as well. 在try-catch块中也正确地“ close()”。 Is there an error when passing these to process()? 将这些传递给process()时是否出错? Any help would be appreciated. 任何帮助,将不胜感激。

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.");
    }
  }
}
public void process(){PrintWriter out, BufferedReader in, Socket echoSocket){

should be 应该

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

otherwise everything seems fine to me 否则对我来说一切都很好

Although, typos notwithstanding, one can only guess what the actual "bug" is, this code has an issue with error handling. 尽管有拼写错误,但人们只能猜测实际的“错误”是什么,但是此代码在错误处理方面存在问题。 Specifically, in the disposal of resources. 具体来说就是在处置资源上。

Discussion about resources 关于资源的讨论

What are resources ? 什么是资源?

Basically : any Java Object that relies on underlying OS level resources. 基本上:依赖底层操作系统级别资源的任何Java对象。 Mostly : IO resources (input and output streams, channels), Sockets. 通常是:IO资源(输入和输出流,通道),套接字。 But more importantly : if the "thing" you're using has a close , dispsose , shutdown or any of the like, it surely holds on to resources internally. 但更重要的是:如果您使用的“东西”具有closedispsoseshutdown或类似的东西,则它肯定会在内部保留资源。
There are some exceptions (notably ByteArrayInputStream holds no resource but memory), but these are implementation details : if you stick to their interface (and you should, this is a "contract"), every stream should be closed. 有一些例外(值得注意的是ByteArrayInputStream拥有任何资源,只有内存),但是这些是实现细节:如果您坚持使用它们的接口(应该,这是一个“契约”),则每个流都应该关闭。
Since Java 7, most of these objects in the Java API implement the AutoCloseable interface, but many 3rd parties have not necessarily ported this to their code (and maybe some can't for other reasons). 从Java 7开始,Java API中的大多数这些对象都实现了AutoCloseable接口,但是许多第三方并不一定将其移植到他们的代码中(也许有些由于其他原因而不能)。

As one of the code reviewers at my company : I stop reading and I reject any code as soon as I do not see a secure call to the close method of a resource. 作为我公司的代码审查员之一:一旦看不到对资源的close方法的安全调用,我将停止阅读并拒绝任何代码。 By secure I mean inside a finally clause, that is guaranteed to be executed. 安全是指在finally子句中保证执行该子句。

Rule of thumb about resources 关于资源的经验法则

Any resource obtained by your program should be freed in a finally clause (some even add : of its own). 程序获得的任何资源都应在finally子句中释放(某些资源甚至可以自己添加:)。

What is the typical lifecycle of a resource Well: 资源井的典型生命周期是什么:

  1. You obtain it 你得到它
  2. You use it 你用吧
  3. You release it 你释放它

In your code, that is 在您的代码中

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

Since Java 7, if the resource object implements AutoCloseable you have a new way of writing that, it's called the "try with resources". 从Java 7开始,如果资源对象实现AutoCloseable您有一种新的编写方式,称为“尝试资源”。

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

You do not see the finally, but it's there, the compiler writes the finally clause for you in that case. 您看不到finally,但是在那里,编译器在这种情况下为您编写了finally子句。

What about multiple resources ? 多种资源呢?

Well : multiple resources, multiple finallys. 好吧:多个资源,多个终结。 The idea is to separate the causes of failures in different finally clauses. 想法是在不同的finally子句中分离失败的原因。 Say you want to copy a file... 假设您要复制文件...

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

Or, with Java 7 syntax, we have the shorter (disclaimer : I have no Java7 right now, so can't really check if this compiles) : 或者,使用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.
}

This is SO MUCH BOILERPLATE ! 这是如此的沸腾!

Yes it is. 是的。 That's why we have libraries. 这就是为什么我们有图书馆。 One could argue you should not write such code. 有人可能会辩称您不应编写此类代码。 Use standard, well behaved libraries like commons IO, or use one of their utility methods. 使用标准的,行为良好的库(如Commons IO),或使用其实用程序方法之一。 Or newer JDK methods like the Files API, and see how this works. 或较新的JDK方法(如Files API),并查看其工作原理。

Commons IO has a handy IOUtils.closeQuietly() suite of methods for closing streams. Commons IO有一个方便的IOUtils.closeQuietly()方法套件,用于关闭流。

Try with resources Gotchas 试用资源Gotchas

There are ramifications in the "try with resources" call that go a bit deeper than that. “尝试资源”调用中的后果要深得多。 These include: What if I want to do something with the exceptions that occur in the finally clause ? 这些包括:如果我想对finally子句中出现的异常做些什么? How do I differentiate that from an exception that would have occured during performCopy ? 如何将它与performCopy期间发生的异常区performCopy Another case is : what happens here : 另一种情况是:这里发生了什么:

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

It happens that an UnsupportedEncodingException is thrown but after the FileInputStream is instanciated. 发生UnsupportedEncodingException异常,但是 FileInputStream实例化之后。 But as the FileInputStream is not the subject of the try clause, it will NOT be closed. 但是由于FileInputStream不是try子句的主题,因此不会关闭它。 An you have a File descriptor leak. 您有文件描述符泄漏。 Try that a thousand times, and your JVM will not be able to open files anymore, you OS will tell you "max number of open files exceeded" ( ulimit generally does that in UNIX) 尝试一千遍,您的JVM将无法再打开文件,您的OS会告诉您“已超过最大打开文件数”(在UNIX中, ulimit通常ulimit

Back to your sockets 回到插座

So what are your resources ? 那你有什么资源呢?

Well, first, we can notice that you have only ONE true resource, your Socket instance, because the Socket javadoc says (javadoc): 好吧,首先,我们可以注意到您只有一个真正的资源,即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}.

So your Input and Output streams are tied to your Socket, and this is enough. 因此,您的输入和输出流与套接字绑定在一起,这就足够了。

What's wrong with your code 您的代码有什么问题

Adding comments one your original code: 在原始代码中添加注释:

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.");
}

A safe implementation 安全实施

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

Try this I think you missed one semicolon 试试这个,我想你错过了一个分号

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

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM