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

7 réponses

cs_Jack Messages postés 14006 Date d'inscription samedi 29 décembre 2001 Statut Modérateur Dernière intervention 28 août 2015 79
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)
0
Utilisateur anonyme
6 févr. 2008 à 20:51
Salut et merci pour cette réponse ultra rapide!!

Oui j'ai créé les usercontrol, la source complète se trouve ici :

http://www.vbfrance.com/codes/HTTP-EXPLORER-SERVEUR-WEB-DEDIE-PARTAGE-MEDIAS_38848.aspx

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()).
0
cs_Jack Messages postés 14006 Date d'inscription samedi 29 décembre 2001 Statut Modérateur Dernière intervention 28 août 2015 79
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)
0
Utilisateur anonyme
11 févr. 2008 à 02:23
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é) ?

Voila le nouveau code (la version en zip ici : http://kabee.free.fr/Projet1.zip) :

frmMain.frm

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

Private Sub usrTransfers_ReadBlockVasYEnvoie(Top As Boolean)

    usrSocket.SendData
       
End Sub

usrSocket.ctl

Public Event SendComplete()

Public Sub SendData()

    'envoi les données
   
    '...
   
    'appel l'évènement send complete
   
    RaiseEvent SendComplete
   
End Sub




usrTransfers.ctl







Public Event ReadBlockVasYEnvoie(Top As Boolean)   'Ici, le paramètre transmis n'a pas d'utilité
Private restant As Integer

Public Sub ReadBlock()

    'On lit les donnés dans un fichier
   
    If restant > 0 Then
   
        restant = restant - 1
        RaiseEvent ReadBlockVasYEnvoie(True)
       
    End If

End Sub

Private Sub UserControl_Initialize()

    restant = 5000

End Sub
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
cs_Jack Messages postés 14006 Date d'inscription samedi 29 décembre 2001 Statut Modérateur Dernière intervention 28 août 2015 79
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)
0
Utilisateur anonyme
15 févr. 2008 à 19:28
Salut, merci pour ta source,

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


quand même


d'avoir des dépassements de piles...
0
Utilisateur anonyme
11 mars 2008 à 14:10
up, y'a t'il une solution sans passer par un timer ou une boucle?
0
Rejoignez-nous