Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Lecture de binaires (encore et toujours...)

2 réponses
Avatar
NaeiKinDus
Bonjour tout le monde. Donc, je code un serveur web, avec une fonction
que j'appelle a chaque requete client, et qui gere l'envoie des
fichiers...
Au premier stade, elle ouvre le fichier, et s'il est bon envoie le
header.
* On boucle, un event est signale (Windows), on retourne dans la
fonction *
Le fichier est deja ouvert, on commence donc la lecture du fichier et
on envoit X octets de donnees.
* On boucle, un event est signale (Windows), on retourne dans la
fonction *
On continue la lecture, on retourne donc a l'etape 2...

Cependant, quand je demande d'afficher la taille de mon fichier,
j'obtiens 0 (-1 si je n'ouvre pas mon fichier a partir d'un
filestream). De meme, la lecture reste desesperement vide de toutes
donnees. Si quelqu'un avait une idee, un conseil...

Request contient la demande du client et file, le fichier demande...

void OBody::getContentType(string &request, string file)
{
map<string, string>::iterator ite;
WIN32_FILE_ATTRIBUTE_DATA FileAttr;
char buffer[4096];
ostringstream oss;
fstream *filestream = new fstream;


if (!cl->fbuff->is_open()) // Le client est neuf car pas eu
d'ouverture du fichier
{
request.clear();
int off = file.find_last_of(".", file.size());
string tmp = file.substr(off + 1, file.size() - off);
request.assign("Content-Type: ");
ite = MIME.find(tmp.c_str());
if (ite == MIME.end())
request.append("application/octet-stream");
else
request.append(ite->second);
if (ite != MIME.end() && ite->second.substr(0, 5) == "text/")
filestream->open(file.c_str(), ios::in);
else
filestream->open(file.c_str(), ios_base::binary);
cl->fbuff = filestream->rdbuf();
int size = cl->fbuff->pubseekpos(0, ios_base::end);
cerr << "Size's: " << size << endl; // Renvoit 0
cl->fbuff->pubseekpos(0, ios_base::beg);
oss << size;
request.append("\r\nAccept-Ranges: bytes\r\n");
request.append("Content-Length: ");
request.append(oss.str());
request.append("\r\n");
}
else if (cl->fbuff->in_avail()) // Il y a un fichier d'ouvert, et des
donnees a lire
{
memset(buffer, '\0', 4096);
int rdsize = cl->fbuff->sgetn(buffer, 4096);
if (rdsize < 0)
{
delete cl->fbuff;
cl->fbuff = NULL;
throw "HTTP/1.1 500 Internal Server Error";
}
request.append(buffer, rdsize);
}
else // Plus rien a lire, on ferme donc la lecture...
{
cl->fbuff->close();
}
}


Merci d'avance :)

2 réponses

Avatar
James Kanze
On Apr 8, 3:37 pm, "NaeiKinDus" wrote:

Bonjour tout le monde. Donc, je code un serveur web, avec une
fonction que j'appelle a chaque requete client, et qui gere
l'envoie des fichiers... Au premier stade, elle ouvre le
fichier, et s'il est bon envoie le header. * On boucle, un
event est signale (Windows), on retourne dans la fonction * Le
fichier est deja ouvert, on commence donc la lecture du
fichier et on envoit X octets de donnees. * On boucle, un
event est signale (Windows), on retourne dans la fonction * On
continue la lecture, on retourne donc a l'etape 2...

Cependant, quand je demande d'afficher la taille de mon fichier,
j'obtiens 0 (-1 si je n'ouvre pas mon fichier a partir d'un
filestream). De meme, la lecture reste desesperement vide de toutes
donnees. Si quelqu'un avait une idee, un conseil...

Request contient la demande du client et file, le fichier demande...

void OBody::getContentType(string &request, string file)
{
map<string, string>::iterator ite;
WIN32_FILE_ATTRIBUTE_DATA FileAttr;
char buffer[4096];
ostringstream oss;
fstream* filestream = new fstream;


Çe ne m'est pas trop clair. Quand est-ce que cette fonction est
appelée ? Et surtout, qu'estce qu'elle fait avec le flux. Il
faut bien qu'elle le stocke ailleurs que dans une variable
locale, si on veut y accéder dans les appels ultérieurs.

if (!cl->fbuff->is_open()) // Le client est neuf car pas eu
d'ouverture du fichier


C'est quoi, cl ?

{
request.clear();
int off = file.find_last_of(".", file.size());


Ce qui renvoie forcément npos, étant donné qu'on commence la
récherche à la fin. Sauf que3 tu stockes le résultat dans un
int, qui ne peut pas forcément (ni même typiquement) contient un
npos.

string tmp = file.substr(off + 1, file.size() - off);


Alors là, je ne suis plus du tout. En fait, déjà dans la ligne
précédante, tu as frolé le comportement indéfini ; ici, tu y
entre carrément dedans. Dans les implémentations typique (mais
ce n'est nullement garanti), off + 1 va valoir 0. Et file.size()
- off va être supérieur à file.size(). Ce qui fait qu'en fait,
le substr est un no-op.

request.assign("Content-Type: ");
ite = MIME.find(tmp.c_str());


Aha. Alors, ce que tu as essayé de faire, c'est e'en extraire
l'« extension » du fichier. Alors, ce qu'il faut, c'est :

size_t extBegin = file.rfind( '.' ) ;
std:;string ext( extBegin == npos
? std::string()
: file.substr( extBegin + 1 ) ;

(On peut bien remplacer rfind avec find_last_of, mais le
deuxième paramètre doit bien être 0, qui est le défaut.)

if (ite == MIME.end())
request.append("application/octet-stream");
else
request.append(ite->second);
if (ite != MIME.end() && ite->second.substr(0, 5) = = "text/")
filestream->open(file.c_str(), ios::in);
else
filestream->open(file.c_str(), ios_base::binary);
cl->fbuff = filestream->rdbuf();
int size = cl->fbuff->pubseekpos(0, ios_base::end);


Comme on a déjà dit, ceci n'est pas portable.
Il ne doit même pas passer le compilateur, parce que le type du
deuxième paramètre de streambuf::pubseekpos est un
std::ios::openmode, et tu lui as passé un std::ios::seekdir.
(Mais je crois que la norme permet à ce que les deux soit des
typedef à des types entiers, et alors, tu n'est plus garanti
d'une erreur lors de la compilation. Seulement, un comportement
indéfini lors de l'exécution.) Aussi, le type de rétour de
streambuf::pubseekpos est un streampos, qui n'est pas forcément
convertible en int.

Si tu es sous Unix, et par hazard, l'implémentation a fait de
std::ios::seekdir et de std::ios::openmode un typedef à int (ce
qui n'est pas le cas de g++, je crois), et que par hazard,
std::ios::end correspond bien à std::ios::in ou std::ios::out,
et qu'en fait streampos se laisse convertir en int, avec la
sémantique que l'int qui en résult correspond à la position dans
le ficher, ça march. Mais il y a pas mal de « si » là dedans.
Bien trop pour mon goût. (Que ça ne marche pas sous Windows, ni
avec g++, limite déjà pas mal l'intérêt.)

cerr << "Size's: " << size << endl; // Renvoit 0


Ou d'autre chose.

Il n'y a qu'une chose que tu peux faire avec la valeur de rétour
d'un seekpos : le stocker (dans un streampos), pour s'en servir
comme paramètre à un seekpos ultérieur. C'est tout.

cl->fbuff->pubseekpos(0, ios_base::beg);
oss << size;
request.append("rnAccept-Ranges: bytesrn");
request.append("Content-Length: ");
request.append(oss.str());
request.append("rn");
}
else if (cl->fbuff->in_avail()) // Il y a un fichier d'ouvert, et des


« else » quoi, vue que c'est garanti que tu n'y arrives
jamais ? Mais fait gaffe à « in_avail ». C'est vraiment rare
qu'il fasse ce qu'on veut (et ce n'est jamais garanti).

donnees a lire
{
memset(buffer, '', 4096);
int rdsize = cl->fbuff->sgetn(buffer, 4096);
if (rdsize < 0)
{
delete cl->fbuff;
cl->fbuff = NULL;
throw "HTTP/1.1 500 Internal Server Error";
}
request.append(buffer, rdsize);
}
else // Plus rien a lire, on ferme donc la lecture...
{
cl->fbuff->close();
}
}


En rapprochant ça avec ce que tu as démandé avant... (J'imagine
que c'était toi -- l'application s'y ressemble), ce que tu
veux, c'est un objet, avec un membre filebuf*, dont le
constructeur l'initialise à null. Ensuite :

-- si le filebuf* est null, tu ouvres le fichier, en déterminer
la longueur. (De façon qui dépend du système : le résultat
d'un seekoff à la fin pourrait marcher sous Unix, à moins
avec g++, à condition de se répositionner au début ensuite.
Je crois, au mois : dans la pratique, j'utilise stat sous
Unix, et GetFileAttributesEx sous Windows, ce qui est
garantie de marcher *SI* j'ouvre en binary par la suite.
Mais selon le type MIME, ouvrir en binary pourrait ne pas
convenir.)

-- sinon, tu lit avec sgetn, comme tu as fais.

Mais il faut bien que l'instance de filebuf vit au-delà de la
fonction, et que le prochain appel de la fonction rétrouve la
même instance. Et que tu oublies un peu l'idée de trouver la
taille sans passer par des fonctions propre au système. (En
fait, dans la pratique, si tu ne lis pas en binaire, la seule
façon fiable de trouver la taille, c'est de lire le fichier, en
comptant les octets que tu as lu. Seulement, la taille que veut
HTTP, c'est la taille que tu vas émettre, qui dépendrait aussi
du type MIME, puisque dans certains cas, tu serais amener à
effectuer des mapping aussi : si le type MIME à un encodage
UTF-8, par exemple, et le fichier a un encodage ISO 8859-1, tu
risques d'envoyer pas mal d'octets en plus.)

--
James Kanze (Gabi Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
NaeiKinDus
On 8 avr, 18:41, "James Kanze" wrote:
On Apr 8, 3:37 pm, "NaeiKinDus" wrote:



Bonjour tout le monde. Donc, je code un serveur web, avec une
fonction que j'appelle a chaque requete client, et qui gere
l'envoie des fichiers... Au premier stade, elle ouvre le
fichier, et s'il est bon envoie le header. * On boucle, un
event est signale (Windows), on retourne dans la fonction * Le
fichier est deja ouvert, on commence donc la lecture du
fichier et on envoit X octets de donnees. * On boucle, un
event est signale (Windows), on retourne dans la fonction * On
continue la lecture, on retourne donc a l'etape 2...
Cependant, quand je demande d'afficher la taille de mon fichier,
j'obtiens 0 (-1 si je n'ouvre pas mon fichier a partir d'un
filestream). De meme, la lecture reste desesperement vide de toutes
donnees. Si quelqu'un avait une idee, un conseil...
Request contient la demande du client et file, le fichier demande...
void OBody::getContentType(string &request, string file)
{
map<string, string>::iterator ite;
WIN32_FILE_ATTRIBUTE_DATA FileAttr;
char buffer[4096];
ostringstream oss;
fstream* filestream = new fstream;


Çe ne m'est pas trop clair. Quand est-ce que cette fonction est
appelée ? Et surtout, qu'estce qu'elle fait avec le flux. Il
faut bien qu'elle le stocke ailleurs que dans une variable
locale, si on veut y accéder dans les appels ultérieurs.


Elle est appelee a chaque reception d'une requete. C'est une methode
de classe, avec un membre filebuf (voila comment je garde la position
de lecture d'un appel a l'autre). Elle lit X caracteres, et est sensee
les stocker dans le string request pour l'envoie vers le client.

if (!cl->fbuff->is_open()) // Le client est neuf car pas eu
d'ouverture du fichier


C'est quoi, cl ?


Ma classe justement, celle qui contient cette fonction et le filebuf.


{
request.clear();
int off = file.find_last_of(".", file.size());


Ce qui renvoie forcément npos, étant donné qu'on commence la
récherche à la fin. Sauf que3 tu stockes le résultat dans un
int, qui ne peut pas forcément (ni même typiquement) contient un
npos.

string tmp = file.substr(off + 1, file.size() - off);


Alors là, je ne suis plus du tout. En fait, déjà dans la ligne
précédante, tu as frolé le comportement indéfini ; ici, tu y
entre carrément dedans. Dans les implémentations typique (mais
ce n'est nullement garanti), off + 1 va valoir 0. Et file.size()
- off va être supérieur à file.size(). Ce qui fait qu'en fait,
le substr est un no-op.

request.assign("Content-Type: ");
ite = MIME.find(tmp.c_str());


Aha. Alors, ce que tu as essayé de faire, c'est e'en extraire
l'« extension » du fichier. Alors, ce qu'il faut, c'est :

size_t extBegin = file.rfind( '.' ) ;
std:;string ext( extBegin == npos
? std::string()
: file.substr( extBegin + 1 ) ;

(On peut bien remplacer rfind avec find_last_of, mais le
deuxième paramètre doit bien être 0, qui est le défaut.)


Merci, j'ai corrige ca :)
Cependant, pour le second parametre de find_last_of, c'est normalement
la position d'arret de la fonction, si je ne m'abuse... d'ou le
file.size !


if (ite == MIME.end())
request.append("application/octet-stream");
else
request.append(ite->second);
if (ite != MIME.end() && ite->second.substr(0, 5) = = "text/")
filestream->open(file.c_str(), ios::in);
else
filestream->open(file.c_str(), ios_base::binary );
cl->fbuff = filestream->rdbuf();
int size = cl->fbuff->pubseekpos(0, ios_base::end);


Comme on a déjà dit, ceci n'est pas portable.
Il ne doit même pas passer le compilateur, parce que le type du
deuxième paramètre de streambuf::pubseekpos est un
std::ios::openmode, et tu lui as passé un std::ios::seekdir.
(Mais je crois que la norme permet à ce que les deux soit des
typedef à des types entiers, et alors, tu n'est plus garanti
d'une erreur lors de la compilation. Seulement, un comportement
indéfini lors de l'exécution.) Aussi, le type de rétour de
streambuf::pubseekpos est un streampos, qui n'est pas forcément
convertible en int.


Effectivement, j'ai mal lu, ou lu une mauvaise doc... Je croyais que
le premier parametre etait l'offset (?), le second la position a
rechercher, et le dernier l'openmod, qui etait un parametre non
obligatoire.


Si tu es sous Unix, et par hazard, l'implémentation a fait de
std::ios::seekdir et de std::ios::openmode un typedef à int (ce
qui n'est pas le cas de g++, je crois), et que par hazard,
std::ios::end correspond bien à std::ios::in ou std::ios::out,
et qu'en fait streampos se laisse convertir en int, avec la
sémantique que l'int qui en résult correspond à la position dans
le ficher, ça march. Mais il y a pas mal de « si » là dedans.
Bien trop pour mon goût. (Que ça ne marche pas sous Windows, ni
avec g++, limite déjà pas mal l'intérêt.)

cerr << "Size's: " << size << endl; // Renvoit 0


Ou d'autre chose.

Il n'y a qu'une chose que tu peux faire avec la valeur de rétour
d'un seekpos : le stocker (dans un streampos), pour s'en servir
comme paramètre à un seekpos ultérieur. C'est tout.

cl->fbuff->pubseekpos(0, ios_base::beg);
oss << size;
request.append("rnAccept-Ranges: bytesrn");
request.append("Content-Length: ");
request.append(oss.str());
request.append("rn");
}
else if (cl->fbuff->in_avail()) // Il y a un fichier d'ouvert, et des


« else » quoi, vue que c'est garanti que tu n'y arrives
jamais ? Mais fait gaffe à « in_avail ». C'est vraiment rare
qu'il fasse ce qu'on veut (et ce n'est jamais garanti).



Note :)


donnees a lire
{
memset(buffer, '', 4096);
int rdsize = cl->fbuff->sgetn(buffer, 4096);
if (rdsize < 0)
{
delete cl->fbuff;
cl->fbuff = NULL;
throw "HTTP/1.1 500 Internal Server Error";
}
request.append(buffer, rdsize);
}
else // Plus rien a lire, on ferme donc la lecture...
{
cl->fbuff->close();
}
}


En rapprochant ça avec ce que tu as démandé avant... (J'imagine
que c'était toi -- l'application s'y ressemble),


En effet !

ce que tu
veux, c'est un objet, avec un membre filebuf*, dont le
constructeur l'initialise à null. Ensuite :

-- si le filebuf* est null, tu ouvres le fichier, en déterminer
la longueur. (De façon qui dépend du système : le résultat
d'un seekoff à la fin pourrait marcher sous Unix, à moins
avec g++, à condition de se répositionner au début ensuite.
Je crois, au mois : dans la pratique, j'utilise stat sous
Unix, et GetFileAttributesEx sous Windows, ce qui est
garantie de marcher *SI* j'ouvre en binary par la suite.
Mais selon le type MIME, ouvrir en binary pourrait ne pas
convenir.)


Donc, comment faire selon le type du fichier ? Pour etre sur que ca
marche a 100 pourcent, que ce soit un binary ou un texte ? D'apres ce
que tu m'as dit avec les txt, il faudrait que je lise completement un
fichier non binaire avant de l'envoyer...? C'est dangereux si le
client demande un fichier texte de 30Mo par exemple !