2010-05-08 11 views
3

Je travaille sur un serveur web en C# et je l'ai en cours d'exécution sur les appels de socket asynchrones. La chose étrange est que pour une raison quelconque, lorsque vous commencez à charger des pages, la 3ème demande est l'endroit où le navigateur ne se connectera pas. Il ne cesse de dire "Connexion ..." et ne s'arrête jamais. Si je frappe arrêter. et puis rafraichissez, il chargera à nouveau, mais si j'essaye une autre fois après cela il fait la chose où il ne charge pas encore. Et ça continue dans ce cycle. Je ne suis pas vraiment sûr de ce qui le fait faire ça.Performances impaires avec le socket serveur asynchrone C#

Le code est en quelque sorte piraté à partir de quelques exemples et d'un ancien code que j'avais. Tous les conseils divers seraient utiles aussi bien.

Heres ma petite classe Listener qui gère tout

(. pastied here pensé qu'il pourrait être plus facile à lire de cette façon)

using System; 
using System.Collections.Generic; 
using System.Net; 
using System.Net.Sockets; 
using System.Text; 
using System.Threading; 
using irek.Request; 
using irek.Configuration; 
namespace irek.Server 
{ 
    public class Listener 
    { 
     private int port; 
     private Socket server; 
     private Byte[] data = new Byte[2048]; 
     static ManualResetEvent allDone = new ManualResetEvent(false); 
     public Config config; 

     public Listener(Config cfg) 
     { 
      port = int.Parse(cfg.Get("port")); 
      config = cfg; 
      ServicePointManager.DefaultConnectionLimit = 20; 
     } 

     public void Run() 
     { 
      server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
      IPEndPoint iep = new IPEndPoint(IPAddress.Any, port); 
      server.Bind(iep); 

      Console.WriteLine("Server Initialized."); 
      server.Listen(5); 
      Console.WriteLine("Listening..."); 
      while (true) 
      { 
       allDone.Reset(); 
       server.BeginAccept(new AsyncCallback(AcceptCon), server); 
       allDone.WaitOne(); 
      } 

     } 

     private void AcceptCon(IAsyncResult iar) 
     { 
      allDone.Set(); 
      Socket s = (Socket)iar.AsyncState; 
      Socket s2 = s.EndAccept(iar); 
      SocketStateObject state = new SocketStateObject(); 
      state.workSocket = s2; 
      s2.BeginReceive(state.buffer, 0, SocketStateObject.BUFFER_SIZE, 0, new AsyncCallback(Read), state); 
     } 

     private void Read(IAsyncResult iar) 
     { 
      try 
      { 
       SocketStateObject state = (SocketStateObject)iar.AsyncState; 
       Socket s = state.workSocket; 

       int read = s.EndReceive(iar); 

       if (read > 0) 
       { 
        state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, read)); 

        SocketStateObject nextState = new SocketStateObject(); 
        nextState.workSocket = s; 
        s.BeginReceive(state.buffer, 0, SocketStateObject.BUFFER_SIZE, 0, new AsyncCallback(Read), nextState); 
       } 
       if (state.sb.Length > 1) 
       { 
        string requestString = state.sb.ToString(); 
        // HANDLE REQUEST HERE 
        byte[] answer = RequestHandler.Handle(requestString, ref config); 
        // Temporary response 
        /* 
        string resp = "<h1>It Works!</h1>"; 
        string head = "HTTP/1.1 200 OK\r\nContent-Type: text/html;\r\nServer: irek\r\nContent-Length:"+resp.Length+"\r\n\r\n"; 
        byte[] answer = Encoding.ASCII.GetBytes(head+resp); 
        // end temp. 
        */ 
        state.workSocket.BeginSend(answer, 0, answer.Length, SocketFlags.None, new AsyncCallback(Send), s); 
       } 
      } 
      catch (Exception e) 
      { 
       Console.WriteLine(e.Message); 
       Console.WriteLine(e.StackTrace); 
       return; 
      } 
     } 

     private void Send(IAsyncResult iar) 
     { 
      try 
      { 
       SocketStateObject state = (SocketStateObject)iar.AsyncState; 
       int sent = state.workSocket.EndSend(iar); 
       state.workSocket.Shutdown(SocketShutdown.Both); 
       state.workSocket.Close(); 
      } 
      catch (Exception) 
      { 

      } 
      return; 
     } 
    } 
} 

Et mon SocketStateObject:

public class SocketStateObject 
{ 
    public Socket workSocket = null; 
    public const int BUFFER_SIZE = 1024; 
    public byte[] buffer = new byte[BUFFER_SIZE]; 
    public StringBuilder sb = new StringBuilder(); 
} 

** EDIT **

J'ai mis à jour le code avec s Quelques suggestions de Chris Taylor.

+0

Vérifiez ma réponse pour les mises à jour supplémentaires. Je remarque que vous avez également une incompatibilité de type d'objet dans la paire BeginSend/Send. –

Répondre

1

Il suffit de regarder le code rapidement, je pense que vous pourriez arrêter enquing vos AsyncReads parce que s.Available retourne 0, je me réfère au code suivant

if (read > 0) 
{ 
    state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, read)); 
    if (s.Available > 0) 
    { 
     s.BeginReceive(state.buffer, 0, SocketStateObject.BUFFER_SIZE, 0, new AsyncCallback(Read), state); 
     return; 
    } 
} 

pour confirmer, modifier ce qui précède à la

if (read > 0) 
{ 
    state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, read)); 

    SocketStateObject nextState = new SocketStateObject(); 
    nextState.workSocket = s; 
    s.BeginReceive(state.buffer, 0, SocketStateObject.BUFFER_SIZE, 0, new AsyncCallback(Read), nextState); 
} 

suivante ce n'est pas la correction complète du code, mais il confirmera si tel est le problème. Vous devez vous assurer que vous fermez vos prises correctement, etc.

Mise à jour J'ai aussi remarqué que vous envoyez le socket en tant que l'état dans l'appel à BeginSend.

state.workSocket.BeginSend(answer, 0, answer.Length, SocketFlags.None, new AsyncCallback(Send), state.workSocket); 

Cependant, votre rappel Send est coulée la AsyncState à SocketStateObject

SocketStateObject state = (SocketStateObject)iar.AsyncState; 

Cela soulèvera InvalidCastExceptions que vous cachez simplement en ajoutant le catch vide. Je suis sûr que d'autres seront d'accord, c'est exceptionnellement mauvaise pratique ayant des captures vides, il cache tant d'informations que vous pourriez utiliser pour déboguer votre problème.

+0

Non, malheureusement, cela n'a pas changé. Donc je ne pense pas que c'était mon problème non plus. Mais cela peut avoir empêché d'autres bugs: P –

+0

@ The.Anti.9, c'est étrange. J'ai juste copié votre code original exactement et pourrait reproduire votre problème. J'ai alors ajouté le changement que j'ai suggéré et à partir de ce point le problème quand loin. Peut-être que vous pouvez poster le code mis à jour? –

+0

ok je l'ai mis à jour. J'ai juste copié et collé le code que vous avez placé sur le mien, donc je ne pense pas que j'ai fait des erreurs. si je pouvais avoir. J'ai travaillé sur d'autres parties de l'application, alors maintenant je pense que nous avons besoin de la cause 'return' sinon elle continue et tente d'analyser une requête vide. et cela jette une exception. –

1

conjecture complètement aléatoire:

http://msdn.microsoft.com/en-us/library/system.net.servicepointmanager.defaultconnectionlimit.aspx

Le nombre maximum de connexions simultanées autorisées par un objet ServicePoint . La valeur par défaut est 2.

+0

C'était une bonne idée, mais pas elle. Les connexions que j'essaie ne sont pas concurrentes de toute façon. Ils sont séparés, l'un après l'autre. Bien que je pensais que pour une raison ou pour une autre, les connexions ne se fermaient pas, cela avait du sens. Mais hélas, pas le cas:/ –

0

Vous devez également noter qu'il existe une condition de concurrence dans votre code.Dans Run(), vous attendez allDone avant d'appeler BeginAccept nouveau:

while (true) 
{ 
    allDone.Reset(); 
    server.BeginAccept(new AsyncCallback(AcceptCon), server); 
    allDone.WaitOne(); // <------ 
} 

Cela est bien, mais dans votre rappel AcceptConn, l'événement est fixé au sommet de la méthode:

private void AcceptCon(IAsyncResult iar) 
{ 
    allDone.Set(); // <------ 

    Socket s = (Socket)iar.AsyncState; 
    Socket s2 = s.EndAccept(iar); 
    SocketStateObject state = new SocketStateObject(); 
    state.workSocket = s2; 
    s2.BeginReceive(state.buffer, 0, SocketStateObject.BUFFER_SIZE, 0, 
     new AsyncCallback(Read), state); 
} 

Le rappel sera exécuté par un thread aléatoire du pool, mais allDone sera défini avant que tout soit réellement fait. Il est tout à fait possible que votre boucle Run() s'exécute à nouveau dans le premier thread avant que le travail dans AcceptCon ne se termine réellement. Cela vous causera de gros problèmes.

Vous devez définir allDone après avoir effectué votre initialisation (et surtout après avoir accédé à tous les membres de la classe non ThreadSafe), comme suit:

private void AcceptCon(IAsyncResult iar) 
{ 

    Socket s = (Socket)iar.AsyncState; 
    Socket s2 = s.EndAccept(iar); 
    SocketStateObject state = new SocketStateObject(); 
    state.workSocket = s2; 

    allDone.Set(); // <------ 

    s2.BeginReceive(state.buffer, 0, SocketStateObject.BUFFER_SIZE, 0, 
     new AsyncCallback(Read), state); 

} 
Questions connexes