Java Sockets en passant entre les méthodes
-
21-12-2019 - |
Question
Je commence tout juste avec Java Programmation Socket, et j'ai été la lecture de la littérature sur les sockets ici.Le code ci-dessous est un exemple d'un livre que j'ai pris qui me demande de trouver le bug.La comparaison avec la littérature même si je ne suis pas le voir tout les bugs.La création de la socket, bufferedreader, et printwriter semblent corrects, et ils sont entourés dans un bloc try-catch ainsi.Le sont correctement "close()"ed dans un bloc try-catch ainsi.Est-il une erreur lors du passage de ces process()?Toute aide serait appréciée.
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.");
}
}
}
La solution
Bien que, malgré les fautes de frappe, on ne peut que deviner ce que le "bug" est, ce code a un problème avec la gestion des erreurs.Plus précisément, dans la disposition de ressources.
Discussion sur les ressources
Quelles sont les ressources ?
En gros :tout Objet Java qui s'appuie sur l'OS sous-jacent au niveau des ressources.Principalement :IO ressources (d'entrée et de sortie des ruisseaux, des canaux), les Sockets.Mais le plus important :si la "chose" que vous utilisez dispose d'un close
, dispsose
, shutdown
ou tout comme, il détient sur les ressources à l'interne.
Il y a quelques exceptions près (notamment ByteArrayInputStream
détient pas de ressources, mais de mémoire), mais ce sont des détails de mise en œuvre :si vous vous en tenez à leur interface (et vous devriez, ce est un "contrat"), tous les cours d'eau doit être fermé.
Depuis Java 7, la plupart de ces objets dans l'API Java de mettre en œuvre la AutoCloseable
l'interface, mais beaucoup de 3ème parties n'ont pas nécessairement portés à leur code (et peut-être que certains ne peuvent pas, pour d'autres raisons).
Comme l'un des code évaluateurs dans mon entreprise :J'ai arrêter de lire et je rejette tout code dès que je ne vois pas un appel sécurisé à l' close
méthode d'une ressource.Par paiement sécurisé, je veux dire à l'intérieur d'une clause finally, qui est garanti pour être exécuté.
Règle de base sur les ressources
Tout ressources obtenues par votre programme doit être libéré dans un finally
clause (certains même ajouter :de son propre).
Quel est le cycle de vie d'une ressource Bien:
- Vous l'obtenir
- Vous l'utilisez
- À la libération
Dans votre code, c'est à
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
}
}
}
Depuis Java 7, si l'objet de la ressource met en œuvre AutoCloseable
vous avez une nouvelle façon d'écrire qui, on l'appelle le "essayez avec des ressources".
try(ResourceObject myObject = getResource()) {
process(myObject);
}
Vous ne voyez pas la enfin, mais il est là, le compilateur écrit la clause finally pour vous dans ce cas.
Ce sur de multiples ressources ?
Bien :des ressources multiples, de multiples finallys.L'idée est de séparer les causes des défaillances dans les différents enfin clauses.Dites que vous voulez copier un fichier...
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*/ }}
}
}
Ou, avec Java 7 de la syntaxe, nous avons la plus courte (avis de non responsabilité :Je n'ai pas de Java7 droit maintenant, ne peut donc pas vraiment vérifier si cette compile) :
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.
}
C'est TELLEMENT passe-partout !
Oui, il est.C'est pourquoi nous avons bibliothèques.On pourrait dire que vous ne devriez pas écrire un tel code.Utilisation standard, bien comporté des bibliothèques comme commons IO, ou d'utiliser une de leurs méthodes de l'utilitaire.Ou plus récent JDK méthodes comme l' Files
API, et voir comment cela fonctionne.
Commons IO a une pratique IOUtils.closeQuietly()
suite de méthodes de fermeture de flux.
Essayez avec des ressources de Pièges
Il y a des ramifications dans le "essayez avec des ressources" appel que aller un peu plus profond que cela.Elles comprennent:Que faire si je veux faire quelque chose avec les exceptions qui se produisent dans la clause finally ?Comment puis-je différencier qu'à partir d'une exception qui aurait eu lieu au cours de performCopy
?Un autre cas est :ce qui se passe ici :
try(Reader reader = new InputStreamReader(new FileInputStream("in"), "an encoding that is not supported")) {
// Whatever
}
Il arrive qu'une UnsupportedEncodingException
est levée, mais après l' FileInputStream
est instanciée.Mais comme l' FileInputStream
n'est pas le sujet de l'essai de la clause, il ne sera PAS fermé.Vous avez un Fichier descripteur de fuite.Essayer ça un millier de fois, et votre JVM ne sera pas en mesure d'ouvrir des fichiers, vous OS vous dira "nombre maximum de fichiers ouverts dépassé" (ulimit
n'a généralement que sous UNIX)
De retour de vos prises de courant
Alors, quelles sont vos ressources ?
Eh bien, tout d'abord, on peut remarquer que la seule vraie ressource, votre Socket exemple, parce que le Socket javadoc dit (javadoc):
* <p> Closing this socket will also close the socket's
* {@link java.io.InputStream InputStream} and
* {@link java.io.OutputStream OutputStream}.
Si votre Entrée et les flux de Sortie sont liés à votre Prise, et c'est assez.
Quel est le problème avec votre code
L'ajout de commentaires un original de votre 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.");
}
Une mise en œuvre sûre
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.
}
}
Autres conseils
public void process(){PrintWriter out, BufferedReader in, Socket echoSocket){
devrait être
public void process(PrintWriter out, BufferedReader in, Socket echoSocket){
sinon tout me semble bien
essayez ceci, je pense que vous avez manqué un point-virgule
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)
{
}