Protocole IP : Une introduction à SCTP [partie 2]

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);
  1. On créé une socket de type SCTP associée à au serveur ayant l'IP 127.0.0.1 et sur le port 62324
  2. On se connecte sur le serveur
  3. On envoie le message
  4. 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 :

  1. 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.
  2. 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.
  3. 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.
  4. Listen : On se met en écoute sur la socket et on accepte 5 connexions simultanées au maximum.
  5. while (1) : On boucle indéfiniment sur les trois fonctions suivantes:
  6. accept() : on attend qu'un nouveau client se connecte
  7. sctp_recvmsg() : on attend un unique message
  8. 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 :

  1. 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...
  2. 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.
  3. 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 !

blogroll

social