Partage
  • Partager sur Facebook
  • Partager sur Twitter

Serveur web écrit en C : envoi d'image :-(

Sujet résolu
    30 novembre 2015 à 21:53:34

    Salut, 

    J'écris un petit serveur web en c++. C'est pédagogique.
    En gros, j'essaie de reproduire le comportement que j'ai avec apache quand je code en php.

    Le principe :
    1 - le navigateur envoie une requete html
    2 - le serveur (en local sur le port 80) l'intercepte, puis il génère les variables d'environnement, globales (post, get, ...), ...
    3 - il cherche le fichier demandé ...
    4 - ... et le renvoie s'il existe 

    Ca marche plutôt bien.  Le hic c'est quand j'essaie d'envoyer des images ou des pdf. La j'obtiens des fichiers corrompus.

    Des idées ???

    (Pour l'instant une bonne partie du code serveur est dans le main, mais ca changera. )
    Voici le code du main : 

    #include <time.h>
    #include <string.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <winsock2.h>
    #include <unistd.h>
    
    #include "sockUtils.h"
    #include "http.h"
    #include "serveur.h"
    #include "include/httprequete.h"
    
    int main(int argc , char *argv[]){ /**/
        printf("\t\t Programme Serveur \n\n");    /**/
    
        int PORT=80;
        int sock_err;
        int r;
        char *name;
        char buffer[1024];
        int bool_begin = 1;
    
        serveur Apache = serveur();
        Apache.documentRoot = "C:/wamp/www/sde_html/";
    
        /**/
        WSADATA wsa;
        int err = WSAStartup(MAKEWORD(2, 2), &wsa);
        if(err < 0)
        {
            puts("WSAStartup failed !");
            exit(EXIT_FAILURE);
        }
    
        while(bool_begin)
        {
            SOCKET sock;
            /*sock = socket(AF_INET , SOCK_STREAM , 0 );*/
            SOCKADDR_IN sin;
            socklen_t recsize = sizeof(sin);
            sock = socket(AF_INET , SOCK_STREAM , 0 );
            if(sock == INVALID_SOCKET)
            {
                printf("socket()\n");
                exit(-1);
            }
    
            SOCKET csock;
            SOCKADDR_IN csin;
            socklen_t crecsize = sizeof(csin);
    
            /*printf("Indiqué un port : "); scanf("%d", &PORT);*/
            sin.sin_addr.s_addr = htonl(INADDR_ANY); /* Adresse IP automatique */
            sin.sin_port = htons(PORT); /* Listage du port */
            sin.sin_family = AF_INET; /* Protocole familial (IP) */
    
            sock_err = bind(sock, (SOCKADDR*)&sin, recsize);
            sock_err = listen(sock, 5);
    
            name = (char *) malloc(TAILLE_NOM);
            r = gethostname(name,(size_t) TAILLE_NOM);
            printf("Serveur sur %s initialise sur le PORT %d !\n", name, PORT);
            free(name);
    
            csock = accept(sock, (SOCKADDR*)&csin, &crecsize);
            printf("Nouvelle connexion\n");
    
            while(sock_err!=SOCKET_ERROR)
            {
                memset (buffer, 0, sizeof (buffer));
                sock_err=recv(csock, buffer, 1024, 0);
                httprequete requete = httprequete();
                requete._buffer(buffer);
    
                //si sock_err = 0, alors la connexion a été arrété avec shutdown, donc on arrete la connexion a notre niveau
                if(sock_err==0) sock_err = -1;
                if(buffer[0]!=0)
                {
                    printf("Chaine Recu : %s\n", buffer);
                }
    
                if(sock_err!=-1){
                    http response = http(csock,Apache);
    
                    try{
                        t_query* infosurl = requete.getQuery();
    
                        if(!Apache.findDocument(infosurl->path.c_str()))
                            throw "404-Document not found.";
    
                        sock_err = response.sendHeader("HTTP/1.1 200 OK");
                        string file = Apache.documentRoot + infosurl->path;
                        string mime = "Content-Type : "+Apache.getMimeType(file.c_str());
                        sock_err = response.sendHeader(mime.c_str());
                        //sock_err = response.sendHeader("Content-Disposition: attachment; filename=\"downloaded.pdf\"");
                        sock_err = response.sendHeader("Access-Control-Allow-Origin:*");
                        sock_err = response.sendHeader("");
                        sock_err = response.sendDocument(infosurl->path.c_str());
                    }catch(const char* e){
                        sock_err = response.sendHeader("HTTP/1.1 404 NOT FOUND");
                        sock_err = response.sendHeader("Content-Type : text/html");
                        sock_err = response.sendHeader("");
                        sock_err = response.sendData((string(e)+"<br/>").c_str(),false);
                        sock_err = response.sendData("Le fichier demandé n'existe pas.",false);
                    }
                } 
                shutdown(csock, 2);
                closesocket(csock);
            }
            printf("Fin de connexion\n");
            shutdown(csock, 2);
            closesocket(csock);
            shutdown(sock, 2);
            closesocket(sock);
        }
        WSACleanup();
    }
    

    Et là le code de la fonction http::sendDocument() qui envoie les fichiers : 

    Ca marche pour les fichiers texte (html, css, c, php ...) mais pas pour les fichiers complexes (images, pdf, ...)

    #include <iostream>
    #include <fstream>
    #include <cstdio>
    #include <winsock2.h>
    
    using namespace std;
    
    #include "http.h"
    #include "sockUtils.h"
    
    //Static methode
    int http::sendMessage(int csock, const char* message,int flags){
        return send(csock,message,strlen(message),flags);
    }
    
    int http::sendHeader(const char* property){
        std::string str = string(property)+"\r\n";
        return http::sendMessage(this->_csock(),str.c_str(),0);
    }
    
    int http::sendData(const char* data, bool withLine){
        std::string str = string(data);
        if(withLine)
            str += "\r\n";
        return http::sendMessage(this->_csock(),str.c_str(),0);
    }
    
    int http::sendDocument(const char* filename) {
        //ouverture en lecture
        string new_filename = this->m_serveur._documentRoot()+string(filename);
        
        FILE* fichier = fopen(new_filename.c_str(),"rb");
        fseek ( fichier , 0 , SEEK_SET );
        try{
            int MAX = 4096;
            char buffer[MAX];
            int write=0,written = 0,read=0,readen=0,lastcount=0;
            //while(fgets(buffer,255,fichier)){
            do{
                memset((void*)buffer,0,MAX);
                read=fread(buffer,1,MAX-1,fichier);
                readen+=read;
                write = 0;
                lastcount = 0;
                string str = string(buffer,MAX-1);
                cout << "str = " << buffer << endl;
                //int sz_buf = strlen(buffer);
                do{
                    int sz = read - lastcount;
                    if(sz<=0)
                        break;
                    char buffer2[sz];
                    memset(buffer2,0,sz);
                    for(int i(0);i<sz;i++){
                        buffer2[i] = buffer[lastcount+i];
                        //printf("%d",buffer2[i]);
                    }
    
                    write += sendData(buffer2,false); 
                    
                    //si le 1er envoi n'a pas fonctionné
                    if(write == lastcount){ //2e essai
                        write += sendData(buffer2,false);
                        cout << "2e essai" << endl;
                    }
                    if(write == lastcount){
                        write += sendData(buffer2,false);
                        cout << "3e essai" << endl;
                    }
                    if(write == lastcount)
                        throw "Erreur : Fichier corrompu.";
                    lastcount = write; 
                }while(write<read && read!=0 && !feof(fichier));
                
                written += write;
                
                if(write==SOCKET_ERROR ){
                    throw "Fichier corrompu";
                }
            }while(read>0 && !feof(fichier));
            printf("%d caractères lus\n",readen);
            printf("%d caractères émis\n",written);
            fclose(fichier);
        }catch(const char* e){
            throw e;
        }
        return 1;
    }
    
    string http::http_decode(string url){
        size_t s = url.find("%20");
        if(s!=std::string::npos)
            return url.replace(s,3, " ");
        return url;
    }
    



    • Partager sur Facebook
    • Partager sur Twitter
      30 novembre 2015 à 22:02:26

      Edit 

      Le code posté c'est surtout pour vous montré la logique. 

      Pour la fonction sendDocument() j'ai rajouté 2 boucles pour m'assurer que les données sont bien envoyés dans le cas des images, pdf, ... mais sans succès.

      Lorsque je fais juste une boucle fgets(buffer)+send(buffer) en 3 lignes max (c'est mon ancien code) des bouts de fichiers disparaissent .

      • Partager sur Facebook
      • Partager sur Twitter
        30 novembre 2015 à 22:09:03

        Salut :)

        L'erreur est la même que dans ce topic. La solution sera la même.

        Un indice : il faudra changer tes fonctions sendData et sendMessage().

        Pour résumer: un fichier texte ne contient pas d'octets à 0, alors qu'un fichier binaire (image, pdf, ...) peut en contenir. Ce que fait que ta fonction sendMessage(), qui se base sur strlen() (qui s'arrête au premier octet à 0) pour connaitre la taille du buffer à envoyer, n'envoie pas toutes les données que tu veux au final dans certains cas.

        -
        Edité par Padawel 30 novembre 2015 à 22:13:38

        • Partager sur Facebook
        • Partager sur Twitter
          1 décembre 2015 à 1:08:21

          +1 Padawel, utiliser des string sur du binaire n'a pas de sens.
          • Partager sur Facebook
          • Partager sur Twitter
            1 décembre 2015 à 1:15:56

            Oui c'est ça apparemment !
            Merci !!

            Mais avant de clore le topic, peut-être que t'as la solution pour le problème qui suit :

            maintenant je parviens bien à envoyer les images, mais elles n'apparaissent pas dans le navigateur. Avec l'outil inspecter l'élément de chrome par exemple, je peux voir que les headers sont bien reçus par le navigateur avec le statut 200 OK, mais pas d'image en vue.
            • Partager sur Facebook
            • Partager sur Twitter
              1 décembre 2015 à 1:28:51

              @Marc Mongenet Merci pour ta contribution à mon problème !!

              D'ailleurs, ton commentaire n'est pas très pertinent. Je ne sais pas ce que t'entends pas utiliser du string sur du binaire, sachant que tout est binaire. Ce n'est qu'une histoire de haut ou bas niveau.
              Le seul code binaire intervient dans la fontion sendDocument() avec fread(). Et dans cette fonction, l'utilisation des string permet de faire une concaténation rapide à la volée ligne 30.

              En voyant le code, tu dois bien te douter que je suis en pleine phase d'essai. C'est loin d'être du code destiné au public.

              J'utilise les string pour aller vite sur l'écriture des classes pour me concentrer sur la partie réseau et transfert de fichiers. 

               

              • Partager sur Facebook
              • Partager sur Twitter
                1 décembre 2015 à 10:31:05

                blixit a écrit:

                @Marc Mongenet Merci pour ta contribution à mon problème !!

                D'ailleurs, ton commentaire n'est pas très pertinent. Je ne sais pas ce que t'entends pas utiliser du string sur du binaire, sachant que tout est binaire. Ce n'est qu'une histoire de haut ou bas niveau.


                Dans ce contexte, binaire se dit par opposition à chaine de caractères. Les données qui ont un type MIME "text" peuvent être traitées avec des chaines de caractères, pas les autres.

                -
                Edité par Marc Mongenet 1 décembre 2015 à 10:31:21

                • Partager sur Facebook
                • Partager sur Twitter
                  1 décembre 2015 à 11:53:27

                  Marc Mongenet a écrit:

                  Dans ce contexte, binaire se dit par opposition à chaine de caractères. Les données qui ont un type MIME "text" peuvent être traitées avec des chaines de caractères, pas les autres.

                  -
                  Edité par Marc Mongenet il y a environ 1 heure


                  Je vais peut-être de décevoir. Fais ce qui suit : 
                  - lit un fichier en mode binaier dans un buffer (char[] par exemple)
                  - essaie d'afficher le buffer : cout << buffer;
                  - essaie d'afficher la chaine string : cout << string(buffer);
                  Tu pourras constater que le 1er n'affiche rien, tandis que le 2nd affiche les caractères binaires (les fameux caractères incompréhensibles).

                  Je reprécise, que l'utilisation des string ici a un objectif visé.

                  Mais je tâcherais de penser à ce que tu as dis dans un futur proche.

                  Merci encore pour tout.
                  • Partager sur Facebook
                  • Partager sur Twitter
                    1 décembre 2015 à 13:12:55

                    Ca fait trop longtemps que je fais du C++ pour connaitre les détails de la classe string.

                    Mais quand je vois par exemple un strlen dans sendMessage, je sais que sendMessage n'est pas capable d'envoyer du binaire.

                    A part ça, le code me semble incroyablement long pour ce qu'il fait.

                    • Partager sur Facebook
                    • Partager sur Twitter
                      1 décembre 2015 à 14:22:31

                      Marc Mongenet a écrit: 

                      A part ça, le code me semble incroyablement long pour ce qu'il fait.


                      Oui, c'est clair !
                      A la base, il faisait 5 lignes. Juste la boucle de lecture->écriture.
                      Tout le bazar autour, c'était pour essayer de trouver l'erreur qui au final était dans strlen.

                      J'optimise le code dès que possible. 

                      • Partager sur Facebook
                      • Partager sur Twitter

                      Serveur web écrit en C : envoi d'image :-(

                      × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                      × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                      • Editeur
                      • Markdown