Client Serveur Tcp

download Client Serveur Tcp

of 16

Transcript of Client Serveur Tcp

  • 8/19/2019 Client Serveur Tcp

    1/16

     

    client-serveur TCP en Java

    Bilal DJELASSI  

    uÄt uÄt uÄt ‹

  • 8/19/2019 Client Serveur Tcp

    2/16

    Comme promis, voici un petit document qui explique un peu le fonctionnement et la

    programmation (très basique) de deux programmes client-serveur (l’un est un client, l’autre un

    serveur). Ceci est présenté ici en Java, et suivi par une capture des trames qui auront circulés

    entre les deux programmes, ainsi que leur analyse (un peu détaillée, elle portera sur le protocole

    TCP).

    Ce document ne traitera que le protocole TCP (au niveau transport) qui permet decommuniquer en mode « connecté ». Je reviendrai sur ça un peu plus loin.

    C’est dans le contexte du TCP que je développerai mes idées.

    Tout d’abord, je répondrai à certaines questions qui peuvent vous tarauder l’esprit :

    •  C’est quoi un client ? un serveur ?

    Un client, comme un serveur, est tout simplement un programme (ou processus) tournant sur

    une machine et qui utilise la pile protocolaire (tout simplement le réseau) pour communiqueravec d’autres programmes. La différence qui existe entre un client et un serveur est la manière

    de procéder pour établir la connexion :

    -  Un serveur est passif, il ne prend pas d’initiative, il reste à l’écoute, en attente d’une

    demande de connexion.

    -  Un client est actif, ce qui veut dire qu’il prend l’initiative de contacter un serveur et

    demander l’ouverture d’une connexion.

    Une fois la connexion établie, la communication entre les deux processus est symétrique,

    bidirectionnelle. Chacun peut envoyer ou recevoir des données comme bon lui semble (enfin

    comme bon nous semble !)

    •  Alors un programme est soit client, soit serveur ?

    Non, un programme peut ne pas utiliser le réseau et ses services. Il peut aussi être client de

    plusieurs serveurs (Internet Explorer par exemple), serveur de plusieurs clients, ou alors les deux

    (comme les logiciels peer2peer de téléchargement). On peut tout faire quoi.

    •  Comment programme-t-on ce genre de chose ?

    C’est faisable en plusieurs langages, mais la complexité varie beaucoup. En C/C++, cela dépend

    de la plateforme et de certaines bibliothèques, et c’est assez complexe. En Java, c’est très

    simplifié et très bien organisé.

    •  A-t-on obligatoirement besoin d’avoir un vrai réseau pour travailler ?

    Non. Il existe ce que l’on appelle l’interface loopback  : il s’agit d’une interface réseau virtuelle

    disponible sur tous les systèmes d’exploitation. Elle porte généralement l’adresse IP 127.0.0.1.

    On peut utiliser cette interface même en n’ayant aucune connexion à un réseau.

    Le problème, c’est que les données qui transitent par cette interface sont traitées jusqu’au

    niveau 3 du modèle OSI (couche réseau : IP). Les données ne sont jamais encapsulées dans des

    trames Ethernet ou autre … Conclusion : on ne peut pas capturer des trames (avec WireShark

    par exemple) sur cette interface, mais on peut faire beaucoup de choses intéressantes !

  • 8/19/2019 Client Serveur Tcp

    3/16

  • 8/19/2019 Client Serveur Tcp

    4/16

    On pourra imaginer le réseau informatique comme le réseau de distribution d’électricité :

    chaque appareil électrique vient se greffer sur le réseau en passant par une prise. Ainsi, on

    pourra dire que chaque programme voulant communiquer ou transmettre des données passe

    par une « prise réseau » qu’on appelle Socket.

    Tout ce que l’on a à faire, c’est créer convenablement la socket, puis l’utiliser pour

    communiquer. Vous vous souvenez de la différence client-serveur ? Cette différence se

    répercutera sur la façon de créer la socket.

    Dans un premier instant, on utilisera l’interface loopback  (soit IP=127.0.0.1).

    Voici les codes sources (Java) du client (l’explication viendra après) :

    Fichier : Client.java

    // packages input/ouput et network à importer import java.io.* ;

    import java.net.* ;

     public class Client

    { public static void  main(String[] args)

    {

    // variable/objet du type Socket 

    Socket s ;

    // nos paramètres de connexion : @IP et port du serveur 

    String server_ip = "127.0.0.1" ;int  port = 43210 ;

    try 

    {

    // message d'acceuil (sur écran) 

    System.out.println(" Programme client ") ;

    System.out.println(" ---------------- ") ;

    System.out.println(" Essai de connection sur : @IP " + server_ip + " PORT " + port);

    // création d'une socket à connecter sur le serveur @IP:10.0.0.2 et PORT: 43210 s = new Socket(server_ip,port) ;

    // ici, la connexion est établie 

    System.out.println(" Connexion établie ") ;

    System.out.println(" ----------------- ") ;

    // obtention des flux d'entrée et de sortie liés à la socket 

    BufferedReader entree =

    new BufferedReader( new InputStreamReader( s.getInputStream() ) ) ;

    PrintStream sortie =new PrintStream( s.getOutputStream() ) ;

    // obtention du flux d'entrée associé au clavier 

    BufferedReader clavier =

    new BufferedReader( new InputStreamReader( System.in ) ) ;

    // communication 

    while(true)

    {

    // On affiche un message d'invite sur l'écran

    System.out.print("Client : ") ;

    // Saisie au clavier 

    String message ;

    message = clavier.readLine();

    // envoi du message saisi au clavier 

    sortie.println(message) ;

    sortie.flush();

    // on affiche un autre message pour la réponse du serveur System.out.print("Serveur : ") ;

    // réception de la réponse du serveur 

    String reponse ;

    reponse = entree.readLine() ;

  • 8/19/2019 Client Serveur Tcp

    5/16

     

    // affichage du message du serveur 

    System.out.println(reponse) ;

    // si le message envoyé est "bye", coupons la connexion if( message.equals("bye") )

    { break ;

    }

    }

    // si on atteint ce point, alors on coupera la connexion s.close() ;

    // ici, la connexion est fermée du côté client 

    System.out.println(" ----------------- ") ;

    System.out.println("Connexion fermée du côté client") ;

    }catch(IOException e)

    {

    System.out.println("OOoops !! Impossible d'établir ou d'utiliser la connexion !") ;

    }

    }

    }

    Explications :import java.io.* ;

    import java.net.* ;

    On importe les packages io (pour Input/Output) et net  (pour Network). On utilisera des classes

    prédéfinies en Java pour le réseau et les entrées/sorties.

     public class Client { … }

    Tout programme Java doit au moins contenir une classe, elle s’appelle ici Client  (le nom est

    choisi pour être évocateur, vous pouvez mettre n’importe quel nom pas déjà utilisé).

     public static void  main(String[] args) { … }

    Point d’entrée principal de notre programme. Ici débutera l’exécution.

    Socket s ;

    On déclare une variable (ou objet en langage Orienté Objet) du type Socket  qui existe (prédéfini)

    dans le package java.net. Pour établir une connexion TCP, il faudra passer par une socket !

    String server_ip = "127.0.0.1" ;

    int  port = 43210 ;

    On déclare ici nos paramètres de connexion. Rappelez-vous, il nous faut 5 paramètres :

    -  Le protocole : TCP dans notre cas. Il est implicitement défini, car en utilisant la classe

    Socket, on travaille toujours en mode connecté (TCP Socket).

    -  IP/Port source (client) : l’IP est connue (pas la peine de la définir, elle sera récupérée

    automatiquement), et le port peut être choisi (par le programmeur) ou bien attribué par le

    système d’exploitation. On choisira cette dernière option On n’a rien à coder !!

    -  IP/Port destination (serveur) : il faudra les préciser. Ce sont les paramètres qui manquent.

    Ils sont déclarés ci-dessus, et vont être utilisés dans ce qui suit.

    A retenir :

    Le client possède toujours tous les paramètres de connexion.

  • 8/19/2019 Client Serveur Tcp

    6/16

     try { … } 

    catch(IOException e) { … }

    Les opérations d’ouverture de connexion et de communication peuvent rencontrer des

    problèmes. Les méthodes que l’on utilise sont donc susceptibles de lancer ou « déclencher » des

    exceptions. Les plus importantes sont les IOException (Input/Output Exception). Voir un cours

    Java pour plus de détails sur les exceptions.

    s = new Socket(server_ip,port) ;

    Ici, on tente d’ouvrir une connexion vers le serveur identifié par l’IP server_ip et le port port . A

    l’exécution de ce code des trames TCP sont émises en direction du serveur pour demander

    l’ouverture de connexion.

    Nous étudierons ces trames un peu plus loin. 

    A la fin de l’exécution de cette ligne, il y a deux cas de figure :

    - Soit tout s’est bien passé, et on obtient un objet de type Socket  qui identifie clairement notreconnexion avec le serveur. On continuera l’exécution à partir de la ligne suivante.

    - Soit un problème est survenu (et il y a tant de causes …) : dans ce cas, une IOException est

    levée et on poursuit le programme dans le bloc catch (en affichant un message d’erreur).

    Donc, en résumé, si cette instruction passe, on est connectés !!

    Ici, un peu de concentration sera nécessaire :

    BufferedReader entree = new BufferedReader( new InputStreamReader( s.getInputStream() ) ) ;

    PrintStream sortie = new PrintStream( s.getOutputStream() ) ;

    La socket s, une fois correctement créée, va nous permettre de communiquer. Le principe en

    Java pour l’échange de données (en général, pas uniquement en réseau) est l’utilisation des

    flux : pour faire simple, les flux sont comme des canaux par lesquels transitent des données (en

    l’occurrence des octets).

    Ainsi, toute opération d’entrée/sortie s’effectue à travers des flux. Par exemple, l’affichage vers

    l’écran (la sortie standard plus exactement) se fait via le célèbre flux « System.out ». La lecture de

    ce qui entré au clavier se fait par le non moins célèbre flux « System.in ».On distingue plusieurs types de flux (prédéfinis en Java), et on peut les classer de différentes

    manières :

    - Suivant le sens : il existe des flux d’entrée et des flux de sortie

    - Suivant le type de données : octets, caractères, chaînes de caractères, fichiers, objets même …

    - etc …

    On retiendra pour l’instant qu’il existe des flux d’entrée et de sortie.

    Ainsi, comme une connexion sert à communiquer, et que l’on peut communiquer dans les deux

    sens (une fois la connexion établie), on peut récupérer deux flux rattachés à la socket :

    - Un flux d’entrée (de type InputStream) : pour le sens serveur client

    - Un flux de sortie (de type OutputStream) : pour le sens client  serveur

  • 8/19/2019 Client Serveur Tcp

    7/16

    On les récupère par deux méthodes de la classe Socket :

    - L’entrée est donnée par : s.getInputStream() (du type InputStream) 

    - La sortie quant à elle par :  s.getOutputStream() (du type OutputStream)

    Les données manipulées par ces flux sont des octets (InputStream et OutputStream sont des

    flux basiques, les plus simples quoi). Pour pouvoir transmettre du texte, on devra les faire

    « évoluer » vers des flux plus adaptés au texte.

    On construit donc des flux améliorés à partir des flux simples avec les longues instructions

    données plus haut. On utilise les constructeurs (c’est normal, on construit) des classes

    prédéfinies dans le package java.io pour faire les transformations suivantes :

    InputStream InputStreamReader BufferedReader

    OutputStream PrintStream

    Une fois ces flux améliorés, on utilisera deux méthodes pour envoyer et recevoir du texte :

    - println : pour « écrire » du texte sur le PrintStream (il est fait pour qu’on puisse printer)- readLine : pour lire du texte sur le BufferedReader (c’est bien un reader, non ?)

    Nous verrons ça plus loin.

    BufferedReader clavier = new BufferedReader( new InputStreamReader( System.in ) ) ;

    Ca rappelle quelque chose … C’est le même tour de passe-passe que celui vu plus haut : on

    améliore le flux System.in relié au clavier. Mais pourquoi n’améliore-t-on pas le System.out  ?

    parcequ’il est très utilisé, et que devoir l’améliorer dans chaque programme serait une perte de

    temps pour les programmeurs. Ainsi a-t-on décidé de le mettre prêt à l’emploi.

    A ce stade, nous travaillons avec 4 flux :

    - Flux d’entrée réseau (nommé entree) 

    - Flux de sortie réseau (nommé sortie) 

    - Flux d’entrée clavier (nommé clavier) 

    - Flux de sortie écran (nommé System.out) 

    while(true) { … }

    Ici, on boucle à l’infini en réitérant les étapes suivantes :

    String message ;

    message = clavier.readLine(); 

    On lit ce que veut transmettre l’utilisateur (au niveau du Client) … On met la ligne saisie dans la

    variable chaîne de caractères message ,

    sortie.println(message) ;

    sortie.flush();

    On écrit ce message sur le flux de sortie réseau (donc le texte sera transmis au serveur), ladeuxième ligne ordonne de vider la mémoire tampon utilisée pour stocker le texte en attente de

    transmission.

  • 8/19/2019 Client Serveur Tcp

    8/16

    String reponse ;

    reponse = entree.readLine() ;

    Ici, on lit ce que nous transmet le serveur sur le flux d’entrée réseau. On met le résultat dans la

    variable chaîne de caractère reponse ,

    System.out.println(reponse) ; 

    Et enfin, on affiche cette réponse sur l’écran.

    if( message.equals("bye") )

    { break ;

    }

    Pour mettre fin à notre conversation avec le serveur, on a rajouté ces petites lignes : si le texte

    entré est « bye » , alors on sort de la boucle infinie.

    s.close() ; 

    En dehors de la boucle while (c-à-d lorsqu’on aura saisi « bye »), on ferme la connexion avec la

    méthode close de la classe Socket . Cela a pour conséquence de générer des trames particulières

    qui mettent fin à la connexion. On les verra un peu plus loin.

    C’est fini !!

    Si vous compilez ce programme et que vous le lancez maintenant, il ne fonctionnera pas … car il

    n’y a pas de serveur à l’écoute. Mettez le programme de côté et passons au serveur.

    La seule différence, c’est dans la manière d’obtenir une socket correctement initialisée. On verra

    uniquement des petites modifications au niveau du programme serveur :

    Fichier : Serveur.java 

    // packages input/ouput et network à importer 

    import java.io.* ;

    import java.net.* ;

     public class Serveur

    { public static void  main(String[] args)

    {// variable/objet du type ServerSocket 

    ServerSocket server_s ;

    // variable du type Socket 

    Socket s ;

    // nos paramètres de connexion : port du serveur uniquement 

    int  port = 43210 ;

    try 

    {

    // message d'acceuil (sur écran) 

    System.out.println(" Programme Serveur ") ;

    System.out.println(" ----------------- ") ;

    System.out.println(" Attente de connection sur : PORT " + port) ;

    // création d'une ServerSocket ouvrant le port 43210 server_s = new ServerSocket(port) ;

    // attente de connexion sur le serveur sur le port 43210 

    s = server_s.accept() ;

  • 8/19/2019 Client Serveur Tcp

    9/16

      // ici, la connexion est établie 

    System.out.println(" Connexion établie ") ;

    System.out.println(" ----------------- ") ;

    // obtention des flux d'entrée et de sortie liés à la socket 

    BufferedReader entree =

    new BufferedReader( new InputStreamReader( s.getInputStream() ) ) ;

    PrintStream sortie =new PrintStream( s.getOutputStream() ) ;

    // communication while(true)

    {

    // On affiche un message explicatif à l'écran

    System.out.print("Client -> Serveur : ") ;

    // Réception du message du client 

    String message ;

    message = entree.readLine();

    // Affichage du message reçu 

    System.out.println(message) ;

    // On confectionne un petite réponse 

    String reponse = "Ce message contient " + message.length() + " caractères" ;

    // Affichage sur écran d'un petit message de réponse au client 

    System.out.print("Serveur -> Client : ") ;

    System.out.println(reponse) ;

    // On envoie cette réponse au client 

    sortie.println(reponse) ;

    sortie.flush() ;

    // Si le message est "bye", le serveur coupe la connexion if( message.equals("bye") )

    { break ;

    }

    }

    // si on atteint ce point, le message est "bye" 

    // on ferme la connexion s.close();

    // ici, la connexion est fermée du côté serveur 

    System.out.println(" ----------------- ") ;

    System.out.println("Connexion fermée du côté serveur") ;

    }

    catch(IOException e)

    {

    System.out.println("OOoops !! Impossible d'établir ou d'utiliser la connexion !") ;

    }

    }

    }

    Explication :

    Il y a beaucoup de ressemblance, dans le corps du programme. On importe toujours les mêmes

    packages. La classe s’appelle cette fois Serveur . C’est dans le corps du « main » que de

    nouvelles choses apparaissent :

    ServerSocket server_s ;

    Socket s ; 

    En plus de la Socket, nous avons une ServerSocket  … n’as-t-on pas dit qu’il fallait une socket ?

    Pourquoi une ServerSocket ?

  • 8/19/2019 Client Serveur Tcp

    10/16

    En fait, le serveur doit indiquer au système d’exploitation (qui gère la pile protocolaire TCP/IP)

    qu’il souhaite « écouter » ce qui arrive en destination d’un port particulier (celui que le serveur

    désire ouvrir). On parle d’ « ouverture de port ».

    La ServerSocket  sert justement à faire cette configuration. Passons en revue nos 5 paramètres de

    connexion :

    - Le protocole : TCP (implicitement connu, car on utilise la classe Socket qui ne fait que ça)- L’IP/port source (serveur) : IP connue, et le port aussi (car on souhaite l’ouvrir)

    - L’IP/port destination (client) : tous deux non connus.

    Que va-t-on faire ? Deux choses :

    - Demandez l’ouverture du port désiré au niveau du serveur

    - Attendre qu’un client nous contacte (contacte le serveur). Lorsqu’un client contacte le serveur,

    les trames reçus contiennent les données manquantes pour fabriquer la socket : on extrait des

    trames de demande de connexion provenant du client l’adresse IP et le port du client.

    On fabrique la socket, et on est prêt à communiquer !!

    int  port = 43210 ;

    Voilà le port que notre serveur va ouvrir … Enfin va tenter d’ouvrir, car on ne sait jamais, il peut y

    avoir des problèmes. Toutes les instructions suivantes sont dans un bloc try :

    server_s = new ServerSocket(port) ;

    Ici, on demande d’ouvrir un port (en l’occurrence 43210 : j’ai choisi ce port dans la zone des

    ports > 1024). A noter qu’un serveur est passif, aucune trame n’est générée ici.

    Si l’instruction réussit, le port est ouvert et associé au programme serveur.

    s = server_s.accept() ; 

    Ici, on attend (au vrai sens du terme) qu’un client se connecte. L’instruction est bloquante, c-à-d

    qu’on reste en ce point jusqu’à ce qu’un client arrive et se connecte.

    La méthode accept appartient à la classe ServerSocket. Appliquée à l’objet s_server, elle renvoie en

    cas de succès une socket valide (en complétant les infos manquantes : IP client et port client).

    La suite est comme pour le client. On n’utilisera pas le flux associé au clavier. On boucle à l’infini

    en passant par ces étapes :

    - On lit le message envoyé par le client,

    - On l’affiche à l’écran (du serveur),

    - On confectionne un réponse : j’ai choisi de compter le nombre de caractères (y compris les

    espaces) et d’envoyer en réponse le texte : « Ce message contient x caractères »

    - On affiche la réponse à l’écran (du serveur),

    - On transmet cette réponse au client.

    Si on reçoit le message « bye », on ferme la connexion.

    Voilà ! c’est fini.

  • 8/19/2019 Client Serveur Tcp

    11/16

     

    Compilez le programme serveur. Le programme serveur doit être lancé avant le client. Vous

    pouvez observez (parfois) au lancement du serveur un message d’alerte de votre pare-feu : un

    pare-feu est un logiciel qui joue un peu le rôle d’un gendarme, on contrôlant qui fait quoi avec

    les connexions. Il évite de répondre à certaines trames et messages douteux, et contrôle qui

    ouvre des ports. Pour fonctionner, vous devez autoriser votre serveur à ouvrir les ports. Cela est

    proposé facilement sur votre pare-feu.Lancez votre client, et miracle, ils sont connectés.

    Un détail : vous pouvez travailler avec n’importe quel environnement de développement Java. Je

    fais mes tests sous Eclipse, mais vous pouvez choisir ce que vous voulez …

    Voici une capture d’écran (j’ai lancé des invites de commandes) :

    Fig1. Client connecté à un serveur, en utilisant l’interface loopback (sur la même machine)

    Remarque : pour les caractères accentués qui s’affiche mal, il y a une explication et une petite

    solution (valable aussi en C/C++). Prochainement …

    Maintenant, si on veut voir les trames, ce n’est pas possible …. (possible sous Unix, mais avec

    une certaine complexité pas évidente à mettre en œuvre).

    Comme solution (il peut en exister d’autres et des meilleures, mais bon je me contente de ça), je

    propose d’utiliser le réseau virtuel mis en place avec VirtualBox (document précédent).

    Si vous n’avez pas lu le document précédent, je vous invite à le faire.

    J’aurai donc deux machines : Une sous Vista (mon OS) et une sous XP.

  • 8/19/2019 Client Serveur Tcp

    12/16

    La machine sous Vista contiendra le client, et l’autre le serveur.

    La configuration IP et Ethernet sont les suivantes :

    - Côté client (Vista) :

    @IP : 10.0.0.1 (à configurer nécessairement soi-même !)

    @MAC : 00-FF-17-21-DE-41 (réglée automatiquement, peut être différente)

    - Côté serveur (XP) :

    @IP : 10.0.0.2 (à configurer nécessairement soi-même !) @MAC : 08-00-27-1D-A0-E9 (réglée automatiquement, peut être différente)

    Nous ferons simplement une petite modification au niveau du client : mettre 10.0.0.2 à la place

    de 127.0.0.1 et recompiler.

    Voici un petit diagramme qui résume l’état de nos programmes connectés :

    On peut lancer WireShark sur n’importe laquelle des machines. On capturera les trames Ethernet

    qui circulent entre les programmes.

    Une fois chaque programme mis sur une machine, j’obtiens la trace d’exécution suivante (le

    texte que j’ai entré est en bleu, ceci n’est qu’un exemple, vous pouvez faire ce que vous voulez) :

    Fig2. Trace d’exécution du client

    @IPsource = 10.0.0.1

    Port source : xyz

    @IPdestinat = 10.0.0.2

    Port destint° :43210

    nputStream

    OutputStream

    @IPsource = 10.0.0.2

    Port source : 43210

    @IPdestinat = 10.0.0.1

    Port destint° : xyz

    nputStream

    OutputStream

    Client TCP

    (sous Vista)

    Serveur TCP

    (sous XP)

    Socket Socket

  • 8/19/2019 Client Serveur Tcp

    13/16

  • 8/19/2019 Client Serveur Tcp

    14/16

     Fig4. Trames capturées entre le client et le serveur

    Je ne présenterai pas ici les détails de chaque protocole …

    Trame 1 : Requête ARP

    0000 ff ff ff ff ff ff 00 ff 17 21 de 41 08 06 00 01  ........ .!.A....

    0010 08 00 06 04 00 01 00 ff 17 21 de 41 0a 00 00 01  ........ .!.A....

    0020 00 00 00 00 00 00 0a 00 00 02  ........ ..

    En gros, cela se traduit par (lecture séquentielle du début jusqu’à la fin) :

    - @MAC destination : ff ff ff ff ff ff Tout le monde (= le domaine de diffusion)

    - @MAC source : 00 ff 17 21 de 41 Le client

    - Protocole Réseau : 08 06 ARP

    - Adressage physique : 00 01 Ethernet

    - Adressage logique : 08 00 IP

    - Longueur @physique : 06 6 octets (le cas des adresses MAC)

    - Longueur @logique : 04 4 octets (le cas d’IPv4)

    - Type ARP : 00 01 requête de résolution @IP vers @MAC

    - @Physique source : 00 ff 17 21 de 41 Le client

    - @Logique source : 0a 00 00 01 10.0.0.1 en décimal : L’IP du client

    - @Physique destination : 00 00 00 00 00 00 Inconnue, c’est elle qu’on cherche

    - @Logique destination : 0a 00 00 02 10.0.0.2 en décimal : L’IP du serveur

    On peut traduire tout cela en langage humain : « Salut tout le monde, je suis la machine

    10.0.0.1, j’habite en 00 ff 17 21 de 41, je cherche la machine 10.0.0.2, qu’elle me dise où puis-jela trouver ! »

  • 8/19/2019 Client Serveur Tcp

    15/16

    En réponse à cette requête que reçoit la machine 10.0.0.2 (et toutes les autres machines), elle

    formule une réponse convenable :

    Trame 2 : Réponse ARP

    0000 00 ff 17 21 de 41 08 00 27 1d a0 e9 08 06 00 01  ...!.A.. '.......

    0010 08 00 06 04 00 02 08 00 27 1d a0 e9 0a 00 00 02  ........ '.......

    0020 00 ff 17 21 de 41 0a 00 00 01 6b 00 32 33 50 18  ...!.A.. ..k.23P.0030 fa db 9f df 00 00 0d 0a 4b 45 42 46  ........ KEBF

    - @MAC destination : 00 ff 17 21 de 41  Le client

    - @MAC source : 08 00 27 1d a0 e9  Le serveur

    - Protocole Réseau : 08 06 ARP

    - Adressage physique : 00 01 Ethernet

    - Adressage logique : 08 00 IP

    - Longueur @physique : 06 6 octets (le cas des adresses MAC)

    - Longueur @logique : 04 4 octets (le cas d’IPv4)

    - Type ARP : 00 02 réponse à une requête de résolution @IP vers @MAC- @Physique source : 08 00 27 1d a0 e9 Le serveur

    - @Logique source : 0a 00 00 02 10.0.0.2 en décimal : L’IP du serveur

    - @Physique destination : 00 ff 17 21 de 41  Le client

    - @Logique destination : 0a 00 00 01 10.0.0.1 en décimal : L’IP du client

    - Bourrage : 6b 00 32 33 50 18 fa db 9f df 00 00 0d 0a 4b 45 42 46 

    Remarque : certaines trames présentent un bourrage, d’autres non … Les tailles des trames

    semblent aussi petites … Je n’ai pas encore résolu cette question.

    A ce stade, la machine client connaît l’@MAC de la machine serveur. On peut maintenant passer

    à l’établissement de connexion TCP. Cela se fait en trois étapes (on parle de poignée de mains

    en trois temps) :

    - Trame 3 : Client  Serveur avec les indicateurs : SYN

    + Seq Num : b6 c9 78 c0 = 3066656960 en décimal

    + Ack Num : 00 00 00 00 = non encore initialisé

    - Trame 4 : Serveur Client avec les indicateurs : SYN & ACK

    + Seq Num : f9 38 9b 12 = 4181236498 en décimal+ Ack Num : b6 c9 78 c1 = 3066656961 en décimal

    - Trame 5 : Serveur Client avec les indicateurs : ACK

    + Seq Num : b6 c9 78 c1 = 3066656961 en décimal

    + Ack Num : f9 38 9b 13 = 4181236499 en décimal

    Voici un diagramme qui illustre cela :

    On voit aussi les N° de ports utilisés pour la communication :

    - 43210 : pour le serveur- 49807 : pour le client

    Fig5. Etablissement de connexion TCPClient Serveur

    SYN

    seq = x

    SYN , ACK

    seq = y , ack = x+1

    ACK

    seq = x+1 , ack = y+1

  • 8/19/2019 Client Serveur Tcp

    16/16

    Ensuite, les échanges de données commencent. Les champs SeqNum et AckNum sont

    incrémentés à chaque fois du nombre d’octets transmis et reçus correctement. Vous pouvez

    observer les trames 6 à 23 sous WireShark, en analysant le contenu de la colonne info. Les

    indicateurs, le SeqNum (sequence number), le AckNum (acknowlegement number), et la

    longueur des données transportées (len) sont données relativement au premières valeurs x  et y  

    utilisées lors de l’établissement de connexion : il est noté 6 au lieu de x+6 = 3066656966 par

    exemple.

    Enfin, dernière étape cruciale, la fermeture de connexion. Bizarrement, la fermeture de

    connexion nécessite 4 échanges de trames, une fermeture et confirmation côté client, et encore

    une fermeture et confirmation côté serveur.

    Voici un diagramme qui explique un peu cela :

    L’initiative de fermeture peut être prise par n’importe quelcôté (client ou serveur).

    Les trames 24 à 27 illustrent ce cas.

    Fig6. Fermeture de connexion TCP 

    PS : Il se peut qu’il y ait des erreurs ! Merci de les signaler !!

    Client Serveur

    FIN & autres

    ack = v

    ACK

    seq = v , ack = u

    FIN & autres

    ack = u’

    ACK

    seq = u’ , ack = v’+1