【问题标题】:Proxy server does not load images代理服务器不加载图像
【发布时间】:2015-12-02 01:57:38
【问题描述】:

我正在尝试使用 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 作为浏览器。 任何想法为什么? 这也是制作代理服务器的最佳方式吗?还有其他方法吗?

【问题讨论】:

  • 创建并配置您的计算机作为 SOCKS5 代理,这比尝试破解 C# 代理服务器要好一千倍。此外,“未加载任何图片”可能是由于传输了二进制内容,但我在您的代码中看到了很多 ASCII 文本转换。
  • 我实际上是在尝试将其用于其他目的。我不需要这样使用代理。我只是想建立一个。
  • 我可以明确地告诉你为什么 SSL 不起作用。 SSL 是加密流量,因此不会发送明文 HTTP 请求。但是,您正在尝试使用 ASCII 解码的输入数据(但没有发送 ASCII 数据)创建您自己的 WebRequest,但这是行不通的。我对代理服务器的想法更像是一个程序,它代表他将所有流量中继到目标,并将流返回,然后我们回到某种 HTTP 代理。
  • 是的,我知道 ssl,但首先我需要它来处理 http 图像。
  • 您是否尝试过使用 fiddler、wireshark 或 charles。我怀疑它会做你想要的......

标签: c# http-proxy


【解决方案1】:

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

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。因此,可以进行通信,我充当代理服务器。

这是一个所有连接都正确完成的屏幕截图:

这个代理服务器远非完美,但基本概念应该很清楚。 This给了我一些启发。

【讨论】:

    【解决方案2】:

    您在二进制图像数据上使用流读取器,这是行不通的。并非每个二进制文件都是有效的 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();
    }
    

    【讨论】:

    • 如果服务器的响应是二进制的,存储在datastream变量中,为什么stream.Write()方法不直接接受呢?
    • 您可以直接通过dataStream.CopyTo(stream)使用它。但是您只能读取一次dataStream,因此我将其保存在内存流中,然后在控制台上将其作为字符串打印并响应。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-22
    • 2015-07-13
    • 2018-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多