Willkommen ~Gast!
Registrieren || Einloggen || Hilfe/FAQ || Staff
Probleme mit der Registrierung im Forum? Melde dich unter registerEin Bild.
Autor Beitrag
000
23.12.2012, 21:45
Alcapone



Hallo Thewall!

Ich arbeite gerade an einem Server mit connection-pooling (disconnect & timeout detection ohne polling) um eine möglichst hohe Anzahl an HTTP Requests zu verarbeiten und in unter 50 ms zu beantworten.

Dabei bin ich auf 3 Probleme gestoßen:

Wenn der Client Daten zum Server Sendet, die Antwort vom Server aber nicht ließt/emfängt, dann blockiert die Recieve Methode des Servers nicht mehr und der Thread verwandelt sich in einen Zombie (while true schleife).
Die Schleife wird beendet, wenn der client die Verbindung schließt oder das Timeout der Recieve Methode überschritten wird. Allerdings finde ich keine Möglichkeit, zu erkennen, ob der client die Daten empfangen/gelesen hat um dementsprechend die Recieve Methode blockieren zu lassen.

Zweites Problem: ich hab keine Idee, wie ich den HTTP Parser implementieren muss. Folgende Klassen habe ich:
Server Klasse: Öffnet einen Server socket, hört auf Verbindungsanfragen und fügt eine eingehende Verbindung dem Connection-Pool hinzu.
Connection Pool Klasse: Kümmert sich um das verwalten der Connections (Verbindungen hinzufügen und entfernen bei disconnect / timeout / socket Exception)
Connection Klasse: Sorgt für das Senden und Empfangen von Daten und überwacht die Verbindung. Wenn Daten empfangen werden, wird ein Event ausgelöst, das den ConnectionPool informiert, wenn Daten empfangen wurden.
In welcher Klasse würde sich das Parsen am besten eignen? Sollte das im Kontext der Connections laufen oder auf dem Server-Thread?

Drittes Problem:
Der Empfangsbuffer beinhaltet beim Empfangen 1024 byte. Wenn eine Nachricht vom Client länger dauert oder größer als der Puffer ist, wird die Recieve Methode mehrmals aufgerufen und holt jeweils die anzahl der empfangenen bytes (max 1024) aus dem stream.
Da der HTTP parser nicht weiß, wann eine Anfrage komplett angekommen ist, weiß ich nicht, wann ich anfangen darf, den Header zu parsen.
Mein Lösungsansatz wäre: Wenn ich Daten reinbekomme, schaue ich, ob das Feld content-length dabei ist und bis zum \r\n\r\n parsen. Alles was nach dem zeilenumbruch kommt, ist der content, der die länge von content-length haben sollte. Allerdings sollte man sich da nicht drauf verlassen. Gibts noch andere möglichkeiten?

Soweit viel Text, aber vlt gibts ja nen findigen, der die antworten kennt.

--

"Everything, that can go wrong, will go wrong" Murphy's Law

zum Seitenanfang zum Seitenende Profil || Suche
001
25.12.2012, 09:57
Alcapone



Inzwischen habe ich meine Gedanken reifen lassen.

Das erste Problem existiert weiterhin, ich habe noch keine Möglichkeit gefunden, zu prüfen, ob der verbundene Client meine Daten auch empfängt und der Recieve Vorgang blockiert wird.

Zum zweiten Problem: Es wird keinen Connection-Pool mehr geben, nur noch einen Threadpool, der alle Threads beinhaltet. Ein Thread steuert eine Verbindung, das HTTP parsing und die weitere Verarbeitung der Daten.

Zum dritten Problem habe ich auch eine Lösung gefunden. Die Connection löst ein OnDataRecieved Event aus, das nicht unbedingt einen kompletten HTTP Request beinhalten muss. Beim Event werden die empfangenen Daten mitgeliefert und an den vom Thread ebenfalls instanzierten HTTPHandler weitergegeben, der ein OnHttpMessageCompleted Event ausführt wenn ein Vollständiger HTTP Request empfangen wurde. Ob ein Request Vollständig ist, wird anhand des HTTP Headers Content-Length geprüft.

Ich hoffe, das ist halbwegs sinnvoll durchdacht...

--

"Everything, that can go wrong, will go wrong" Murphy's Law

zum Seitenanfang zum Seitenende Profil || Suche
002
02.01.2013, 04:33
flokralle



Zum ersten Problem:
Send gibt doch zurück wieviel Bytes gesendet wurden.
Wenn also der Returnwert > 0 ist
-> Daten empfangen, sonst nicht.

--

zum Seitenanfang zum Seitenende Profil || Suche
003
03.01.2013, 23:32
Alcapone



Hallo flokralle,

danke für den Tip, darauf hab ich nicht geachtet. Gilt das auch für den nonblocking mode?
Der nonblocking mode ist notwendig, um den Thread zu beenden (Abfangen des 10053 error codes), wenn der Server eine response schickt, die Verbindung vom Client aber geschlossen wurde.
http://support.ipswitch.com/kb/WSK-19980702-EM02.htm

MSDN sagt da nur lapidar:

Zitat:
In nonblocking mode, Send may complete successfully even if it sends less than the number of bytes in the buffer.
So wie ich das verstanden habe, wartet der Server nicht auf eine Rückantwort vom Client, die ihm sagt, wieviele Bytes empfangen wurden (nonblocking).

Nichtsdestotrotz werde ich das morgen mal ausprobieren und erfolg oder misserfolg hier schildern.

--

"Everything, that can go wrong, will go wrong" Murphy's Law


Dieser Beitrag wurde am 03.01.2013 um 23:42 von Alcapone bearbeitet.
zum Seitenanfang zum Seitenende Profil || Suche
004
04.01.2013, 13:00
flokralle



Nein,

Zitat:
In nonblocking mode, Send may complete successfully even if it sends less than the number of bytes in the buffer.
damit ist nur gemeint, dass Send eventuell nicht den kompletten Buffer sendet und man deshalb prüfen soll ob der Rückgabewert von send so groß wie die Größe des Buffer ist und nicht eventuell kleiner.
(wenn z.B. während dem send die Verbindung abgebrochen wird)

--

zum Seitenanfang zum Seitenende Profil || Suche
005
23.10.2013, 14:21
Alcapone



Falls es jemanden noch interessiert:

Seit DotNet 3.5 gibt es SocketAsyncEventArgs, die speziell für hochperformante Serverapplikationen eingeführt wurde, da das allozieren der IAsyncResult Objekte hiermit wegfällt.

Bezüglich Threading und Blocking:
Fällt hierbei weg, da sich das Betriebssystem darum kümmert.

Bezüglich Protokollen:
Es empfiehlt sich, eine Message-Pipeline zu verwenden, aus der sich Worker die fertig empfangenen Nachrichten (Anhand des Protokolltyps) abholen können.

Eine (HTTP) Message ist dann fertig, wenn der Header + alle bytes von dem Headerfeld Content-Length empfangen wurden. Header, die kein Content-Length Feld enthalten, werden bis zum \r\n\r\n gelesen, es gibt dann keinen Content, daher wird nur der Header in der Message-Pipeline abgelegt.

Es sollte ein Timeout festgelegt werden, bis wann Header und Contentdaten empfangen werden, da sonst unnötig Ressourcen vom Server blockiert werden.

--

"Everything, that can go wrong, will go wrong" Murphy's Law

zum Seitenanfang zum Seitenende Profil || Suche