• 20 heures
  • Facile

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Mis à jour le 20/12/2017

TP : ZChat

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Ceci est le dernier TP que vous aurez à faire, il va reprendre toutes les notions que vous avez acquises au cours de votre apprentissage. N'hésitez pas à prendre le temps de bien le faire et de ne pas vous précipiter à la correction. Si vous avez des doutes sur comment faire telle ou telle chose, ne vous en faites pas, revenez sur vos pas et relisez le chapitre correspondant.

Profitez de ce TP pour essayer les deux manières de communiquer que nous venons d'étudier, chacune a son avantage.

Cahier des charges

Oui !!! Enfin le moment où l'on met en œuvre tout ce que l'on a appris ! Pour cela, un classique : le chat !

Je parle là du « tchat » et non pas de l’animal… Alors pour ceux qui ne savent pas, un chat (tchat) est un programme permettant de communiquer avec d'autres personnes : Skype, MSN, mIRC, le chat de Facebook, etc.

On va donc en créer un en VB .Net en utilisant toutes les connaissances que nous venons d'accumuler pendant ce chapitre. Il va donc être en réseau (de quoi discuter avec les collègues au bureau).

Objectifs

Notre TP de chat sera composé de deux parties : le client et le serveur.

Le client contiendra l'interface graphique, c'est le programme que nous allons diffuser à nos collègues. Il faudra pouvoir grâce à lui :

  • Se connecter au serveur de chat ;

  • Spécifier son pseudo ;

  • Écrire des messages ;

  • Lire les messages des autres utilisateurs du chat.

Le serveur, quant à lui, sera lancé uniquement sur un seul poste. Il va s'occuper d'accepter les connexions des clients, de récupérer les messages qu'ils envoient et de les diffuser aux autres clients.

Astuces

Je vous le dit tout de suite (je ne suis pas un sadique non plus :diable: ), le serveur et le client auront besoin de threads. Le client aura quant à lui besoin plus spécifiquement de delegates (niark niark !).

Pour l'interface graphique du client, ne vous compliquez pas la vie.

Et la communication devra être utilisée soit avec les sockets purs et durs, soit avec les TCPListener/TCPClient.

Voilà, vous avez toutes les informations ! À vous les Zéros !

La correction

J'espère que ça s'est bien passé amis Zéros !

Le client

L'interface

Nous allons commencer par la correction du client. La figure suivante vous montre l'interface que j'ai réalisée.

Mon tchat
Mon tchat

Les messages sont affichés dans une listbox où j'ai choisi de rajouter un item par message. Vous pouvez parfaitement utiliser une textbox vérouillée et en multilignes.

Le reste est très basique : boutons et textbox.

Code
Imports System.Net.Sockets
Imports System.Net
Imports System.Threading

Public Class ZChat

    Dim MonSocketClient As Socket
    Dim MonThread As Thread

    'Delegate pour écrire un message
    Delegate Sub dEcrit(ByVal Texte As String)
    Private Sub Ecrit(ByVal Texte As String)
        Me.LST_MESSAGES.Items.Add(Texte)
    End Sub

    'Delegate pour modifier les contrôles suite à une déconnexion
    Delegate Sub dDeconnexion()
    Private Sub Deconnexion()
        Me.TXT_IP.Enabled = True
        Me.TXT_PORT.Enabled = True
        Me.TXT_MESSAGE.Enabled = False
        Me.TXT_PSEUDO.Enabled = True
        Me.BT_CONNEXION.Enabled = True
        Me.BT_DECONNEXION.Enabled = False
        Me.BT_ENVOI.Enabled = False
    End Sub

    Private Sub enFermeture() Handles Me.FormClosing
        If Not MonSocketClient Is Nothing Then 'Si le socket a été créé
            MonSocketClient.Close() 'On le ferme
        End If
        If Not MonThread Is Nothing Then 'Si le thread a été créé
            If MonThread.IsAlive Then 'S'il tourne
                MonThread.Abort() 'On le stoppe
            End If
        End If
    End Sub

    Private Sub BT_DECONNEXION_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BT_DECONNEXION.Click
        Deconnexion() 'On effectue la modification des contrôles
        MonSocketClient.Close() 'On ferme le socket
        EcritureMessage("Deconnecté sur serveur.", 1)
    End Sub

    Private Sub BT_CONNEXION_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BT_CONNEXION.Click
        'Vérification des entrées
        If Me.TXT_PSEUDO.Text = "" Then
            EcritureMessage("Erreur, vous devez spécifier un pseudo avant de vous connecter", 2)
            Return 'Ne continue pas
        End If
        If Me.TXT_IP.Text = "" Or Me.TXT_PORT.Text = "" Then
            EcritureMessage("Erreur, vous devez spécifier un port et une addresse ID à laquelle vous connecter.", 2)
            Return 'Ne continue pas
        End If

        MonSocketClient = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) 'Initialise le socket

        Try
            Dim MonEP As IPEndPoint = New IPEndPoint(IPAddress.Parse(Me.TXT_IP.Text), Me.TXT_PORT.Text) 'Entre les informations de connexion
            MonSocketClient.Connect(MonEP) 'Tente de se connecter
            TraitementConnexion()
        Catch ex As Exception
            EcritureMessage("Erreur lors de la tentative de connexion au serveur. Vérifiez l'ip et le port du serveur." & ex.ToString, 2)
        End Try

    End Sub

    Private Sub BT_ENVOI_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BT_ENVOI.Click
        Dim Mess As Byte() = System.Text.Encoding.UTF8.GetBytes(Me.TXT_MESSAGE.Text)
        Dim Envoi As Integer = MonSocketClient.Send(Mess) 'Envoi du pseudo au serveur
        Me.TXT_MESSAGE.Text = "" 'Efface la ligne
    End Sub

    Sub TraitementConnexion()
        EcritureMessage("Connexion au serveur réussie !", 1)
        'Change les statuts des contrôles
        Me.TXT_IP.Enabled = False
        Me.TXT_PORT.Enabled = False
        Me.TXT_MESSAGE.Enabled = True
        Me.TXT_PSEUDO.Enabled = False
        Me.BT_CONNEXION.Enabled = False
        Me.BT_DECONNEXION.Enabled = True
        Me.BT_ENVOI.Enabled = True

        'Envoi du pseudo au serveur
        Dim Mess As Byte() = System.Text.Encoding.UTF8.GetBytes(Me.TXT_PSEUDO.Text)
        Dim Envoi As Integer = MonSocketClient.Send(Mess) 'Envoi du pseudo au serveur

        MonThread = New Thread(AddressOf ThreadLecture)
        MonThread.Start()
    End Sub

    Sub ThreadLecture()
        While (MonSocketClient.Connected) 'Tant qu'on est connecté au serveur
            Dim Bytes(255) As Byte
            Dim Recu As Integer
            Try
                Recu = MonSocketClient.Receive(Bytes)
            Catch ex As Exception 'Erreur si fermeture du socket pendant la réception
                EcritureMessage("Connexion perdue, arrêt de la réception des données ...", 1)
                If Not Me.IsDisposed Then 'Si ce n'est pas le client qui est en cours de fermeture
                    Me.Invoke(New dDeconnexion(AddressOf Deconnexion))
                End If
            End Try
            Dim Message As String
            Message = System.Text.Encoding.UTF8.GetString(Bytes)
            Message = Message.Substring(0, Recu)
            EcritureMessage(Message)
        End While
    End Sub

    ''' <summary>
    ''' Écrit un message dans la fenêtre de chat
    ''' </summary>
    ''' <param name="Message"></param>
    ''' <param name="Type">0 : Message normal | 1 : Information | 2 : Erreur </param>
    ''' <remarks></remarks>
    Sub EcritureMessage(ByVal Message As String, Optional ByVal Type As Integer = 0)
        Dim Texte As String = ""
        Select Case Type
            Case 1
                Texte &= "INFO : "
            Case 2
                Texte &= "ERREUR : "
            Case Else
        End Select
        Texte &= Message
        Try
            Me.Invoke(New dEcrit(AddressOf Ecrit), Texte)
        Catch ex As Exception
            Exit Sub
        End Try
    End Sub

End Class

Pas de complications dans ce code, j'explique :

  • On envoie le pseudo lors de la connexion ;

  • On affiche sans traitement tout ce que le serveur nous envoie ;

  • Un thread de lecture effectue cette réception, puis l'affichage ;

  • Si la réception vient à échouer, on nous considère comme déconnecté (serveur off, connexion réseau coupée, etc.).

Ici j'ai utilisé un socket plutôt qu'un TCPClient. Donc, j'ai utilisé les méthodes Receive et Send.

Il faut pas mal de tests Try… Catch en réseau, une erreur peut arriver à n'importe quelle étape : connexion, réception, etc.

Et bien veiller à fermer le thread et le socket lors de la fermeture du programme (sinon le thread continuera son exécution à l'arrière-plan et le programme ne se fermera pas complètement).

Je vous laisse faire les correspondances pour deviner quel nom j'ai attribué à quel composant graphique, mais je pense avoir été assez clair. :)

Le serveur

Imports System.Net.Sockets
Imports System.Net
Imports System.Threading

Module ServeurChat

    Dim port As String = "8080"
    Dim ListeClients As List(Of Client) 'Liste destinée à contenir les clients connectés

    Sub Main()

        'Crée le socket et l'IP EP
        Dim MonSocketServeur As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
        Dim MonEP As IPEndPoint = New IPEndPoint(IPAddress.Any, port)

        ListeClients = New List(Of Client) 'Initialise la liste

        MonSocketServeur.Bind(MonEP) 'Lie le socket à cette IP
        MonSocketServeur.Listen(1) 'Se met en écoute

        Console.WriteLine("Socket serveur initialisé sur le port " & port)

        While True 'Boucle à l'infini
            Console.WriteLine("En attente d'un client.")
            'Se met en attente de connexion et appelle TraitementConnexion() lors d'une connexion.
            Dim SocketEnvoi As Socket = MonSocketServeur.Accept() 'Bloquant tant que pas de connexion
            TraitementConnexion(SocketEnvoi) 'Traite la connexion du client
        End While

    End Sub

    Sub TraitementConnexion(ByVal SocketEnvoi As Socket)

        Console.WriteLine("Socket client connecté, création d'un thread.")

        Dim NouveauClient As New Client(SocketEnvoi) 'Crée une instance de « client »
        ListeClients.Add(NouveauClient) 'Ajoute le client à la liste

        'Crée un thread pour traiter ce client et le démarre
        Dim ThreadClient As New Thread(AddressOf NouveauClient.TraitementClient)
        ThreadClient.Start()
    End Sub

    Sub Broadcast(ByVal Message As String)

        'Écrit le message dans la console et l'envoie à tous les clients connectés
        Console.WriteLine("BROADCAST : " & Message)
        For Each Cli In ListeClients
            Cli.EnvoiMessage(Message)
        Next

    End Sub

    Private Class Client
        Private _SocketClient As Socket 'Le socket du client
        Private _Pseudo As String 'Le pseudo du client

        'Constructeur
        Sub New(ByVal Sock As Socket)
            _SocketClient = Sock
        End Sub

        Sub TraitementClient()

            Console.WriteLine("Thread client lancé. ")

            'Le client vient de se connecter
            Dim Bytes(255) As Byte 'Tableau de 255 : on ne reçoit que 255 caractères au maximum
            'Réception du premier message contenant le pseudo
            Dim Recu As Integer
            Try
                Recu = _SocketClient.Receive(Bytes) 'Reçoit les premières données : le pseudo
            Catch ex As Exception
                Console.WriteLine("Erreur pendant la réception du pseudo d'un client ... Fermeture du client")
                Return
            End Try

            _Pseudo = System.Text.Encoding.UTF8.GetString(Bytes)
            _Pseudo = _Pseudo.Substring(0, Recu) 'Retire les caractères inutiles

            Broadcast(_Pseudo & " identifié sur le chat") 'Diffuse le message à tout le monde 
            While (_SocketClient.Connected)
                Try
                    Dim Message As String
                    Recu = _SocketClient.Receive(Bytes)
                    'Message reçu
                    Message = System.Text.Encoding.UTF8.GetString(Bytes)
                    Message = Message.Substring(0, Recu) 'Retire les caractères inutiles
                    Broadcast(_Pseudo & " dit : " & Message) 'Diffuse le message à tout le monde 
                Catch ex As Exception 'Le client est déconnecté
                    ListeClients.Remove(Me) 'Le supprime de la liste des clients connectés
                    _SocketClient.Close() 'Ferme son socket
                    Broadcast(_Pseudo & " déconnecté.") 'Diffuse le message à tout le monde 
                    Return 'Fin de la fonction
                End Try
            End While

        End Sub

        Sub EnvoiMessage(ByVal Message As String)
            Dim Mess As Byte() = System.Text.Encoding.UTF8.GetBytes(Message)
            Dim Envoi As Integer = _SocketClient.Send(Mess)
            Console.WriteLine(Envoi & " bytes envoyés au client " & _Pseudo)
        End Sub
    End Class

End Module

Je pense avoir tout commenté, mais je vais résumer le fonctionnement.

Quand un client se connecte, une classe nommée Client est instanciée est est ajoutée à une liste (un tableau). Cette classe est utilisée pour stocker le socket du client. Un thread est ensuite lancé, il s'occupe d'écouter les messages reçus par le client. Le premier message est obligatoirement le pseudo du client.

Dès qu'un message doit être diffusé, il appelle la fonction Broadcast. Cette fonction parcourt tous les clients connectés (ceux de la liste) et appelle leur fonction EnvoiMessage qui envoie un message à un client. Ainsi, chaque client reçoit le message que l'on souhaite diffuser.

Vous l'avez remarqué, j'ai encore utilisé ici la classe de base : Socket. À vous d'adapter ce projet pour utiliser des TCPClient/TCPListener.

Conclusion

Nous voilà au bout de notre dernier TP ensemble, les amis.

J'espère qu'il a su éclairer vos lanternes sur la communication réseau.

Le chat est un exemple basique de la communication, mais après libre à vous d'adapter ce programme à tous vos besoins : messages plus longs, mise en forme etc.

N'hésitez pas à mettre en œuvre les TCPClient/TCPListener en les utilisant dans ce programme, je voulais bien vous montrer l'utilisation des sockets qui offrent plus de possibilités qu'eux.

Améliorations possibles

N'hésitez pas à améliorer votre chat ! Le côté serveur est une bonne base, maintenant personnalisez le client !

  • Essayer de trouver un moyen pour faire défiler les messages de bas en haut, comme un vrai chat.

  • Mettez de la couleur aux messages d'erreur ou d'information.

  • Intégrez des smileys, de la mise en forme, que sais-je encore !

  • Implémentez une fonctionnalité d'envoi de fichiers aux clients (ça c'est du challenge :) ).

Voilà très chers Zéros, le réseau c'est terminé ! Vous êtes assez grands désormais !

  • Ce TP est une bonne base pour effectuer de la communication plus poussée, comme de la diffusion de fichiers ou de musiques.

  • Bien penser à la sécurité lorsque l'on pratique du développement réseau.

La partie sur le réseau, c'est terminé ! Lisez les annexes pour quelques notions complémentaires, mais je pense vous avoir transmis tout mon savoir !

Exemple de certificat de réussite
Exemple de certificat de réussite