JAVA : Transmissions de données entre classes

Fermé
Canaboss Messages postés 49 Date d'inscription dimanche 1 juin 2008 Statut Membre Dernière intervention 22 août 2009 - 17 sept. 2008 à 20:58
Marco la baraque Messages postés 996 Date d'inscription vendredi 9 mai 2008 Statut Contributeur Dernière intervention 5 novembre 2009 - 23 sept. 2008 à 23:53
Bonjour,

Je suis coincé au niveau d'un programme client dans l'optique d'un dialogue client/serveur

Le client est programmé dans ma classe CMain_Client, c'est dans cette classe notamment que je crée le socket de connexion au serveur et le BufferedReader pour écouter ce que le serveur envoie

Pour ne pas avoir de problèmes, je veux utiliser des threads, j'ai donc créer une classe CClient_lecture() qui dérive de Thread. Voici son contenu :

public class CClient_lecture extends Thread{

BufferedReader BR_ReadFromServer;
//Constructeur
public CClient_lecture(BufferedReader BR_ReadFromServer){
}

public void run(){
//Declaration
String ReceivedLine;
//Programme
try {
System.out.println("on a bien lancé le thread");
while (true){
ReceivedLine = BR_ReadFromServer.readLine();
System.out.println(ReceivedLine);
}
}
catch (IOException e) {
e.printStackTrace();
}
}
}



Cette classe est appelée par la classe CMain_Client par le code suivant :
Thread Class_lire = new CClient_lecture(BR_ReadFromServer);
Class_lire.start();


Et quand j'exécute le code, j'obtiens un NullPointerException au niveau de la ligne en gras, je pense donc que je ne passe pas l'argument correctement ... Pouvez vous m'aider ? Est ce un problème avec mon constructeur ?
A voir également:

8 réponses

Marco la baraque Messages postés 996 Date d'inscription vendredi 9 mai 2008 Statut Contributeur Dernière intervention 5 novembre 2009 328
17 sept. 2008 à 21:20
Bonsoir !
Quand tu fais ReceivedLine = BR_ReadFromServer.readLine();, ça utilise ton attribut d'instance
BufferedReader BR_ReadFromServer; défini pour ta classe CClient_lecture.
Tu as évidemment un NullPointerException parce que ton attribut n'est jamais instancié.

Pour éviter cela, tu peux gérer ça dans ton constructeur, comme tu le dis :
//Constructeur
public CClient_lecture(BufferedReader BR_ReadFromServer){
 this.BR_ReadFromServer = BR_ReadFromServer;
} 


Pour plus de lisibilité, évite tout de même d'appeler tes instances par des noms commençant par une majuscule.

Comme ça ça marchera mieux (si le BufferedReader que tu as passé est correctement instancié évidemment).

Cordialement
0
Canaboss Messages postés 49 Date d'inscription dimanche 1 juin 2008 Statut Membre Dernière intervention 22 août 2009 10
18 sept. 2008 à 08:28
Merci pour cette réponse qui fonctionne parfaitement !!!

J'ai un autre problème à mon avis assez simple :

Lorsque que le client envoie la ligne "*", le serveur coupe la connexion ...

j'utilise socketclient.close() et MyServer.close() ... Mais cela me renvoit encore une nullPointerException ...

Dois-je utiliser Try Finally ? Et si c'est le cas que je dois je inclure dedans ? Merci de votre aide !
-1
Marco la baraque Messages postés 996 Date d'inscription vendredi 9 mai 2008 Statut Contributeur Dernière intervention 5 novembre 2009 328
18 sept. 2008 à 11:00
Bonjour,
En général, les blocs try/catch, c'estpour gérer des exceptions contrôlées, par exemple quand tu définis tes propres exceptions, et que tu les lèves de manière avoir pouvoir adapter un comportement en fonction du bon déroulement (ou non de ton programme).
Tu peux bien évidemment catcher des exceptions classiques pour adapter le comportement de ton application. Ceci dit, en général on ne catche pas des NullPointerException : quand tu as un NPE, ça signifie que tu essaies d'utiliser un objet qui n'a pas été instancié, donc :
- soit tu gères ça avec un if
- soit tu repères l'objet en question et tu l'initialises, parce que ça vient d'une erreur de programmation, et ça n'a pas à être traité comme un comportement 'normal' pouvant survenir dans ton application.

j'utilise socketclient.close() et MyServer.close(). Oui, mais ces trucs là, c'est des instances de quelles classes ? Tu les a créé comment ?
Donne nous le code qui a servi à les instancier, les appels aux constructeurs, et on pourra sans doute aller un peu plus loin.

Bonne journée.
-1
Canaboss Messages postés 49 Date d'inscription dimanche 1 juin 2008 Statut Membre Dernière intervention 22 août 2009 10
18 sept. 2008 à 19:55
Voici le code de ma classe Serveur, avec en gras les parties qui me semblent importantes pour la résolution du problème :

import java.net.*;
import java.io.*;
import java.lang.*;

//La classe serveur gère la connexion avec les clients et les sockets.

public class CServeur {

static final int MyPort = 4170;
Socket client;

ServerSocket MyServer = null;


//Fonction de départ
public void Start() throws IOException{
try {
client=Connect(client,MyServer);
Speak(client);
}
finally{
client.close();
MyServer.close();

}
}

//Fonction de connexion avec création des sockets et renvoi le socket client qu'on
//doit réutiliser
private Socket Connect(Socket client, ServerSocket MyServer) throws IOException{
MyServer = new ServerSocket(MyPort);
System.out.println("Listening");
client = MyServer.accept();
System.out.println("Accepted");
System.out.println(client.isConnected());
return client;

}

//Fonction qui permet de parler au client. Nécessite en entrée le Socket client et
//le chaine de caractères a envoyé
private void Speak(Socket client) throws IOException{
String LineReceived;
System.out.println(client.isConnected());
BufferedReader lecture = new BufferedReader(new InputStreamReader(client.getInputStream()));
BufferedWriter ecriture = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));

ecriture.write("Bienvenue à toi");
ecriture.flush();

while(true){
try {
LineReceived = lecture.readLine();
System.out.println(LineReceived);
if(LineReceived.equals("*")){
break;
}
}
catch (IOException e) {
e.printStackTrace();
}

}
ecriture.close();
lecture.close();
}
}
-1
Marco la baraque Messages postés 996 Date d'inscription vendredi 9 mai 2008 Statut Contributeur Dernière intervention 5 novembre 2009 328
18 sept. 2008 à 23:24
Rebonsoir,

Alors un petit commentaire tout d'abord : en java, tes objets sont des références. Autrement dit, tu n'as pas de passages de paramètres par recopie comme ça se fait dans d'autres langages (sauf pour les types primitifs). Donc tu pourrais réécrire ton code comme ceci :
//Fonction de départ
public void Start() throws IOException{
try {
connect();
Speak(client);
}
finally{
client.close();
MyServer.close();
}
}

//Fonction de connexion avec création des sockets et renvoi le socket client qu'on
//doit réutiliser
private void connect() throws IOException {
MyServer = new ServerSocket(MyPort);
System.out.println("Listening");
client = MyServer.accept();
System.out.println("Accepted");
System.out.println(client.isConnected());
} 


En effet, dans ce cas, les références à MyServer et client de la méthode connect() référencent les attributs de ton instance de classe (pas besoin dans ce cas de les passer en paramètre, ou de les retourner (parce que quand tu fais client = MyServer.accept(), puis client = Connect(...), au final, c'est comme client référence le même attribut, c'est comme si tu faisais i=i, ça ne sert à rien).

Maintenant on va parler un peu d'exceptions. Imaginons une interface avec 3 classes : A, B et C.
A, c'est la classe sur laquelle l'utilisateur entre des données (par exemple dans un formulaire).
C, c'est la classe qui enregistre ces données dans une base de données.
B, c'est une classe intermédiaire.

Imaginons maintenant 2 cas :
- 1er cas : L'utilisateur entre une mauvaise information. Ton information est entrée dans A (méthode "enregistreDonnees()"), passe par B (méthode "passeDonnees()"), et vas dans C (méthode "sauve()"). Là, tu as une méthode de ta classe C qui détecte que l'information est erronée : tu lances une exception. Ici, comme c'est l'utilisateur qui est responsable de l'erreur, il convient de lui retourner cette erreur :
sauve sera affecté d'un "throws MonException", passeDonnees() aussi, et enfin, tu vas avoir un bloc try catch dans enregistreDonnees() afin de catcher les exceptions de type MonException, et d'afficher un message cohérent à l'utilisateur pour lui indiquer pourquoi ça ne marche pas.
-2ème cas : Même cinématique, sauf que l'exception n'est pas due à l'utilisateur, mais à ton application (par exemple la donnée que tu essaies d'enregistrer ne peut pas s'enregistrer parce que ta base de données n'est pas accessible). Dans ce cas, C va remonter l'exception ConnexionException à B, qui lui va la catcher pour faire un traitement transparent pour l'utilisateur (par exemple va stocker les données dans une file d'attente, lancer un timer et retenter de sauvegarder les données 30minutes plus tard).

Ces deux exemples, je les ai choisis pour te montrer que les exceptions peuvent être de 2 sortes : soit une erreur de l'utilisateur, auquel cas tu remontes l'exception au plus haut niveau pour l'en avertir, soit tout simplement un cas fonctionnel qui n'est pas "normal" dans le sens où en principe ça ne doit pas se produire, mais que tu gères proprement car "ça arrive tout de même quelquefois", et il faut bien que ton application tienne le coup.

Si je te dis tout ça, c'est parce que dans ton cas, ce que tu as fait ne sert à rien. Un bloc try sans catch, c'est inutile. Donc soit tu catches les IOException au lieu de les faire remonter, sois tu enlèves ton "try-finally".

Le fait que tu aies ici encore un NPE m'étonne car :
- MyServer = new ServerSocket(MyPort); Si une IOException est lancée depuis cet appel, MyServer ne sera jamais affecté, et tu auras une IOException ou une SecurityException, pas un NullPointerException.
- client = MyServer.accept(); Si une IOException est lancée depuis cet appel, client ne sera jamais affecté, et tu auras une IOException. Cependant en principe tu dois avoir une IOException et non pas un NullPointerException, donc je ne pense pas que ce soit le cas.

Le mieux pour toi est de (après avoir viré ton try-finally) remonter la pile d'appel pour déterminer plus précisément d'où vient le NullPointerException.

Cordialement
-1
Canaboss Messages postés 49 Date d'inscription dimanche 1 juin 2008 Statut Membre Dernière intervention 22 août 2009 10
19 sept. 2008 à 20:03
Merci pour tes éclairages, mais je ne suis pas sur d'avoir tout compris :(

Pk tu fais

connect();
Speak(client);


Pourquoi tu laisse l'attribut client dans Speak ?
-1

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
Canaboss Messages postés 49 Date d'inscription dimanche 1 juin 2008 Statut Membre Dernière intervention 22 août 2009 10
19 sept. 2008 à 20:21
Désolé, j'ai encore un élement à t'apporter ...

L'exception NPE se passe lorsque le client envoi *, c'est dire que ca finit speak !
-1
Marco la baraque Messages postés 996 Date d'inscription vendredi 9 mai 2008 Statut Contributeur Dernière intervention 5 novembre 2009 328
19 sept. 2008 à 23:19
Hello,
Pourquoi tu laisse l'attribut client dans Speak ? Effectivement, il ne faut pas le laisser.

L'exception NPE se passe lorsque le client envoi *, c'est dire que ca finit speak ! Nous y voilà. Je cherchais désespérément pourquoi un NullPointerException alors qu'il ne devait pas y en avoir, mais en fait l'erreur est plus loin...

En fait c'est à cause de ton :
while(true){
 try {
  LineReceived = lecture.readLine();
  System.out.println(LineReceived);
  if(LineReceived.equals("*")){
   break;
  }
 }
 catch (IOException e) {
 e.printStackTrace();
} 


Tu lis bien le char '*', mais le problème est que tu ne sors pas de ta boucle infinie. A mon avis, LineReceived ne doit pas être égale à "*", mais plutôt à une chaîne du type "*\n". Si c'est effectivement le cas, ton code actuel ne te fait pas sortir de ta boucle, et au tour suivant, tu as ton lecture.readLine(); qui te retourne null, et donc si tu fais LineReceived.equal(...), tu as un NPE.
-1
Canaboss Messages postés 49 Date d'inscription dimanche 1 juin 2008 Statut Membre Dernière intervention 22 août 2009 10
22 sept. 2008 à 00:38
j'ai essayé de remplacer

if(LineReceived.equals("*")){

par

if(LineReceived.equals("*\n")){

mais alors, le programme ne s'arrête plus :(
-1
Marco la baraque Messages postés 996 Date d'inscription vendredi 9 mai 2008 Statut Contributeur Dernière intervention 5 novembre 2009 328
22 sept. 2008 à 11:11
Bonjour,
Au temps pour moi, il ne faut pas mettre le '\n' :
Returns:
A String containing the contents of the line, not including any line-termination characters, or null if the end of the stream has been reached


En principe tu devrais donc sortir avec ton '*'. Ton System.out.println t'affiche bien '*' quand il faut? Peux tu vérifier que tu rentres bien dans ton if avant que ton programme se termine?

Merci
-1
Canaboss Messages postés 49 Date d'inscription dimanche 1 juin 2008 Statut Membre Dernière intervention 22 août 2009 10
23 sept. 2008 à 02:15
Effectivement, cela marche mieux avec simplement "*"

Je ne comprends pas du tout ton poste (le numéro 5), lorsque j'essaye d'appliquer ton conseil plus rien ne fonctionne alors que quand je continues dans ma logique, j'ai trouvé une solution :

//Fonction de connexion avec création des sockets et renvoi le socket client qu'on
//doit réutiliser
private void connect() throws IOException {
MyServer = new ServerSocket(MyPort);
System.out.println("Listening");
client = MyServer.accept();
System.out.println("Accepted");
System.out.println(client.isConnected());
}

Je l'ai transformé en :

//Fonction d'initialisation du serveur
private ServerSocket CreateServer(ServerSocket MyServer) throws IOException{
MyServer = new ServerSocket(MyPort);
return MyServer;
}

//Fonction de connexion avec création du socket client et renvoi de celui ci
//doit réutiliser
private Socket Connect(Socket client, ServerSocket MyServer) throws IOException{
System.out.println("Listening");
client = MyServer.accept();
System.out.println("Accepted");
System.out.println(client.isConnected());
return client;
}

et maintenant cela fonctionne ... Tant mieux, mais j'aimerais comprendre ce que tu as tenté de m'expliquer, je déteste ne pas comprendre !
-1
Marco la baraque Messages postés 996 Date d'inscription vendredi 9 mai 2008 Statut Contributeur Dernière intervention 5 novembre 2009 328
23 sept. 2008 à 21:46
Hello Canaboss,
Alors tout d'abord, concernant ton code, j'avoue que je ne comprends pas. Les deux versions font strictement la même chose, donc je ne vois pas pourquoi la seconde marche mieux que la première... Enfin bref, ça marche c'est le principal, mais c'est déroutant tout de même.

Je suppose que ce que tu n'as pas compris, c'est le passage par référence, donc je vais essayer de te réexpliquer différemment.
En fait, vu la manière dont tu codes, on dirait que tu as appris à coder en impératif avant d'apprendre la programmation objet.
Je pense que tu connais le C, peut-être aussi le C++, donc je vais me baser sur des exemples simples (même si tu ne connais pas ces langages, tu vas comprendre).

1- Le passage par copie
En C :
int fonction(int a) {
return ++a;
}

int main(int argc, char *argv[])
{
int a = 5;
a = fonction(a);
printf("%d\n", a);
}

Ca affiche 6.
L'effet du passage par copie :
dans mafonction, a est alloué dans la pile. Quand tu le passes à mafonction2, ce n'est pas a qui est passé, mais un nouveau int est alloué dans la pile, et c'est cette copie qui va être utilisée dans mafonction2. Enfin, quand tu retournes a, un troisième int est copié dans la pile pour être retourné.

Le problème : bah tu écris en mémoire, donc tu 'perds' du temps, et surtout, tu écris des données qui ne servent pas forcément, et tu gaspilles de l'espace (ici c'est des objets légers, mais si tu passes un fichier de plusieurs Mo en paramètre, bonjour les dégâts, tu vas avoir un stack overflow à coup sûr).

2- Le passage par pointeur
Toujours en C
void fonction(int * a) {
++(*a);
}

int main(int argc, char *argv[])
{
int a = 5;
fonction(&a);
printf("%d\n", a);
}
Ca affiche 6 aussi.
Ici on passe le pointeur à la fonction, donc l'adresse en mémoire. Ca permet donc à fonction de modifier l'objet sans faire de copie, et donc pas non plus besoin de retourner l'objet vu qu'il a été modifié en mémoire (et non pas une copie comme dans l'exemple précédent). Les pointeurs c'est bien mais ça peut être dangereux car :
- si tu sais pas trop les utiliser t'as plein de fuites mémoire et de bugs
- tu peux faire des effets de bord

3- le passage par référence
Enfin, tu as le passage par référence en C++, qui ressemble fortement au passage par pointeur dans le sens où tu n'as pas de copie, tu passes une adresse. Mais c'est plus sécurisé parce que tu n'as que des droits limités sur la référence.

En java, à part pour les types primitifs (byte, char, short, int, long, float, double... j'en oublie peut-être) qui se font par copie, les objets sont passés par référence. C'est pourquoi quand tu fais :
Object o = new Object();
o = changeMe(o);

Ca ne sert à rien, car o est déjà modifié.

Je ne sais pas si j'ai été plus clair, mais je l'espère.
Cordialement
-1
Canaboss Messages postés 49 Date d'inscription dimanche 1 juin 2008 Statut Membre Dernière intervention 22 août 2009 10
23 sept. 2008 à 23:33
Merci pour ces lumieres !!

Effectivement, j'ai appris le C avant le JAVA, c'est peut etre pourquoi je code comme cela :) !!
En fait j'avais bien compris ce que tu m'as propose alors, mais pourquoi mon code ne marche pas quand je fais ce que tu dis ... je vois vraiment pas !! Est ce que ca peut avoir un rapport avec les threads ?
-1
Marco la baraque Messages postés 996 Date d'inscription vendredi 9 mai 2008 Statut Contributeur Dernière intervention 5 novembre 2009 328
23 sept. 2008 à 23:53
Rebonsoir,
Tu ne m'as pas dit que tu avais plusieurs threads, mais je ne pense pas que ce soit un problème de ce genre (qu'un thread initialise ton MyServer, puis qu'un autre le fasse pointer sur null avant que ton client ne l'utilise, je trouve pas ça crédible. D'autant plus que ce que tu fais actuellement fonctionne, et que finalement c'est la même chose).

Pour moi le mystère reste entier.
Si j'étais toi, j'essaierais simplement de poser des breakpoints dans Eclipse à chaque étape pour voir l'état de tes objets, et voir ce qui cloche juste avant la levée du NPE.

Cordialement
-1