Problème de récursivité infernal dans un serveur web
Utilisateur anonyme
-
6 févr. 2008 à 17:38
Utilisateur anonyme -
11 mars 2008 à 14:10
Bonjour
Voilà, je suis confronté à un problème de récursivité assez gênant dans un serveur web.
J'ai schématisé le code complet qui est très gros en quelques lignes de façon à ce que ça soit très clair.
Pour faire simple ça suit le schéma classique d'un serveur web : initialisation du transfert >> lecture d'un bloc dans un filereader si données restantes > 0 octet >> envoi du bloc au socket >> fin de lenvoi du socket >>lecture d'un bloc dans un filereader si données restantes > 0 octet ...
Au bout d'un moment ça fait un jolie "espace de pile insufisant" en IDE et un "Erreur d'execution '-2147417848, erreur automation automation, l'objet invoqué s'est déconnecté de ses clients." en compilé malgré le point de sortie lorsque la fin du fichier est atteinte.
Y'a t-il une solution à ce problème sans changer la logique du code (filereader > socket > filreader..)
Voici les quelques lignes de codes :
frmMain.frm
(form)
Private Sub usrSocket_SendComplete()
'fin de l'envoi du bloc, on lit la suite dans le fichier
usrTransfers.ReadBlock
End Sub
Private Sub DataArrival_Click()
'demarre le transfert du fichier
usrTransfers.ReadBlock
End Sub
usrSocket.
ctl
(user control)
Public Event SendComplete()
Public Sub SendData()
'envoi les données
'...
'appel l'évènement send complete
RaiseEvent SendComplete
End Sub
usrTransfers.ctl
(user control)
Public Sub ReadBlock()
'On lit les donnés dans un fichier
'if reste_des_donnés_a_envoyer then
frmMain.usrSocket.SendData
'End If
End Sub
A voir également:
Problème de récursivité infernal dans un serveur web
cs_Jack
Messages postés14006Date d'inscriptionsamedi 29 décembre 2001StatutModérateurDernière intervention28 août 201579 6 févr. 2008 à 20:23
Salut
C'est du côté du UserControl qu'il faut investiguer.
C'est toi qui l'a créé ou l'as tu récupéré sur vbfrance ou ailleurs ? (questionne éventuellement le créateur)
Juste une chose que je vois dans le dernier ReadBlock, et je ne sais pas si c'est ça qui empile les données jusqu'à plus avoir de mémoire (mais fort possible).
Tu es dans un UserControl et tu fais appel à une Forme appartenant au projet qui l'héberge.
Non, surtout pas.
Utilise un évènement RaiseEvent XYZ pour renvoyer les données à ta forme mère et dans ta forme, dans la procédure XYZ de ton UserControl, renvoie les données à l'autre UserControl "usrSocket" si besoin
Vala
Jack, MVP VB
NB : Je ne répondrai pas aux messages privés
<hr />Le savoir est la seule matière qui s'accroit quand on la partage (Socrate)
Le problème c'est que je ne vois pas du tout comment sortir de ce système en boucle sur ce genre d'appli (datarrival >> readblock >> sendcomplete >> readblock...) à moins d'utiliser une très sale boucle ou pire, un timer! Les quelques lignes de mon post simplifient à l'extrême l'appli complète mais en gros la logique est là. Seule différence : dans usrSocket.ctl, send_complete n'est pas appelé par une ligne de code directement mais par le socke t
lorsque les données ont été envoyés
via un système complexe de subclassing (mais ça ne change rien au problème).
Je pense que le problème ce situe dans la sub
usrSocket_SendComplete, un test devrait peu être fait avant d'appeler la fonction
ReadBlock, mais quoi? Un DoEvents ne change rien...
Pour l'appel vers une fonction se trouvant dans la
form
mère tu as raison c'est pas recommandé mais dans le projet complet j'utilise un événement (usrTransfers_DataRead()).
cs_Jack
Messages postés14006Date d'inscriptionsamedi 29 décembre 2001StatutModérateurDernière intervention28 août 201579 9 févr. 2008 à 00:49
Re
Ce que je veux dire, c'est que dans ton UserControl (UC), tu dois créer un Event afin de renvoyer les données à ta forme hôte, comme tu l'as fait pour ton SendComplete :
Public Event ReadBlockVasYEnvoie(Top As Boolean) ' Ici, le paramètre transmis n'a pas d'utilité
Dans ton ReadBlock, au moment où tu veux envoyer l'info, tu fais un
RaiseEvent ReadBlockVasYEnvoie(True)
Ensuite, dans ta Forme hôte, tu auras l'évènement usrTransfers_ReadBlockVasYEnvoie qui te fournira le top.
Dans cette procédure, côté forme hôte, qu'il faut envoyer la commande usrSocket.SendData
Seule la forme connait ce UserControl
Il ne faut à aucun moment que usrTranfers fasse référence à usrSocket, et réciproquement.
Si tu ajoutes cet évènement à ton UC après l'avoir posé sur ta forme, il y a de fortes chances pour que la forme ne connaisse pas l'évènement nouvellement créé : Il te faudra donc surement supprimé le UC de ta forme puis le remettre pour que la forme analyse ses composantes.
Vala
Jack, MVP VB
NB : Je ne répondrai pas aux messages privés
<hr />Le savoir est la seule matière qui s'accroit quand on la partage (Socrate)
Merci de prendre le temps de me répondre, j'essaie de résoudre ce problème depuis trois ans dans mon appli et ça fait plaisir ;)
J'ai testé ce que m'a conseillé mais j'ai toujours une erreur "erreur automation, l'objet invoqué s'est déconnecté de ses clients". Concrètement, que penses-tu qu'il faille modifier pour ne plus avoir cette erreur (j'ai aussi ajouté un compteur symbolique pour dire de sortir à un moment de la récursivité) ?
cs_Jack
Messages postés14006Date d'inscriptionsamedi 29 décembre 2001StatutModérateurDernière intervention28 août 201579 15 févr. 2008 à 10:08
Salut
Merci pour le lien vers la source, c'est plus facile.
Ok, je comprends.
Au démarrage, tu vas dans usrTranfers.ReadBlock, tu envoies un RaiseEvent qui déclenche un nouvel appel vers usrSocket.SendComplete.
Un RaiseEvent s'exécute tout de suite.
Mais le problème, c'est qu'on ne revient jamais de cet appel puisque tu lances une autre tâche qui, de surcroit, relance une chaine d'autres RaisEvent
La pile s'engorge, c'est clair.
En fait, même si RaiseEvent est élégant, il faut ici simplement utiliser des Functions qui renverront le résultat, tu seras ainsi certain de ne pas rester coincé dans la procédure appelante (ça va simplifier les choses) :
Dans usrSocket : Public Sub SendData()
'envoi les données
'...
'appel l'évènement send complete
' Le RaiseEvent qui suit n'est pas utile
' Il peut simplement servir à afficher des données côté frmMain
' mais ne doit pas servir de déclencheur d'action; la fin (End Sub)
' sufit pour signaler que c'est terminé
' RaiseEvent SendComplete
End Sub
Dans usrTransfers :
Supprimer la déclaration de ReadBlockVasYEnvoie
+
Public Function ReadBlock() As Boolean
'On lit les donnés dans un fichier
If restant > 0 Then
restant = restant - 1
Debug.Print restant ' pour test
ReadBlock = True
End If
End Function
Dans frmMain :
Private Sub DataArrival_Click()
'demarre le transfert du fichier
' Tant que ReadBlock renvoie True, on lance les appels à SendData
Me.MousePointer = vbHourglass
Do While usrTransfers.ReadBlock
usrSocket.SendData
DoEvents ' ça ne fait pas de mal, surtout avec les Winsocks
Loop
Me.MousePointer = vbDefault
End Sub
Vala
Jack, MVP VB
NB : Je ne répondrai pas aux messages privés
<hr />Le savoir est la seule matière qui s'accroit quand on la partage (Socrate)
J'utilisais cette méthode auparavant (j'ai même utilisé un timer) mais le problème c'est que , par ex, sur un transfert de 1 go par le net le process tourne à 100% en permanence même avec un débit extrêmement faible, même à l'arrêt!
Pour donner un exemple concret, lors d'un streaming avec winamp avec cette méthode même lors de la mise en pause le process est toujours à 100%!
Dans ma source actuelle, je tente de sortir de la boucle récursive 'infernale' en la 'coupant' à un endroit en passant par l'appel d'un windows message sur un objet créé et subclassé (modSubClsCustWinMsg.bas) mais il arrive