IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Entrée/Sortie Java Discussion :

Java, Sockets et images


Sujet :

Entrée/Sortie Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Septembre 2017
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Septembre 2017
    Messages : 15
    Par défaut Java, Sockets et images
    Bonjour à tous,

    Je viens à l'aide sur ce forum car je suis bloqué pour un projet d'études (réalisation d'un proxy http). Projet assez simple, interpréter la requête (GET) du client, la traiter, l'envoyer au serveur web, traiter la réponse, et l'envoyer au client. Tout fonctionne nickel à l'exception de deux "détails":

    - Accept-Encoding: gzip
    - La réception d'image
    - un soucis de performance

    Dès que "j'active" la compression je reçois un "content encoding error" dans le browser par contre si je supprime le "Accept-Encoding: gzip" ça passe sans problème...
    Je pense que ce problème est lié au second, car aucune image ne passe, dans wireshark il me dit que les données sont corrompues, mais je ne vois pas par quoi... le site que je dois pouvoir afficher est le suivant:

    http://httpd.apache.org/docs/2.2/fr/

    On peut y voir la plume en haut à gauche ainsi que la toute petite flèche vers la gauche, seul ces deux objets ne sont pas affiché...

    Par contre sur cet autre site aucun problème: http://httpwg.org/specs/ (à l'exception de l'image en haut à droite qui est en https).

    Je me demande s'il n'y aurait pas un décalage de bit quelque part qui ferait que cela est lu comme "corrompu" mais je ne vois absolument pas où, surtout que tout le reste fonctionne !!

    Le dernier point, le soucis de performance voici ce que je vois dans wireshark (par exemple)

    - (temps = 0) requête envoyée par le client;
    - (temps + 0,182026 s) requête traitée et envoyée au serveur;
    - (temps + 0,170815 s) paquet n°1 de la réponse reçu sur le serveur;
    - (temps + 0 s) paquet n°2 de la réponse reçu sur le proxy;
    - (temps + 0,000205 s) paquet n°3 de la réponse reçu sur le proxy;
    - (temps + 0,000062 s) paquet n°4 de la réponse reçu sur le proxy;
    - (temps + 0,000099 s) paquet n°5 de la réponse reçu sur le proxy (dernier paquets);
    - (temps + 29,990383 s) paquet n°1 de la réponse envoyé au client;
    - (temps + 0,000001 s) paquet n°2 de la réponse envoyé au client;
    - (temps + 0,000001 s) paquet n°3 de la réponse envoyé au client;
    - (temps + 0,000001 s) paquet n°4 de la réponse envoyé au client;
    - (temps + 0,000001 s) paquet n°5 de la réponse envoyé au client;

    On peut voir qu'entre la réception sur le serveur proxy et l'envoi vers le client il s'écoule 30sec. Est-ce que quelqu'un pourrait me dire si éventuellement il y a un timer? Je ne configure rien dans mon programme, et par défaut le timeout sur le socket est à "0" ce qui signifie "infini"...

    Est-ce que vous pouvez m'aider?

    Merci d'avance !

    Hjacquemin

  2. #2
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    Citation Envoyé par hjacquemin Voir le message
    - Accept-Encoding: gzip
    - La réception d'image
    M'est d'avis (sans voir le code, difficile) que tu a traité la communication comme un flux texte, avec un StreamReader ou à tout le moins un encoder. C'est une erreur. Le HTTP, c'est du binaire, tu dois le traiter comme tel. Avec une parsing en texte, tu va détruire tout le contenu binaire (encodages asiatiques et fichiers binaires).
    Citation Envoyé par hjacquemin Voir le message
    Par contre sur cet autre site aucun problème: http://httpwg.org/specs/
    Les images de ce site sont en SVG (donc texte) ce qui me conforte dans mon idée que c'est ça ton problème.


    Citation Envoyé par hjacquemin Voir le message
    On peut voir qu'entre la réception sur le serveur proxy et l'envoi vers le client il s'écoule 30sec. Est-ce que quelqu'un pourrait me dire si éventuellement il y a un timer? Je ne configure rien dans mon programme, et par défaut le timeout sur le socket est à "0" ce qui signifie "infini"...
    Tu n'attendrais pas aussi que le serveur ferme sagement sa socket avant d'enoyer ta réponse au client par hasard? Les connections http sont persistents (+-15 à 30 secondes suivant la configuration du serveur) et c'est au client de les cloturer. Dans cette connection il peut y avoir plusieurs demandes http. https://en.wikipedia.org/wiki/HTTP_p...ction#HTTP_1.1
    Si tu attends bêtement que le serveur cloture la connection pour envoyer ta réponse au client, ça va effectivement être très lent à coup de timeouts.

  3. #3
    Membre averti
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Septembre 2017
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Septembre 2017
    Messages : 15
    Par défaut
    Bonjour Tchize,

    Citation Envoyé par tchize_ Voir le message
    M'est d'avis (sans voir le code, difficile) que tu a traité la communication comme un flux texte, avec un StreamReader ou à tout le moins un encoder. C'est une erreur. Le HTTP, c'est du binaire, tu dois le traiter comme tel. Avec une parsing en texte, tu va détruire tout le contenu binaire (encodages asiatiques et fichiers binaires).
    Merci de tout retour, j'ai la même impression que toi, mais j'avais déjà adapté mon code pour travailler en byte. A la base, je travaillais avec BufferedReader pour avoir facile à lire mon header avec la méthode readLine() en string puis j'ai changer le tout. Maintenant, j'envoie le InputSteam à ma fonction et je le copie dans un byte array pour travailler avec. Pour me faciliter la vie, je créer un BufferedReader à partir de l'array (juste pour traiter le header) et ensuite pour le Body je le prends depuis le byte array sans jamais y toucher pour ne pas corrompre les données (voici le code de ma méthode).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
     
    public byte[]  TraitementReponse(InputStream reponseServeur ) {
     
            final String CRLF = "\r\n";
     
            final int buffer = 33554432;
            final int maxTailleObj;
            String statusLigne = "";
            String headers = "";
            BufferedReader reponseServeurBuff = null;
            ByteArrayInputStream byteArrayInputStream = null;
            byte[] tmpArray = null;
            int count = 0;
            //byte[] body = new byte[maxTailleObj];
     
            int length = -1;
            boolean obtenirStatusLigne = false;
     
     
            try{
                // tempBuff = new InputStream(reponseServeur);
                tmpArray = org.apache.commons.io.IOUtils.toByteArray(new InputStreamReader(reponseServeur),"UTF-8");
     
                reponseServeurBuff = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(tmpArray)));
     
                //reponseServeurBuff = new BufferedReader(new InputStreamReader(tempBuff));
                String ligne = reponseServeurBuff.readLine();
     
                /**Dans la boucle ci-dessous on va lire chaque ligne de la réponse du serveur web.
                 * Tans que la taille de la ligne est différente de zéro la variable "statusLigne" récupère le contenu de cette ligne.
                 * Si le contenu de cette ligne = 0, on ajoute "\r\n" à la fin pour dire que notre requete est finie.
                 *
                 * Ensuite on test chaque ligne afin de trouvé la taille de la réponse.
                 * On récupère ensuite cette taille dans notre variable "length".
                 */
                while(ligne.length() !=0){
                    if (!obtenirStatusLigne){
                        statusLigne = ligne;
                        obtenirStatusLigne = true;
                    }
                    else{
                        headers += ligne + CRLF;
                    }
                    if (ligne.startsWith("Content-Length:") ||
                            ligne.startsWith("Content-length:")){
                        String[] tmp = ligne.split(" ");
                        length = Integer.parseInt(tmp[1]);
                    }
                    ligne = reponseServeurBuff.readLine();
                }
            }catch(IOException e) {
                System.out.println("Erreur de lecture du headers venant du serveur WEB: " + e);
            }
     
            byte[] body = new byte[length];
            String res = "";
            res = statusLigne + CRLF;
            res += headers;
            res += CRLF;
     
            int test = res.getBytes().length;
     
            byteArrayInputStream = new ByteArrayInputStream(tmpArray,test,tmpArray.length);
            // try{
            int bytesLu = 0;
            byte buf[] = new byte[buffer];
            boolean boucle = false;
     
            /**
             * La condition ci-dessous est utilisée dans le cas où il n'y aurait de "Content-length" dans la réponse.
             * On boucle ensuite dans le while jusqu'à la fermeture de la connexion.
             */
            if (length == -1){
                boucle = true;
            }
     
            /**
             * Lecture du corps de la réponse et on copie le contenu dans la variable body.
             * On s'arrête quand le nombre de bytesLu = Length ou jusque ce que la connexion se coupe.
             */
     
            while (bytesLu < length || boucle){
                int size = byteArrayInputStream.read(buf,0, buffer);
                if (size == -1) {
                    break;
                }
                /**
                 * Copie des bytes dans le body en s'assurant qu'on ne dépasse pas la taille d'objet maximum.
                 */
                for (int i =0;
                     1 < size && (i+ bytesLu) < length;
                     i++){
     
                    body[bytesLu + i] = buf[i];
                    /** trouver ce qu'il faut faire ici action avec buf[i]*/
                }
                bytesLu += size;
            }
     
            byte[] byteRequete = new byte[res.getBytes().length + body.length];
            System.arraycopy(res.getBytes(), 0, byteRequete, 0, res.getBytes().length);
            System.arraycopy(body, 0, byteRequete, res.getBytes().length, body.length);
     
            return byteRequete;
        }
    Tu n'attendrais pas aussi que le serveur ferme sagement sa socket avant d'enoyer ta réponse au client par hasard? Les connections http sont persistents (+-15 à 30 secondes suivant la configuration du serveur) et c'est au client de les cloturer. Dans cette connection il peut y avoir plusieurs demandes http. https://en.wikipedia.org/wiki/HTTP_p...ction#HTTP_1.1
    Si tu attends bêtement que le serveur cloture la connection pour envoyer ta réponse au client, ça va effectivement être très lent à coup de timeouts.
    J'avoue que pour ce point, je ne sais pas trop. Si j'analyse un peu la chose, je vois que mon serveur proxy envoi quasi immédiatement le flux vers le serveur web après réception de la requete GET. Mes sockets sont ouverts de la même manière dans les deux sens et je ne comprends pas cette différence de traitement.

    Voici les commande que j'utilise pour ouvrir et envoyer le flux sur mon socket. J'ai volontairement tronqué le code pour ne laisser que l'essentiel. S'il faut le code complet, j'ajouterai un zip.


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
     
    Socket serveur = null;
    .
    .
    .
     
                try {
                    /**
                     * Création socket pour la commnication "Proxy --> Server Web".
                     * Création du flux de sortie nécessaire à l'établissement de la connexion.
                     */
                    serveur = new Socket(InetAddress.getByName(requeteClient.getHost()), requeteClient.getPort());
                    DataOutputStream requeteVersServeur = new DataOutputStream(serveur.getOutputStream());
     
                    /** Ajout de la requete customisée, créer dans la classe "ClientHandling" au flux de sortie.
                     * La requete est envoyer sous forme de String et convertie en Bytes via la méthode writeBytes().
                     * On force l'écriture du buffer dans le stream via l'appel de la méthode flush().
                     * Fermeture du tampon et libaration des ressources associées via la méthode close().
                     */
                    requeteVersServeur.flush();
                    requeteVersServeur.writeBytes(requeteClient.toString());
                    requeteVersServeur.flush();
     
                } catch (UnknownHostException e) {
                    System.out.println("Unknown host: " + requeteClient.getHost());
                    System.out.print(e);
                    return;
                } catch (IOException e) {
                    System.out.println("Erreur lors de l'écriture de la requête: " + e);
                    return;
                }
     
                try{
                    reponseServeur = new DataInputStream(serveur.getInputStream());
                }
                catch (IOException e){
                    System.out.println("Error lors de la lecture de la réponse du serveur: " + e);
                }
                //Ici je récupère ma réponse traitée (voir code ci-dessus);
                reponseTraitee = TraitementReponse(reponseServeur);
     
                // ici je set la réponse dans la variable d'un objet pour mon cache
                requeteClient.setReponse(reponseTraitee);
     
     
                try{
     
                // Mise à jour de mon cache
                    ProxyServeur.AddToCache(requeteClient);
     
                // Création du stream de réponse vers le client web
                    versClient = new BufferedOutputStream(client.getOutputStream());
     
                // Envoi de la réponse au client
                    versClient.write(requeteClient.getReponse());
     
                    versClient.flush();
                    versClient.close();
     
     
                    client.close();
                    serveur.close();
    .
    .
    .

    Merci d'avance pour l'aide !!

  4. #4
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    Citation Envoyé par hjacquemin Voir le message
    ensuite pour le Body je le prends depuis le byte array sans jamais y toucher pour ne pas corrompre les données (voici le code de ma méthode).
    Plusieurs problèmes à ta méthode:
    Tu récupère ton byte[] en le retransformant depuis le Reader. Ce n'est pas une opération neutre. Quel est l'intérêt de prendre des bytes, d'en faire une String puis d'en refaire des bytes. Banni les Reader de ton code, ça ira mieux. Pour information, l'opération

    byte[] => String => byte[] ne retourne pas nécessairement la même séquence de byte. L'utf-8 interdit certaines séquence et le reader va les dropper, la String obtenue peut avoir été compactée car certains caractères peuvent avoir plusieurs représentations possible (genre é peut être codé comme le caractère unicode é ou comme le caractère "modificateur d'accent aigu" suivit de la lettre e). Enfin rien ne garanti que ton StreamReader va lire en utf-8.

    Ensuite, tu lit jusqu'au bout du Stream. Comme déjà mentionné dans mon post précédent, la lecture d'un réponse HTTP, ce n'est pas aussi simple.

    Pour moi tu devrais lire la réponse directement dans une array de bytes jusqu'à trouver la séquence binaire <CR><LF><CR<LF>. Tout ce qui se trouve avant fait partie des header. Tout ce qui se trouve après fait partie du content. Une fois tes header complets, tu peux les parser, les interpréter et construire ta réponse proxy, partie header. Ensuite, tu list bloc par bloc la suite de ta réponse jusque la fin du segment http et tu envoie ça au client tel quel.
    Ton code devrait donc contenir des inputStream avec ses méhode read(byte[]....) seulement.


    J'avoue que pour ce point, je ne sais pas trop. Si j'analyse un peu la chose, je vois que mon serveur proxy envoi quasi immédiatement le flux vers le serveur web après réception de la requete GET. Mes sockets sont ouverts de la même manière dans les deux sens et je ne comprends pas cette différence de traitement.

    Citation Envoyé par hjacquemin Voir le message
    Voici les commande que j'utilise pour ouvrir et envoyer le flux sur mon socket. J'ai volontairement tronqué le code pour ne laisser que l'essentiel. S'il faut le code complet, j'ajouterai un zip.
    Là aussi je vois quelques problèmes. Tu travaille avec un DataOutputStream. Tu ne devrais pas avoir besoin de cette classe. Tout comme les Reader/Writer, cette classe va te triturer tes donnée d'une manière que tu ne veux pas. Reste avec les InputStream et les OutputStream. Pareil, ta requête est une String. Pourquoi? Ton HTTP ne devrais être que des bytes, précédés des headers dont chaque ligne se fini par CRLF. L'ensemble forme un byte[]. Ton code marchera très bien pour envoyer un GET à la con vers le Serveur, mais si je fais un PUT d'un fichier binaire, tu va avoir exactement le même problème que celui que tu as à réceptionner un binaire. Oublie même l'idée d'avoir des Strings pour tes requêtes / réponse, tu ne peux pas.

    Tu dois aussi mettre en place un mécanisme plus intelligent pour gérer tes sockets. Les browsers vont tenter d'envoyer plusieurs requête sur la même socket, et toi tu ferme la socket violement. Il faut te renseigner, il y a plusieurs moyen de faire ça, mais en tant que proxy, ton code doit être capable de traiter à la fois le comportement des serveur là dessus et les exigences des clients. Tu dois traiter le fait que le serveur utilise content-lenght ou des delimiter pour marquer la fin de la réponse sans cloturer sa socket, et tu dois traiter le faire que le client veuille garder sa socket ouverte.

  5. #5
    Membre averti
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Septembre 2017
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Septembre 2017
    Messages : 15
    Par défaut
    Bonjour,

    Merci pour la réponse complète !

    Je passe en string car j'ai plus facile à interpréter mon header de la sorte, je début en Java et j'avoue que je ne vois pas commenter traiter mon flux 100 en byte. Du moins comment parser certaine partie comme juste le header pour l'analyser par la suite.

    Si je te suis, il faut uniquement utiliser des "InputStream" et "OutputStream" mais si je dois analyser mon header comment faire en byte par byte sans le convertir en string ???

  6. #6
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    Rien ne t'empêche une fois que tu a récupérer les bytes correspondant aux headers de les transformer en String après, mais tu ne dois pas faire ça pour le reste du stream. Le plus simple pour trouver la fin des header, je pense, c'est de repérer la séquence <CR><LF><CR><LF>

Discussions similaires

  1. Java : socket, octects perdus
    Par thebloodyman dans le forum Entrée/Sortie
    Réponses: 2
    Dernier message: 03/09/2007, 09h39
  2. [JAVA] Redimension d'image
    Par elanari dans le forum 2D
    Réponses: 10
    Dernier message: 08/03/2007, 23h49
  3. Java et traitement image
    Par martini37 dans le forum 2D
    Réponses: 13
    Dernier message: 14/02/2007, 12h48
  4. Débutant Java, Sockets et observers
    Par nouknouk dans le forum Entrée/Sortie
    Réponses: 8
    Dernier message: 19/12/2006, 14h01
  5. [Java][Socket] Pas de lecture de flux
    Par mavina dans le forum Entrée/Sortie
    Réponses: 7
    Dernier message: 20/10/2006, 19h02

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo