Discussion:
probleme de chargement interrompu de donnees par socket.
(trop ancien pour répondre)
Jean-Francois Ortolo
2008-07-22 20:46:59 UTC
Permalink
Bonjour

Je fais du chargement de données de sites externes par des fonctions
de socket telles que fsockopen, suivie de fputs ( pour la requête )
puis de fgets ( pour le header ), puis de fread pour les données.

J'utilise les fonctions données par un Américain sur les commentaires
de la fonction fsockopen(), dans le PHP Manual.

En gros, voici le code:

$fp = fsockopen($host, 80, &$errno, &$errstr, 300);

// test sur $fp en cas d'erreur.

// pour fixer le timeout sur les données
stream_set_timeout($fp, 3000);

// le header est en mode HTTP 1.1 obligatoire,
// le serveur n'accepte que cela.
$send = $contenu_de_l_header;

fputs($fp, $send);

unset($send);
$send = '';
do
{
$send .= fgets($fp, 4096);
}while( strpos($send, "\r\n\r\n") === false)

// Première fonction de l'Américain,
// pour détecter les contenus chunked,
// et permettre la décompression si besoin
// lors de la lecture des données.
$info=decode_header($send);

unset($send);
$send = '';
while(!feof($fp))
{
// il faut un fread pour interpréter le contenu
// brut en binaire.
$send .= fread($fp, 16384);
}

fclose($fp);

// Deuxième fonction de l'Américain,
// elle interprète les contenus chunked ou compressés,
// et les restitue suivant ce qu'indique le contenu
// de l'header.
$send = decode_body($info, $send);

// Enfin, on met le contenu
// dans une array.
unset($var);
$var = split("[\n]", $send);

// Et voila, le contenu de la page html
// est dans la variables indicée, ligne par ligne.

// ***********************************************



Donc, voici mon problème:

Tout se passe comme si les données dans certains cas ( aléatoires )
n'alimentaient pas correctement la variable $var, qui ne contient pas
la balise <head>, détectée par eregi("<head>", $donnee)

Cependant, j'ai mis un timeout de 300 secondes à l'ouverture du
socket ( d'ailleurs c'est sûr que le socket est ouvert, car le
traitement d'erreur ne détecte rien ), ce qui largement suffisant car le
paramètre $host est en réalité l'adresse ip en dur, qui est correcte.

De plus j'ai spécifié un time out pour les données de 3000 secondes,
soit 50 minutes, et d'ailleurs le timeout apparent réel, est de l'ordre
de 3 ou 4 minutes.

Je lance ces chargements de manière répétitive pour différentes pages
du même site, et à peu près une fois sur 7 ou 8 ( aléatoire ), la balise
<head> n'est pas détectée dans la variable indicée $var[]

Je parcours la variables $var avec un indice de 0 à (count($var) -
1), ce qui devrait être correct.

Je fais subir à chaque lignes rencontrées, une conversion de
utf-8vers iso-8859-15 avec la fonction decode faite de cette manière:

function decode($var) {
$send = ereg_replace("\r$", "", $var);
$send1 = iconv("UTF-8",
"ISO-8859-15//TRANSLIT//IGNORE", $send);
$send2 = str_replace(chr(189), "oe", $send1);
$send3 = str_replace("\r", "", $send2);
return($send3);
}

Donc en gros, à chaque lecture de ligne:

unset($donnee);
$donne = decode($var[$k]);

Donc, je fais:

$trouve = false;
for($i = 0; $i < count($var); $i++)
{
unset($donnee);
$donnee = decode($var[$i]);

// pour sortir de la boucle
if(eregi("<\/html>", $donnee))
break;

// pour débuter le traitement
if(eregi("<head>", $donnee))
$trouve = true;

if($trouve)
{
// Traitement...

}
}


Le problème, c'est que dans certains cas, le traitement ne se fait
pas du tout.


Il ne devrait pas y avoir de problème, les données sont des pages
html en utf-8, qui devraient bien être traduites en iso par la fonction
decode(). D'ailleurs pour un autre site du même genre, çà marche.

D'où vient l'erreur ? ;)

Merci beaucoup de vos réponses.

Bien à vous.

Amicalement.

Jean-François Ortolo
Olivier Miakinen
2008-07-22 21:55:18 UTC
Permalink
Bonjour,
Post by Jean-Francois Ortolo
Je fais du chargement de données de sites externes par des fonctions
de socket telles que fsockopen, suivie de fputs ( pour la requête )
puis de fgets ( pour le header ), puis de fread pour les données.
J'utilise les fonctions données par un Américain sur les commentaires
de la fonction fsockopen(), dans le PHP Manual.
$fp = fsockopen($host, 80, &$errno, &$errstr, 300);
[...]
// Première fonction de l'Américain,
// pour détecter les contenus chunked,
// et permettre la décompression si besoin
// lors de la lecture des données.
$info=decode_header($send);
Est-ce qu'il ne serait pas plus simple d'utiliser Curl qui fait déjà
tout ça et beaucoup plus ? <http://fr2.php.net/curl>
Post by Jean-Francois Ortolo
unset($send);
$send = '';
La première de ces deux lignes ne me paraît pas très utile, vu qu'il y a
la deuxième.
Post by Jean-Francois Ortolo
[...]
// Deuxième fonction de l'Américain,
// elle interprète les contenus chunked ou compressés,
// et les restitue suivant ce qu'indique le contenu
// de l'header.
$send = decode_body($info, $send);
Idem supra (à propos de Curl à la place du code de l'Américain).
Post by Jean-Francois Ortolo
// Enfin, on met le contenu
// dans une array.
unset($var);
$var = split("[\n]", $send);
1) L'unset() est inutile ;
2) dans une expression régulière, "[\n]" est équivalent à "\n" ;
3) preg_split() est généralement plus rapide que split() ;
4) explode() est toujours plus rapide que split() ou preg_split() ;
5) les fins de ligne sont censées être en \r\n.

Donc, je remplacerais bien les deux lignes ci-dessus par :
$var = explode("\r\n", $send);
Post by Jean-Francois Ortolo
Tout se passe comme si les données dans certains cas ( aléatoires )
n'alimentaient pas correctement la variable $var, qui ne contient pas
la balise <head>, détectée par eregi("<head>", $donnee)
Quand tu dis que c'est aléatoire, cela veut dire que la même valeur de
$send donne parfois un $var correct et parfois un $var incorrect ? Ou
bien est-ce que pour toi « aléatoire » est un synonyme de « je n'ai pas
su dire quelle différence est significative » ?
Post by Jean-Francois Ortolo
[...]
Je lance ces chargements de manière répétitive pour différentes pages
du même site, et à peu près une fois sur 7 ou 8 ( aléatoire ), la balise
<head> n'est pas détectée dans la variable indicée $var[]
Ce qu'il faudrait savoir, c'est quel est le contenu exact de $send, puis
celui de $var (la fonction print_r() est ton amie).
Post by Jean-Francois Ortolo
Je parcours la variables $var avec un indice de 0 à (count($var) -
1), ce qui devrait être correct.
Il y a plus simple : <http://fr.php.net/foreach>.
Post by Jean-Francois Ortolo
Je fais subir à chaque lignes rencontrées, une conversion de
function decode($var) {
$send = ereg_replace("\r$", "", $var);
Vu que tu as fait le split() avec "\n" et qu'il est censé n'exister
aucun "\r" seul dans un flux HTML/HTTP, un str_replace avec "\r" devrait
suffire (à supprimer complètement si tu as déjà suivi mon conseil de
l'explode() avec "\r\n").
Post by Jean-Francois Ortolo
$send1 = iconv("UTF-8",
"ISO-8859-15//TRANSLIT//IGNORE", $send);
Je ne savais pas qu'on pouvait utiliser les deux paramètres //TRANSLIT
et //IGNORE l'un derrière l'autre. C'est bon à savoir, merci.
Post by Jean-Francois Ortolo
$send2 = str_replace(chr(189), "oe", $send1);
Tiens ? Tu n'aimes pas le « œ » ? Pourquoi lui particulièrement et pas,
par exemple, « Œ » ou « æ » ?
Post by Jean-Francois Ortolo
$send3 = str_replace("\r", "", $send2);
À supprimer si tu as suivi les conseils précédents.
Post by Jean-Francois Ortolo
[...]
// pour débuter le traitement
if(eregi("<head>", $donnee))
$trouve = true;
Attention, même si je n'en ai jamais vu, il est possible de mettre des
attributs lang et dir à l'élément HEAD. Par ailleurs en HTML il est même
autorisé d'omettre la balise <head> (de même que la balise </html> que
tu testes plus haut).
Post by Jean-Francois Ortolo
Le problème, c'est que dans certains cas, le traitement ne se fait
pas du tout.
Procédons par ordre.
1) Est-ce que le contenu de $send est déjà incorrect ?
2) Est-ce que le contenu de $send est correct mais pas celui de $var ?
3) Est-ce que le contenu de $var est correct mais ça échoue quand même ?
Post by Jean-Francois Ortolo
Il ne devrait pas y avoir de problème, les données sont des pages
html en utf-8, qui devraient bien être traduites en iso par la fonction
decode(). D'ailleurs pour un autre site du même genre, çà marche.
Tu as une URL pour qu'on voie au moins le contenu envoyé ?
Post by Jean-Francois Ortolo
D'où vient l'erreur ? ;)
Excellente question, à laquelle tu es pour le moment le seul à pouvoir
répondre. Commence par les trois questions ci-dessus ($send incorrect,
$var incorrect ou traitement incorrect).


Cordialement,
--
Olivier Miakinen
Jean-Francois Ortolo
2008-07-23 08:59:43 UTC
Permalink
Post by Olivier Miakinen
Bonjour,
Post by Jean-Francois Ortolo
D'où vient l'erreur ? ;)
Excellente question, à laquelle tu es pour le moment le seul à pouvoir
répondre. Commence par les trois questions ci-dessus ($send incorrect,
$var incorrect ou traitement incorrect).
Cordialement,
Bonjour Monsieur

Je suis en train de mettre dans des fichiers "avant.htm" et
"apres.htm", les contenus bruts de var[$i] et decode($var[$i])

Je n'ai plus qu'à lancer le script.

Je pourrai consulter ces fichiers, pour savoir les contenus exacts de
$var[$i], et dans quelle mesure le décodage a un impact sur les données.

Je vous tiens au courant.

En ce qui concerne le "\r\n" à la fin des lignes des pages html, je
n'étais pas sûr de cela. Au cas où il puisse y avoir un simple saut de
ligne, ne serait-ce qu'un seule fois, j'aurais bien des problèmes avec
explode("\r\n", $send)

La raison pour laquelle j'utilise un indice numérique, est que je
peux incrémenter cet indice comme je veux à l'intérieur de la boucle de
lecture, pour les besoins du traitement. Il me semble que je ne le
pourrais pas avec une boucle foreach().

Merci beaucoup pour vos conseils.

Bien à vous.

Amicalement.

Jean-François Ortolo
--
Visitez mon site gratuit donnant des Statistiques,
des Pronostics et des Historiques Graphiques
sur les Courses de Chevaux:
http://www.ortolojf-courses.com/
Olivier Miakinen
2008-07-23 09:22:58 UTC
Permalink
Post by Jean-Francois Ortolo
[...]
En ce qui concerne le "\r\n" à la fin des lignes des pages html, je
n'étais pas sûr de cela. Au cas où il puisse y avoir un simple saut de
ligne, ne serait-ce qu'un seule fois, j'aurais bien des problèmes avec
explode("\r\n", $send)
Tous les protocoles Internet en mode texte imposent des fins de ligne
en CR+LF (et interdisent CR seul et LF seul, quoique certains autorisent
CR+NUL dans certains cas très restreints).
Post by Jean-Francois Ortolo
La raison pour laquelle j'utilise un indice numérique, est que je
peux incrémenter cet indice comme je veux à l'intérieur de la boucle de
lecture, pour les besoins du traitement. Il me semble que je ne le
pourrais pas avec une boucle foreach().
En effet. C'est peut-être possible en mélangeant each et foreach,
mais même si c'était le cas je le déconseillerais pour des raisons
de lisibilité.

Au fait, je confirme que boucler de 0 à (count($var) - 1) comme tu le
fais est parfaitement correct.

Cordialement,
--
Olivier Miakinen
Jean-Francois Ortolo
2008-07-23 15:54:23 UTC
Permalink
Bonjour Monsieur

En fait, les données n'étaient bel et bien pas téléchargées du tout,
ceci de manière aléatoire, par suite de la surcharge du serveur.

Ce que je ne m'explique toujours pas, c'est que le timeout réel est
bien inférieur à 5 minutes, qui n'est que le timeout du fsockopen(), qui
fonctionne lui, alors que le timeout théorique du streaming est bien
égal à 50 minutes.

En tout cas, le problème est résolu, car je répète les chargements
jusqu'à ce que la variable réceptrice ait une longueur minimale ( au
moins 5000 octets ). Avec ce hack, j'ai toutes les données, au prix d'un
temps d'attente parfois assez long.

J'ai noté jusqu'à 5 essais de chargement consécutifs pour la même
url, dont le dernier réussi.

Je m'attendais, compte tenu du caractère aléatoire du problème, à ce
que ce soit un problème aléatoire de surcharge du serveur, et donc
d'avoir la possibilité de charger les données après un certain nombre
d'essais. Mais pffoouuu... ;)

Donc, problème résolu.

Merci beaucoup de vos conseils.

Bien à vous.

Amicalement.

Jean-François Ortolo
--
Visitez mon site gratuit donnant des Statistiques,
des Pronostics et des Historiques Graphiques
sur les Courses de Chevaux:
http://www.ortolojf-courses.com/
Thierry B\.
2008-07-23 20:01:59 UTC
Permalink
--{ Olivier Miakinen a plopé ceci: }--
Post by Olivier Miakinen
Tous les protocoles Internet en mode texte imposent des fins de ligne
en CR+LF (et interdisent CR seul et LF seul, quoique certains autorisent
CR+NUL dans certains cas très restreints).
Sur les protocoles, oui, mais je ne pense pas que cela concerne
les contenus du genre page html.
--
<Eva> Vous, les geek, vous avez vraiment une case en moins...
<Dieu> C'est pas qu'on a une case en moins, c'est juste qu'on compte
à partir de zéro.
Olivier Miakinen
2008-07-23 20:42:11 UTC
Permalink
Post by Thierry B\.
--{ Olivier Miakinen a plopé ceci: }--
Post by Olivier Miakinen
Tous les protocoles Internet en mode texte imposent des fins de ligne
en CR+LF (et interdisent CR seul et LF seul, quoique certains autorisent
CR+NUL dans certains cas très restreints).
Sur les protocoles, oui, mais je ne pense pas que cela concerne
les contenus du genre page html.
C'est vrai pour HTML seul, par exemple pour une page HTML stockée dans
le système de fichiers local et récupérée par une url de type file://.
Mais là, c'est une page HTML ayant transité sur le réseau via le
protocole HTTP, et d'ailleurs la détection de la fin des entêtes se
fait par « strpos($send, "\r\n\r\n") ».

D'un autre côté, rien ne nous empêche de mettre ceinture et bretelles
pour le cas où le serveur HTTP ferait mal son boulot, mais je doute que
ce soit nécessaire.
Thierry B\.
2008-07-24 17:06:40 UTC
Permalink
--{ Olivier Miakinen a plopé ceci: }--
Post by Olivier Miakinen
Post by Thierry B\.
Sur les protocoles, oui, mais je ne pense pas que cela concerne
les contenus du genre page html.
C'est vrai pour HTML seul, par exemple pour une page HTML stockée dans
le système de fichiers local et récupérée par une url de type file://.
Mais là, c'est une page HTML ayant transité sur le réseau via le
protocole HTTP, et d'ailleurs la détection de la fin des entêtes se
fait par « strpos($send, "\r\n\r\n") ».
Et une fois que le header est envoyé, le serveur web envoie le
contenu du fichier.html sans le modifier. En tout cas, chez moi,
c'est comme ça que ça se passe. Pas de rajout de <CR>... Ceci dit,
il y a peut-être des serveurs http qui font autrement, mais j'en
doute fort.
--
Q : If someone has the code in python for a buffer overflow, please post it.
A : Python does not support buffer overflows, sorry.
--{ comp.lang.python }--
Olivier Miakinen
2008-07-24 20:53:03 UTC
Permalink
Post by Thierry B\.
Et une fois que le header est envoyé, le serveur web envoie le
contenu du fichier.html sans le modifier. En tout cas, chez moi,
c'est comme ça que ça se passe. Pas de rajout de <CR>...
D'accord, et merci pour l'info.

Désolé, je n'avais pas fait l'essai avant de raconter n'importe quoi.
Continuer la lecture sur narkive:
Loading...