Ich verwende Socket
class für meinen Webclient. Ich kann HttpWebRequest
nicht verwenden, da Socks-Proxys nicht unterstützt werden. Also muss ich die Header analysieren und die Chunked-Codierung selbst handhaben. Die schwierigste Sache für mich ist, die Länge des Inhalts zu bestimmen, also muss ich es Byte für Byte lesen. Zuerst muss ich ReadByte()
verwenden, um die letzte Kopfzeile zu finden ("\ r \ n \ r \ n" Kombination), dann überprüfe, ob der Körper eine Transfer-Codierung hat oder nicht. Wenn dies der Fall ist, muss ich die Größe des Blocks lesen:
Aber dieser Ansatz hat eine sehr schlechte Leistung. Können Sie eine bessere Lösung vorschlagen? Vielleicht einige Open-Source-Beispiele oder Bibliotheken, die HTTP-Anfrage über Sockets behandeln (nicht sehr groß und kompliziert, aber ich bin ein Noob). Am besten wäre es, einen Link zu einem Beispiel zu schreiben, das den Nachrichtentext liest und die Fälle korrekt behandelt, wenn: Inhalt chunked-encoding, gzip- oder deflate-encoded ist, Content-Length-Header entfällt (Nachricht endet, wenn die Verbindung geschlossen wird). So etwas wie der Quellcode der HttpWebRequest-Klasse.
Upd: Meine neue Funktion sieht so aus:
%Vor% Wo GetHeaders()
und isResponseBodyComplete()
verwenden m_responseData
( MemoryStream
) mit bereits empfangenen Daten.
Ich schlage vor, dass Sie das nicht selbst implementieren - das HTTP 1.1-Protokoll ist ausreichend komplex, um dies zu einem Projekt von mehreren Mannmonaten zu machen.
Die Frage ist, gibt es einen HTTP-Request-Protokoll-Parser für .NET? Diese Frage wurde zu SO gestellt. In den Antworten finden Sie verschiedene Vorschläge, einschließlich Quellcode für die Verarbeitung von HTTP-Streams.
Konvertieren der Raw HTTP-Anforderung in das HTTPWebRequest-Objekt
BEARBEITEN: Der Rotorcode ist ziemlich komplex und schwer zu lesen / zu navigieren als Webseiten. Der Implementierungsaufwand für das Hinzufügen von SOCKS-Support ist jedoch viel geringer als das Implementieren des gesamten HTTP-Protokolls selbst. Sie werden in höchstens ein paar Tagen etwas haben, auf das Sie sich verlassen können, das auf einer bewährten Implementierung basiert.
Die Anfrage und die Antwort werden in NetworkStream
, m_Transport
in der Klasse Connection
gelesen / geschrieben. Dies wird in diesen Methoden verwendet:
beide in Zypern
Der Socket wird in
erstellt %Vor%Sie können also diese Methode ändern, um einen Socket für Ihren Socks-Server zu erstellen, und den erforderlichen Handshake ausführen, um die externe Verbindung zu erhalten. Der Rest des Codes kann gleich bleiben.
Ich habe diese Informationen in etwa 30 Minuten auf den Seiten im Internet gesammelt. Dies sollte viel schneller gehen, wenn Sie diese Dateien in eine IDE laden. Es scheint eine Last zu sein, diesen Code lesen zu müssen - schließlich ist das Lesen von Code viel schwieriger als das Schreiben, aber Sie machen nur kleine Änderungen an einem bereits etablierten, funktionierenden System.
Um sicher zu sein, dass die Änderungen in allen Fällen funktionieren, ist es ratsam, auch zu testen, wenn die Verbindung unterbrochen ist, um sicherzustellen, dass der Client die gleiche Methode erneut herstellt und die SOCKS-Verbindung erneut herstellt und die SOCKS-Anforderung sendet .
Wenn das Problem ein Engpass ist, weil ReadByte
zu langsam ist, schlage ich vor, dass Sie den Eingabestream mit StreamBuffer
umschließen. Wenn das Leistungsproblem, das Sie beanspruchen, teuer ist, weil kleine Lesevorgänge das Problem für Sie lösen.
Du brauchst das auch nicht:
%Vor%HTTP by Design erfordert, dass der Header nur aus ASCII-Zeichen besteht. Sie wollen oder wollen es nicht wirklich in tatsächliche .NET-Zeichenfolgen umwandeln (Unicode).
Wenn Sie den EOF des HTTP-Headers finden möchten, können Sie dies für eine gute Leistung tun.
%Vor% Wenn die Zeichenkette \r\n\r\n
aktiviert ist, wird k
gleich 0x0d0a0d0a
In den meisten http-Anfragen sollte ein Header mit der Bezeichnung content-length angegeben werden, der angibt, wie viele Bytes sich im Hauptteil der Anfrage befinden. Dann ist es nur eine Frage der Zuweisung der entsprechenden Menge an Bytes und Lesen dieser Bytes auf einmal.
Obwohl ich der Meinung von mdma eher zustimmen würde, dass Sie so hart wie möglich versuchen sollten, Ihren eigenen HTTP-Stack zu implementieren, könnte ein Trick, den Sie in Betracht ziehen, das Lesen von mässig großen Chunks sein. Wenn Sie einen Lesevorgang ausführen und ihm einen Puffer geben, der größer ist als der verfügbare Puffer, sollte er Ihnen die Anzahl der gelesenen Bytes zurückgeben. Dies sollte die Anzahl der Systemaufrufe reduzieren und Ihre Leistung erheblich beschleunigen. Sie müssen die Puffer jedoch immer noch so scannen, wie Sie es jetzt tun.
Ein Blick auf den Code eines anderen Kunden ist hilfreich (wenn nicht verwirrend): Ссылка
Ich mache momentan auch so etwas. Ich finde den besten Weg, um die Effizienz des Clients zu erhöhen, ist die Verwendung der asynchronen Socket-Funktionen zur Verfügung gestellt. Sie sind ziemlich low-level und werden beschäftigt, warten und sich mit Threads befassen. Alle diese haben Begin
und End
in ihren Methodennamen. Aber zuerst würde ich es mit Blockierung versuchen, nur damit Sie die Semantik von HTTP aus dem Weg bekommen. Dann können Sie an Effizienz arbeiten. Denken Sie daran: Vorzeitige Optimierung ist schlecht - also machen Sie es in Ordnung, dann optimieren Sie alles!
Auch: Ein Teil Ihrer Effizienz könnte in Ihrer Verwendung von ToArray()
liegen. Es ist bekannt, dass es ein bisschen teuer ist. Eine bessere Lösung könnte sein, Ihre Zwischenergebnisse in einem byte[]
-Puffer zu speichern und sie an eine StringBuilder
mit der korrekten Codierung anzufügen.
Lesen Sie für gezippte oder deflationierte Daten alle Daten ein (denken Sie daran, dass Sie möglicherweise nicht alle Daten erhalten, wenn Sie das erste Mal fragen. Verfolgen Sie, wie viele Daten Sie eingelesen haben und hängen Sie weiter an der gleiche Puffer). Dann können Sie die Daten mit GZipStream(..., CompressionMode.Decompress)
dekodieren.
Ich würde sagen, dass dies nicht so schwierig ist, wie manche vielleicht meinen, du musst nur ein bisschen abenteuerlustig sein!
Alle Antworten hier auf die Erweiterung von Socket und / oder TCPClient scheinen etwas wirklich Offensichtliches zu übersehen - dass HttpWebRequest auch eine Klasse ist und daher erweitert werden kann.
Sie müssen keine eigene HTTP / Socket-Klasse schreiben. Sie müssen HttpWebRequest einfach um eine benutzerdefinierte Verbindungsmethode erweitern. Nach dem Verbinden aller Daten ist Standard-HTTP und kann wie gewohnt von der Basisklasse behandelt werden.
%Vor%Der SOCKS-Handshake ist nicht besonders komplex. Wenn Sie ein grundlegendes Verständnis von Programmiersteckplätzen haben, sollte es nicht lange dauern, die Verbindung zu implementieren. Danach kann HttpWebRequest das HTTP-Heavy-Lifting durchführen.
Vielleicht möchten Sie sich die TcpClient
-Klasse in System.Net
ansehen, es ist ein Wrapper für einen Socket, der die grundlegenden Operationen vereinfacht.
Von dort aus müssen Sie das HTTP-Protokoll lesen. Seien Sie auch bereit, einige Zip-Operationen durchzuführen. Http 1.1 unterstützt GZip von seinen Inhalten und Teilblöcken. Du wirst einiges lernen müssen, um sie mit der Hand zu analysieren.
Basic Http 1.0 ist einfach, das Protokoll ist online gut dokumentiert, unsere freundliche Nachbarschaft Google kann Ihnen dabei helfen.
Ich würde einen SOCKS-Proxy erstellen, der HTTP tunneln kann und dann die Anforderungen von HttpWebRequest annehmen und weiterleiten kann. Ich denke, das wäre viel einfacher, als alles neu zu erstellen, was HttpWebRequest tut. Sie könnten mit Privoxy beginnen oder einfach Ihre eigenen Rollen machen. Das Protokoll ist einfach und dokumentiert hier:
Und auf den RFCs, zu denen sie verlinken.
Sie haben erwähnt, dass Sie viele verschiedene Proxies haben müssen - Sie könnten für jeden einen lokalen Port einrichten.