Я только начинаю с 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.");
}
}
}
Хотя, несмотря на опечатки, можно только догадываться, что такое "ошибка", этот код имеет проблему с обработкой ошибок. В частности, в распоряжении ресурсов.
В основном: любой объект Java, который опирается на базовые ресурсы уровня ОС. В основном: ресурсы ввода-вывода (входные и выходные потоки, каналы), сокеты. Но что еще более важно: если "вещь", которую вы используете, имеет close
, dispsose
, shutdown
или любое подобное, она, безусловно, держится за ресурсы внутри страны.
Есть некоторые исключения (в частности, ByteArrayInputStream
содержит ресурса, кроме памяти), но это детали реализации: если вы придерживаетесь своего интерфейса (и вы должны это "контракт"), каждый поток должен быть закрыт.
Начиная с Java 7, большинство этих объектов в Java API реализуют интерфейс AutoCloseable
, но многие сторонние стороны необязательно портируют это на свой код (и, возможно, некоторые не могут по другим причинам).
Как один из рецензентов кода в моей компании: я прекращаю чтение, и я отклоняю любой код, как только я не вижу безопасного вызова метода close
ресурса. Под защитой я подразумеваю внутри предложения 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
}
}
}
Начиная с 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()
для закрытия потоков.
В вызове "попробуйте с ресурсами" есть разветвления, которые идут немного глубже, чем это. К ним относятся: Что делать, если я хочу что-то сделать с исключениями, которые возникают в предложении 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.
}
}
public void process(){PrintWriter out, BufferedReader in, Socket echoSocket){
должно быть
public void process(PrintWriter out, BufferedReader in, Socket echoSocket){
в противном случае все кажется мне хорошим
Попробуйте это, я думаю, вы пропустили одну точку с запятой
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)
{
}