Il existe plusieurs manières de permettre à des animations Flash de communiquer entre elles, de la solution la plus lourde impliquant des flux divers (datas, vidéos, audio...) au simple échange de fragments de texte. Macromedia, en son temps, avait ouvert la voix avec Flash Communication Server (devenu Adobe Media Server). Au fil du temps, d'autres solutions sont venus compléter cette proposition. Coté artillerie lourde, la passerelle AMF PHP est venu de manière gratuite proposer ses services et permet également la gestion de flux audio et vidéo. Mais il est vrai, que bien souvent il n'est pas nécessaire de déménager des montagnes afin de permettre à deux animations Flash d'échanger simplement quelques bribes d'informations. Tout n'est alors question que de contexte... les animations se trouvent elles sur la même machine, ou du moins dans le même environnement d'exploitation ou alors les animations sont elles déployées au travers du réseaux sur des machines distinctes? Dans le premier cas nous pourrons avoir recour à la classe local connection, ou tout autre dérivé basé sur AsBroadcaster, pour l'autre nous pouvons nous appuyer sur l'objet XML Socket.v1.11
Fichier Temporaire du développement
L'objet et les serveurs XML Socket
La classe XML Socket a été mise en place aussi bien en AS2 qu'en AS3 pour permettre à des animations Flash d'utiliser des Sockets de communication. Ces sockets sont en fait des mécanismes qui permettent des échanges d'informations textuelles, en entrée et en sortie, se basant sur le protocole TCP/IP. Afin de permettre le bon fonctionnement des ces échanges il est nécessaires d'utiliser un serveur XML Socket adapté. Il en existe différent modèles allant de l'open source aux solutions propriétaires.
Pour illustrer cette prise en main, nous allons utiliser le serveur AquaServer mis en place par Brenden Hall de FigLeaf. Il est difficile aujourd'hui de le trouver sur le web, mais quelques tutoriaux le propose encore en téléchargement, notamemt sur le site du zéro.
Cahier des charges de la prise en main
Basons nous sur le cas d'étude concret que rencontre la société pour qui ce tutorial a été écrit. Coté serveur, un simulateur industriel écrit en un langage non web qui doit communiquer en envoyant des données de manières récurantes. Coté client, deux types d'animations Flash qui les réceptionnent. Une première animation qui peut également envoyer des données vers le système et une seconde complètement passive qui ne pourra que recevoir l'ensemble des données, provenant soit du simulateur, soit de l'autre animation Flash.
Nous pouvons alors envisager de faire communiquer ce système hétérogène au travers de l'utilisation d'un socket XML. Nous allons reproduire cet environnement au travers de trois animations Flash, une coté serveur et deux coté client. Distinguons l'unique animation coté serveur comme étant le coeur de l'application et deux animations coté client. La première qui ne peut que recevoir des données (IN) et la seconde qui peut recevoir des données mais également en transmettre (IN_OUT). Afin de pouvoir facilement les distinguer appelons les respectivement; serveur.fla, client-in.fla et client in-out.fla.
Lancement du serveur
Avant de mettre le serveur en activité, nous allons nous assurer de la validité du port utilisé. En fonction du serveur utilisé, il est possible d'accéder à un fichier de configuration externe et détaillé (ex Palabre propose une fichier de configuration relativement complet 'etc/palabre.conf'). En ce qui concerne Aqua, tout se passe en ligne de commande. Il suffit d'ouvrir le fichier 'lance.bat' avec NotePad++ et de modifier si besoin est le port 1024 utilisé par défaut. Ensuite un double click sur ce même fichier lance le serveur et ouvre l'invite de commande.
Trois commandes qui parlent d'elles mêmes sont alors accessibles :
>HELP >LIST >QUIT
Test de connection au serveur XML Socket
Avant d'aller plus loin dans l'étude complète, nous allons vérifier le bon fonctionnement du serveur et étudier les diverses phases de la communication à mettre en place. Pour une simple et unique animation Flash fera l'affaire. Créons un fichier Flash AS2 et enregistrons le sous connection_socket_01.fla. Ouvrir la fenêtre Actions et saisir le code ci-dessous :
var xmlSocket = new XMLSocket();
xmlSocket.onConnect=function(arg) {
if (arg == true) {
trace("Connection réussie")
}
}
xmlSocket.connect("127.0.0.1",1024);
Le code parle de lui même, il suffit de créer une instance de l'objet XMLSocket et de pointer la fonction de connection vers l'URL du serveur, dans notre cas le serveur local, en précisant le port utilisé au chapitre précédant. Le gestionnaire d'évènement de connection permet d'intercepter la connection et de s'assurer par le paramètre arg reçu du bon déroulement des opérations. Lancez donc cette animation et la fenêtre de sortie devrait afficher le message "Connection réussie".
Echanges de données avec le XML Socket
La connection établie nous allons mettre en place un modèle d'envoi/réception de données au format XML. Un simple écouteur sur la scène permettra d'envoyer des infos sur un clic souris. Enrgistrez l'animation sous connection_socket_02.fla et ajouter l'écouteur au code déjà présent :
var ecouteur = new Object()
ecouteur.onMouseDown = function(){
trace("coucou")
}
Mouse.addListener(ecouteur)
Un rapide test nous permet de nous assurer que tout fonctionne correctement, enregistrons donc l'animation sous connection_socket_03.fla et remplaçons l'instruction trace("coucou") par l'envoi d'un message au serveur XMLSocket. Pour faire simple, nous pouvons à ce stade utiliser une instance XML créant à la volée une instruction XML litérale.
ecouteur.onMouseDown = function(){
var xml_a_envoyer = new XML('<message>Ceci est un texte accentué</message>')
xmlSocket.send(xml_a_envoyer)
}
Pour intercepter ce message nous allons définir un écouteur sur l'évènement onXML de la classe XMLSocket. Pour informations, nous avons accès à deux types d'écouteur; onData et onXML. Le premier s'émet lorsqu'un message a été téléchargé depuis le serveur, le second lorsqu'un message XML est reçu. Bien que les deux soient utilisables dans cette situation, nous utiliserons le second.
xmlSocket.onXML=function(xmlrecu) {
trace(xmlrecu)
}
Lors du premier test, tout fonctionne correctement, y compris la gestion du caractère accentué, mais si nous cliquons plusieurs fois le message renvoyé contient à la fois une chaine litérale ainsi qu'un ensemble de caractères invisibles tels que des retours chariots ou des espaces. Ceci ne nous permet pas d'utiliser correctement les informations reçues. Pour remédier à cela, nous devrions faire appel à la propriété ignoreWhite de l'objet XML en l'initialisant à true (par défaut elle est initialisée à false), ceci afin de demander au player Flash de ne pas prendre en compte ces caractères en les traitant comme des noeuds. Le soucis reste que cette instruction se doit d'être utilisée sur la classe XML avant que tout contenu XML n'y soit défini. Il nous faut donc intervenir au niveau du prototype de la classe afin d'agir par défaut sur l'ensemble des instances XML qui sont crées. Ajoutons donc une première instruction à notre code et testons à nouveau.
XML.prototype.ignoreWhite = true;
Mise en place d'un composant et de ses classes dérivées
Afin de représenter les actions du simulateur et les modifications de certains paramètres, ou informations transmises, nous allons mettre en place un composant proposant la représentation visuelle d'une donnée et de sa valeur. Nous ajouterons la possibilité d'agir manuellement sur la valeur affichée. Le composant doit être décliné en deux représentations, celle utilisée par la partie serveur.fla et celle utilisée par les parties client_in.fla et client_in_out.fla. Nous ne détaillerons pas la mise en place des classes utilisées (step.as et steppeur.as) , où seule les fonction constructeurs sont renseignées afin d'initialiser correctement les valeurs par défaut du composant ainsi que la mise en place des écouteurs permettant de modifications manuellement les valeurs affichées. Dressons uniquement le tableau des variables d'instances et de classes afin d'en définir leur rôle et utilisation.
| Variables | Types | Classes | Descriptions |
|---|---|---|---|
| moins | MovieClip | steppeur | Clip représentant le bouton moins du steppeur |
| plus |
MovieClip | steppeur | Clip représentant le bouton plus du steppeur |
| valeur | TextField | steppeur - step | Textfield représentant le conteneur de la valeur du steppeur |
| nom | TextField | steppeur - step | Textfield représentant le conteneur du libellé du composant du steppeur |
| titre | String | steppeur - step | Chaine de carctère représentant le libellé du steppeur |
| defaut | Number | steppeur | Valeur du composant du steppeur |
| id | String | steppeur - step | Identifiant permettant de pointer vers le composant |
| composant | Object | steppeur - step | Objet permettant de recenser l'ensemble des composants steppeur ou step utilisé par les animations. Chaque objet est alors identifié par son id |
En complément de la classe adjointe au symbole du steppeur et du step, nous devrons définir certaines propriétés de composant. Nous reviendrons sur cette étape lors de la réalistion des diverses animations serveur et clients. Commençons par le simulateur coté serveur...
Création de l'animation serveur.fla
Comme nous l'avons vu plus tôt, il n'y a pas de réelle complexité à mettre en place la couche XML Socket... reprennons donc le code comme nous l'avions écrit lors du premier test. Automatisons simplement au passage l'envoi de données, au travers d'un Timer, qui fera cela en toute autonomie toutes les X millisecondes.
XML.prototype.ignoreWhite = true;
var timer;
var xmlSocket:XMLSocket = new XMLSocket();
xmlSocket.onConnect = function(arg) {
timer = setInterval(sendDatas, 1000);
};
xmlSocket.onXML = function(argXML) {
trace(argXML)
};
xmlSocket.connect("127.0.0.1",1024);
function sendDatas() {
var xml = new XML('<message>coucou</message>');
xmlSocket.send(xml);
}
Avant de formater la chaine XML en fonction de nos besoins mettons en place les divers composants issus de la classe steppeur.as. Plaçons trois instances sur la scène et personalisons les pour refléter divers types de valeurs que pourrait renvoyer un réel simulateur industriel... disons la température, le débit et la pression d'une machine hydraulique. Personalisons les au travers des propriétés de symboles. Pour cela sélectionnons le symbole dans la bibliothèque et depuis le menu contextul optons pour "Définition du composant". Définissons trois séries de paramètres en fonction du tableau suivant :
| Nom | Variable | Valeur | Type |
|---|---|---|---|
| ID | id | {a,b,c,d,e,f} | List |
| Label | titre | Composant | String |
| Valeur par défaut... | defaut | 100 | Number |
Ensuite il suffit de sélectionner tour à tour chacune des instances présentes sur la scène et depuis l'inspecteur de propriétés, dans la rubrique Paramètres de composant, nous pouvons renseigner les valeurs suivantes :
| Composant | ID | Label | Valeur par défaut... |
|---|---|---|---|
| 1 | a | Température | 154 |
| 2 | b | Débit | 5 |
| 3 | c | Pression | 28 |
La première phase de l'animation est maintenant terminée, et avant d'aller plus loin, il faut nous assurer de formatter les données représentées par ces composants vers le Socket. Il nous faut donc maintenant nous tourner vers le schéma que le XML doit adopter et tout en le structurant le renseigner des données adéquates. Pou cela nous allons regarder de plus près, et paradoxalement avec du recul, cette communication XML.
Modélisation du fichier XML utilisé
Le fichier XML va devoir transporter et formater l'ensemble de l'information, d'une part pour les composants mis en place à l'étape précédente, mais également à d'autres fins comme les messages textuels, les instructions de FOAD et les messages remontant vers le simulateur. Pour l'instant nous pouvons distinguer au moins quatre familles principales :
| Balise / Familles | Description et utilité |
|---|---|
| message | Des messages textuels de type IRC ou chat en échange entre les diverses animations utilisées (toutes pourront les recevoir, mais seules serveur.fla et client_in_out pourront les transmettre) |
| simulateur | Les informations provenant du simulateur (serveur.fla) à destination de chaque (client_in) et (client_in_out) |
| foad | Les informations propre aux échanges entre applications clients (de client_in_out vers client_in) |
| instruction | Les instructions qui vont remonter de l'animation client_in_out vers le simulateur (serveur) |
Nous pouvons alors envisager un fichier XML contenant 4 noeuds principaux correspondant chacun à une des familles vus précédemment. Les attributs de chacun de ces noeuds viendront affiner la nature et le format des contenus transportés. Le tableau ci-dessous va permettre d'en identifier et d'en qualifier certains.
| Balise | Attribut | Description |
|---|---|---|
| communication | rel | permet d'identifier la nature de l'animation émétrice |
| composant | moins | MovieClip ecouteur de l'action de décrementation de la valeur |
| composant | plus | MovieClip ecouteur de l'action d'incrementation de la valeur |
| composant | nom | TextField contenant le libellé du composant |
| composant | valeur | TextField contenant la valeur du composant |
| composant | id | Identifiant du composant (dans une variable de classe) |
| composant | titre | Libellé du composant |
| composant | defaut | Valeur du composant |
Nous pouvons d'ores et déjà dresser un premier profil du fichier XML à mettre en place
<?xml version="1.0" encoding="utf-8"?>
<communication rel="">
<message></message>
<simulateur>
<composant moins="" plus="" nom="" valeur="" id="" titre="" defaut="" />
<!-- D'autres composants -->
</simulateur>
<foad></foad>
<instruction></instruction>
</communication>
Intégration de ce fichier XML dans l'animation serveur.fla
Afin de générer le fichier XML, nous avons deux possibilités, soit utiliser une chaine de caractère que l'on utilisera comme un XML littéral, soit utiliser le DOM et créer chaque élément en tant que XMLNode et constuire ainsi pas à pas l'arborescence globale. Nous allons utiliser la seconde piste et mettre en ce sens en place une fonction qui nous permettra de répter les tâches de manière plus simple et évolutive. De quoi avons nous besoin :
- construire un noeud
- lui ajouter un contenu de type Noeud de texte
- ajouter l'ensemble des attributs de ce noeud
- et enfin, attacher ce noeud au reste de l'arborescence
A cet effet, créons la fonction xmlise(xml, noeud, val, args) qui attends quatre paramètres à savoir l'arborescence globale, le noeud, son contenu textuel et un objet représentant l'ensemble des attributs :
function xmlise(xml, noeud, val, args) {
var node = xml.createElement(noeud);
if (val != null) {
var textNode = xml.createTextNode(val);
node.appendChild(textNode);
}
for (var i in args) {
node.attributes[i] = args[i];
}
return node;
}
Son utilisation est on ne peut plus simple... et vérifions le directement au sein de la fonction sendDatas(). N'oublions pas que cette fonction est invoquée de manière automatique par le Timer. Commeçons par l'envoi de la partie <message> en n'oubliant pas d'ajouter le script ci-dessous et de placer un texte par défaut dans le champs texte du message avant de tester l'animation.
function sendDatas() {
var xml = new XML();
var doc = xmlise(xml, "communication", null, {rel:"serveur"});
doc.appendChild(xmlise(xml, "message", msg_field.text, {}));
xml.appendChild(doc);
xmlSocket.send(xml);
}
Si l'on prend soin de traiter l'information depuis la fonction xmlSocket.onXML, la fenêtre de sortie doit bien renvoyer le message toute les X millisecondes. Poursuivons en transmettant cette fois ci les divers paramètres de composant.
S'il est vrai que la balise <message> ne contient qu'une chaine de caractère qui peut être ajouter comme un simple TextNode, il en est différement pour la balise <simulateur>, qui elle contient d'autres balises de type <composant>. Chacune de ces balises devant refleter les divers composant présents sur la scène. Nous allons filtrer cette situation et adapter notre fonction à cette particularité. Le plus simple est d'user d'un switch / case pour cela :
if (val != null) {
switch (noeud) {
case "message" :
break;
case "simulateur":
break;
}
}
Rappelons nous que dans notre classe steppeur.as, nous avons une variable de classe, composant, qui est en fait un objet contenant chacune des instances de la classe... utilisons là pour créer chacune des balises <composant>. Rien de plus simple... invoquons l'ajout du noeud <simulateur> depuis la fonction sendDatas avec les paramètres adéquats :
doc.appendChild(xmlise(xml, "simulateur",steppeur.composant , {}));
et adaptons le code au sein du case "simulateur" de la fonction xmlise() :
case "simulateur":
for (var i in val){
node.appendChild(xmlise(xml, "composant", null,val[i]));
}
break;
Bien qu'ils ne soient pas utiliser pour l'instant, il ne nous reste plus qu'à invoquer la création des deux autres noeuds de premier niveau afin de finaliser la création du fichier XML.
doc.appendChild(xmlise(xml, "foad", null, {}));
doc.appendChild(xmlise(xml, "instruction", null, {}));
Création de l'animation client_in.fla
Passons maintenant à la première animation client qui va permettre de recevoir et afficher les informations envoyées par le simulateur. La base de la connection au socket reste identique, seules les instructions d'affichage vont être ajoutées à l'interface. Tout va donc être piloté par la fonction écouteur onXML(). Dans un premier temps, afin de s'assurer de la bonne réception des infos, plaçons un simple trace renvoyant l'argument reçu.
xmlSocket.onXML = function(argXML) {
trace(xml)
};
Cette fois ci pour tester l'animation, nous avons besoin de lancer au préalable l'animation serveur.fla afin de génrer l'envoi de données coté simulateur. Une fois lancée, cette animation pourra continuer en tâche de fond afin de ne pas systèmatiquement devoir lancer les deux animations à chaque fois. La fenêtre de sortie doit afficher les informations reçus et en regardant de plus près il peut y avoir un problème du coté des caractères spéciaux, les caractères accentués en particuliers. Le plus simple pour ne pas générer ce genre d'erreur est de doter chacune de nos animations, dès la première ligne de script, d'une instruction de gestion de l'encodage :
System.useCodepage = true;
La récupération des données dans l'argument argXML revient à une simple manipulation de données XML qui vont devoir être réinjectées dans les composants ou parties appropriés de l'animation. Il n'y a rien en AS2 qui nous permet de cibler directement un noeud par son nodeName, nous allons devoir parser l'ensemble de l'information pour en extraire le contenu au fur et à mseure. Dans un premier temps, nous allons hard coder l'extraction des données correspondantes à la balise <simulateur>. Il s'agit en fait du deuxième noeud de la racine du document soit racine.firstChild.childNodes[1].
Une fois ce noeud récupéré, concentrons nous sur la récupération des diverses données de chaque composants. En parsant les noeuds contenus dans la balise <simulateur> nous pouvons donc tour à tour accéder à chaque composant. Rappelons nous que l'ensemble de ces données sont d'une part contenus dans les attributs de la balise et d'autre part dans les variables d'instance du composant. De même chaque instance de composant est contenu dans la variable de classe composant de la classe step.as et reste accessible depuis son identifiant; soit step.composant[id]. Il suffit alors de pointer vers une fonction au sein de la classe qui va pour chaque instance appliquer la valeur adhoc.
var simulateur = argXML.firstChild.childNodes[1];
for (var i = 0; i < simulateur.childNodes.length; i++) {
var att = simulateur.childNodes[i].attributes;
step.composant[att['id']].afficheComposant(att['defaut']);
}
Afin de rendre ce code plus évolutif, et de ce fait moins figé par cette version hard codée, mettons en place une boucle qui va parser tour à tour les noeuds racine de l'argument argXML. A chaque itération, nous pourrons comparer la valeur du nodeName et au travers d'un switch / case affecter le traitement nécessaire. Le code est un peu plus détaillé mais reste très explicite :
var argXML = argXML.firstChild;
for (var nd = 0; nd < argXML.childNodes.length; nd++) {
var ndTemp = argXML.childNodes[nd];
switch (ndTemp.nodeName) {
case "simulateur" :
for (var i = 0; i < ndTemp.childNodes.length; i++) {
var att = ndTemp.childNodes[i].attributes;
step.composant[att['id']].afficheComposant(att['defaut']);
}
break;
}
}
La première partie de l'information envoyée est bien retranscrite sur les composants, prenons maintenant en compte le message textuel. Il suffit de rajouter une condition à la structure switch / case pour récupérer et traiter la balise adéquate en récupérant le nodeValue de la balise. Nous allons également externaliser la gestion de cette fonctionalité sur une fonction externe à la boucle, afin d'anticiper un éventuel accès depuis un autre point du programme.
case "message" : afficheMessage(ndTemp.firstChild.nodeValue) break;
function afficheMessage(arg){
msg_field.text = arg
}
Création de l'animation client_in_out.fla
crossdomain.xml
http://help.adobe.com/en_US/AS2LCR/Flash_10.0/help.html?content=00000471.html






