2015-09-06 138 views
0

我正在尝试使用C#制作代理服务器。 这里是我的代码:代理服务器不加载图像

static void Main(string[] args) 
{ 
    TcpListener server = null; 
    try 
    { 
     // Set the TcpListener on port 13000. 
     Int32 port = 13000; 
     IPAddress localAddr = IPAddress.Parse("127.0.0.1"); 

     // TcpListener server = new TcpListener(port); 
     server = new TcpListener(localAddr, port); 

     // Start listening for client requests. 
     server.Start(); 

     // Buffer for reading data 
     Byte[] bytes = new Byte[256]; 
     String data = null; 

     WebRequest request; 
     WebResponse response; 




     // Enter the listening loop. 
     while (true) 
     { 
      Console.Write("Waiting for a connection... "); 

      // Perform a blocking call to accept requests. 
      // You could also user server.AcceptSocket() here. 
      TcpClient client = server.AcceptTcpClient(); 
      Console.WriteLine("Connected!"); 


      data = null; 

      // Get a stream object for reading and writing 
      NetworkStream stream = client.GetStream(); 

      int i; 
      String[] input; 


      // Loop to receive all the data sent by the client. 
      while (stream.DataAvailable) 
      { 
       data = null; 
       i = stream.Read(bytes, 0, bytes.Length); 
       // Translate data bytes to a ASCII string. 
       data = System.Text.Encoding.ASCII.GetString(bytes, 0, i); 
       Console.WriteLine(String.Format("Received: {0}", data)); 

       input = data.Split(); 

       Console.WriteLine("\n\r\n input[1]" + input[1] + "\n"); 

       Stream dataStream; 
       StreamReader reader; 
       string responseFromServer; 

       try 
       { 
        request = WebRequest.Create(input[1]); 

        response = request.GetResponse(); 
        // Process the data sent by the client. 
        data = data.ToUpper(); 



        dataStream = response.GetResponseStream(); 
        // Open the stream using a StreamReader for easy access. 
        reader = new StreamReader(dataStream); 
        // Read the content. 
        responseFromServer = reader.ReadToEnd(); 
        // Display the content 
        Console.WriteLine(responseFromServer); 
        // Clean up the streams and the response. 

        byte[] msg = System.Text.Encoding.ASCII.GetBytes(responseFromServer); 

        // Send back a response. 
        stream.Write(msg, 0, msg.Length); 
        // Console.WriteLine("Sent: {0}", data); 
        //stream.Write(); 



        reader.Close(); 
        response.Close(); 
       } 
       catch (System.UriFormatException e) 
       { 
        Console.WriteLine("Exception due to" + e.Data); 
        Console.WriteLine("Input[1] = " + input[1]); 

       } 



       data = null; 

      } 

      // Shutdown and end connection 
      client.Close(); 
     } 
    } 
    catch (SocketException e) 
    { 
     Console.WriteLine("SocketException: {0}", e); 
    } 
    finally 
    { 
     // Stop listening for new clients. 
     server.Stop(); 
    } 


    Console.WriteLine("\nHit enter to continue..."); 
    Console.Read(); 
} 

它不适用于SSL请求工作,但似乎为HTTP工作。 但是,它不加载任何图像。 我使用Firefox作为浏览器。 任何想法为什么? 也是这是制作代理服务器的最佳方式吗?还有其他方法吗?

+0

我实际上正在尝试将其用于其他目的。我不需要像这样使用代理。我只是想建立一个。 – user3927312

+0

我可以明确告诉你为什么SSL不起作用。 SSL是加密流量,因此不会发送明文HTTP请求。然而,你试图用ASCII解码的输入数据创建你自己的'WebRequest'(但没有发送ASCII数据),这是行不通的。我对代理服务器的想法更多的是代表他将所有流量转发给目标的程序,并将流返回,然后我们又回到某种HTTP代理。 –

回答

3

经过一些测试,我写了自己的代码。

using System; 
using System.Linq; 
using System.Net; 
using System.Net.Sockets; 
using System.IO; 
using System.Threading; 
using System.Text; 

namespace SharpProxy 
{ 
    class MainClass 
    { 
     private static void StartAcceptingClient(IAsyncResult ar) 
     { 
      var tcpClient = server.EndAcceptTcpClient(ar); 
      server.BeginAcceptTcpClient(new AsyncCallback(StartAcceptingClient), null); 

      // Read the data stream from the client. 
      NetworkStream stream = tcpClient.GetStream(); 
      byte[] buffer = new byte[256]; 
      Console.WriteLine("====== GOT A NEW TCP CLIENT ====== " + tcpClient.Client.RemoteEndPoint.ToString()); 

      int read = stream.Read(buffer, 0, 1); 
      MemoryStream saved = new MemoryStream(); 
      saved.Write(buffer, 0, read); 
      bool isValid = false; 
      while (read > 0) 
      { 
       read = stream.Read(buffer, 0, 1); 
       saved.Write(buffer, 0, read); 

       //Check if the last four bytes were a double \r\n. 
       var aBytes = saved.ToArray(); 
       int len = aBytes.Length; 
       if (aBytes.Length >= 4 && aBytes[len - 1] == '\n' && aBytes[len - 2] == '\r' && aBytes[len - 3] == '\n' && aBytes[len - 4] == '\r') 
       { 
        isValid = true; 
        break; 
       } 
      } 
      Console.WriteLine("End of receive."); 
      string originalRequest = Encoding.ASCII.GetString(saved.ToArray()); 
      byte[] origBytes = saved.ToArray(); 
      saved.Close(); 
      Console.WriteLine(originalRequest); 
      if (!isValid) 
      { 
       Console.WriteLine("This wasn't a valid request"); 
       return; 
      } 
      //Find the hoster and do our own request. 
      string host = originalRequest.Split(new char[] { '\n' }).First(line => line.StartsWith("Host:")); 
      host = host.Substring(5).Trim(); //Cut of rest. 
      Console.WriteLine("The host is: " + host); 
      //Do our own request. 
      try 
      { 
       Socket sProxy = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
       sProxy.Connect(host, 80); 
       sProxy.Send(origBytes); 
       //Now route everything between the tcpclient and this socket... 

       //create the state object 
       var state = new ProxyState() { ourSocket = sProxy, incomingClient = stream }; 
       sProxy.BeginReceive(state.ReceiveBuffer, 0, state.ReceiveBuffer.Length, SocketFlags.None, new AsyncCallback(Receiver), state); 

       stream.BeginRead(state.SendBuffer, 0, state.SendBuffer.Length, new AsyncCallback(SendToHTTPServer), state); 
      } 
      catch (Exception) { Console.WriteLine("Exception while doing our own request"); } 
     } 

     static TcpListener server = null; 
     public static void Main(string[] args) 
     { 
      try 
      { 
       // Set the TcpListener on port 13000. 
       Int32 port = 13000; 
       IPAddress localAddr = IPAddress.Parse("0.0.0.0"); 
       // TcpListener server = new TcpListener(port); 
       server = new TcpListener(localAddr, port); 
       // Start listening for client requests. 
       server.Start(); 
       Console.WriteLine("Server started on " + server.LocalEndpoint.ToString()); 
       server.BeginAcceptTcpClient(new AsyncCallback(StartAcceptingClient), null); 

       while (true) 
        Thread.Sleep(10); 
      } 
      catch (Exception) { Console.WriteLine("Setting up the server failed"); } 
     } 

     private static void SendToHTTPServer(IAsyncResult ar) 
     { 
      try 
      { 
       ProxyState back = (ProxyState)ar.AsyncState; 
       int rec = back.incomingClient.EndRead(ar); 
       //Push this content to the server 
       back.ourSocket.Send(back.SendBuffer.Take(rec).ToArray()); 
       back.incomingClient.BeginRead(back.SendBuffer, 0, back.SendBuffer.Length, new AsyncCallback(SendToHTTPServer), back); 
      } 
      catch (Exception e) { Console.WriteLine("Exc. when sending to server: " + e.ToString()); } 
     } 

     static void Receiver(IAsyncResult state) 
     { 
      try 
      {  
       ProxyState back = (ProxyState)state.AsyncState; 
       int rec = back.ourSocket.EndReceive(state); 
       //Set up the back and forth connections 
       back.incomingClient.Write(back.ReceiveBuffer, 0, rec); 
       back.ourSocket.BeginReceive(back.ReceiveBuffer, 0, back.ReceiveBuffer.Length, SocketFlags.None, new AsyncCallback(Receiver), back); 
      } 
      catch (Exception e) { Console.WriteLine("Exc. when receiving from client: " + e.ToString()); } 
     } 


     //Every proxy connection has an end an and a beginning, plus a 
     //Sending buffer and a receive buffer 
     class ProxyState 
     { 
      public NetworkStream incomingClient { get; set; } 
      public Socket ourSocket { get; set; } 
      private byte[] buffReceive = new byte[512]; 
      private byte[] buffSend = new byte[512]; 
      public byte[] ReceiveBuffer { get { return buffReceive; } set { buffReceive = value; } } 
      public byte[] SendBuffer { get { return buffSend; } set { buffSend = value; } } 
     } 
    } 
} 

下面是它的工作原理:我监听端口,并等待HTTP请求。这是由一个双回车和一个换行结束,\r\n\r\n。一旦发生这种情况,我尝试使用Linq语句解析请求中的原始主机。我打开自己的套接字到服务器,并使用异步回调。基本上,您需要将所有来自代理发起程序的内容写入HTTP-Server,并且HTTP-Server发回的所有内容都需要推回给原始客户端。这就是为什么我设置了自己的状态对象,它只保存了连接到原始HTTP服务器的接收客户端和Socket。因此,沟通可以发生,我充当代理服务器。

下面是用做对所有连接的截图:

clonk

proxy settings

这个代理服务器是远非完美,但基本概念要清晰。 This给了我一些启发。

1

您对二进制映像数据使用流式读取器,这是行不通的。并非每个二进制文件都是有效的ASCII编码字符串您应该将响应作为二进制读取,并将其作为二进制写入其他流。您可以尝试将其转换为ascii以将其打印到控制台,但不要使用转换后的文本进行响应,因为所有无效的ascii字符都将转换为? -s。我修改了代码,首先读取MemoryStream中的响应,然后写回。写入控制台的数据仍然被转换,但其他地方没有用户。

static void Main(string[] args) 
{ 
    TcpListener server = null; 
    try 
    { 
     // Set the TcpListener on port 13000. 
     Int32 port = 13000; 
     IPAddress localAddr = IPAddress.Parse("127.0.0.1"); 

     // TcpListener server = new TcpListener(port); 
     server = new TcpListener(localAddr, port); 

     // Start listening for client requests. 
     server.Start(); 

     // Buffer for reading data 
     Byte[] bytes = new Byte[256]; 
     String data = null; 

     WebRequest request; 
     WebResponse response; 




     // Enter the listening loop. 
     while (true) 
     { 
      Console.Write("Waiting for a connection... "); 

      // Perform a blocking call to accept requests. 
      // You could also user server.AcceptSocket() here. 
      TcpClient client = server.AcceptTcpClient(); 
      Console.WriteLine("Connected!"); 


      data = null; 

      // Get a stream object for reading and writing 
      NetworkStream stream = client.GetStream(); 

      int i; 
      String[] input; 


      // Loop to receive all the data sent by the client. 
      while (stream.DataAvailable) 
      { 
       data = null; 
       i = stream.Read(bytes, 0, bytes.Length); 
       // Translate data bytes to a ASCII string. 
       data = System.Text.Encoding.ASCII.GetString(bytes, 0, i); 
       Console.WriteLine(String.Format("Received: {0}", data)); 

       input = data.Split(); 

       Console.WriteLine("\n\r\n input[1]" + input[1] + "\n"); 

       Stream dataStream; 
       StreamReader reader; 
       string responseFromServer; 

       try 
       { 
        request = WebRequest.Create(input[1]); 

        response = request.GetResponse(); 
        // Process the data sent by the client. 
        data = data.ToUpper(); 



        dataStream = response.GetResponseStream(); 

        MemoryStream ms = new MemoryStream(); 
        dataStream.CopyTo(ms); 

        ms.Position = 0; 
        // Open the stream using a StreamReader for easy access. 
        reader = new StreamReader(ms); 
        // Read the content. 
        responseFromServer = reader.ReadToEnd(); 
        // Display the content 
        Console.WriteLine(responseFromServer); 
        // Clean up the streams and the response. 

        byte[] msg = ms.ToArray(); 

        // Send back a response. 
        stream.Write(msg, 0, msg.Length); 
        // Console.WriteLine("Sent: {0}", data); 
        //stream.Write(); 



        reader.Close(); 
        response.Close(); 
       } 
       catch (System.UriFormatException e) 
       { 
        Console.WriteLine("Exception due to" + e.Data); 
        Console.WriteLine("Input[1] = " + input[1]); 

       } 



       data = null; 

      } 

      // Shutdown and end connection 
      client.Close(); 
     } 
    } 
    catch (SocketException e) 
    { 
     Console.WriteLine("SocketException: {0}", e); 
    } 
    finally 
    { 
     // Stop listening for new clients. 
     server.Stop(); 
    } 


    Console.WriteLine("\nHit enter to continue..."); 
    Console.Read(); 
} 
+0

如果服务器的响应是存储在'datastream'变量中的二进制文件,为什么'stream.Write()'方法不能直接接受它? – user3927312

+0

你可以直接使用'dataStream.CopyTo(stream)'。但是,只能读取一次'dataStream',因此我将它保存在内存流中,然后作为字符串在控制台上打印并作出响应。 –

相关问题