Avec l’avènement de la téléphonie mobile, on constate un rapprochement très fort entre les technologies télécom et l’informatique. Là où il y a encore une quinzaine d’années un service devait être implémenté sur l’ensemble des commutateurs (on utilise généralement le terme anglais "switch") télécom d’un opérateur, il sera aujourd’hui déporté sur des serveurs informatiques. Ce rapprochement est d’autant plus visible sur les services dits "à valeur ajoutée" pour lesquels les plateformes ont une patte connectée sur le réseau télécom via les protocoles de la pile SS7 (voir [1] ou [2] pour une introduction au "Signaling System 7") et une autre sur le réseau IP, permettant, d’une part, le développement et le déploiement rapides d’applications et, d’autre part, leur interconnexion avec le système d’information de l’opérateur pour la facturation, pour la gestion des abonnements, etc. Parmi ce type de services, on peut citer le téléchargement de logos et de sonneries, les musiques qui remplacent les sonneries d’attente ou les services à base de SMS tels le chat, les flashs d’alerte divers et variés (actualités, bourse, banque...), etc. C’est sur le développement de services à base de SMS que nous allons nous pencher au cours de cet article.Travailler avec les SMS peut, à première vue, paraître un peu passéiste lorsque la tendance se porte sur les MMS, les messageries instantanées de type Messenger ou MSN, issues du monde Internet, voire le mail. L’augmentation du trafic SMS chez tous les opérateurs dément cette affirmation. Le SMS est en effet un moyen très simple de communication, disponible pour tous les abonnés d’un opérateur sans qu’il soit nécessaire de définir (ni de mémoriser) des identifiants spécifiques, style adresses mail ou pseudos, qu’il est possible de recevoir sur tous les terminaux du marché, même les plus simples. Développer des services SMS peut donc être très intéressant pour les opérateurs eux-mêmes, pour des fournisseurs de services tiers ou pour des sociétés telles des banques pour fournir des services de type alertes.
Dans la suite de cet article, nous allons préciser le fonctionnement technique de la transmission d’un SMS, discuter brièvement du protocole utilisé, SMPP et d’une bibliothèque Java, smppapi, qui implémente ce protocole. Nous mettrons en pratique ces discussions en implémentant deux services, l’un très simple, permettant d’envoyer un SMS en ligne de commande, l’autre un peu plus complexe faisant du gatewaing mail. Trois programmes de démonstration sont fournis : les deux services et un squelette d’application permettant de réaliser des expériences sur smppapi. Ils sont situés dans le répertoire dev. Ces répertoires ont tous la même structure : le répertoire dev/<programme> contient un .jar exécutable (à lancer par la commande java -jar <programme>.jar) ainsi qu’un fichier build.xml pour la compilation par Ant. dev/<programme>/src/etc contient les fichiers de configuration et dev/<programme>/src/org/buguigny/<programme> contient les sources java.
Les SMS, comment ça marche ?
Même si, pour développer des applications SMS, il n’est pas nécessaire de connaître l’architecture détaillée de l’infrastructure technique d’un opérateur mobile, il peut néanmoins être intéressant d’en avoir une idée générale.
Éléments techniques d’un réseau GSM
Un opérateur GSM se base sur un certain nombre d’éléments techniques :
- Le premier élément est le terminal de l’abonné, appelé "MS" (Mobile station) dans le vocabulaire GSM. Pour ce qui nous concerne, la MS peut envoyer ou recevoir des SMS.
- Le réseau d’accès est composé d’antennes (BTS, Base Tranceiver Station) et de contrôleurs d’antennes (BSC, Base Station Controler) qui permettent aux terminaux de se connecter (d’accéder) au réseau par radio.
- Le réseau cœur est connecté au réseau d’accès et reçoit les demandes de l’utilisateur. Il est composé d’un ou plusieurs MSC (Mobile Switching Center, le commutateur ou switch), chacun couvrant une zone géographique et de bases de données techniques. Le MSC vérifie si l’abonné appelant a les autorisations nécessaires pour le service demandé en interrogeant des éléments tiers, puis assure l’itinérance lorsqu’un abonné appelle en se déplaçant, l’acheminement des appels, ou délègue la réalisation du service demandé à une plate-forme tiers (par exemple le SMS-C dans le cas d’envoi de SMS). Les bases techniques sont au nombre de trois : le HLR (Home Location Register), le VLR (Visitor Location Register) et l’AUC (AUthentication Center). Le HLR liste les services auxquels l’abonné a accès (par exemple le roaming, les appels internationaux, la réception ou l’émission d’appels, etc.). Le VLR est, d’une part, une copie de certaines informations contenues dans le HLR, mais il contient également les informations de localisation de l’abonné qui permettent au MSC de réaliser l’acheminement des appels via le bon BSC sur le bon BTS. Finalement, l’AUC gère les clefs secrètes utilisées pour authentifier les demandes de service et pour chiffrer les communications. Les AUC sont généralement associés aux HLR. Notons que dans le cas de connexions data GPRS ou EDGE, des éléments additionnels doivent être mis en place (SGSN et GGSN).
- En aval du réseau cœur, on trouve deux ensembles d’éléments : le système d’information et les plateformes de service. Le système d’information est en charge de la gestion commerciale du client. Typiquement, il contient des applications de type CRM, de facturation ou de data warehouse. Les plateformes de service permettent de rendre des services supplémentaires aux abonnés comme par exemple la messagerie vocale, l’envoi de MMS ou l’envoi de SMS.
La figure 1 présente de manière schématique et simplifiée les éléments discutés ci-dessus. Les lecteurs n’ayant pas peur d’entrer dans le vif du sujet peuvent se référer à [3].

Figure 1 : Architecture schématique des éléments techniques d’un opérateur GSM
Fonctionnement des SMS
Il y a trois classes de SMS :
- Les messages venant d’un mobile : l’abonné envoie un message. On parle alors de SMS-MO, MO pour Mobile Originating.
- Les messages arrivant à un mobile : l’abonné reçoit un message. On parle alors de SMS-MT, MT pour Mobile Terminating.
- Les messages en diffusion (broadcast) : l’opérateur envoie le même message à un ensemble d’abonnés localisés dans une zone géographique.
Le service SMS requiert une plate-forme dédiée appelée "SMS-C" (SMS Center). Le SMS-C (en orange sur la figure 1) est connecté au switch en utilisant les protocoles de la pile SS7. Il est identifié sur le réseau télécom par un numéro de téléphone afin que le MSC puisse lui transmettre les messages à envoyer. Le SMS-C est connecté au système d’information, en général par un lien TCP/IP qui permet, par exemple, de recueillir les informations nécessaires à la facturation du client. Finalement, le SMS-C peut également être connecté à des applications pouvant recevoir ou envoyer des SMS, les services SMS. Le protocole utilisé dans ce cas est SMPP (Short Message Peer to Peer) dont nous parlerons plus tard. Ce lien peut être utilisé, par exemple, par la messagerie vocale pour prévenir un abonné qu’un message a été déposé dans sa boîte.
Lors de l’envoi d’un SMS par un client (SMS-MO), le terminal code le corps du message et l’envoie au MSC avec deux informations supplémentaires : le numéro du destinataire et le numéro du SMS-C à utiliser. Ce dernier est configuré dans le téléphone et permet au MSC de relayer le SMS au SMS-C qui le stocke. Si le message n’a pu être délivré au SMS-C, le réseau envoie un acquittement négatif. Le SMS-C tente ensuite d’envoyer le SMS vers le destinataire (SMS-MT) à travers le MSC. Si la première tentative échoue (par exemple, si le terminal du destinataire est éteint), le message est stocké et d’autres tentatives sont réalisées à intervalles réguliers. Au bout d’un certain nombre d’échecs, ou après un certain délai, le message est effacé.
Le destinataire d’un message peut être un abonné de l’opérateur ou celui d’un autre opérateur, mais peut également être une application, typiquement identifiée par un numéro court (mais ce n’est pas obligatoire), disons le 100. Dans ce cas, le SMS-C doit être configuré de sorte que si le numéro du destinataire est le 100, il ne tente pas de l’envoyer vers le MSC, mais utilise le lien SMPP pour déclencher l’application destinataire. De la même manière, l’expéditeur peut aussi être une application : pour envoyer un message à un abonné, elle doit se connecter sur le SMS-C, créer un paquet SMPP avec au minimum le numéro du destinataire et le corps du message et l’envoyer au SMS-C. Dans le jargon SMPP, les applications et les services sont nommés "ESME" pour External Short Message Entity.
Le protocole SMPP : Short Message Peer to Peer
SMPP est un protocole ouvert défini par le SMS Forum (www.smsforum.net). La version la plus récente est la 5.0, mais la plupart des SMS-C implémentent la version 3.4, voire la 3.3. Les spécifications pour les versions 3.4 et 5.0 sont disponibles sur le site du SMS Forum.
D’un point de vue technique, SMPP est un protocole binaire de niveau 7 (couche application) et s’appuie sur un transport basé sur X25 ou TCP/IP. C’est un protocole où l’initiative d’un échange peut être prise soit au niveau du SMS-C (envoi d’un SMS vers le service qui se traduit par l’envoi d’une requête SMPP depuis le SMS-C vers ce service) ou au niveau du service (le service envoie une requête SMPP vers le SMS-C). SMPP est également asynchrone : le service peut envoyer (ou recevoir) plusieurs SMS avant de recevoir (envoyer) les acquittements. Les deux entités, SMS-C et service, doivent donc agir comme des serveurs. L’implémentation directe par une application d’une couche SMPP peut par conséquent s’avérer assez complexe. La bibliothèque Java que nous allons utiliser cache autant que possible cette difficulté.
Le schéma de fonctionnement typique d’une application est le suivant : l’application se connecte sur le SMS-C et les échanges de paquets (dans le langage réseau, on parle de Packet Data Unit, PDU) SMPP peuvent commencer dans un sens comme dans l’autre. Il y a en réalité trois types de connexions possibles qu’il faut choisir en fonction des besoins du service. Une connexion de type transmitter (TX) ne permet à l’application que d’envoyer des messages au SMS-C. On utilisera ce type de connexion pour des services d’alerte ou de notification. Une connexion de type receiver (RX) ne donnera à l’application que la possibilité de recevoir des SMS. Elle pourra être utilisée pour des services de type gatewaying. Finalement, il existe un mode mixte, transceiver (TRX), où l’application peut envoyer et recevoir des SMS.
SMPP est composé d’un peu moins d’une trentaine d’opérations et à chacune d’entre elles est associé un PDU. Ceux-ci sont composés d’un en-tête suivi d’un corps dont le contenu dépend du type de l’opération. Les opérations implémentées par les PDU sont de type connexion, déconnexion, envoi de SMS, réception de SMS, envoi en masse, acquittements, etc. A chaque requête doit correspondre un acquittement. Nous n’entrerons pas plus dans les détails des PDU SMPP : la bibliothèque que nous utiliserons définit des objets pour chacun qu’entre eux et gère leur encodage. La norme complète est disponible sur [4]
Pour terminer cette discussion sur SMPP, analysons un échange de PDU entre une application et un SMS-C. Cet échange est illustré figure 2.

Figure 2 : Exemple d’échanges de PDU SMPP entre une application et le SMS-C
- Pour commencer, l’application établit la connexion réseau sur le SMS-C en (1). Ceci n’est pas une opération SMPP, mais tout simplement l’ouverture d’une socket vers un serveur.
- L’application s’enregistre sur le SMS-C en tant que transceiver lui permettant d’envoyer et de recevoir des SMS (2). L’acquittement est envoyé en (3).
- En (4) et en (5) l’application envoie deux SMS. L’acquittement pour le SMS 2 est reçu en (6).
- L’application reçoit un SMS du SMS-C (7) et renvoie aussitôt l’acquittement en (8).
- L’application reçoit en (9) l’acquittement pour l’envoi du SMS 2 qui a pu être retardé.
- Finalement, en (10), l’application se déconnecte du SMS-C et reçoit en (11) l’acquittement de cette demande.
Faire fonctionner une application SMS
Avant de nous lancer dans le développement, voyons comment faire fonctionner un service. Pour résumer ce qui précède, nous avons besoin : d’un SMS-C, d’un MSC, d’un VLR, d’un HLR, d’un AUC, d’un BCS, d’une BTS et sans doute de quelques éléments supplémentaires que j’aurais oubliés... Heureusement, les choses sont plus simples. La première solution est de négocier (commercialement), avec un opérateur qui possède toute l’infrastructure, la mise à disposition d’un compte (lien réseau vers son SMS-C et droits d’accès). Les opérateurs télécom ne sont intéressés par ce type d’opération que si le trafic SMS engendré est important : plusieurs dizaines voire centaines de milliers de SMS par jour. La deuxième solution est de se rapprocher d’un broker de SMS qui, lui, aura négocié avec des opérateurs télécom des volumes importants et qui vous revendra l’envoi ou la réception de SMS à la dizaine, à la centaine, au millier, etc. On en trouve de très nombreux en effectuant une recherche sur le web. La plupart de ces brokers ne permettent malheureusement que l’envoi et offrent des interfaces propriétaires souvent basées sur HTTP (HTTP pur, XML sur HTTP, etc.). Pour un fonctionnement plus "artisanal" (rien de péjoratif dans cet adjectif), on peut utiliser Kannel [5] qui peut agir en tant que SMS-C une fois couplé à un modem GSM, mais cette direction n’a pas été creusée.
Développement de services SMS
Les outils nécessaires au développement
Même si vous avez accès à un SMS-C, aucun opérateur ne vous laissera faire la mise au point de votre application directement sur sa plate-forme. Par conséquent, il est nécessaire d’utiliser un simulateur qui permettra en plus de faire des tests plus poussés que sur un SMS-C en production. En recherchant sur le WEB, on trouve facilement deux simulateurs : celui de LogicaCMG [6], sous licence Logica Open Source License v1.0, et "SMPPSim" de Selenium Software [7], sous licence GLP v2. Pour nos expérimentations, nous utiliserons ce dernier. Outre la simulation d’un SMS-C, SMPPSim permet également de simuler l’envoi de SMS à partir d’un terminal. Ceci est réalisé par une interface Web.
L’installation de SMPPSim est très facile. Téléchargez SMPPSim.tar.gz et décompressez-le dans un répertoire. La racine contient le fichier startsmppsim.sh qui permet de lancer le simulateur. Le répertoire conf contient le fichier smppsim.props qui permet de configurer le simulateur. Il y a de nombreuses options, les principales étant :
# Le port d’écoute SMPP SMPP_PORT=2775 # login et mot de passe pour les accès SMPP SYSTEM_ID=smppclient PASSWORD=password # Le port d’écoute du serveur web pour l’administration et la simulation # d’envoi à partir d’un terminal HTTP_PORT=88
Comme expliqué dans le paragraphe consacré à SMPP, il est préférable d’utiliser une API capable de gérer les aspects de codage et de décodage des PDU, mais également l’asynchronisme inhérent au protocole. Là aussi, une rapide recherche donne deux résultats : la "Java SMPP Library de Logica" [6] et "smppapi" que nous utiliserons. smppapi est disponible sur Sourceforge en LGPL (voir [8]).
Qu’il s’agisse de SMPPSim ou smppapi, les deux fonctionnent sous Java. La dernière version de SMMPSim requiert Java 1.5. smppapi utilise Log4J et Commons-Logging que nous inclurons dans les jars exécutables ou dans le CLASSPATH. Finalement, Ant nous aidera dans les compilations.
Prise en main de smppapi
La meilleure manière de prendre en main une API est de l’utiliser. Nous commencerons donc par un exemple très simple : un programme en ligne de commande qui enverra un message à un abonné. Bien entendu, puisque le SMS-C est un simulateur, aucun SMS réel ne partira. Notre programme, smssender, sera appelé de la manière suivante : java -jar smssender.jar 0123456789 Un petit message.
Le programme sera divisé en deux fichiers. SMSSender.java définit la classe SMSSender. Le constructeur positionne les paramètres de connexion. open() ouvrira la connexion, close() la fermera. Finalement, sendMessage() enverra le message au destinataire. Le code dans Main.java analyse la ligne de commande, crée un objet SMSSender et appelle les méthodes pour réaliser l’envoi.
Le source complet de SMSSender est présenté ci-dessous. Nous l’analyserons morceau par morceau dans les paragraphes suivants.
01:package org.buguigny.smssender;
02:
06:import ie.omk.smpp.message.SubmitSM;
07:import ie.omk.smpp.Connection;
08:import ie.omk.smpp.message.BindResp;
09:import ie.omk.smpp.message.SMPPPacket;
10:import ie.omk.smpp.Address;
11:import ie.omk.smpp.message.SMPPResponse;
14:import ie.omk.smpp.message.UnbindResp;
16:import org.apache.commons.logging.Log;
17:import org.apache.commons.logging.LogFactory;
18:
19:public class SMSSender {
20:
21: // l’objet Connection qui va gérer les transactions réseau
22: private Connection cnx;
23: // Le nom du serveur qui héberge le SMS-C
24: private String host;
25: // Le port TCP/IP sur lequel le SMS-C écoute les connexions SMPP
26: private int port;
27: // Le login et le mot de passe pour se connecter au SMS-C
28: private String uname;
29: private String passwd;
30: // Un logguer pour des traces propres
31: private static Log log = LogFactory.getLog(SMSSender.class);
32:
33: // constructeur passif: initialisation des membres
34: public SMSSender(String host, int port, String uname, String passwd) {
35: cnx = null;
36: this.host = host;
37: this.port = port;
38: this.uname = uname;
39: this.passwd = passwd;
40: }
41:
42: // Connexion au SMS-C
43: public boolean open() {
44: try {
45: cnx = new Connection(host, port, false);
46: BindResp resp = cnx.bind(Connection.TRANSMITTER, uname, passwd, "SMSSender");
47: if(resp.getCommandStatus() != 0) return false;
48: }
49: catch(Exception e) {
50: log.fatal(“Problème lors du open(): "+e);
51: return false;
52: }
53: return true;
54: }
55: // Déconnexion du SMS-C
56: public void close() {
57: try {
58: UnbindResp resp = cnx.unbind();
59: }
60: catch(Exception e) {
61: log.error(“Problème dans close(): "+e);
62: }
63: }
64:
65: // Envoi de message
66: public boolean sendMessage(String srcNO, String dstNO, String mess) {
67: SubmitSM sm = null;
68: try {
69: sm = (SubmitSM)cnx.newInstance(SMPPPacket.SUBMIT_SM);
70: }
71: catch(Exception e) {
72: log.fatal(“Problème à la création de SubmitSM: "+e);
73: return false;
74: }
75:
76: Address dst = new Address(0, 0, dstNO);
77: Address src = new Address(0, 0, srcNO);
78: sm.setDestination(dst);
79: sm.setSource(src);
80: sm.setMessageText(mess);
81: SMPPResponse resp = null;
82: try {
83: resp = cnx.sendRequest(sm);
84: }
85: catch(Exception e) {
86: log.error(“Problème à l’envoi du message: "+e);
87: return false;
88: }
89:
90: if(resp.getCommandStatus() != 0) {
91: log.error(“Ne peut pas envoyer le message.”);
92: return false;
93: }
94: return true;
95: }
96:}
Connexion au SMS-C: méthode open()
La méthode open() est définie à partir de la ligne 42. La première opération (ligne 45) met en place une connexion au niveau réseau sur le SMS-C. Les deux premiers paramètres du constructeur de l’objet Connection sont le nom ou l’adresse IP du SMS-C (localhost pour nous) et le port. Le port doit avoir la valeur que nous avons fixée dans le fichier de paramètres, 2775. Le troisième paramètre indique à l’API si elle doit traiter les messages de manière synchrone (false) ou asynchrone (true). Puisque notre application n’envoie qu’un seul SMS, la connexion sera synchrone. Le second point est d’enregistrer l’application auprès du SMS-C en précisant dans quel sens les SMS vont être échangés (TRANSMITTER dans le sens service vers SMS-C), les accréditations (login et mot de passe comme indiqués dans le fichier de configuration) et finalement le nom du service qui se connecte. La création de la Connection ou l’appel à bind() peuvent lancer des exceptions : mauvais nom de host, mauvais mot de passe, etc. Pour alléger le source, toutes ces exceptions sont attrapées par un catch(Exception e) global. L’appel à bind() envoie un PDU au SMS-C qui renvoie un acquittement sous forme de PDU représenté par un objet de la classe BindResp. La méthode getCommandStatus() renvoie un entier non nul si une erreur est survenue et zéro sinon. Notons finalement qu’un bind() résulte en une négociation entre l’application et le SMS-C sur la version du protocole à utiliser, sur les éventuels paramètres optionnels acceptés, etc. Le résultat de cette négociation est mémorisé par l’objet Connection et servira à la gestion d’erreurs lors de la création de PDU, comme nous le verrons plus loin.
Si on lance le programme et si on regarde les traces côté simulateur, on voit, entre autres, les lignes suivantes (pour plus de lisibilité, celles-ci ont été tronquées de la date ainsi que de la criticité ; des numéros de lignes ont par contre été rajoutés, de même que dans le code au-dessus) :
01: : BIND_TRANSMITTER: 02: Hex dump (50) bytes: 03: 00000032:00000002:00000000:00000001: 04: 736D7070:636C6965:6E740070:61737377: 05: 6F726400:534D5353:656E6465:72003400: 06: 0000 07: cmd_len=50,cmd_id=2,cmd_status=0,seq_no=1,system_id=smppclient 08: password=password,system_type=SMSSender,interface_version=52,addr_ton=0 09: addr_npi=0,address_range= 10: 11: New transmitter session bound to SMPPSim 12: : BIND_TRANSMITTER_RESP: 13: Hex dump (24) bytes: 14: 00000018:80000002:00000000:00000001: 15: 534D5050:53696D00: 16: cmd_len=0,cmd_id=-2147483646,cmd_status=0,seq_no=1,system_id=SMPPSim
La ligne 1 affiche le nom du PDU reçu et les lignes suivantes le dump hexadécimal (3 à 6) et les champs (lignes 7 à 9) de ce PDU. Nous n’entrerons pas dans le détail des composants du PDU. Comme indiqué précédemment, toute requête doit entraîner une réponse dont le PDU est dumpé dans les lignes 12 à 16.
Déconnexion du SMS-C : méthode close()
La déconnexion du SMS-C est réalisée très simplement par l’appel de la méthode cnx.unbind(); où cnx est la Connection initialisée dans la méthode open.
Envoi d’un SMS: méthode sendMessage()
La méthode sendMessage prend trois paramètres. srcNO est le numéro de l’abonné qui envoie le message et qui sera transmis tel quel au destinataire. Dans notre cas, il s’agit de l’application. La validité de ce numéro n’est pas vérifiée si bien qu’il est même possible d’y mettre une chaîne de caractères. dstNO est le numéro de téléphone du destinataire. Pour que le SMS soit acheminé correctement, il est évident que ce numéro doit d’une part être correct et attribué à un abonné valide. Finalement, mess est le message à transmettre. L’envoi de message est réalisé en trois étapes : création du PDU d’envoi, initialisation de certains membres du PDU, puis finalement envoi avec vérification de l’acquittement. Les messages doivent avoir une longueur de 160 caractères au maximum et sont encodés sur 7 bits, ce qui interdit l’utilisation d’accents. D’autres méthodes d’encodage sont possibles (8 ou 16 bits), au prix de la longueur du message.
La création de tous les PDU doit être réalisée par la méthode newInstance de la classe Connection qui initialisera, en fonction de la connexion ouverte, le numéro de séquence du paquet, ainsi que la version de SMPP utilisée (smppapi sait gérer SMPP 3.3 ou 3.4). newInstance prend en paramètre un entier qui identifie le type de PDU à créer. Les entiers à passer en paramètre sont à choisir parmi les constantes définies à cet effet dans la classe SMPPPacket. Ainsi sm = (SubmitSM)cnx.newInstance(SMPPPacket.SUBMIT_SM); crée un PDU de type SUMBIT_SM. newInstance peut lancer deux types d’exception : BadCommandIDException si l’entier passé à newInstance ne correspond à aucun type de PDU et VersionException si le PDU demandé n’est pas défini par la version de SMPP gérée par cette connexion.
Les membres du PDU SUMBIT_SM à initialiser sont les numéros de l’expéditeur, celui du destinataire et le message à proprement parler. Les numéros de téléphone sont gérés pas les objets de la classe Address. La création d’un objet Address requiert trois informations : le Type Of Number (ou TON), le Numbering Plan Indicator (ou NPI), et enfin le numéro du destinataire à proprement parler. Le TON et le NPI sont définis dans des normes internationales (voir [9], page 81, la norme Q931 de L’Union Internationale des Télécommunications, ITU) et ont pour objectif d’aider le MSC au routage du message. Le TON spécifie si le numéro est un numéro national, international, court, etc. et peut prendre les valeurs suivantes : 0, 1, 2, 3, 4, 6 et 7. "0" représente la valeur "inconnu". Le NPI spécifie le type de plan de numérotation, par exemple, appel voix, appel télex, etc. et prend les valeurs suivantes : 0, 1, 3, 4, 8, 9, 15. Comme précédemment, "0" représente la valeur "inconnu". Ces constantes ne sont pas définies dans la bibliothèque (et donc non vérifiées), si bien que la valeur qui sera indiquée dans le constructeur sera insérée dans le PDU et transmise au SMS-C, puis au MSC avec de possibles conséquences négatives sur le routage au pire, ou un acquittement négatif au mieux. Nous utiliserons "0", c’est-à-dire "inconnu" pour ces deux valeurs. Le troisième paramètre du constructeur est le numéro de téléphone. L’initialisation du PDU SUBMIT_SM est réalisée lignes 76 à 80.
L’envoi du message se fait lignes 81 à 93. La méthode sendRequest de la classe Connection envoie les PDU de tout type, lance des exceptions en cas de problème et renvoie un acquittement en mode synchrone ou null en mode asynchrone (nous y reviendrons).
Analyse des traces côté SMS-C
Les traces correspondant à l’envoi du message avec la ligne de commande java -jar smssender.jar 0612345678 Ceci est un sms sont présentées ci-après. On observe sur le dump (lignes 7 à 12) que de nombreux champs additionnels ont été initialisés à des valeurs par défaut. Le dernier, short_message contient le message que nous souhaitons envoyer.
01: INFO : Standard SUBMIT_SM: 02: INFO Hex dump (61) bytes: 03: INFO 0000003D:00000004:00000000:00000002: 04: INFO 00000032:35310000:00303631:32333435: 05: INFO 36373800:00000000:00000000:000F4365: 06: INFO 63692065:73742075:6E20736D:73 07: INFO cmd_len=61,cmd_id=4,cmd_status=0,seq_no=2,service_type=,source_addr_ton=0 08: INFO source_addr_npi=0,source_addr=251,dest_addr_ton=0,dest_addr_npi=0 09: INFO dest_addr=0612345678,esm_class=0,protocol_ID=0,priority_flag=0 10: INFO schedule_delivery_time=,validity_period=,registered_delivery_flag=0 11: INFO replace_if_present_flag=0,data_coding=0,sm_default_msg_id=0,sm_length=15 12: INFO short_message=Ceci est un sms 13: INFO 14: WARNING Validity period is not set: defaulting to 5 minutes from now 15: INFO Generated default validity period=061116110122000+
Mode asynchrone
Nous l’avons dit précédemment, SMPP est par nature asynchrone. L’asynchronisme permet d’améliorer les performances de l’application, mais complique le code. La bibliothèque smppapi nous fournit les outils pour le gérer.
Fonctionnement de l’API en mode asynchrone
En mode asynchrone, l’application doit pouvoir répondre aux évènements au fur et à mesure qu’ils sont reçus. Lors de la création d’une Connection, l’API crée un thread qui effectuera les opérations d’échange de PDU avec le SMS-C. Lors de la réception d’un PDU, ce thread doit fournir le PDU à l’application. Une façon élégante de procéder est d’utiliser le pattern Observer et d’implémenter dans l’application l’interface ConnectionObserver. Celle-ci définit deux méthodes qui doivent être implémentées dans le service et qui sont appelées par l’API :
public void update(Connection cnx, SMPPEvent evt)est appelée lorsque l’API souhaite communiquer au service des informations sur l’état du thread d’écoute. La méthodeint getType()de la classeSMPPEventrenvoie l’une des trois constantes suivantes :RECEIVER_EXCEPTION,RECEIVER_EXITouRECEIVER_START, respectivement lorsque le thread d’écoute reçoit une exception non fatale, c’est-à-dire lorsque l’exception ne le fait pas se terminer, lorsqu’il se termine normalement ou du fait d’une exception fatale ou lorsqu’il démarre.public void packetReceived(Connection cnx, SMPPPacket evt)est appelée lorsqu’un paquet est reçu sur la connexion réseau. La classeSMPPPacketdéfinit des constantes pour l’ensemble des PDU susceptibles d’être reçus (par exempleDELIVER_SM,ENQUIRE_LINK,BIND_TRANSCEIVER_RESP, etc.) qui sont renvoyées par la méthodeint getCommandId().
Squelette d’un service asynchrone
Le squelette d’un service fonctionnant en mode asynchrone est présenté ci-dessous.
01:package org.buguigny.asynchrone1;
02:// les imports nécessaires: import ie.omk.smpp. ...
03:
04:public class SMSService extends Thread implements ConnectionObserver {
05: // Membres privés: Connection, host, port, login, mdp, etc.
06: // private static SMSService instance = null; // pour la gestion du singleton
07:
08: // Un constructeur qui initialise les membres.
09: // Privé pour un fonctionnement en singleton
10: private SMSService(String ht, int pt, String un, String pd) {
11: // ...
12: }
13:
14: // Création d’une instance unique de ce service via le constructeur privé
15: public static SMSService getInstance(String ht, int pt, String un, String pd) {
16: if(instance == null)
17: instance = new SMSService(...);
18: return instance;
19: }
20:
21: // Démarrage du thread du service. Cette méthode initialisera la
22: // connexion au SMS-C, endormira le thread du service jusqu’à la fin du programme.
23: // Les méthodes du service seront exécutées par un thread de l’API
24: public void run() {
25: // cnx = new Connection(..., true);
26: // cnx.addObserver(this)
27: // cnx.bind(...);
28: // wait();
29: }
30:
31: // traitement des évènements reçus de l’api
32: public void update(Connection cnx, SMPPEvent event) {
33: // switch(event.getType()) {
34: // case SMPPEvent.RECEIVER_EXIT: ...; break
35: // case SMPPEvent.RECEIVER_EXCEPTION : ...; break
36: // case SMPPEvent.RECEIVER_START : ...; break
37: // }
38: }
39:
40: // traitement des paquets SMPP reçus depuis le SMS-C
41: public void packetReceived(Connection conn, SMPPPacket pdu) {
42: // switch(pdu.getCommandId()) {
43: // case SMPPPacket.DELIVER_SM: ...; break;
44: // case SMPPPacket.BIND_TRANSCEIVER_RESP: ...; break;
45: // case ... : ...; break;
46: // }
47: }
48:}
On voit ligne 4 que SMSService sera un thread. Ceci permet d’isoler le service du thread principal de l’application afin d’en faciliter la gestion, dans le cas où il y aurait plusieurs services dans une même application ou pour la gestion de la perte de connexion avec le SMS-C.
Le deuxième point à noter est que notre service est un singleton : le constructeur est privé (ligne 10) et la création d’une instance est réalisée par la méthode SMSService getInstance(...) (lignes 15 à 19). La raison en est la suivante. Si on pouvait créer plusieurs instances de SMSService, on établirait plusieurs connexions avec les mêmes accréditations au SMS-C. Il n’est pas clairement spécifié dans la norme comment le SMS-C doit gérer plusieurs connexions du même service, mais lors des différentes expériences effectuées, on s’aperçoit que lors de l’envoi d’un SMS par un client, les services sont invoqués à tour de rôle (round robin), ce qui peut compliquer la gestion des données si celles-ci doivent être partagées entre plusieurs requêtes (par exemple pour un suivi de session). Il est par ailleurs possible qu’un SMS-C donné interdise plusieurs connexions ou, au contraire, qu’il envoie les requêtes simultanément sur l’ensemble des connexions.
La méthode run() implémente la réalisation de la connexion au SMS-C. La ligne 26 enregistre notre service auprès de la connexion afin que celle-ci puisse invoquer les méthodes update() et packetReceived de l’observer. Une bonne manière de procéder serait de "sortir" le code correspondant à la gestion de la connexion dans une méthode annexe afin de gérer les ruptures de connexion avec le SMS-C (voir dans l’exemple complet asynchrone1). run() endort finalement le thread du service : les méthodes de l’observer seront invoquées par un thread de l’API.
Les évènements relatifs à l’état de la connexion sont communiqués à l’application par la méthode update() (lignes 32 à 37). C’est notamment sur réception d’un RECEIVER_EXIT qu’on pourra débloquer le thread de service (en appelant la méthode notify()) pour sortir de l’application ou pour tenter une reconnexion.
Finalement, toute la logique de service est implémentée dans la méthode packetReceived(). A chaque fois qu’un PDU est reçu par la connexion, un SMPPPacket est généré et communiqué au service qui doit agir en conséquence. Il ne faut pas oublier de renvoyer des PDU de réponse à chaque requête envoyée par le SMS-C. Dans le cas contraire, on risque soit de bloquer le SMS-C qui serait en attente de trop d’acquittements, soit de provoquer l’envoi d’acquittements négatifs par le SMS-C. Un PDU dont nous n’avons pas encore parlé est ENQUIRE_LINK qui peut être envoyé après une certaine période d’inactivité, le plus souvent par le SMS-C, pour vérifier que l’entité à l’autre bout de la connexion est toujours en vie. L’expéditeur s’attend alors à recevoir un ENQUIRE_LINK_RESP qui, s’il n’est pas reçu, peut le conduire à fermer la connexion. L’API permet d’automatiser l’envoi des réponses à ENQUIRE_LINK si on invoque autoAckLink(true); sur l’objet connexion. C’est d’ailleurs le paramétrage par défaut. Il est également possible de demander à l’API d’acquitter automatiquement l’ensemble des requêtes (appel de autoAckMessages(true);), mais ce n’est pas conseillé. Nous le ferons cependant pour plus de simplicité.
Traces de fonctionnement d’un service asynchrone
Le répertoire dev/asynchrone1 contient un exemple d’implémentation de ce squelette. Avant d’analyser son fonctionnement, un petit mot sur le simulateur que nous utilisons. Pour simuler l’envoi d’un SMS par un client, SMPPSim fournit une interface web accessible par l’URL http://localhost:88/inject_mo.htm. Un screenshot est présenté, sur la figure 3. L’interface offre la possibilité de saisir le texte d’un SMS, les numéros de l’expéditeur et du destinataire. D’autres champs peuvent également être saisis pour être ajoutés au PDU envoyé, mais ne sont pas nécessaires pour notre exemple. Un click sur Submit Message provoque l’envoi du SMS vers l’application.

Figure 3 : Interface de simulation d’envoi de SMS à partir d’un terminal
Si on lance asynchrone1.jar et qu’on simule l’envoi d’un SMS, on obtient les traces suivantes :
01: DEBUG [main][org.buguigny.asynchrone1.SMSService] getInstance() 02: DEBUG [main][org.buguigny.asynchrone1.SMSService] SMSService() 03: DEBUG [Thread-0][org.buguigny.asynchrone1.SMSService] run() 04: DEBUG [Thread-0][org.buguigny.asynchrone1.SMSService] connect() 05: INFO [Thread-0][org.buguigny.asynchrone1.SMSService] Connection au host localhost:2775 06: INFO [Thread-0][ie.omk.smpp.Connection] Creating receiver thread 07: INFO [Thread-0][org.buguigny.asynchrone1.SMSService] Connection au host localhost:2775 OK 08: INFO [Thread-0][org.buguigny.asynchrone1.SMSService] Binding au SMS-C (localhost:2775). User: smppclient 09: INFO [Thread-0][ie.omk.smpp.Connection] Binding to the SMSC as type 3 10: INFO [Thread-0][ie.omk.smpp.Connection] Opening network link. 11: INFO [Thread-0][ie.omk.smpp.net.TcpLink] Opening TCP socket to localhost/127.0.0.1:2775 12: INFO [Thread-0][ie.omk.smpp.Connection] Setting state 1 13: INFO [Thread-0][org.buguigny.asynchrone1.SMSService] Bind au SMS-C (localhost:2775). User: smppclient OK 14: INFO [ReceiverDaemon][ie.omk.smpp.Connection] Receiver thread started 15: INFO [ReceiverDaemon][org.buguigny.asynchrone1.SMSService] Reçu evenement: RECEIVER_START: ie.omk.smpp.event.ReceiverStartEvent@a3736 16: INFO [ReceiverDaemon][ie.omk.smpp.Connection] Setting state 2 17: WARN [ReceiverDaemon][ie.omk.smpp.Connection] Disabling optional parameter support as no sc_interface_version parameter was received 18: INFO [ReceiverDaemon][ie.omk.smpp.Connection] Notifying observers of packet received 19: INFO [ReceiverDaemon][org.buguigny.asynchrone1.SMSService] Reçu Message d’acquittement au Bind 20: INFO [ReceiverDaemon][ie.omk.smpp.Connection] deliver_sm_resp sent. 21: INFO [ReceiverDaemon][ie.omk.smpp.Connection] Notifying observers of packet received 22: INFO [ReceiverDaemon][org.buguigny.asynchrone1.SMSService] Reçu Message SMS: ‘test de message’
Les traces sont obtenues par Log4J. Nous verrons plus tard où et comment les paramètres de trace sont positionnés. Les informations tracées sont la criticité, le nom du thread, la classe et un message. L’initialisation de l’application est faite entre les lignes 1 à 19 et l’envoi du SMS entre les lignes 20 et 22. La création du service est faite dans les lignes 1 et 2 par le thread main qui donne ensuite la main au thread Thread-0 qui correspond à notre service. Dans les lignes 3 à 13, on peut suivre la création de la connexion que ce soit au niveau de l’API (traces des classes ie.omk.smpp.*) qu’au niveau de l’application (classe org.buguigny.asynchrone1.SMSService). Le thread Thread-0 est finalement endormi dans la méthode run() et un thread de l’API, ReceiverDaemon, prend ensuite la main pour gérer la connexion et notifier au service des différents évènements. Ainsi, lorsqu’elle reçoit un message, la connexion (et donc ReceiverDaemon) informe l’ensemble des observers qui se sont enregistrés (nous n’en avons qu’un seul, SMSService) en appelant packetReceived() (lignes 18, 19 et 21, 22). Notons que nous avons positionné l’acquittement à toutes les requêtes à vrai. Ligne 20 correspond à l’acquittement automatique du PDU DELIVER_SM.
Faisons une expérience. Dans un mode de fonctionnement normal, le SMS-C tourne en permanence, l’application s’y connecte et les échanges de messages peuvent commencer. Lançons le simulateur dans une fenêtre et l’application dans une autre. Tuons ensuite le simulateur (Control-C), ce qui dans un environnement de production correspondrait au plantage du SMS-C. On verrait sur les traces que le thread Thread-0 est réveillé et que la phase de connexion recommence. On observerait un comportement semblable si on tentait de lancer l’application alors que le simulateur n’était pas encore lancé. Ce comportement n’est pas pris en charge par l’API, mais par l’application elle-même.
Faisons une autre expérience. Pour simuler un certain temps de traitement lors de la réception d’un SMS (par exemple un accès à une base de données), la gestion du PDU DELIVER_SM dans le service met en place une boucle d’attente de 0 à 10000 (voir fichier SMSService.java, méthode packetReceived). Pour simuler une forte charge sur le SMS-C, lancez le simulateur, envoyez quelques SMS (une dizaine) par l’interface web du simulateur, puis, seulement après, lancez l’application. Ci-dessous sont données les traces avec l’information sur l’heure d’exécution.
08:05:25,944 INFO [ReceiverDaemon][org.buguigny.asynchrone1.SMSService] Reçu Message ... 08:05:26,945 INFO [ReceiverDaemon][org.buguigny.asynchrone1.SMSService] Reçu Message ... 08:05:27,947 INFO [ReceiverDaemon][org.buguigny.asynchrone1.SMSService] Reçu Message ... 08:05:28,948 INFO [ReceiverDaemon][org.buguigny.asynchrone1.SMSService] Reçu Message ... 08:05:29,970 INFO [ReceiverDaemon][org.buguigny.asynchrone1.SMSService] Reçu Message ...
On observe que c’est toujours le même thread, ReceiverDaemon, qui exécute la méthode de notification au service. L’API fonctionne dans un mode asynchrone, mais ne fait tourner qu’un seul thread ! Le ralentissement de traitement est du à la boucle de simulation de charge dans le service. Il est évident que dans un environnement de production où on peut être amené à gérer 10, 50, voire plus de 1000 SMS par seconde, ce type de fonctionnement n’est pas acceptable.
L’API nous offre une solution à ce problème. Dans son architecture interne, elle définit deux classes pour la distribution des évènements : SimpleEventDispatcher et ThreadedEventDispatcher. SimpleEventDispatcher est monothreadé. Il est utilisé par défaut. ThreadedEventDispatcher est quant à lui multithreadé, mais, attention, la documentation nous informe qu’il est hautement expérimental. Lors de son initialisation, ThreadedEventDispatcher crée un pool de threads et pour chaque évènement à notifier, un thread différent invoque packetReceived ou update(). La mise en place de ce dispatcher ainsi que son paramétrage sont réalisés dans le fichier de configuration smppapi.properties que nous verrons plus bas. Pour terminer, voici les traces obtenues avec le ThreadedEventDispatcher où on voit que l’ensemble des requêtes est traité dans la même fraction de seconde.
08:36:31,897 INFO [EventDispatch2][org.buguigny.asynchrone1.SMSService] Reçu Message ... 08:36:31,897 INFO [EventDispatch3][org.buguigny.asynchrone1.SMSService] Reçu Message ... 08:36:31,897 INFO [EventDispatch4][org.buguigny.asynchrone1.SMSService] Reçu Message ... 08:36:31,897 INFO [EventDispatch5][org.buguigny.asynchrone1.SMSService] Reçu Message ... 08:36:31,907 INFO [EventDispatch6][org.buguigny.asynchrone1.SMSService] Reçu Message ...
Fichiers de configuration
Pour fonctionner, smppapi a besoin de deux fichiers de configuration. Le premier, log4j.properties, définit les paramètres des traces de l’API. On peut également y ajouter les paramètres de trace du service (voir fichier dev/asynchrone1/src/etc/log4j.properties). Le second fichier, smppapi.properties, définit les paramètres de fonctionnement de l’API elle-même. Ces paramètres sont documentés dans le javadoc de la classe APIConfig. Les principaux sont (voir le fichier dev/asynchrone1/src/etc/amppapi.properties pour un exemple complet) :
smppapi.net.buffersize_inetsmppapi.net.buffersize_outspécifient les tailles des buffers d’entrées/sorties. La valeur peut être spécifiée en octets (i. e. : 4096 pour 4Ko) ou en kilo-octets (i. e. : 4k).smppapi.net.autoflushspécifie si un flush doit être envoyé à chaque fois qu’un paquet est envoyé. La valeur esttrueoufalse.smppapi.connection.rcv_daemon.ioex_countspécifie le nombre d’exceptions d’entrées/sorties après lequel la connexion enverra un évènementRECEIVER_EXIT.smppapi.event.dispatcherpositionne le type d’event dispatcher à employer. La valeur peut êtreSimpleEventDispatcherouThreadedEventDispatcher(ou un dispatcher de votre cru...). Dans le cas duThreadedEventDispatcher, deux paramètres additionnels doivent être positionnés :smppapi.event.threaded_dispatcher.pool_sizedéfinit la taille du pool de threads.smppapi.event.threaded_dispatcher.queue_sizedéfinit la taille de la file FILO du dispatcher.
Développement avancé
La gestion en mode asynchrone présentée dans les sections précédentes, bien que simple, n’est pas très élégante, car elle mélange les aspects techniques (le switch avec les différents évènements internes ou réseau, la gestion des reconnexions) avec des aspects fonctionnels. L’API nous fournit une solution qui permet de séparer la logique métier de la logique technique. Nous verrons dans cette section la solution proposée et nous l’utiliserons sur un exemple un peu plus complexe.
Description du service SMS2Mail
Le service SMS2Mail permet à un abonné A d’envoyer un mail à un abonné B via le service SMS. Le fonctionnement est le suivant : A envoie un SMS à un numéro court de l’opérateur. Le SMS devra avoir le format suivant : <no de tel de B><espace><message>. Si A a souscrit au service (ce qui suppose que l’opérateur connaît son adresse mail), si le numéro de B est correct (le numéro est syntaxiquement correct et l’abonné existe) et si B a souscrit également au service (on connaît également son adresse), SMS2Mail enverra le mail contenant <message> à B et enverra un SMS de confirmation à A. Dans le cas contraire, le service doit envoyer un SMS d’erreur à A. Les sources complets de ce service se trouvent dans le répertoire sms2mail.
D’un point de vue technique, le service doit être capable de recevoir et d’envoyer des SMS. La connexion devra donc fonctionner en mode transceiver. Par ailleurs, l’analyse de la structure du SMS, mais surtout l’envoi de mail ne sont pas immédiats : pour traiter plusieurs SMS en parallèle, il est grandement préférable d’utiliser le dispatcher multithreadé. Finalement, la logique métier décrite ci-dessus devra être implémentée dans la partie gérant l’évènement DELIVER_SM, puisque le service est déclenché sur réception de SMS. Si on applique le template de squelette présenté auparavant, la classe SMSService contiendrait la gestion des connexions et reconnexions, le switch de gestion des évènements et, en plein milieu du switch, la logique métier, ce qui n’est optimal ni pour la mise au point du service ni pour sa maintenance future. La solution consisterait à définir une classe ServiceLogic dont des méthodes spécifiques seraient appelées en fonction des évènements reçus. Ainsi, notre logique métier pourrait être isolée dans une méthode qui pourrait s’appeler traiteReceptionDELIVER_SM. C’est à peu de chose près la solution proposée par l’API.
La classe SMPPEventAdapter
La classe SMPPEventAdapter permet une gestion plus simple des évènements produits par l’API, qu’il s’agisse de d’évènements internes, traités dans l’observer dans update(), ou d’évènements réseau, traités par packetReceived(). Elle définit une méthode par type d’évènement reçu. Ces méthodes ont une signature générique, void nomMethode(Connection cnx, <ObjetEvenement> evt) et sont vides par défaut. Ainsi, la méthode appelée lors de la réception d’un paquet DELIVER_SM est public void deliverSM(Connection cnx DeliverSM evt), où DeliverSM est un objet représentant le paquet reçu sur lequel on pourra appliquer les méthodes evt.getSource().getAddress(), evt.getDestination().getAddress() et evt.getMessageText() pour récupérer respectivement l’adresse (i. e. : le numéro de téléphone) de l’expéditeur, celle du destinataire et le texte du message SMS envoyé. Pour mettre en place ce mécanisme, il faut définir une sous-classe de SMPPEventAdapter et surcharger les méthodes correspondantes aux évènements qu’on souhaite traiter. Pour terminer, notons que SMPPEventAdapter implémente l’interface ConnectionObserver, mais les méthodes update() et packetReceived() sont non surchargeables, car déclarées en final. Elles sont chargées d’invoquer les méthodes de gestion des évènements.
Structure du service SMS2Mail
Avec tous ces éléments en main, notre service sera implémenté de la manière suivante. Nous allons implémenter dans la classe SMS2MailService les aspects purement techniques : création de la connexion et gestion des pertes du lien. La classe SMS2MailServiceLogic, sous-classe de SMPPEventAdapter, se situe entre le technique et le fonctionnel purs et est responsable de la réception et de l’envoi de SMS. Finalement, MailSender est en charge de la logique métier à proprement parler.
SMS2MailService est très proche de la classe SMSService que nous avons définie dans le squelette asynchrone1. La différence principale est que ce n’est plus SMSService qui est l’observer, mais SMS2MailServiceLogic. Comme précédemment, SMS2MailService est un thread qui reste bloqué dans la méthode run() en attente d’être réveillée en cas de perte de connexion. La perte de connexion sera détectée par l’API et le service en sera informé par l’invocation des méthodes receiverExit() et receiverExitException(). Afin qu’elles puissent à leur tour invoquer notify() sur le bon objet, une référence sur SMS2MailService doit être définie dans SMS2MailServiceLogic.
La réception de SMS et l’envoi de SMS de notification au client A sont implémentées dans la méthode deliverSM() de SMS2MailServiceLogic. Les contraintes purement métier (vérification de la souscription au service, de l’existence du client B, envoi effectif du mail, etc.) sont définies dans la méthode sendMail() de MailSender. Lors de la réception d’un SMS, deliverSM() extrait le numéro de téléphone de l’expéditeur ainsi que son message, crée un objet MailSender et appelle sendMail(). En fonction du résultat, elle envoie par SMS la notification correspondante à A.
MailSender simule les accès en base de données client en définissant une table statique de clients, baseClient. On supposera qu’un client présent dans baseClient et pour lequel l’adresse mail est différente de null est un client qui a souscrit au service. On suppose également que le plan de numérotation de notre opérateur fictif définit les numéros de téléphone au format 01xxxx. La méthode sendMail() réalise les opérations suivantes et renvoie "0" en cas de succès ou un code d’erreur :
- analyse du format du message qui doit être de la forme <no de tel de B><espace><message> ;
- vérification que A a souscrit au service ;
- vérification que B est un client qui existe dans la base et qu’il a souscrit au service ;
- simulation de l’envoi du mail. Le succès ou l’échec sont simulés en tirant à pile ou face.
Vous pouvez maintenant essayer le service avec différents types de clients, ceux définis dans la base ayant ou non souscrit au service et des clients dont le numéro n’existe pas. Suivez les traces de l’application et du simulateur dans leurs fenêtres respectives.
Conclusion
Nous avons présenté dans cet article les bases nécessaires au développement d’applications SMS en Java. Bien entendu, tous les paramétrages et toutes les possibilités n’ont pu être abordés ici, mais il ne faut jamais oublier que pour bien prendre en main une technologie, il faut jouer avec, essayer les différentes options, analyser ses erreurs. Amusez-vous bien, car les possibilités sont grandes.
Références
[1] Signaling System 7 (SS7) : http://www.iec.org/online/tutorials/ss7/
[2] SS7 Tutorial : http://www.pt.com/tutorials/ss7/index.html
[3] LAGRANGE (Xavier), GODLEWSKI (Philippe), TABBANE (Sami), Réseaux GSM, Hermes Science Publications, 5ème édition, 2000.
[4] Short Message Peer to Peer protocol specification : http://www.smsforum.net/
[5] Kannel : Open Source WAP and SMS Gateway (www.kannel.org) : http://www.kannel.org/
[6] Logica : SMPP Software : http://opensmpp.logica.com/
[7] Selenium Software : simulateur de SMS-C : http://www.seleniumsoftware.com/
[8] http://smppapi.sourceforge.net/
[9] International Telecommunication Union, Q.931 (05/98) Spécification de la couche 3 de l’interface utilisateur-réseau RNIS pour la commande de l’appel de base : http://www.itu.int/rec/T-REC-Q.931/fr/


