Сокеты Java, передаваемые между методами

1

Я только начинаю с Java Socket программирования, и я читал литературу на патрубках здесь. Нижеприведенный код - образец из учебника, который я взял, который просит меня найти ошибку. Сравнивая с литературой, я не вижу никаких ошибок. Создание сокета, буферизатора и печатающего устройства кажется правильным, и они также окружены блоком try-catch. Правильно "close()" также редактируются в блоке try-catch. Есть ли ошибка при передаче данных процессу()? Любая помощь будет оценена по достоинству.

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.");
    }
  }
}
  • 0
    Дает ли учебник пример того, как код ведет себя некорректно, или он просто говорит: «Есть ошибка, найди ее»?
  • 0
    @ajb - Нет, просто краткое изложение того, как работают сокеты, это как «поиск ошибки».
Показать ещё 8 комментариев
Теги:
sockets
bufferedreader
printwriter

3 ответа

2
Лучший ответ

Хотя, несмотря на опечатки, можно только догадываться, что такое "ошибка", этот код имеет проблему с обработкой ошибок. В частности, в распоряжении ресурсов.

Обсуждение ресурсов

Какие ресурсы?

В основном: любой объект Java, который опирается на базовые ресурсы уровня ОС. В основном: ресурсы ввода-вывода (входные и выходные потоки, каналы), сокеты. Но что еще более важно: если "вещь", которую вы используете, имеет close, dispsose, shutdown или любое подобное, она, безусловно, держится за ресурсы внутри страны.
Есть некоторые исключения (в частности, ByteArrayInputStream содержит ресурса, кроме памяти), но это детали реализации: если вы придерживаетесь своего интерфейса (и вы должны это "контракт"), каждый поток должен быть закрыт.
Начиная с Java 7, большинство этих объектов в Java API реализуют интерфейс AutoCloseable, но многие сторонние стороны необязательно портируют это на свой код (и, возможно, некоторые не могут по другим причинам).

Как один из рецензентов кода в моей компании: я прекращаю чтение, и я отклоняю любой код, как только я не вижу безопасного вызова метода 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 в этом случае.

Как насчет нескольких ресурсов?

Ну: несколько ресурсов, несколько окончаний. Идея состоит в том, чтобы разделить причины сбоев в разных окончательных предложениях. Предположим, вы хотите скопировать файл...

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, такие как API Files, и посмотрите, как это работает.

Commons IO имеет удобный набор методов IOUtils.closeQuietly() для закрытия потоков.

Попробуйте с ресурсами Gotchas

В вызове "попробуйте с ресурсами" есть разветвления, которые идут немного глубже, чем это. К ним относятся: Что делать, если я хочу что-то сделать с исключениями, которые возникают в предложении finally? Как я могу отличить это от исключения, которое произошло бы во время выполнения команды " performCopy? Другой случай: что здесь происходит:

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

Бывает, что FileInputStream 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}.

Таким образом, ваши потоки ввода и вывода привязаны к вашему гнезду, и этого достаточно.

Что не так с вашим кодом

Добавление комментариев к вашему исходному коду:

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 bad !
    return;
}
// Let 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.
    }
}
3
public void process(){PrintWriter out, BufferedReader in, Socket echoSocket){

должно быть

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

в противном случае все кажется мне хорошим

  • 0
    Хотя это и правда, мне остается задуматься, сказал ли автор книги о сокетах «найди ошибку», а затем добавил синтаксическую ошибку в пример кода.
  • 0
    Это была ошибка пользователя, когда я копировал код. Мои извинения, но, конечно, ошибка.
0

Попробуйте это, я думаю, вы пропустили одну точку с запятой

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

}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню