Mon socket reçoit des données, répond, puis après... chais pas...

Mon socket reçoit des données, répond, puis après... chais pas... - C#/.NET managed - Programmation

Marsh Posté le 28-12-2005 à 16:41:35    

Salut,
 
Vu que j'ai rien à foutre au taff, j'essaie de faire de la veille techno histoire de me constituer des librairies de softs à utiliser...
 
J'ai décidé de tenter de me faire une DLL qui me permette d'accéder à des fichiers sur un serveur distant, sans utiliser le protocole NETBIOS ni FTP. Le but, c'est de pouvoir travailler sur n'importe quel port, et utiliser un format d'échange basé sur du XML passant par HTTP 1.1 (et peut-être d'autres protocoles si besoin).
 
Pour le moment, je suis en train d'écrire le début du service serveur, c'est à dire la partie qui va tourner sur le PC qui doit partager ses fichiers.
Je suis loin d'avoir commencé le début du commencement ma bon...
 
J'ai trouvé sur un site comment écrire une appli serveur utilisant des connections asychrones. Je pense que c'est ce que je peux faire de mieu pour ce qui est des performances, non ?
 
Ensuite, je suis allé sur la MSDN et j'ai transformé le schmilblik en service.
 
Bon, ça marche, c'est super.
 
Sauf que j'ai un problème et une question :
- Lorsque je me connecte en telnet sur le service, je tape quelques trucs plus la suite de caractère idiquant la fin d'échange. Jusque là, pas de problème.
- Le service me répond alors ce qu'il est censé me répondre, c'est à dire "yo" suivi de la chaîne "^^" indiquant la fin d'un échange.
 
Sauf que là, la connection du côté serveur n'écoute plus, et telnet ne s'en rend apparement pas compte : je peux taper n'importe quoi dans ma fenêtre et ça fait plus jamais rien. D'où vient le problème ?
 
Ensuite, lorsque j'ai demandé l'arrêt du service, j'ai pas tout compris à ce qui s'est passé d'après le log...
En effet, le truc s'arrête, puis se remet à écouter, et reçoit même une demande de connection... avant de s'arrêter comme un bourrin !
 
Bref, c'est pas gagné :D
 
Ci-dessous le projet (enfin, les), ainsi que le log correspondant au test que j'ai effectué.
 
Les sources et les EXE testés :
http://www.bci-logs.fr/magictalk.zip
 
Le test :


net start magictalkserver
telnet localhost 1000
prout^^
(kill de la fenêtre cause telnet parti en live après réception de "yo^^" )
net stop magictalkserver


 
Le log (pour ceux que ça intéresse, le projet "logFile" est standalone et bien pratique :)) :


################################################################################
MagicTalkServer is starting on 28/12/2005 16:06:27
Service MagicTalk Server version 1.0 debug
Legend :
PP : Application parameter (see INI file)
II : Informal message
EE : Blocking error
ee : Non-blocking error
WW : Warning message
################################################################################
 
28/12/2005 II : Démarrage du service
28/12/2005 II : Début de l'écoute sur le port 1000
28/12/2005 II : Apparement, on vient de se connecter à moi
28/12/2005 II : J'ai de la lecture
28/12/2005 II : J'ai pas encore tout reçu, j'attends encore
28/12/2005 II : J'ai de la lecture
28/12/2005 II : J'ai pas encore tout reçu, j'attends encore
28/12/2005 II : J'ai de la lecture
28/12/2005 II : J'ai pas encore tout reçu, j'attends encore
28/12/2005 II : J'ai de la lecture
28/12/2005 II : J'ai pas encore tout reçu, j'attends encore
28/12/2005 II : J'ai de la lecture
28/12/2005 II : J'ai pas encore tout reçu, j'attends encore
28/12/2005 II : J'ai de la lecture
28/12/2005 II : J'ai pas encore tout reçu, j'attends encore
28/12/2005 II : J'ai de la lecture
28/12/2005 II : J'ai pas encore tout reçu, j'attends encore
28/12/2005 II : J'ai de la lecture
28/12/2005 II : J'ai pas encore tout reçu, j'attends encore
28/12/2005 II : J'ai de la lecture
28/12/2005 II : Je crois que j'ai reçu tout ce qu'il y avait à recevoir
28/12/2005 II : J'envoie de la lecture
28/12/2005 II : J'émet des données
28/12/2005 II : Nettoyage des sockets
28/12/2005 II : Socket mort détecté : on le vire
28/12/2005 II : Destruction d'un socket
28/12/2005 II : Tiens, j'ai planté
28/12/2005 II : Fin du nettoyage des sockets
28/12/2005 II : Nettoyage des sockets
28/12/2005 II : Fin du nettoyage des sockets
28/12/2005 II : Nettoyage des sockets
28/12/2005 II : Fin du nettoyage des sockets
28/12/2005 II : Arrêt du service
28/12/2005 II : Arrêt demandé, destruction de tous les sockets
28/12/2005 II : Apparement, on vient de se connecter à moi
28/12/2005 II : OK, normalement je dois pouvoir recevoir des connections
 
################################################################################
MagicTalkServer is ending...
Process summary :
Readen application parameters : 0
Informal messages               36
Blocking errors                 0
Non-blocking errors             0
Warning messages                0
################################################################################
 
 
 
 
 


 
 
-- Edit : Correction de l'url du fichier ZIP car Lycos ne permet pas de télécharger un fichier


Message édité par Arjuna le 29-12-2005 à 11:10:41
Reply

Marsh Posté le 28-12-2005 à 16:41:35   

Reply

Marsh Posté le 28-12-2005 à 16:42:13    

Voilà, si qq1 pouvait me dire ce qu'il se passe et m'indiquer comme résoudre ces problème, ce serait super sympa :)

Reply

Marsh Posté le 28-12-2005 à 16:43:59    

Ah, aussi, je veux bien qu'on m'explique pourquoi la ligne "Tiens, j'ai planté" :D Parceque je ne pense pas que ce soit normal que cette exception se lance alors que j'ai rien fait de mal...

Reply

Marsh Posté le 28-12-2005 à 17:39:13    

Ca passionne les foules, c'est fou.
 
Pour reprendre le problème du telnet (le principal problème en fait) : lorsque j'ai terminé de recevoir les données, le code laisse la connection ouverte, mais il n'affiche plus.
On le voit d'ailleurs, puisqu'à ce moment telnet se contente d'afficher les infos qu'on tape, alors que lorsque le serveur écoute, il n'affiche rien, puisque tout est aussitôt consommé par le serveur.
Par contre, la connection reste ouverte, sinon telnet afficherait un message comme quoi il est déconnecté, et ce n'est pas le cas.

Reply

Marsh Posté le 28-12-2005 à 17:40:49    

Exemple : telnet www.google.com 80
 
On tapes 2 fois <entrée>
 
On retourne à l'invite car le site web a fermé la connection, voyant qu'il n'avait rien à traîter.

Reply

Marsh Posté le 28-12-2005 à 17:43:13    

Et ça, c'est si je tape :
 


telnet www.google.com 80
GET / HTTP/1.0
 
 


 
Retour :

Code :
  1. HTTP/1.0 302 Found
  2. Location: http://www.google.fr/
  3. Set-Cookie: PREF=ID=14fcab8f82ffce4c:TM=1135788126:LM=1135788126:S=c9zvbMIdzfk9g
  4. 1zU; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com
  5. Content-Type: text/html
  6. Server: GWS/2.1
  7. Content-Length: 221
  8. Date: Wed, 28 Dec 2005 16:42:06 GMT
  9. Connection: Keep-Alive
  10. <HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=us-ascii"
  11. >
  12. <TITLE>302 Moved</TITLE></HEAD><BODY>
  13.                                       <H1>302 Moved</H1>
  14.                                                         The document has moved
  15.                                                                               <A
  16. HREF="http://www.google.fr/">here</A>.
  17. </BODY></HTML>
  18. Perte de la connexion à l'hôte.


Telnet reste ouvert histoire de montrer le résultat, mais il indique bien que la connection est perdue.


Message édité par Arjuna le 28-12-2005 à 17:45:09
Reply

Marsh Posté le 29-12-2005 à 10:52:55    

Ca inspire toujours les foules, c'est fou :(
 
J'ai fait quelques modifs au niveau des logs, et j'ai plus d'infos sur ce qu'il se passe.
 
Apparement :
- Après émission de données, mon socket part bel et bien en live : pour le service, il est toujours en train d'écouter, alors que ce n'est pas le cas
- Lorsque je cherche à détruire mon socket, j'ai un problème de parcours de la collection dans laquelle il est stocké, et c'est ça qui fait l'erreur durant le nettoyage
- Lorsque j'arrête le service, on socket qui n'est plu censé être actif reprends la main puis s'arrêté.
 
Log :


29/12/2005 09:33:20 II : Données reçues
29/12/2005 09:33:20 II : Lecture des données reçues
29/12/2005 09:33:20 II : Données reçues trouvées, on les stocke dans un coin
29/12/2005 09:33:20 II : Il n'y a plus rien à recevoir
29/12/2005 09:33:20 II : Je réponds un A/R
29/12/2005 09:33:20 II : Envoie d'un message
29/12/2005 09:33:20 II : J'efface le buffer de réception
29/12/2005 09:33:20 II : Emission de données
29/12/2005 09:37:46 II : Nettoyage des sockets inactifs
29/12/2005 09:37:46 II : Socket dans un état de timeout trouvé : on le détruit (ID=)
29/12/2005 09:37:46 II : Destruction d'un socket
29/12/2005 09:37:46 II : Etat du socket détruit
29/12/2005 09:37:46 II : Socket en cours de travail, on le kill
29/12/2005 09:37:46 II : Socket killé
29/12/2005 09:37:46 II : Socket détruit
29/12/2005 09:37:46 II : Socket  détruit
29/12/2005 09:37:46 ee : Plantate durant le nettoyage des sockets inactifs
29/12/2005 09:37:46 ee : La collection a été modifiée ; l'opération d'énumération peut ne pas s'exécuter.
29/12/2005 09:37:46 ee :    at System.Collections.ArrayListEnumeratorSimple.MoveNext()
   at MagicTalkServer.Server.CheckSockets(Object eventState)
29/12/2005 09:37:46 II : Fin du nettoyage des sockets
29/12/2005 09:42:46 II : Nettoyage des sockets inactifs
29/12/2005 09:42:46 II : Fin du nettoyage des sockets
29/12/2005 09:43:07 II : Arrêt du service
29/12/2005 09:43:07 II : Arrêt demandé, destruction de tous les sockets
29/12/2005 09:43:08 II : Demande de connection reçue
29/12/2005 09:43:08 II : Socket a terminé l'écoute
29/12/2005 09:43:08 II : Fin d'écoute
29/12/2005 09:43:08 II : Etablissement de la connection
29/12/2005 09:43:08 II : Connection établie, début de l'écoute


 
Fonctions incriminée :

Code :
  1. ///  
  2.  /// Decription: Call back method to handle incoming data.
  3.  ///  
  4.  /// <param name="ar">Status of an asynchronous operation.</param>
  5.  protected void ReadCallback(IAsyncResult ar)
  6.  {
  7.   log.AddMessage("Données reçues" );
  8.   String content = String.Empty;
  9.   // Retrieve the state object and the handler socket
  10.   // from the async state object.
  11.   StateObject state = (StateObject) ar.AsyncState;
  12.   Socket handler = state.workSocket;
  13.   try
  14.   {
  15.    log.AddMessage("Lecture des données reçues" );
  16.    // Read data from the client socket.
  17.    int bytesRead = handler.EndReceive(ar);
  18.    if (bytesRead > 0)
  19.    {
  20.     log.AddMessage("Données reçues trouvées, on les stocke dans un coin" );
  21.     // There might be more data, so store the data received so far.
  22.     Monitor.Enter(state);
  23.     state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
  24.     Monitor.Exit(state);
  25.     // Check for end-of-file tag.
  26.     // If it is not there, read more data.
  27.     content = state.sb.ToString();
  28.     if (content.IndexOf("^^" ) > -1)
  29.     {
  30.      log.AddMessage("Il n'y a plus rien à recevoir" );
  31.      // All the data has been read from the
  32.      // client. Display it on the console.
  33.      //Console.WriteLine("Read {0} bytes from socket.\nData: {1}",
  34.      // content.Length, content);
  35.      // Process the received message
  36.      state.TimeStamp = DateTime.Now;
  37.      log.AddMessage("Je réponds un A/R" );
  38.      Send(state.workSocket, "yo\n" );
  39.      //HandleMessages(state, content);
  40.      // Echo the data back to the client.
  41.      // Send(state, content);
  42.      log.AddMessage("J'efface le buffer de réception" );
  43.      state.sb.Length = 0;
  44.     }
  45.     else
  46.     {
  47.      log.AddMessage("J'ai pas encore tout reçu, j'attends encore" );
  48.      // Not all data received. Get more.
  49.      handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(this.ReadCallback), state);
  50.     }
  51.    }
  52.    else
  53.    { // Disconnected
  54.     log.AddBlockingError("Déconnection avant la fin de la transmission." );
  55.     RemoveSocket(state);
  56.    }
  57.   }
  58.   catch (System.Net.Sockets.SocketException es)
  59.   {
  60.    log.AddBlockingError("Erreur réseau", es);
  61.    RemoveSocket(state);
  62.    if (es.ErrorCode != 64)
  63.    {
  64.     log.AddBlockingError(string.Format("ReadCallback Socket Exception: {0}, {1}.", es.ErrorCode, es.ToString()));
  65.    }
  66.   }
  67.   catch (Exception e)
  68.   {
  69.    log.AddBlockingError("Erreur inconnue", e);
  70.    RemoveSocket(state);
  71.    if (e.GetType().FullName != "System.ObjectDisposedException" )
  72.    {
  73.     log.AddMessage(string.Format("ReadCallback Exception: {0}.", e.ToString()));
  74.    }
  75.   }
  76.  }
  77.  ///  
  78.  /// Description: Check for dormant sockets and close them.
  79.  ///  
  80.  /// <param name="eventState">Required parameter for a timer call back
  81.  /// method.</param>
  82.  private void CheckSockets(object eventState)
  83.  {
  84.   log.AddMessage("Nettoyage des sockets inactifs" );
  85.   lostTimer.Change(System.Threading.Timeout.Infinite,
  86.    System.Threading.Timeout.Infinite);
  87.   try
  88.   {
  89.    foreach (StateObject state in connectedSocks)
  90.    {
  91.     if (state.workSocket == null)
  92.     { // Remove invalid state object
  93.      log.AddWarning(string.Format("Socket dans un état invalide trouvé : on le détruit (ID={0})", state.id));
  94.      Monitor.Enter(connectedSocks);
  95.      if (connectedSocks.Contains(state))
  96.      {
  97.       log.AddWarning("Le socket à détruire est bien trouvé" );
  98.       connectedSocks.Remove(state);
  99.       Interlocked.Decrement(ref sockCount);
  100.       log.AddWarning("Le socket à détruire est bien détruit" );
  101.      }
  102.      else
  103.      {
  104.       log.AddNonBlockingError("On ne trouve plus le socket à détruire !" );
  105.      }
  106.      Monitor.Exit(connectedSocks);
  107.      log.AddWarning(string.Format("Socket {0} détruit", state.id));
  108.     }
  109.     else
  110.     {
  111.      if (DateTime.Now.AddTicks(-state.TimeStamp.Ticks).Minute > timeoutMinutes)
  112.      {
  113.       log.AddMessage(string.Format("Socket dans un état de timeout trouvé : on le détruit (ID={0})", state.id));
  114.       RemoveSocket(state);
  115.       log.AddMessage(string.Format("Socket {0} détruit", state.id));
  116.      }
  117.     }
  118.    }
  119.   }
  120.   catch (Exception e)
  121.   {
  122.    log.AddNonBlockingError("Plantate durant le nettoyage des sockets inactifs", e);
  123.   }
  124.   finally
  125.   {
  126.    log.AddMessage("Fin du nettoyage des sockets" );
  127.    lostTimer.Change(Server.timerTimeout, Server.timerTimeout);
  128.   }
  129.  }
  130.  ///  
  131.  /// Decription: Stop the threads for the port listener.
  132.  ///  
  133.  public void Stop()
  134.  {
  135.   log.AddMessage("Arrêt demandé, destruction de tous les sockets" );
  136.   int lcv;
  137.   lostTimer.Dispose();
  138.   lostTimer = null;
  139.   for (lcv = 0; lcv < numThreads; lcv++)
  140.   {
  141.    if (!serverThread[lcv].IsAlive)
  142.     threadEnd[lcv].Set(); // Set event if thread is already dead
  143.   }
  144.   ShuttingDown = true;
  145.   // Create a connection to the port to unblock the listener thread
  146.   Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  147.   IPEndPoint endPoint = new IPEndPoint(IPAddress.Loopback, this.portNumber);
  148.   sock.Connect(endPoint);
  149.   //sock.Close();
  150.   sock = null;
  151.   // Check thread end events and wait for up to 5 seconds.
  152.   for (lcv = 0; lcv < numThreads; lcv++)
  153.    threadEnd[lcv].WaitOne(5000, false);
  154.  }
  155.  ///  
  156.  /// Decription: Call back method to accept new connections.
  157.  ///  
  158.  /// <param name="ar">Status of an asynchronous operation.</param>
  159.  private void AcceptCallback(IAsyncResult ar)
  160.  {
  161.   log.AddMessage("Demande de connection reçue" );
  162.   // Signal the main thread to continue.
  163.   allDone.Set();
  164.   // Get the socket that handles the client request.
  165.   Socket listener = (Socket) ar.AsyncState;
  166.   Socket handler = listener.EndAccept(ar);
  167.   // Create the state object.
  168.   StateObject state = new StateObject();
  169.   state.workSocket = handler;
  170.   state.TimeStamp = DateTime.Now;
  171.   try
  172.   {
  173.    log.AddMessage("Etablissement de la connection" );
  174.    Interlocked.Increment(ref sockCount);
  175.    Monitor.Enter(connectedSocks);
  176.    connectedSocks.Add(state);
  177.    Monitor.Exit(connectedSocks);
  178.    log.AddMessage("Connection établie, début de l'écoute" );
  179.    handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,new AsyncCallback(this.ReadCallback), state);
  180.    if (sockCount > this.maxSockets)
  181.    {
  182.     log.AddBlockingError("Trop de connections, on détruit la connection" );
  183.     RemoveSocket(state);
  184.     //handler.Shutdown(SocketShutdown.Both);
  185.     //handler.Close();
  186.     handler = null;
  187.     state = null;
  188.    }
  189.   }
  190.   catch (SocketException es)
  191.   {
  192.    log.AddBlockingError("Impossible d'établir la connection", es);
  193.    RemoveSocket(state);
  194.   }
  195.   catch (Exception e)
  196.   {
  197.    log.AddBlockingError("Impossible d'établir la connection (2)", e);
  198.    RemoveSocket(state);
  199.   }
  200.  }


 
D'après le log, c'est la dernière fonction qui était restée blockée (certainement sur le allDone.Set()) et qui se débloque lors de l'arrêt du service.

Reply

Marsh Posté le 29-12-2005 à 11:07:10    

Bon, j'ai résolu le premier problème : l'exception qui empêche la destruction de tous les sockets inactifs.
 

Code :
  1. ///  
  2.  /// Description: Check for dormant sockets and close them.
  3.  ///  
  4.  /// <param name="eventState">Required parameter for a timer call back
  5.  /// method.</param>
  6.  private void CheckSockets(object eventState)
  7.  {
  8.   log.AddMessage("Nettoyage des sockets inactifs" );
  9.   lostTimer.Change(System.Threading.Timeout.Infinite,
  10.    System.Threading.Timeout.Infinite);
  11.   try
  12.   {
  13.    bool mustContinue = true;
  14.    while(mustContinue)
  15.    {
  16.     mustContinue = false;
  17.     foreach (StateObject state in connectedSocks)
  18.     {
  19.      if (state.workSocket == null)
  20.      { // Remove invalid state object
  21.       mustContinue = true;
  22.       log.AddWarning(string.Format("Socket dans un état invalide trouvé : on le détruit (ID={0})", state.id));
  23.       Monitor.Enter(connectedSocks);
  24.       if (connectedSocks.Contains(state))
  25.       {
  26.        log.AddWarning("Le socket à détruire est bien trouvé" );
  27.        connectedSocks.Remove(state);
  28.        Interlocked.Decrement(ref sockCount);
  29.        log.AddWarning("Le socket à détruire est bien détruit" );
  30.       }
  31.       else
  32.       {
  33.        log.AddNonBlockingError("On ne trouve plus le socket à détruire !" );
  34.       }
  35.       Monitor.Exit(connectedSocks);
  36.       log.AddWarning(string.Format("Socket {0} détruit", state.id));
  37.       break;
  38.      }
  39.      else
  40.      {
  41.       if (DateTime.Now.AddTicks(-state.TimeStamp.Ticks).Minute > timeoutMinutes)
  42.       {
  43.        mustContinue = true;
  44.        log.AddMessage(string.Format("Socket dans un état de timeout trouvé : on le détruit (ID={0})", state.id));
  45.        RemoveSocket(state);
  46.        log.AddMessage(string.Format("Socket {0} détruit", state.id));
  47.        break;
  48.       }
  49.      }
  50.     }
  51.    }
  52.   }
  53.   catch (Exception e)
  54.   {
  55.    log.AddNonBlockingError("Plantate durant le nettoyage des sockets inactifs", e);
  56.   }
  57.   finally
  58.   {
  59.    log.AddMessage("Fin du nettoyage des sockets" );
  60.    lostTimer.Change(Server.timerTimeout, Server.timerTimeout);
  61.   }
  62.  }


 
Par contre, pour le reste, je ne comprends pas...

Reply

Marsh Posté le 29-12-2005 à 11:13:56    

Et j'aimerais bien que mon socket continue à écouter après émission d'une info... Ou se détruire à la limite, mais faire un truc quoi...
 
Parceque là, je vais rapidement arriver à saturation si j'ai 1 socket mort par échange, vu que je ne flush les socket qu'au bout des 5 minutes de timeout...

Reply

Marsh Posté le 29-12-2005 à 12:13:09    

Là, je reste sur mon derrière... Je suis quand même pas le seul à vouloir faire un serveur Socks asynchrone...


Message édité par Arjuna le 29-12-2005 à 12:13:28
Reply

Marsh Posté le 29-12-2005 à 12:13:09   

Reply

Marsh Posté le 09-01-2006 à 11:29:45    

Question : c'est quoi la différence entre ton code et ça :
http://msdn.microsoft.com/library/ [...] socket.asp
 
Ou bien : c'est quoi qui va pas ou que tu capte pas, par rapport à la solution MS ?
 
Désolé, j'ai pas le temps de lire tout ton post, mais comme j'ai déjà joué avec des sockets je peux ptet te filer un coup de pouce.


---------------
Tout est normal, suffit de comprendre pourquoi.
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed