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

Windows Forms Discussion :

[C# 1.1] - Event & Thread


Sujet :

Windows Forms

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Rédacteur
    Avatar de Erakis
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2003
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : Canada

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 523
    Par défaut [C# 1.1] - Event & Thread
    RBonjour à tous.

    J'ai dois concevoir un petit serveur TCP/IP multi-thread. Voilà le début :
    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
    public class TCPServer
    {
        // Déclaration des délégés
        public delegate void ClientConnected( int iClientID );
        public delegate void ClientDisconnected( int iClientID );
        public delegate void ClientErrorOccured( int iClientID, Exception e );
        public delegate void ClientDataReceived( int iClientID, byte[] data );
        
        // Déclaration des événements
        public event ClientConnected OnClientConnected;
        public event ClientDisconnected OnClientDisconnected;
        public event ClientErrorOccured OnClientErrorOccured;
        public event ClientDataReceived OnClientDataReceived;
    
        // Déclaration des membres privés
        private string m_IPAddress = "127.0.0.1";
        private int m_PortNumber = 2222;
        private bool m_bIsRunning = false;
        private TcpListener m_TCPListener = null;
        // ... 
    
        // Événements par défaut 
        private void ClientConnectedEvent( int iClientID )
        {
           if (OnClientConnected != null)
               OnClientConnected( iClientID );
        }
        private void ClientDataReceivedEvent( int iClientID, byte[] data )
        {
           if (OnClientDataReceived != null)
               OnClientDataReceived( iClientID, data );
        }
        // ... 
        
        // Constructeur 
        public TCPServer( string ipAddress, int iPortNumber )
        {
             m_PortNumber = iPortNumber;
             m_IPAddress = ipAddress;
        }
    
        // Démarrage du server 
        public void Start() 
        {
            Thread serverThread = new Thread( new ThreadStart( Listen ) );
            serverThread.Start();
        }
    
        // Main loop du serveur 
    
        private void Listen()
        {
    	m_bIsRunning = true;
    	m_TCPListener = new TcpListener( System.Net.IPAddress.Parse(m_IPAddress), m_PortNumber );
    	m_TCPListener.Start();
    	while( m_bIsRunning )
    	{
                      // Waiting for a connection
                      Socket clientSocket = null;
                      try
                      {
                         clientSocket = m_TCPListener.AcceptSocket();
                      }
                      catch (SocketException se )
                      {
                          ClientErrorOccuredEvent( -1, se );
                          continue;
                      }
    
                      // Client connecté
                      if (clientSocket.Connected)
                      {
                          m_ClientID++;
                          // Déclancher l'événement de connection
                          ClientConnectedEvent( newClientID );
    
                          // Créer un object ServerClientThread
                          ServerClientThread sct = new ServerClientThread( newClientID, clientSocket );
    					
    			
                         // Démarage du nouveau processus (gestion du client connecté)
                         Thread serverThread = new Thread( new ThreadStart( sct.Process ) );
                         serverThread.Start();
                         // ... 
                      }
          // ... 
          }
       }
    }
    Maintenant voici la classe Wrapper ServerClientThread :
    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
    public class ServerClientThread
    {
        // Déclaration des membres privés
        private bool m_bStopProcess = false;
        // ...
    
        // Constructeur 
        public ServerClientThread( int iClientID, Socket clientSocket )
        {
            m_ClientID = iClientID;
            m_ClientSocket = clientSocket;
        }
    
        // Point d'entré du thread 
        public void Process()
        {
           while (!m_bStopProcess)
           {
    // À partir d'ici je créer un NetworkStream et j'attend les
    entrées de donnée. Cependant, comment puis-je déclancher l'événement
    ClientDataReceived de la classe TCPServer qui à
    démarré ce thread ? Sous C++ j'aurais déclarer cette classe comme étant
    FRIEND mais en c# ??? 
           }        
        }
    }
    Alors voilà, je crois bien que ma question est claire
    Merci pour votre aide.

  2. #2
    Rédacteur
    Avatar de abelman
    Inscrit en
    Février 2003
    Messages
    1 106
    Détails du profil
    Informations forums :
    Inscription : Février 2003
    Messages : 1 106
    Par défaut
    Salut

    Pas mal ton avatar ;-)

    Pour ton problème essaye de passer à chaque instance de la classe ClientServerThread l'objet TcpServer en cours

    Par exemple lors de la connexion d'un client dans ta fonction TcpServer.Listen

    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
                    
    // Client connecté
                      if (clientSocket.Connected)
                      {
                          m_ClientID++;
                          // Déclancher l'événement de connection
                          ClientConnectedEvent( newClientID );
    
                          // Créer un object ServerClientThread
                          ServerClientThread sct = new ServerClientThread( newClientID, clientSocket , this); // ça se passe ici
    					
    			
                         // Démarage du nouveau processus (gestion du client connecté)
                         Thread serverThread = new Thread( new ThreadStart( sct.Process ) );
                         serverThread.Start();
                         // ... 
                      }
    Comme ça dans la classe ClientServerThread (tu as mis TCPServer dans ton deuxième listing ;-)), tu pourras faire ça

    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
    public class TCPServer
    {
        // Déclaration des membres privés
        private bool m_bStopProcess = false;
        private TcpServer m_server;
        // ...
     
        // Constructeur 
        public ServerClientThread( int iClientID, Socket clientSocket, TcpServer server)
        {
            m_ClientID = iClientID;
            m_ClientSocket = clientSocket;
            m_ server = server;
        }
     
        // Point d'entré du thread 
        public void Process()
        {
           while (!m_bStopProcess)
           {
    // À partir d'ici je créer un NetworkStream et j'attend les
    entrées de donnée. Cependant, comment puis-je déclancher l'événement
    ClientDataReceived de la classe TCPServer qui à
    démarré ce thread ? Sous C++ j'aurais déclarer cette classe comme étant
    FRIEND mais en c# ??? 
                // Comme ça ???
                server.ClientDataReceived(m_ClientID, data);
           }        
        }
    }

  3. #3
    Rédacteur
    Avatar de Erakis
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2003
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : Canada

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 523
    Par défaut
    Bonjour,

    J'y avais déjà pensé mais il y a un problème avec cette solution :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    private void ClientDataReceivedEvent( int iClientID, byte[] data )
    {
        if (OnClientDataReceived != null)
            OnClientDataReceived( iClientID, data );
    }
    Comme c'est un événement par défaut, elle est déclarée PRIVATE. Elle ne sera pas accessible depuis un autre classe.

    En terme de robusteste de classe, cette méthode devrait rester PRIVATE non ?

  4. #4
    Rédacteur
    Avatar de abelman
    Inscrit en
    Février 2003
    Messages
    1 106
    Détails du profil
    Informations forums :
    Inscription : Février 2003
    Messages : 1 106
    Par défaut
    Pour moi cela ne pose pas de problème qu'elle soit publique, car elle ne fait rien d'autre que appeller un eventuel handler. Si l'event est public (c'est obligé), je ne vois pas pour quoi le handler par défaut ne le serais pas

    Si tu ne veux pas la rendre publique, tu peux toujours la mettre internal. Comme cela elle sera accessible uniquement aux classe de ton assembly (ta DLL je suppose).

    Par ailleurs, si jamais tu souhaites faire des classes qui dérivent de TCPServer, rend la public overridable ou internal protected overridable.

    @+

  5. #5
    Rédacteur
    Avatar de Erakis
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2003
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : Canada

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 523
    Par défaut
    Merci beaucoup pour vos réponses.

    abelman je suis d'accord avec ton idée mais dans le cas ou cette classe pourrait se retrouver sur le marché, je ne trouve pas très PROPRE de publier des méthodes qui ne font que lancer les événements attachés.

    J'ai longtemps dévelopé avec C++ et la solution ici aurait été de placer la classe ServerClientThread comme étant FRIEND de la classe TCPServer. De cette manière, la classe ServerClientThread aurait eu accès au méthode privée de la classe TCPServer. Je sais que sous C# il existe le mot clé INTERNAL mais il n'a pas du tout la même utilité que le mot FRIEND du C++. Par conséquent, il peu arriver que certainnes classes soit développées sous le même assembly et que l'utilisateur nono puisse avoir accès à des fonctions qu'il ne devrait tout simplement pas voir... De toute façon, si l'on jette un petit coup d'oeil à la classe System.Windows.Forms.Button, je ne crois pas qu'on y voit le handler par défaut. En fait il est PROTECTED :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    protected override void OnClick (
        EventArgs e
    )
    Bref, j'ai trouvé un autre moyen plus PROPRE pour les circonstances. J'ai converti la classe ServerClientThread :

    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
    private struct ClientThreadObject
    {
        public Socket ClientSocket;
        public bool StopProcess;
        public bool ProcessStopped;
        public int ClientID;
        public ClientThreadObject(int iClientID, Socket clientSocket)
        {
            ClientID = iClientID;
            ClientSocket = clientSocket;
            StopProcess = false;
            ProcessStopped = true;
        }
    }
    J'ai créé la fonction Process() privée à ma classe TCPServer. Comme je développe avec le Framework 1.1 je ne peux pas passer directement des paramètres à mon Thread et je ne désire pas utiliser le ThreadPool pour des raisons personnelles. Donc j'ai ajouté une ArrayList à ma classe TCPServer, et à l'intérieur je conserve chacun des objects ClientThreadObject créé pour chaque Thread. Lorsque la fonction Process() est démaré pour un Thread, je récupère l'object courant par :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    public void Process()
    {
        ClientThreadObject ctl = (ClientThreadObject)m_ClientThreadList[m_ClientThreadList.Count - 1];
         // Loop du thread ici
    }
    Par la suite, j'ai une référence sur chaque Thread et du même coup mes méthodes public qui exécutaient mes événements ont pu redevenir PRIVÉ.

    Enfin ! Tout fonctionne très bien.
    Merci à tous

  6. #6
    Rédacteur
    Avatar de abelman
    Inscrit en
    Février 2003
    Messages
    1 106
    Détails du profil
    Informations forums :
    Inscription : Février 2003
    Messages : 1 106
    Par défaut
    OK.

    Pour info, j'ai un peu la même classe. Mes handlers par défaut sont protected chez moi. La classe TCPServer est abstraite et ce sont les classes dérivées (TCPServerXXX) qui les redéfinissent éventuellement.

    Par ailleurs, pourquoi ne pas utiliser le ThreadPool? Sache que la solution 'un thread par client' devient lourde si tu as beaucoup de clients.(100 clients --> 100 threads)
    Rien ne t'empêche de créer ta propre classe ThreadPool pour palier au défauts de celle de .NET

    @+

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Events et threads
    Par HamzuS The Great dans le forum Développement Windows
    Réponses: 1
    Dernier message: 16/12/2010, 22h18
  2. Récupération d'event avec thread
    Par koyot3 dans le forum VB.NET
    Réponses: 1
    Dernier message: 17/09/2010, 08h25
  3. Création events pour thread
    Par syphon22 dans le forum Langage
    Réponses: 3
    Dernier message: 19/01/2010, 08h50
  4. Swing Event Dispatching Thread
    Par YeFFreY dans le forum EDT/SwingWorker
    Réponses: 14
    Dernier message: 23/06/2008, 10h53
  5. [C#]Thread, event/delegate et Form
    Par doccpu dans le forum Windows Forms
    Réponses: 28
    Dernier message: 01/08/2005, 18h35

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