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