2009-02-07 8 views
10

Je suis en train de remplacer:Sockets in C#: Comment obtenir le flux de réponse?

void ProcessRequest(object listenerContext) 
{ 
    var context = (HttpListenerContext)listenerContext; 
    Uri URL = new Uri(context.Request.RawUrl); 
    HttpWebRequest.DefaultWebProxy = null; 
    HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(URL); 
    httpWebRequest.Method = context.Request.HttpMethod; 
    httpWebRequest.Headers.Clear(); 
    if (context.Request.UserAgent != null) httpWebRequest.UserAgent = context.Request.UserAgent; 
    foreach (string headerKey in context.Request.Headers.AllKeys) 
    { 
     try { httpWebRequest.Headers.Set(headerKey, context.Request.Headers[headerKey]); } 
      catch (Exception) { } 
    } 

    using (HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse()) 
    { 
     Stream responseStream = httpWebResponse.GetResponseStream(); 
     if (httpWebResponse.ContentEncoding.ToLower().Contains("gzip")) 
      responseStream = new GZipStream(responseStream, CompressionMode.Decompress); 
     else if (httpWebResponse.ContentEncoding.ToLower().Contains("deflate")) 
       responseStream = new DeflateStream(responseStream, CompressionMode.Decompress); 

     MemoryStream memStream = new MemoryStream(); 

     byte[] respBuffer = new byte[4096]; 
     try 
     { 
      int bytesRead = responseStream.Read(respBuffer, 0, respBuffer.Length); 
      while (bytesRead > 0) 
      { 
       memStream.Write(respBuffer, 0, bytesRead); 
       bytesRead = responseStream.Read(respBuffer, 0, respBuffer.Length); 
      } 
     } 
     finally 
     { 
      responseStream.Close(); 
     } 

     byte[] msg = memStream.ToArray(); 

     context.Response.ContentLength64 = msg.Length; 
     using (Stream strOut = context.Response.OutputStream) 
     { 
      strOut.Write(msg, 0, msg.Length); 
     } 
    } 
    catch (Exception ex) 
    { 
     // Some error handling 
    } 
} 

avec prises. Voilà ce que j'ai jusqu'à présent:

void ProcessRequest(object listenerContext) 
{ 
    HttpListenerContext context = (HttpListenerContext)listenerContext; 
    Uri URL = new Uri(context.Request.RawUrl); 
    string getString = string.Format("GET {0} HTTP/1.1\r\nHost: {1}\r\nAccept-Encoding: gzip\r\n\r\n", 
       context.Request.Url.PathAndQuery, 
       context.Request.UserHostName); 

    Socket socket = null; 

    string[] hostAndPort; 
    if (context.Request.UserHostName.Contains(":")) 
    { 
     hostAndPort = context.Request.UserHostName.Split(':'); 
    } 
    else 
    { 
     hostAndPort = new string[] { context.Request.UserHostName, "80" }; 
    } 

    IPHostEntry ipAddress = Dns.GetHostEntry(hostAndPort[0]); 
    IPEndPoint ip = new IPEndPoint(IPAddress.Parse(ipAddress.AddressList[0].ToString()), int.Parse(hostAndPort[1])); 
    socket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp); 
    socket.Connect(ip); 

BEGIN NOUVEAU CODE

Encoding ASCII = Encoding.ASCII; 
Byte[] byteGetString = ASCII.GetBytes(getString); 
Byte[] receiveByte = new Byte[256]; 
string response = string.Empty; 
socket.Send(byteGetString, byteGetString.Length, 0); 
Int32 bytes = socket.Receive(receiveByte, receiveByte.Length, 0); 
response += ASCII.GetString(receiveByte, 0, bytes); 
while (bytes > 0) 
{ 
bytes = socket.Receive(receiveByte, receiveByte.Length, 0); 
strPage = strPage + ASCII.GetString(receiveByte, 0, bytes); 
} 
socket.Close(); 

string separator = "\r\n\r\n"; 
string header = strPage.Substring(0,strPage.IndexOf(separator)); 
string content = strPage.Remove(0, strPage.IndexOf(separator) + 4); 

byte[] byteResponse = ASCII.GetBytes(content); 
context.Response.ContentLength64 = byteResponse .Length; 
context.Response.OutputStream.Write(byteResponse , 0, byteResponse .Length); 
context.Response.OutputStream.Close(); 

FIN NOUVEAU CODE

Après avoir connecté à la prise, je ne sais pas comment obtenir le Réponse du flux pour décompresser et renvoyer à context.Response.OutputStream

Toute aide sera appréciée. Merci. À la vôtre.

EDIT 2: Avec cette édition semble maintenant fonctionner très bien (même que HttpWebRequest au moins). Trouvez-vous une erreur ici?

EDIT 3: Fausse alerte ... Encore ne peut pas obtenir ce travail

EDIT 4: je devais ajouter les lignes au code de Scott suivants ... parce que pas toujours le premier aux octets de reponseStream sont le nombre magique de gzip. La séquence semble être: 0x0a (10), 0x1f (31), 0x8b (139). Les deux derniers sont le nombre magique de gzip. Le premier numéro était toujours avant dans mes tests.

if (contentEncoding.Equals("gzip")) 
{ 
    int magicNumber = 0; 
    while (magicNumber != 10) 
     magicNumber = responseStream.ReadByte(); 
    responseStream = new GZipStream(responseStream, CompressionMode.Decompress); 
} 
+0

@Matias: Je dois demander, pourquoi voulez-vous faire cela? Vous allez dépenser tant de frais généraux à dupliquer ce que HttpWebRequest et HttpWebResponse font déjà pour vous. – casperOne

+0

La réponse pour cela est ici: http://stackoverflow.com/questions/521977/is-it-possible-to-change-headers-order-using-httpwebrequest –

Répondre

11

Voici du code qui fonctionne pour moi.

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Net; 
using System.Net.Sockets; 
using System.Text; 
using System.IO.Compression; 

namespace HttpUsingSockets { 
    public class Program { 
     private static readonly Encoding DefaultEncoding = Encoding.ASCII; 
     private static readonly byte[] LineTerminator = new byte[] { 13, 10 }; 

     public static void Main(string[] args) { 
      var host = "stackoverflow.com"; 
      var url = "https://stackoverflow.com/questions/523930/sockets-in-c-how-to-get-the-response-stream"; 

      IPHostEntry ipAddress = Dns.GetHostEntry(host); 
      var ip = new IPEndPoint(ipAddress.AddressList[0], 80); 
      using (var socket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp)) { 
       socket.Connect(ip); 
       using (var n = new NetworkStream(socket)) { 
        SendRequest(n, new[] {"GET " + url + " HTTP/1.1", "Host: " + host, "Connection: Close", "Accept-Encoding: gzip"}); 

        var headers = new Dictionary<string, string>(); 
        while (true) { 
         var line = ReadLine(n); 
         if (line.Length == 0) { 
          break; 
         } 
         int index = line.IndexOf(':'); 
         headers.Add(line.Substring(0, index), line.Substring(index + 2)); 
        } 

        string contentEncoding; 
        if (headers.TryGetValue("Content-Encoding", out contentEncoding)) { 
         Stream responseStream = n; 
         if (contentEncoding.Equals("gzip")) { 
          responseStream = new GZipStream(responseStream, CompressionMode.Decompress); 
         } 
         else if (contentEncoding.Equals("deflate")) { 
          responseStream = new DeflateStream(responseStream, CompressionMode.Decompress); 
         } 

         var memStream = new MemoryStream(); 

         var respBuffer = new byte[4096]; 
         try { 
          int bytesRead = responseStream.Read(respBuffer, 0, respBuffer.Length); 
          while (bytesRead > 0) { 
           memStream.Write(respBuffer, 0, bytesRead); 
           bytesRead = responseStream.Read(respBuffer, 0, respBuffer.Length); 
          } 
         } 
         finally { 
          responseStream.Close(); 
         } 

         var body = DefaultEncoding.GetString(memStream.ToArray()); 
         Console.WriteLine(body); 
        } 
        else { 
         while (true) { 
          var line = ReadLine(n); 
          if (line == null) { 
           break; 
          } 
          Console.WriteLine(line); 
         } 
        } 
       } 
      } 
     } 

     static void SendRequest(Stream stream, IEnumerable<string> request) { 
      foreach (var r in request) { 
       var data = DefaultEncoding.GetBytes(r); 
       stream.Write(data, 0, data.Length); 
       stream.Write(LineTerminator, 0, 2); 
      } 
      stream.Write(LineTerminator, 0, 2); 
      // Eat response 
      var response = ReadLine(stream); 
     } 

     static string ReadLine(Stream stream) { 
      var lineBuffer = new List<byte>(); 
      while (true) { 
       int b = stream.ReadByte(); 
       if (b == -1) { 
        return null; 
       } 
       if (b == 10) { 
        break; 
       } 
       if (b != 13) { 
        lineBuffer.Add((byte)b); 
       } 
      } 
      return DefaultEncoding.GetString(lineBuffer.ToArray()); 
     } 
    } 
} 

Vous pouvez remplacer cela par Socket/NetworkStream et économiser un peu de travail.

using (var client = new TcpClient(host, 80)) { 
     using (var n = client.GetStream()) { 
    } 
} 
+0

hey !, merci pour votre réponse !!!! mais je reçois une exception: InvalidDataException: Le nombre magique dans l'en-tête GZip est incorrect. Assurez-vous que vous passez dans un flux GZip. Le problème semble être dans cette ligne: int bytesRead = responseStream.Read (respBuffer, 0, respBuffer.Length); Des idées? –

+0

Exécutez-le dans le débogage et vérifiez que les deux premiers octets sont 0x1f 0x8b (le nombre magique gzip), peut-être il y a une ligne supplémentaire ou un préfixe ou quelque chose. – Scott

+0

Merci pour votre réponse. J'ai ajouté quelques lignes après "if (contentEncoding.Equals (" gzip ")) {" pour chercher le début du nombre magique de gzip. C'est sur mon premier post. Meilleures salutations! –

4

Socket, par définition, est le niveau bas pour accéder au réseau. Vous pouvez même utiliser des protocoles de datagramme avec une socket. Dans ce cas, un flux n'a aucun sens.

Bien que je ne sache pas pourquoi vous faites ce que HttpWebRequest accomplit facilement, pour lire/écrire des données sur un socket, vous utilisez les méthodes Send/Receive. Si vous souhaitez avoir un flux comme l'accès à un socket TCP, vous devez utiliser les classes TcpClient/TcpListener qui enveloppent un socket et fournissent un flux réseau pour celui-ci.

3

Il existe également la classe NetworkStream qui prend en paramètre un Socket. ;)