2012-04-13 5 views
2

Wir entwickeln eine .NET CF 3.5-Anwendung auf einer Windows Embedded CE 6-Plattform. Wir versuchen, einen kleinen Webserver (HTTP 1.0) in .NET zu implementieren, der eine WebApp bereitstellen und auf einfache REST-Anforderungen reagieren soll.Erhöhen Sie den maximalen Rückstand in Windows CE, indem Sie SOMAXCONN ändern?

Unsere Implementierung folgt dem Muster in diesem MSDN-Artikel: http://msdn.microsoft.com/en-us/library/aa446537.aspx. Wir verwenden einen TCP-Abhörsocket, Async-Callbacks in Kombination mit BeginAccept, EndAccept, BeginRecieve und EndReceive.

Eine eingehende Verbindung am Listening-Port wird von einem asynchronen Rückruf akzeptiert. (siehe http://msdn.microsoft.com/en-us/library/5bb431f9.aspx). Durch den Aufruf der EndAccept-Methode teilen wir dem empfangenden Port innerhalb dieses asynchronen Callbacks die Verbindung zu einem neuen Socket mit und geben den Port frei, so dass neue eingehende Verbindungsanforderungen vom empfangenden Port akzeptiert werden können. Die bereits angenommene Anfrage wird in einem eigenen Thread mit bearbeitet (weil sie im asynchronen Callback bearbeitet wird).

Wir haben bereits versucht, die Zeit zwischen BeginAccept und EndAccept zu minimieren. Während dieser Zeit zwischen dem Aufruf von BeginAccept und EndAccept werden eingehende Verbindungsanforderungen in die Backlog-Warteschlange des hörenden Sockets gestellt. Die Länge dieser Warteschlange kann über den sogenannten Backlog-Parameter konfiguriert werden - dieser Parameter hat ein plattformabhängiges Maximum. Wenn die Backlog-Warteschlange erschöpft ist, werden neue tcp-Verbindungsanforderungen während des Drei-Wege-Handshakes zurückgewiesen (Client/Browser erhält eine RST als Antwort auf seine syn).

Jetzt stießen wir auf das Problem, dass die meisten modernen Browser wie Firefox, Chrome, Safari, z. Verwenden Sie bis zu 15 (oder mehr) gleichzeitige Verbindungen, um Daten von einem Server zu laden (die maximale Anzahl gleichzeitiger Verbindungen pro Host kann in Firefox mit ungefähr konfiguriert werden: config -> network.http.max-connections-pro-server). Wenn eine Seite geladen wird, stellt der Browser bei Bedarf 15 Verbindungen her, abhängig von der Anzahl der Ressourcen, die geladen werden müssen (z. B. Bilder, Javascript oder CSS-Dateien).

Die Methode .NET CF socket.listen (siehe http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.listen.aspx) ermöglicht die Definition einer Rückstandsnummer.
Aus unserem Verständnis sollten wir einen Rückstand von mehr als 15 z. 20 oder so, weil alle Verbindungsanforderungen gleichzeitig vom Browser ausgelöst werden, so dass unser kleiner Webserver von 15 gleichzeitigen Verbindungsanfragen getroffen wurde. Eine zu kleine Rückstandswarteschlangengröße führt zu abgebrochenen TCP-Verbindungen, da nicht alle eingehenden Verbindungen eine Warteschlange sein können, bis der abhörende Socket sie alle akzeptieren kann. In Firebug oder Chrome werden diese Anforderungen als "abgebrochen" angezeigt. Also haben wir den Rückstand in unserem Fall von socket.listen (20) auf 20 erhöht und gehofft, dass alles in Ordnung wäre und bereit wäre, selbst die gierigsten Browser zu überstehen.

Problem ist, der Parameter backlog im Aufruf von socket.listen() ist still auf SOMAXXCON (max. 5 Verbindungen in unserem Fall) festgelegt. Wenn Sie eine höhere Zahl als diese festlegen, hat dies keine Auswirkungen. Wenn ein Browser z.B. 16 gleichzeitige Socket-Verbindungen, einige gingen verloren, weil einige Sockets einfach nicht in die Backlog-Queue von 5 passen und die TCP-Verbindung eine TCP-RST vom Webserver bekommt - und einige Ressourcen auf der Webseite fehlen.

Gibt es eine Möglichkeit, die SOMAXXCON in Windows Embedded CE 6.0 zu ändern? (Wir können das Plattform-Image ändern - nutzen Sie plattform builder). Oder gibt es einen Fehler in unserem Verständnis dieser Angelegenheit?

Wir haben den Quellcode angebracht, die wir zur Zeit mit:

public class StateObject 
    { 
     // Client socket. 
     public Socket workSocket = null; 
     // Size of receive buffer. 
     public const int BufferSize = 1024; 
     // Receive buffer. 
     public byte[] buffer = new byte[BufferSize]; 
     // Received data string. 
     public StringBuilder sb = new StringBuilder(); 
    } 

public void StartListening() 
    { 
     logger.Debug("Started Listening at : " + this.listeninghostIp + ":" + this.listeningport); 
     IPEndPoint localEP = new IPEndPoint(IPAddress.Parse(this.listeninghostIp), Convert.ToInt32(this.listeningport)); 
     listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 

     listener.Bind(localEP); 
     listener.Listen(10); 
     ThreadPool.QueueUserWorkItem(new WaitCallback(CheckForConnections)); 
    } 

public void CheckForConnections() 
    { 
     try 
     { 
      logger.Debug("listening successfully started! Waiting for incoming connections..."); 
      listener.BeginAccept(new AsyncCallback(acceptCallback), listener); 
      } 
     catch (Exception ex) 
     { 
      logger.Error("Exception Occured while starting Listening : " + ex.Message.ToString()); 
     } 
    } 

private void acceptCallback(IAsyncResult ar) 
    { 
     try 
     { 

      Socket listener = (Socket)ar.AsyncState; 
      listener.BeginAccept(new AsyncCallback(acceptCallback), listener); 
      Socket handler = listener.EndAccept(ar); 
      StateObject state = new StateObject(); 
      state.workSocket = handler; 
      handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, 
     new AsyncCallback(ReadCallback), state); 

      logger.Debug("listening socket accepted connection..."); 

     } 
     catch (Exception ex) 
     { 
      logger.Error("Error on acceptCallback. Error: " + ex.Message); 
     } 

public void ReadCallback(IAsyncResult ar) 
    { 
     String content = String.Empty; 

     StateObject state = (StateObject)ar.AsyncState; 
     Socket handler = state.workSocket; 
     int bytesRead = handler.EndReceive(ar); 

     if (bytesRead > 0) 
     { 
      state.sb.Append(Encoding.ASCII.GetString(
      state.buffer, 0, bytesRead)); 
     } 
     ClientConnectionFactory.createConnection(ar.AsyncState); 
} 

Antwort

-1

Ich glaube, du den falschen Weg Weg nach unten - Sie können auf jeden Fall dieses sceanrio handhaben, ohne den Rückstand Anforderungsnummer zu ändern.

Die Clientanforderungen werden an Port 80 gesendet, die Antworten gehen jedoch nicht an Port 80 zurück. Dies bedeutet, dass Sie asynchrone Sockethandling verwenden können, um die Anforderung zu empfangen, und diese dann zum Analysieren übergeben und antworten Sie auf diese Weise, dass die nachfolgenden Anforderungen nicht auf die vollständige Bearbeitung der vorherigen Anforderungen warten. Wir verwenden diese Technik in unserem Padarn-Webserver und haben keine Probleme, mehrere Anfragen von einzelnen Client-Browsern oder sogar mehrere Anfragen von mehreren gleichzeitigen Clients zu bearbeiten.

+0

Hallo Chris, danke für deine schnelle Antwort, ich habe meinen Beitrag aktualisiert, um mehr Einblick zu geben und unser Verständnis dieser speziellen Angelegenheit zu zeigen/zu verifizieren! Aus meiner Sicht verwenden wir bereits asynchrone Socket-Handhabung. – Chris

+0

Ich muss die Padarn-Quelle ansehen, um genau zu sehen, wie unser Muster aussieht, aber ich bin mir sicher, dass wir den Rückstand nicht anpassen und dass wir keine Probleme mit verloren haben Anforderungen, auch von mehreren Clients und wenn eine Seite viele Includes verwendet, die viele Anforderungen an den Server zum Rendern der Seite erfordern. Ich werde sehen, ob ich bald dazu komme, aber es wird wahrscheinlich Montag sein. – ctacke

+0

Bitte, Sie sollten die Frage zuerst beantworten, dann können Sie einen Ratschlag geben, ein Ratschlag allein ist gut, nur um ein Kommentar zu sein. –

Verwandte Themen