IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

VB.NET Discussion :

Example: TCP Server + Thread + Accès aux composants GUI D'une form depuis le thread


Sujet :

VB.NET

  1. #1
    Membre habitué

    Profil pro
    Inscrit en
    Février 2005
    Messages
    317
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 317
    Points : 183
    Points
    183
    Par défaut Example: TCP Server + Thread + Accès aux composants GUI D'une form depuis le thread
    Vu que j'ai pas mal galéré avec ça, surtout avec cette histoire de delegates, je poste ici un example qui permets d'accepter une connection et de démarrer un thread.
    Mon soucis était de pouvoir accéder aux données de ma forme principale depuis mes threads.
    En gros, j'ai un thread qui accepte les connections clients, et démarre un autre thread pour chacune d'elle.
    Le but de mon soft est de pouvoir récuper via le réseau le contenu d'une listview distante.

    En espérant que ça serve

    Coté serveur, classe réseau:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
     
    Imports System.Net.Sockets
    Imports System.Net
    Imports System.Threading
    Imports System.IO
    Imports System.Xml
    Imports CrocoScheduler.functions
     
    Public Class remoteConsole
        Private port As Integer
        Private listener As TcpListener
        Private serverThread As Thread
     
        Public Sub New(rport As Integer)
            Me.port = rport
        End Sub
     
        Public Sub setport(rport As Integer)
            If rport <> port Then
                port = rport
                stopServer()
                startServer()
            End If
        End Sub
     
        Public Sub startServer()
            frmMain.log("Starting console server")
            If Not IsNothing(listener) Then
                listener.Stop()
            Else
                listener = New TcpListener(IPAddress.Any, port)
            End If
            If Not IsNothing(serverThread) Then
                serverThread.Abort()
            End If
     
            listener.Start()
     
            serverThread = New Thread(AddressOf startThread) 'starting thread to accept connections
            serverThread.Start()
        End Sub
     
     
     
        Private Sub localLog(msg As String, sev As String)
            Dim main As frmMain = CType(Application.OpenForms(0), frmMain)
            Dim logger As New frmMain.dlog(AddressOf main.log)
            main.Invoke(logger, msg, sev)
        End Sub
    Private Function getLvOverviewitemLocal() As ListView.ListViewItemCollection
            Dim main As frmMain = CType(Application.OpenForms(0), frmMain)
            'Dim f As New frmMain.dgetlvOverviewItems(AddressOf main.getlvOverviewItems)
            'Return main.Invoke(f)
            Return main.Invoke(Function()
                                   Return main.lvOverView.Items
                               End Function)
        End Function
     
     
        Private Function getlvScheduledItemsLocal() As ListView.ListViewItemCollection
            Dim main As frmMain = CType(Application.OpenForms(0), frmMain)
            'Dim f As New frmMain.dgetlvScheduledItems(AddressOf main.getlvScheduledItems)
            'Return main.Invoke(f)
            'Return f()
            Return main.Invoke(Function()
                                   Return main.lvScheduledEvents.Items
                               End Function)
     
        End Function
     
     
        Private Sub processClientConnection(client As TcpClient)
            Try
     
                Dim TCPstream As NetworkStream = client.GetStream()
                Dim TCPReader As StreamReader = New StreamReader(TCPstream)
                Dim TCPWriter As StreamWriter = New StreamWriter(TCPstream)
                Dim pass As String = TCPReader.ReadLine()
                If pass = getAppData().RCPassword Then
     
                    Me.localLog("Password accepted", "DEBUG")
     
                    Dim XmlDoc As XmlDocument = New XmlDocument()
                    XmlDoc.LoadXml("<response></response>")
                    Dim scheduled As XmlElement = XmlDoc.CreateElement("scheduled")
                    Dim overView As XmlElement = XmlDoc.CreateElement("overview")
                    Dim items As ListView.ListViewItemCollection = Me.getlvScheduledItemsLocal()
                    For Each item As ListViewItem In items
     
                        Dim line As XmlElement = XmlDoc.CreateElement("line")
                        line.SetAttribute("color", CStr(item.BackColor.ToArgb()))
                        line.SetAttribute("color-hex", colorToHexString(item.BackColor))
                        For i = 0 To item.SubItems.Count() - 1
                            Dim element As XmlElement = XmlDoc.CreateElement("column")
                            element.InnerText = item.SubItems(i).Text
                            line.AppendChild(element)
                        Next
                        scheduled.AppendChild(line)
                    Next
     
                    items = Me.getLvOverviewitemLocal()
                    For Each item As ListViewItem In items
                        Dim line As XmlElement = XmlDoc.CreateElement("line")
                        line.SetAttribute("color", CStr(item.BackColor.ToArgb()))
                        line.SetAttribute("color-hex", colorToHexString(item.BackColor))
     
                        For i = 0 To item.SubItems.Count() - 1
                            Dim element As XmlElement = XmlDoc.CreateElement("column")
                            element.InnerText = item.SubItems(i).Text
                            line.AppendChild(element)
                        Next
                        overView.AppendChild(line)
                    Next
     
                    XmlDoc.DocumentElement.AppendChild(scheduled)
                    XmlDoc.DocumentElement.AppendChild(overView)
                    TCPWriter.Write(XmlDoc.InnerXml)
                Else
                    Me.localLog("Password not correct from " & client.Client.RemoteEndPoint.ToString(), "ERROR")
                    TCPWriter.Write("FUCK YOU")
                End If
                TCPWriter.Flush()
                client.Close()
            Catch ex As Exception
                Me.localLog("Error in thread/socket " & ex.Message, "ERROR")
            End Try
        End Sub
     
        Private Sub startThread()
     
            Me.localLog("Starting console server thread", "INFO")
            While True
                Dim TCPClient As TcpClient = listener.AcceptTcpClient()
                Me.localLog("Incoming connection from " & TCPClient.Client.RemoteEndPoint.ToString(), "DEBUG")
                Dim clientThread As Thread = New Thread(Sub()
                                                            processClientConnection(TCPClient)
                                                        End Sub)
                clientThread.Start() 'starting thread to process client connection
            End While
     
     
        End Sub
     
        Public Sub stopServer()
            If Not IsNothing(listener) Then
                listener.Stop()
            End If
            serverThread.Abort()
            frmMain.log("Stopped console server")
        End Sub
     
    End Class
    Quelques fonctions avec leur delegate de ma form principale:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
        Delegate Function dgetlvOverviewItems() As ListView.ListViewItemCollection
        Public Function getlvOverviewItems() As ListView.ListViewItemCollection
            Return lvOverView.Items
        End Function
     
        Delegate Function dgetlvScheduledItems() As ListView.ListViewItemCollection
        Public Function getlvScheduledItems() As ListView.ListViewItemCollection
            Return lvScheduledEvents.Items
        End Function
     
       Delegate Sub dlog(msg As String, sev As String)
        Public Sub log(msg As String, Optional sev As String = "INFO")
    ' do sth here
    end sub
    et mon client qui utilise aussi des threads
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
     
    Imports System.Net.Sockets
    Imports System.Net
    Imports System.IO
    Imports System.Threading
    Imports System.Xml
     
    Public Class frmMain
     
        Private Sub btnConnect_Click(sender As Object, e As EventArgs) Handles btnConnect.Click
            connectDisconnect(Not timerRefresh.Enabled)
        End Sub
     
        Delegate Sub dconnectDisconnect(connect As Boolean)
        Private Sub connectDisconnect(connect As Boolean)
            If Not connect Then
                timerRefresh.Enabled = False
                btnConnect.Text = "Connect"
                txtAddress.Enabled = True
                txtPassword.Enabled = True
                txtPort.Enabled = True
     
            Else
                If IsNumeric(txtPort.Text) Then
                    timerRefresh.Enabled = True
                    btnConnect.Text = "Disconnect"
                    txtAddress.Enabled = False
                    txtPassword.Enabled = False
                    txtPort.Enabled = False
                    refreshData()
                Else
                    MsgBox("Please put a valid port number")
                End If
            End If
        End Sub
     
        Private Sub connectDisconnectThread(connect As Boolean)
            Dim conn As New dconnectDisconnect(AddressOf connectDisconnect)
            Me.Invoke(conn, connect)
        End Sub
     
        Sub updateOrInsertLVItem(ByRef lv As ListView, values As String(), colKeys As Integer, color As Color)
            For Each item As ListViewItem In lv.Items
                Dim isEqual As Boolean = True
                For i = 0 To colKeys
                    isEqual = isEqual And (values(i) = item.SubItems(i).Text)
                Next
     
                If isEqual Then
                    For i = 0 To UBound(values)
                        If item.SubItems(i).Text <> values(i) Then
                            item.SubItems(i).Text = values(i)
                        End If
                    Next
                    If item.BackColor <> color Then
                        item.BackColor = color
                    End If
                    Exit Sub
                End If
            Next
            Dim Line As ListViewItem = New ListViewItem(values)
            Line.BackColor = color
            lv.Items.Add(Line)
     
        End Sub
     
        Delegate Sub dparseData(data As String)
        Private Sub parseData(data As String)
     
            Dim xmlDoc = New XmlDocument()
            Try
                xmlDoc.LoadXml(data)
            Catch ex As Exception
                MsgBox("Error " & ex.Message)
                connectDisconnect(False)
            End Try
     
            For Each eventSubNode As XmlNode In xmlDoc.SelectNodes("response/overview/line")
                Dim values As String()
                ReDim values(eventSubNode.ChildNodes.Count)
                For i = 0 To eventSubNode.ChildNodes.Count - 1
                    values(i) = eventSubNode.ChildNodes(i).InnerText
                Next
                Dim c As Color = Color.FromArgb(CInt(eventSubNode.Attributes("color").Value))
                updateOrInsertLVItem(lvOverView, values, 3, c)
            Next
     
            For Each eventSubNode As XmlNode In xmlDoc.SelectNodes("response/scheduled/line")
                Dim values As String()
                ReDim values(eventSubNode.ChildNodes.Count)
                For i = 0 To eventSubNode.ChildNodes.Count - 1
                    values(i) = eventSubNode.ChildNodes(i).InnerText
                Next
                Dim c As Color = Color.FromArgb(CInt(eventSubNode.Attributes("color").Value))
                updateOrInsertLVItem(lvScheduledEvents, values, 3, c)
            Next
     
        End Sub
        Private Sub parseDataThread(data As String)
            Dim parser As New dparseData(AddressOf parseData)
            Me.Invoke(parser, data)
        End Sub
     
        Private Sub refreshDataThread()
            Try
                Dim TCPClient As TcpClient = New TcpClient(txtAddress.Text, CInt(txtPort.Text))
                Dim TCPstream As NetworkStream = TCPClient.GetStream()
                Dim TCPReader As StreamReader = New StreamReader(TCPstream)
                Dim TCPWriter As StreamWriter = New StreamWriter(TCPstream)
                TCPWriter.WriteLine(txtPassword.Text)
                TCPWriter.Flush()
                Dim resp As String = TCPReader.ReadToEnd()
                Me.parseDataThread(resp)
            Catch ex As Exception
                MsgBox("Error " & ex.Message)
                connectDisconnectThread(False)
            End Try
     
        End Sub
     
        Private Sub refreshData()
            Dim clientThread = New Thread(AddressOf refreshDataThread)
            clientThread.Start()
        End Sub
     
        Private Sub timerRefresh_Tick(sender As Object, e As EventArgs) Handles timerRefresh.Tick
            refreshData()
        End Sub
     
        Private Sub frmMain_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
            saveAppData()
        End Sub
     
        Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles Me.Load
            txtAddress.Text = getAppData().RCAddress
            txtPassword.Text = getAppData().RCPassword
            txtPort.Text = getAppData().RCPort
            connectDisconnect(getAppData().Connected)
        End Sub
    End Class
    Je sais c'est pas beaucoup commenté tout ça, mais si y'a des questions n'hésitez pas

  2. #2
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    c'est le problème des tutos sur le net, on peut tomber sur des sources datant un peu
    parce que les delegate plus personne n'en utilise depuis quelques années ...

    par contre tu as du rater le tuto sur les try/catch, parce que ca c'est toujours d'actualité
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  3. #3
    Membre habitué

    Profil pro
    Inscrit en
    Février 2005
    Messages
    317
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 317
    Points : 183
    Points
    183
    Par défaut
    Ah, et quelle est la nouvelle façon pour pouvoir accéder a des objets GUI via un thread alors ?
    Pour les try catch, j'en ai mis 2 dans la partie client

  4. #4
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    selon la version de vs la syntaxe a évolué, depuis 2008 c'est plus simple, sur 2010 c'est action+sub anonyme, depuis 2012 ca donne ca je crois

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    me.invoke(sub() me.textbox1.text = ""
                    me.textbox2.text = "a"
              end sub)
    rien d'autre à écrire, c'est plus concis et plus clair
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  5. #5
    Membre habitué

    Profil pro
    Inscrit en
    Février 2005
    Messages
    317
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 317
    Points : 183
    Points
    183
    Par défaut
    Effectivement c'est plus clair. Merci !

  6. #6
    Membre habitué

    Profil pro
    Inscrit en
    Février 2005
    Messages
    317
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 317
    Points : 183
    Points
    183
    Par défaut
    Par contre, qu'en est-t-il des fonctions.
    J'ai teste ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
       Private Function getlvScheduledItemsLocal() As ListView.ListViewItemCollection
            Dim main As frmMain = CType(Application.OpenForms(0), frmMain)
            Return main.Invoke(Function()
                                   Return main.lvScheduledEvents.Items
                               End Function)
     
        End Function
    Mais j'ai un exception:
    Cross-Thread operation not valid: control lvScheduledEvents accessed from a thread other ...

    Se pourrait-il que cela ait a voir avec le fait que j'utilise cette fonction dans un for each ?

  7. #7
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    le soucis n'est pas l'accès à la propriété Items du listview, tu peux y accéder depuis un autre thread
    c'est que depuis l'appelant tu parcours ces items qui ne veulent pas être accédés depuis un autre thread

    c'est pour ca (et pour plein d'autres raisons) qu'on travaille rarement sur les données des controles
    on récupère les données (base de données ou autre) qu'on stocke dans une classe/collection non graphique
    on affiche à partir de la classe/collection, et on peut travailler dessus sans relire les controles
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  8. #8
    Membre habitué

    Profil pro
    Inscrit en
    Février 2005
    Messages
    317
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 317
    Points : 183
    Points
    183
    Par défaut
    Merci du tuyau. J'ai modifié le code et je fait une copie locale des items avant le foreach et ça semble marcher. Le truc bizarre c'est qu'il semble que le programme crashais que si le socket mettais du temps, c'est à dire quand je me connectais via un réseau lent...
    Si j'ai toujours des soucis je ferais une sub avec variables locales dans ma form principales

  9. #9
    Expert éminent Avatar de Graffito
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    5 993
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2006
    Messages : 5 993
    Points : 7 903
    Points
    7 903
    Par défaut
    En UDP, chaque fois qu'on envoie un message d'un coté, on reçoit le message complet de l'autre coté.

    En TCP, on doit voir la réception comme un flux dans lequel on reçoit une suite d'octets ordonnés, mais dont la décomposition en messages est aléatoire en réception : on peut par exemple recevoir en un coup la fin du message 1 + message 2 complet + début du message 3.

    Apparemment, ton code ne gère pas cet aspect de la réception. Pour cela, on encapsule généralement le message :
    • en le faisant précéder par un entier indiquant sa longueur,
    • ou avec un header (par exemple un caractère STX) au début du message et un trailer (par exemple un caractère ETX) en fin de message.


    Une petite remarque lors de l'utilisation d'un thread secondaire : Les exceptions rencontrées ne s'affichant pas, on doit mettre un try/catch général dans le thread et gérer la remontée de l'exception vers le thread principal.
    " Le croquemitaine ! Aaaaaah ! Où ça ? " ©Homer Simpson

Discussions similaires

  1. [VBA][OLE] Problème d'accès aux composants
    Par generalgreg dans le forum VBA Access
    Réponses: 7
    Dernier message: 13/06/2009, 13h19
  2. [singleton][thread]accès aux methodes
    Par philippe13 dans le forum Concurrence et multi-thread
    Réponses: 8
    Dernier message: 24/10/2008, 11h12
  3. SQL Server & VB6: accès aux utilisateurs
    Par GodGives dans le forum MS SQL Server
    Réponses: 4
    Dernier message: 30/01/2008, 14h09
  4. Acces aux composants d'une autre class
    Par Yann39 dans le forum AWT/Swing
    Réponses: 8
    Dernier message: 13/01/2007, 22h16

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo