jeu. 26 mars 2015
| tags: réseaux logiciel libre code
Après avoir introduit SCTP/IP dans la partie précédente. J'ai écrit un
petit client /server en C que vais décrypter dans cette partie.
Que les développeurs professionnels m'en excusent, mais le code présenté
ici est celui d'un amateur qui bien qu'ayant une eu une solide formation
en C, n'a pas codé en C depuis... presque une quinzaine d'année.Par
ailleurs, le but du code écrit ci-dessous est d'être didactique, je
déconseille donc fortement de le reprendre tel quel pour développer
quelque chose de solide. Pour le moment, ce code n'apporte rien comparé
à du TCP ou de l'UDP, il s'agit dans cette partie de poser les bases
d'un client et d'un serveur SCTP.
Description de l'archive
Le tarball avec le code
L'archive associée à ce billet comporte 5 fichiers :
compile.sh va compiler le client et le server (j'ai laissé tomber le
makefile...)
sctp_functions.c regroupe l'essentiel des fonctions réseaux
nécessaire à l'écriture d'un bête client/server sctp
sctp_functions.h les headers du fichier sus-cité, pas la peine de
s'en préoccuper
sctp_server est le code du server
sctp_client le code du client
Idéalement il faudrait avoir 2 fenêtres disponible : une pour le client
et l'autre pour le serveur. Après avoir ouvert l'archive, il faut
compiler le programme avec compile.sh :
matou@nausicaa:~/Travaux/SCTP$ ./compile.sh
sctp_server.c: In function ‘main’:
sctp_server.c:32:7: warning: variable ‘message_status’ set but not used [-Wunused-but-set-variable]
matou@nausicaa:~/Travaux/SCTP$
Il reste un petit warning, mais c'est pas grand chose. Dans la fenêtre
du serveur, exécuter le serveur :
matou@nausicaa:~/Travaux/SCTP$ ./sctp_server
Awaiting connections
Le serveur ne rend pas la main, il attend qu'un client se connecte. Dans
la fenêtre du client exécuter le client :
matou@nausicaa:~/Travaux/SCTP$ ./sctp_client
Socket creation
Connection to server
Send message
matou@nausicaa:~/Travaux/SCTP$
Le client a rendu la main, il a décrit ses actions. De retour coté
serveur on a désormais :
matou@nausicaa:~/Travaux/SCTP$ ./sctp_server
Awaiting connections
Data : Ceci est un message
Awaiting connections
Le serveur a été contacté par le client qui lui a transmis un message
("ceci est un message") et attend désormais une nouvelle connexion.
Vous pouvez relancer plusieurs fois le client pour voir le message
apparaitre côté serveur.
Analyse du client
La partie cliente n'est pas bien compliqué. Concrètement elle ressemble
trait pour trait à une communication en TCP ou en UDP. Pour résumer on a
les étapes suivantes :
link = create_sctp_socket(&socket_definition,"127.0.0.1",62324);
connection_to_server(link, socket_definition);
send_message_unsafe(link,message);
On créé une socket de type SCTP associée à au serveur ayant l'IP
127.0.0.1 et sur le port 62324
On se connecte sur le serveur
On envoie le message
Et on s'arrête là
Ca a l'air simple écrit comme ça, en fait la réalité est un peu plus
complexe car j'ai camouflé la plupart des options en écrivant cette
surcouche de trois fonctions. On y reviendra plus tard.
Analyse du serveur
listener = create_sctp_socket(&server_address,"127.0.0.1",62324);
bind( listener, (struct sockaddr *)&server_address, sizeof(server_address) );
set_sctp_server_options(listener);
listen( listener, 5 );
while( 1 )
client_connection = accept( listener, NULL, NULL );
sctp_recvmsg( client_connection, message, sizeof(message), ...);
close(client_connexion)
On a donc les étapes suivantes :
create_sctp_socket : On créé une socket associée à l'IP du
serveur et au port destination. Contrairement à la partie cliente
j'ai appelé la variable qui stocke ce socket "listener" pour les
différencier, mais concrètement c'est la même fonction qui a été
appelée avec les mêmes arguments : la structure de données renvoyée
est absolument identique.
bind : on demande au système d'associer la définition de la
socket précédente avec une vraie interface et un vrai port.
set_sctp_server_options : C'est la partie la plus
intéressante du code : tout le reste vous le trouverez dans
n'importe quel tutoriel client/serveur TCP/IP. C'est dans cette
fonction que sont camouflées la plupart des options intéressantes
propre à SCTP. J'y reviens dans la prochaine section.
Listen : On se met en écoute sur la socket et on accepte 5
connexions simultanées au maximum.
while (1) : On boucle indéfiniment sur les trois fonctions
suivantes:
accept() : on attend qu'un nouveau client se connecte
sctp_recvmsg() : on attend un unique message
close() : et on ferme la connexion pour revenir à l'étape du accept
Epicétou.
Les options propres à sctp
Dans create_sctp_socket la fonction socket() permet de choisir le
type de protocole utilisé. Pour de l'UDP nous aurions socket(AF_INET,
SOCK_DGRAM, IPPROTO_UDP) et pour du TCP socket(AF_INET,
SOCK_STREAM, IPPROTO_TCP) . Mais pour du SCTP nous avons plusieurs
modes. Dans ce code j'ai utilisé socket(PF_INET, SOCK_STREAM,
IPPROTO_SCTP) qui est le mode one-to-one le plus proche de
TCP-UDP et qui a pour objectif de faciliter le portage d'applications
vers du SCTP, mais qui nous prive de la possibilité de bénéficier du
multi-homing.Il est possible de choisir le mode one-to-many avec les
options suivantes : socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP),
mais le reste du code ne serait plus nécessairement compatible.
Dans set_sctp_server_options nous avons :
sctp_config.sinit_num_ostreams = 5;
sctp_config.sinit_max_instreams = 5;
sctp_config.sinit_max_attempts = 4;
setsockopt(mysocket, IPPROTO_SCTP, SCTP_INITMSG, &sctp_config, ...);
Et là ça se corse, car il nous faut rentrer dans les détails et la
littérature sur le sujet est peu abondante. La structure sctp_initmsg
permet de définir les options initiales de sctp. Ici nous avons :
sinit_num_ostreams : Définie le nombre de conversations parallèles
maximum qui pourront être ouvertes en sortie (out streams).
Attention, je ne parle pas de connexion ou de socket simultanées
mais du nombre de flux au sein d'une unique connexion
client->serveur. Cela ne nous sert à rien avec le code présenté
dessus...
sinit_num_instreams : Le nombre de conversations simultanées
maximum gérées par le serveur. Par contre, je n'ai pas trouvé si
cela était une limite par connexion ou une limite sur la totalité
des connexions. Si vous avez des pistes cela m'intéresse.
sinit_max_attempts : C'est le nombre de fois que la pile SCTP
tente de faire un INIT vers une destination avant de considérer
qu'elle n'est pas accessible.
Note : toutes ces options ont des valeurs par défaut que j'aurais pu
laisser en positionnant leurs valeurs à "0". Mais comme les morceaux
de codes dont je me suis inspiré pour faire cet article modifiaient tous
ces valeurs, j'en ai profité pour aborder le sujet.
Pour la suite
Pour celles et ceux qui ont déjà écrit des programmes utilisant du
TCP/IP cet article n'apporte encore pas grand chose. Dans la prochaine
partie j'aborderais le multi-homing et les conversations parallèles car
ce sont les vrais atouts de SCTP sur UDP et TCP. Ca sera nécessairement
un article avec beaucoup de code. Excusez-moi les non-développeurs,
j'aurais également d'autres articles pour vous sur d'autres sujets.
Liens :
La man page linux SCTP : absolument nécessaire pour connaitre toutes les options.
comments