2010-11-20 8 views
1

Ich entwickle einen TCP-Server in .NET C#. Es verwendet asynchrone E/A (E/A-Vervollständigung), um eine große Anzahl von Clients gleichzeitig zu verarbeiten. Im Moment habe ich alle TCP-Verbindungen in einer Liste, die ich ständig durchsuche, um eine Änderung in der Zustandsmaschine einer bestimmten Verbindung zu suchen. Die Zustandsmaschine für eine gegebene Verbindung wird aktualisiert, sobald die I/O-Vervollständigung bestimmte Flags setzt.C# .NET Server mehrere Verbindungen abfragen - gibt es einen besseren Weg?

Ich bin mir sicher, es gibt einen besseren Weg, dies zu tun - die aktuelle Implementierung ist sehr prozessorintensiv, da ich nicht auf ein Update warten, sondern polling ohne Drosselung blockiert. Es interessiert mich nicht wirklich, ob mein Server Zyklen verschwendet, aber ich vermute, es ist ein schlechtes Design. Ich versuche, einen Weg zu finden, eine bestimmte Verbindung nur dann zu verarbeiten, wenn I/O-Beendigungssignale dort etwas zu behandeln sind, und zu warten (d. H. Schlafen), wenn nicht. Kann jemand einen guten Weg vorschlagen, dies zu tun?

Ich dachte, dass einige Thread-Synchronisierungs-Dinge funktionieren könnten, wo der Haupt-Thread, der Schleife ist wartet auf jede E/A-Beendigung, um es zu veröffentlichen. Die E/A-Vervollständigung wird jedoch manchmal unter Verwendung des aufrufenden Threads ausgeführt (wenn Daten sofort verfügbar sind, usw.), so dass dies zu Problemen mit dieser Lösung führen würde.

Alles, was Sie vorschlagen können, wäre sehr geschätzt!

Hier ist der (vereinfachten) Schleife ist, die durch den Haupt-Thread ausgeführt wird (rgClient ist die Liste der Clients):

//Do communications on each client we currently have connected. 
//This loops runs backwards so we can delete elements on the fly 
//without have to iterate through more than once. 
lock (rgClient) 
{ 
    for (i = rgClient.Count - 1; i >= 0; i--) 
    { 
     if (!rgClient[i].DoComm()) 
     { 
      rgClient[i].DoClose(); 
      rgClient.RemoveAt(i); 
     } 
    } 
} 

DoComm() für die Verbindung eine Aktualisierung der Zustandsmaschine durchführt, die umfasst Ausführen der Aktivitäten des aktuellen Status und dann ggf. Übergang in einen neuen Zustand. Hier ist der Zustand der Klasse ein einfaches „ack“ Paket für das Senden:

class StateAck : State 
{ 
    public StateAck(TextBox txtOutputExt, Form fmOwner) 
     : base(txtOutputExt, fmOwner) 
    { 
     fWriting = false; 
    } 

    public override bool DoExecute(out Type tpNextState) 
    { 
     PktAck pkt; 

     if (!base.DoExecute(out tpNextState)) 
     { 
      return false; 
     } 

     //Start a write if we haven't yet 
     if (!fWriting) 
     { 
      pkt = new PktAck(); 
      fWriting = true; 

      return FPutPkt(pkt.rgbSerialize()); 
     } 

     //Is the read finished/has an error occurred? 
     if (fDataErrorWrite) 
     { 
      return false; 
     } 

     //Process the data 
     if (fDataWritten) 
     { 
      tpNextState = typeof(StateIdle); 
     } 

     return true; 
    } 

    private bool fWriting; 
} 

Ausführung durchläuft DoExecute() jedesmal DoComm() von dem Haupt-Thread genannt. In 99% der Fälle passiert nichts. Wenn der Schreibvorgang (der durch den Aufruf von FPutPkt() ausgelöst wird) ein Flag dies signalisiert und dann der nächste Zustand auf "Leerlauf" gesetzt wird. Was ich tun möchte, ist, dass der Haupt-Thread nur Clients überprüft, die ihre Netzwerkaktivität beendet haben und etwas haben, das aktualisiert werden muss, um die konstanten und redundanten Durchläufe durch DoExecute() zu vermeiden.

+2

Ich würde vorschlagen, dass Sie Ihren Code zeigen. Auf diese Weise haben wir eine Diskussionsgrundlage. –

+0

Und warum kann die I/O-Vervollständigungsroutine das nicht behandeln? Ist das nicht der Sinn der I/O-Vervollständigung? –

+0

können Sie diesen Artikel http://www.codeproject.com/KB/IP/dotnettcp.aspx überprüfen – Iraklis

Antwort

0

Ich fand eine Lösung, die ziemlich gut zu funktionieren scheint. Verwenden Sie einen EventWaitHandler (System.Threading) mit automatischem Reset auf WaitOne() am Ende jedes Durchgangs durch die Schleife. Dann kann ein beliebiger Callback oder sekundärer Thread den EventWaitHandler durch Aufruf von EWH.Set() signalisieren, wodurch die Schleife einen weiteren Durchlauf durchführen kann. Große Möglichkeit, die CPU-Auslastung einer Polling-Schleife ohne größere Änderungen am Programmablauf zu eliminieren. Hoffe es hilft jemandem.