MaitreTeTe
Messages postés37Date d'inscriptionjeudi 4 février 2016StatutMembreDernière intervention 1 décembre 2018
-
Modifié le 1 déc. 2018 à 17:39
Whismeril
Messages postés18641Date d'inscriptionmardi 11 mars 2003StatutContributeurDernière intervention 3 octobre 2023
-
1 déc. 2018 à 22:47
Bonjour,
Voici mon problème, sa fais 3 jours intense que je cherche a réglé mon problème et j'ai tenté plusieurs chose et surement pas toujours bien exploité.
Je suis en construction d'un chat. Le serveur est un Form et non une console affin d'interagir plus facilement en t'en qu'administrateur et chaque client est une Form aussi. Pour le moment tout fonctionne sauf lorsque je clique sur le bouton ( Fermer le serveur ). Bref, le serveur crée un Thread sur le Serveur qui est en attente d'un client avec Accept() et bien évidement ce bloc et attend un cleint. S'il y a un client, il passe en construction du client avec un autre thread. C'est a dire le client envoie sont pseudo. Le serveur vérifie si le pseudo est déjà utilisé et le seveur retourne un message si le Pseudo est accepté ou refusé. S'il est accepté, un dernier thread pour chaque socket est lancé qui tourne en boucle. Si le Pseudo est refusé, le serveur retourne qu'il refuse le pseudo et alors les socket son fermer des 2 coté.
Jusque là, tout fonctionne nickel.
Puis viens le moment ou je clique sur fermer le serveur pour éventuellement le rouvrir possiblement plus tard sans fermer la Form, Form qui sera fort probablement implanter dans un autre programme plus tard.
Pour fermer le thread qui est présentement en attend d'un client, je dois utiliser MonSocketServeur.Close(). La le thread s'arrête. Sans ça, un client qui tente de ce connecter va ce voir la connexion autorisé et tombe en attente de recevoir le retour du serveur (Le thread traitement de la connexion) qui lui est maintenant désactivé, donc le client bloc. Mais lorsque par la suite, après avoir fait MonSocketServeur.Close() et que je clique sur le bouton (Connexion), j'ai une erreur qu'il peu pas travailler sur un objet supprimer. Cela fais présentement 1 grosse semaine que je travail a comprendre ce genre de programmation, c'est complètement nouveau pour moi.
Voici mon codage (J'ai suivi le cours sur le site (OpenClassroom) et j'ai reconstruit le code étape par étape pour tenté de le comprendre et suivre chaque action:
P.S. J'ai tenté Accept() avec Select() pour un timeout mais j'ai trouvé que du code C# et je ne suis pas capable de le traduire.
P.S. J'ai tenté aussi de créé une class spécifique au lancement du client que je pourais faire Thread.Abort() mais j'ai pas réussis a bien l'implanter et c'a fonctionnais pas. Sinon Si je fais MonThread.Abort() Qui est le Thread du Accept() sa bloque le programme complet.
Imports System.Net.Sockets
Imports System.Net
Imports System.Threading
Public Class Serveur
Dim port As String = "8080"
Dim ThreadServeur As Thread
Dim MonSocketServeur As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
Dim MonEP As IPEndPoint = New IPEndPoint(IPAddress.Any, port)
Shared Statut As Boolean
Shared ListePseudo As New List(Of String)
Shared ListeClients As List(Of Client) 'Liste destinée à contenir les clients connectés
Delegate Sub dEcritServeurConsole(ByVal Message As String)
Delegate Sub dUserListeMod(ByVal Pseudo As String, ByVal Ajout As Boolean)
Delegate Sub dMessageConsole(ByVal Message As String)
Delegate Sub dFonctionEnvoiTous(ByVal Message As String)
Delegate Sub dStatutServeur(ByVal Statut As Boolean)
Delegate Sub dFermerSocket()
Private Sub Serveur_Load(sender As Object, e As EventArgs) Handles MyBase.Load
LBLStatut.BackColor = Color.LightYellow
Dim ips() As IPAddress = Dns.GetHostAddresses(Dns.GetHostName)
For Each ipa As IPAddress In ips
If ipa.AddressFamily = Sockets.AddressFamily.InterNetwork Then
EcritServeurConsole(DateTime.Now.ToString("dd-MM-yyyy") & " " & TimeString & " | ADRESSE IP DU SERVEUR : " & ipa.ToString)
End If
Next
End Sub
Private Sub Serveur_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
Statut = False
For Each Cli In ListeClients
Cli.FermerSocket()
Next
MonSocketServeur.Close()
StatutServeur(Statut)
EcritServeurConsole(DateTime.Now.ToString("dd-MM-yyyy") & " " & TimeString & " | Fermeture du serveur ...")
ListeClients.Clear()
ListePseudo.Clear()
End Sub
' ACTIONs DES OBJETS DE LA FORM
Private Sub BTServStart_Click(sender As Object, e As EventArgs) Handles BTServStart.Click
MonSocketServeur.Bind(MonEP) 'Lie le socket à cette IP
ThreadServeur = New Thread(AddressOf Serveur)
ThreadServeur.Start()
End Sub
Private Sub BTServStop_Click(sender As Object, e As EventArgs) Handles BTServStop.Click
Statut = False
For Each Cli In ListeClients
Cli.FermerSocket()
Next
MonSocketServeur.Close()
StatutServeur(Statut)
EcritServeurConsole(DateTime.Now.ToString("dd-MM-yyyy") & " " & TimeString & " | Fermeture du serveur ...")
ListeClients.Clear()
ListePseudo.Clear()
End Sub
Private Sub Exclusion_Click(sender As Object, e As EventArgs) Handles Button1.Click
If LSTUSER.SelectedItem = Nothing Then
Else
For Each Cli In ListeClients
Dim Raison As String = InputBox("Raison :")
Cli.DeconnectClient(LSTUSER.SelectedItem.ToString, Raison)
Next
End If
End Sub
' FIN DES ACTIONS DES OBJETS DE LA FORM
' SUB TRAITANT LES OBJETS DE LA FORM
Sub EcritServeurConsole(Message As String)
TXTServConsole.Text = Message & vbCrLf & "----------------------------------------------------------------------------------------------------------" & vbCrLf & TXTServConsole.Text
End Sub
Sub StatutServeur(Statut As Boolean)
If Statut = True Then
BTServStart.Enabled = False
BTServStop.Enabled = True
LBLStatut.Text = "Serveur ON"
LBLStatut.BackColor = Color.LightGreen
Else
BTServStart.Enabled = True
BTServStop.Enabled = False
LBLStatut.Text = "Serveur OFF"
LBLStatut.BackColor = Color.LightYellow
End If
End Sub
Sub MessageConsole(Message As String)
Dim Origine As String
Dim Nouveau As String
Origine = TXTMessage.Text
Nouveau = "----------------------------------------------------------------------------------------------------------" & Environment.NewLine & Origine
TXTMessage.Text = Nouveau
Origine = TXTMessage.Text
Nouveau = Message & Environment.NewLine & Origine
TXTMessage.Text = Nouveau
End Sub
Sub UserListeMod(ByVal Pseudo As String, ByVal Ajout As Boolean)
If Ajout = True Then
LSTUSER.Items.Add(Pseudo)
Else
LSTUSER.Items.Remove(Pseudo)
End If
End Sub
' FIN DES SUB TRAITANT LES OBJETS DE LA FORM
' SUB TRAITANT LE SERVEUR
Sub Serveur()
ListeClients = New List(Of Client) 'Initialise la liste
MonSocketServeur.Listen(1) 'Se met en mode écoute
Statut = True
Me.Invoke(New dStatutServeur(AddressOf StatutServeur), Statut)
Me.Invoke(New dEcritServeurConsole(AddressOf EcritServeurConsole), DateTime.Now.ToString("dd-MM-yyyy") & " " & TimeString & " | SERVEUR LANCÉ !")
While Statut = True 'Boucle à l'infini
'Se met en attente de connexion et appelle TraitementConnexion() lors d'une connexion.
Try
Dim SocketEnvoi As Socket = MonSocketServeur.Accept() 'Bloquant tant que pas de connexion
TraitementConnexion(SocketEnvoi)
Catch ex As Exception
End Try
End While
End Sub
Sub TraitementConnexion(ByVal SocketEnvoi As Socket)
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(Me)
End Sub
' FIN DES SUB TRAITANT LE SERVEUR
Private Class Client
Private SocketClient As Socket 'Le socket du client
Private Pseudo As String 'Le pseudo du client
Private TraitementFonctionComplete As Boolean 'Le pseudo du client
Public FrmSource
'Constructeur
Sub New(ByVal Sock As Socket)
SocketClient = Sock
End Sub
Protected Overrides Sub Finalize()
End Sub
Sub FermerSocket()
SocketClient.Close()
End Sub
Sub DeconnectClient(ByVal Nom As String, ByVal Raison As String)
If Nom = Pseudo Then
Dim Envoi As Integer = SocketClient.Send(System.Text.Encoding.UTF8.GetBytes("20//" & Raison))
SocketClient.Close() 'Ferme son socket
End If
End Sub
Sub TraitementClient(ByVal Source)
TraitementFonctionComplete = True
Dim Bytes(500) As Byte 'Tableau de 255 : on ne reçoit que 255 caractères au maximum
Dim Recu As Integer
FrmSource = Source
'Le client vient de se connecter
'Réception du premier message contenant le pseudo
Try
Recu = SocketClient.Receive(Bytes) 'Reçoit les premières données : le pseudo
Catch ex As Exception
FrmSource.Invoke(New dEcritServeurConsole(AddressOf FrmSource.EcritServeurConsole), DateTime.Now.ToString("dd-MM-yyyy") & " " & TimeString & " | ERREUR : Le client ne c'est pas initialisé !")
Return
End Try
Pseudo = System.Text.Encoding.UTF8.GetString(Bytes)
Pseudo = Pseudo.Substring(0, Recu) 'Retire les caractères inutiles
If Not ListePseudo.Contains(Pseudo) Then
ListePseudo.Add(Pseudo)
EnvoiMessageTous("10//PSEUDOACTUALISE")
FrmSource.Invoke(New dEcritServeurConsole(AddressOf FrmSource.EcritServeurConsole), DateTime.Now.ToString("dd-MM-yyyy") & " " & TimeString & " | " & Pseudo & " c'est connecté !")
FrmSource.Invoke(New dUserListeMod(AddressOf FrmSource.UserListeMod), Pseudo, True)
'Source.Invoke(New dFonctionEnvoiTous(AddressOf Source.FonctionEnvoiTous), "01//PSEUDOACTUALISE")
Else
SocketClient.Send(System.Text.Encoding.ASCII.GetBytes("REFUS"))
SocketClient.Close()
ListeClients.Remove(Me)
FrmSource.Invoke(New dEcritServeurConsole(AddressOf FrmSource.EcritServeurConsole), DateTime.Now.ToString("dd-MM-yyyy") & " " & TimeString & " | Une connection a été refusé !")
Exit Sub
End If
While (SocketClient.Connected)
If TraitementFonctionComplete = True Then
Try
Recu = SocketClient.Receive(Bytes)
Message = System.Text.Encoding.UTF8.GetString(Bytes)
Message = Message.Substring(0, Recu) 'Retire les caractères inutiles
TraitementFonctionComplete = False
MessageGEN = Message
TraitementMessage(Message)
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
ListePseudo.Remove(Pseudo)
FrmSource.Invoke(New dUserListeMod(AddressOf FrmSource.UserListeMod), Pseudo, False)
EnvoiMessageTous("10//PSEUDOACTUALISE")
FrmSource.Invoke(New dEcritServeurConsole(AddressOf FrmSource.EcritServeurConsole), DateTime.Now.ToString("dd-MM-yyyy") & " " & TimeString & " | " & Pseudo & " c'est déconnecté !")
Return 'Fin de la fonction
End Try
End If
End While
End Sub
Sub TraitementMessage(ByVal MessageSource As String)
Dim Type As String
Type = MessageSource.Substring(0, 2)
Message = MessageSource.Substring(4, MessageSource.Length - 4)
If Type = "00" Then
EnvoiMessageTous("00//" & Pseudo & " : " & Message)
FrmSource.Invoke(New dMessageConsole(AddressOf FrmSource.MessageConsole), Pseudo & " : " & Message)
End If
If Type = "10" Then
If Message = "PSEUDOLISTE" Then
For Ligne = 0 To ListePseudo.Count - 1
Dim TraitementFonction As Boolean
TraitementFonction = False
SocketClient.Send(System.Text.Encoding.ASCII.GetBytes(ListePseudo(Ligne)))
Do While Not TraitementFonction = True
Try
Dim Bytes(255) As Byte 'Tableau de 255 : on ne reçoit que 255 caractères au maximum
Dim Recu As Integer
Recu = SocketClient.Receive(Bytes)
Message = System.Text.Encoding.UTF8.GetString(Bytes)
Message = Message.Substring(0, Recu) 'Retire les caractères inutiles
If Message = "NEXT" Then
TraitementFonction = True
ElseIf Message = ("ERREUR") Then
TraitementFonctionComplete = True
Exit Sub
End If
Catch ExPseudo As Exception
End Try
Loop
Next
SocketClient.Send(System.Text.Encoding.ASCII.GetBytes("FINLISTE"))
End If
If Message = "HEURE" Then
SocketClient.Send(System.Text.Encoding.ASCII.GetBytes("00//" & Now.ToLongTimeString)) 'Envoie l'heure au client
End If
End If
TraitementFonctionComplete = True
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)
End Sub
Sub EnvoiMessageTous(ByVal Message As String)
If Statut = True Then
For Each Cli In ListeClients
Cli.EnvoiMessage(Message)
Next
End If
End Sub
End Class
End Class
EDIT : Ajout du LANGAGE dans les balises de code (la coloration syntaxique). Explications disponibles ici : ICI
Whismeril
Messages postés18641Date d'inscriptionmardi 11 mars 2003StatutContributeurDernière intervention 3 octobre 2023629 1 déc. 2018 à 19:17
Bonsoir
Tout d’abord, merci a NHenry d’avoir rendu ton code lisible.
Voir l'encart qu’il a ajouté en bas de ton message et surtout le lien vers « comment le faire toi même »
Parce que vois tu déjà, que 300 lignes écrites par un autre c’est pas simple à suivre, mais si en plus c’est en mode « node pad » personne ne va essayer et ta question restera sans réponse.
P.S. J'ai tenté Accept() avec Select() pour un timeout mais j'ai trouvé que du code C# et je ne suis pas capable de le traduire.
j'ai une erreur qu'il peu pas travailler sur un objet supprimer.
A quelle ligne et quelle est l’erreur exacte?
MaitreTeTe
Messages postés37Date d'inscriptionjeudi 4 février 2016StatutMembreDernière intervention 1 décembre 2018 1 déc. 2018 à 20:04
L'erreur ce créé la deuxième fois que je clique sur le bouton (Connexion) du serveur. Sois après avoir cliquer sur (Déconnexion) du serveur en utilisant MonSocketServeur.Close() (Qui celons ce que j'ai lu ferme et détruit le socket)
Private Sub BTServStart_Click(sender As Object, e As EventArgs) Handles BTServStart.Click
MonSocketServeur.Bind(MonEP) 'Lie le socket à cette IP
ThreadServeur = New Thread(AddressOf Serveur)
ThreadServeur.Start()
End Sub
J'ai tenté de déplacé cette ligne de commande mais sa change rien
Dim MonSocketServeur As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
Whismeril
Messages postés18641Date d'inscriptionmardi 11 mars 2003StatutContributeurDernière intervention 3 octobre 2023629 1 déc. 2018 à 20:48
Quelle ligne exactement et quelle erreur exactement?
MaitreTeTe
Messages postés37Date d'inscriptionjeudi 4 février 2016StatutMembreDernière intervention 1 décembre 2018 1 déc. 2018 à 21:17
J'AI TROUVÉ !!!!!!
Alors Voici ma solution:
Au lieu d'écrire ceci dans les variable de ma class :
Dim MonSocketServeur As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
J'ai écris ceci :
Dim MonSocketServeur As Socket
Et si j'écrivais ceci sur mon bouton (Connexion du serveur)
Private Sub BTServStart_Click(sender As Object, e As EventArgs) Handles BTServStart.Click
Dim MonSocketServeur As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
MonSocketServeur.Bind(MonEP) 'Lie le socket à cette IP
ThreadServeur = New Thread(AddressOf Serveur)
ThreadServeur.Start()
End Sub
Et bien dans les autres Sub automatiquement MonSocketServeur devenais Nothing Meme si dans ma class de ma form j'avais le code Dim MonSocketServeur As Socket
Alors j'ai tenté cette methode et ça passe !!!!!
Private Sub BTServStart_Click(sender As Object, e As EventArgs) Handles BTServStart.Click
Dim MonSocketServeurLance As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
MonSocketServeur = MonSocketServeurLance
MonSocketServeur.Bind(MonEP) 'Lie le socket à cette IP
ThreadServeur = New Thread(AddressOf Serveur)
ThreadServeur.Start()
End Sub
Résultat, tout Fonctionne !!!
Merci beaucoup pour votre aide. Après trois jour de recherche infructueuse, Soulagement !
Whismeril
Messages postés18641Date d'inscriptionmardi 11 mars 2003StatutContributeurDernière intervention 3 octobre 2023629 1 déc. 2018 à 22:47
Je vais tempérer ton enthousiasme.
C’est sympa de me remercier, mais je n’ai pas encore fait grand chose.
Maintenant, j’ai un peu plus d’éléments, je vais pouvoir éclairer un point où deux.
D’abord, ajouter un bout de code par-ci, en enlever un par-là peut parfois résoudre ton problème. Mais le comprendre est plus efficace.
Tu avais un problème de portée de variable.
Une variable existe à partir du moment où elle a un nom, un type et une valeur.
Mais elle est aussi cloisonnée à une « zone géographique ».
Elle débute à un endroit, et finit quand elle croise un
End TrucMuche
C’est ce qu’on appelle la portée.
Class UnTruc
Dim maVariable as new QuelqueChose 'maVariable existe jusqu’à End Class
Mais, y’a un mais. Il peut exister dans une « sous zone géographique » une variable du même nom, et ce seront 2 variables différentes.
Class UnTruc
Dim maVariable as QuelqueChose 'maVariable vaut Nothing, y’a pas de New
Sub Bidule()
Dim maVariable as new QuelqueChose 'maVariable existe jusqu’à End Sub et n’est pas la même que celle de la classe
End Sub
Sub Machin()
maVariable.Chose() 'maVariable c’est celle de la classe, elle vaut Nothing => erreur
End Sub
1 déc. 2018 à 20:04
J'ai tenté de déplacé cette ligne de commande mais sa change rien
1 déc. 2018 à 20:48
1 déc. 2018 à 21:17
Alors Voici ma solution:
Au lieu d'écrire ceci dans les variable de ma class :
J'ai écris ceci :
Et si j'écrivais ceci sur mon bouton (Connexion du serveur)
Et bien dans les autres Sub automatiquement MonSocketServeur devenais Nothing Meme si dans ma class de ma form j'avais le code Dim MonSocketServeur As Socket
Alors j'ai tenté cette methode et ça passe !!!!!
Résultat, tout Fonctionne !!!
Merci beaucoup pour votre aide. Après trois jour de recherche infructueuse, Soulagement !
1 déc. 2018 à 22:47
C’est sympa de me remercier, mais je n’ai pas encore fait grand chose.
Maintenant, j’ai un peu plus d’éléments, je vais pouvoir éclairer un point où deux.
D’abord, ajouter un bout de code par-ci, en enlever un par-là peut parfois résoudre ton problème. Mais le comprendre est plus efficace.
Tu avais un problème de portée de variable.
Une variable existe à partir du moment où elle a un nom, un type et une valeur.
Mais elle est aussi cloisonnée à une « zone géographique ».
Elle débute à un endroit, et finit quand elle croise un
C’est ce qu’on appelle la portée.
Mais, y’a un mais. Il peut exister dans une « sous zone géographique » une variable du même nom, et ce seront 2 variables différentes.