1

最近、私の学校では http プロキシ クラスに関するプロジェクトがあります。その目的は、ブラウザで url を入力すると、リクエストがプロキシに送信され、ホストを抽出した後、新しいリクエストと宛先サーバーへのリクエストを再作成し、宛先サーバーからデータを受信して​​ブラウザに送り返すことです。ページをレンダリングします。

非同期 HTTP プロキシ サーバー クラスを作成します。StateObject クラスには 2 つのソケットがあります。1 つはブラウザからの新しいリクエストを処理するための workSocket で、もう 1 つは宛先ソケットを処理するための destinationSocket です。

ブラウザーから宛先サーバーに新しい要求を送信する前に、destinationSocket とハンドラー ソケットを StateObject オブジェクトに格納します。そうすることで、AsynCallBack ReceiveFromDestinationServer 関数で、destinationSocket を取得して宛先サーバーからデータを受信し、workSocket を取得して受信したデータをブラウザに送り返すことができます。そして受信したデータが終わるまでBeginReceiveを再度実行します。

この例外は、コールバック ReceiveFromDestinationServer メソッドの Socket の Send メソッドで発生します。

修正を手伝ってください。読んでくれてありがとう。

これは私のコードです:

public class ServerListerner
{
    private const int TCPPort = 80;
    private const string EOF = "\r\n";        
    // Thread signal.
    public static ManualResetEvent allDone = new ManualResetEvent(false);

    public void StartListening()
    {
        // Data buffer for incoming data.
        byte[] bytes = new Byte[1024];

        // Establish the local endpoint for the socket.
        IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, 9000);

        // Create a TCP/IP socket.
        Socket listener = new Socket(AddressFamily.InterNetwork,
            SocketType.Stream, ProtocolType.Tcp );

        // Bind the socket to the local endpoint and listen for incoming connections.
        try {
            listener.Bind(localEndPoint);
            listener.Listen(100);

            while (true) {
                // Set the event to nonsignaled state.
                allDone.Reset();

                // Start an asynchronous socket to listen for connections.                    
                listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);

                // Wait until a connection is made before continuing.
                allDone.WaitOne();
            }

        } catch (Exception e) {
            MessageBox.Show(e.ToString());
        }       
    }

    private void AcceptCallback(IAsyncResult ar) {
        // Signal the main thread to continue.
        allDone.Set();
        // Get the socket that handles the client request.
        Socket listener = (Socket) ar.AsyncState;
        Socket handler = listener.EndAccept(ar);

        // Create the state object.
        StateObject state = new StateObject();
        state.workSocket = handler;
        // And begin receive data from client
        handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
            new AsyncCallback(ReadCallback), state);
    }

    private void ReadCallback(IAsyncResult ar)
    {
        String content = String.Empty;        
        // Retrieve the state object and the handler socket
        // from the asynchronous state object.
        StateObject state = (StateObject) ar.AsyncState;
        Socket handler = state.workSocket;

        // Read data from the client socket. 
        int bytesRead = handler.EndReceive(ar);
        Thread.Sleep(10);
        if (bytesRead > 0) {            
            // There  might be more data, so store the data received so far.
            state.sb.Append(Encoding.ASCII.GetString(state.buffer));

            content = state.sb.ToString();
            if(content != "")
            {
                // All the data has been read from the client. 
                // Change data to string array to easy access.
                string[] requestLines = Regex.Split(content, EOF);
                // Get remote host
                string remoteHost = requestLines[0].Split(' ')[1].Replace("http://", "").Split('/')[0];

                // Create a destination socket and connect to remote host at TCP port
                Socket destinationSocket= new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                destinationSocket.Connect(remoteHost, TCPPort);
                // Send the data to destination socket.
                state.workSocket = handler;
                state.destinationSocket = destinationSocket;
                destinationSocket.Send(state.buffer);
                destinationSocket.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveFromDestinationServer), state);
            } else {
                // Not all data received. Get more.
                handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                    new AsyncCallback(ReadCallback), state);
            }
        }
    }

    private void ReceiveFromDestinationServer(IAsyncResult ar)
    {                        
            StateObject state = (StateObject)ar.AsyncState;
            Socket destinationSocket = state.destinationServer;
            Socket client = state.workSocket;
            int bytesRead = destinationSocket.EndReceive(ar);
            if (bytesRead > 0)
            {                    
                client.Send(state.buffer, bytesRead, SocketFlags.None);                    
                destinationSocket.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveFromDestinationServer), state);
            }
            else
            {
                // Exception here
                destinationSocket.Shutdown(SocketShutdown.Both);                
                destinationSocket.Close();
                client.Close();
            }             


    }
}

更新: ReceiveFromDestinationServer 関数 bytesRead の後に Thread.Sleep(10) を追加しました。スリープは重要だと思います。set が 10 の場合、一部のサイトはすばやく読み込みます。一部のサイトは一部の情報のみを読み込みます。set が 100 の場合、サイトの読み込み後、アプリケーションは自動的に終了します。しかし、例外はまだ存在します。

4

2 に答える 2

0

リモートホストが実行されていないか、接続しようとしているポートをリッスンしていない可能性があります。telnetを使用してサーバーアプリケーションの状態を確認する必要がある場合があります

 telnet ipaddr port
于 2012-04-27T17:44:14.003 に答える
0

あなたのコードは、クライアントとサーバーの間で対話するためのものだと思います。「AcceptCallback」の「ハンドラ」は、宛先に送信する代わりにブラウザにバイトを送信するためのソケットです。
したがって、http プロキシを実現するには、別の作業ペア (非同期)/1 つ (同期) ソケットを作成する必要があります。最初に、リクエストとレスポンスがどのように見えるかを明確にする必要があります。
ブラウザは最初にあなたに送信します:

browserIP ===> proxyIP
GET domainname.com
....

そしてプロキシは依存を行います:

proxyIP ===> domainname.com'sIP
GET domain name
....

プロキシが domainname.com からの応答を取得したら、それを browserIP に送り返します。.NET では、X-forwarded-by などのプロキシ固有の http ヘッダーも処理する必要があります。等

ただし、コードの一部:
Socket listener = (Socket) ar.AsyncState;
Socket handler = listener.EndAccept(ar);// この行は、ブラウザにバイトを送り返すためだけに新しいソケットを作成します: そのため、ブラウザはリクエストと同じ応答を受け取ります。

    // Create the state object.  
    StateObject state = new StateObject();  

したがって、別の新しいソケットとハンドラーの新しいペアを作成する必要があります。実際に要求を宛先に送信するには。そして、それらを作成する前に。宛先の実際の IP アドレスを取得する必要があります (プロキシは不明であり、クライアントは宛先 [domainname.com] の IP アドレスを知りません)。

これら 2 つを変更して、動作するかどうかを確認します。

  1. StateObject に 2 つのソケット destinationServer と destinationSocket がありますか??? そして、destinationServer は常に null のようです。したがって、ReceiveFromDestinationServer: line2では、代わりにstate.destinationSocketを使用する必要があります。
  2. ReadCallback:line29では、destinationSocket.Send(state.buffer,bytesRead,SocketFlags.None);を使用する必要があります。代わりに、またはバッファー全体を宛先に送信すると、「400 bad request」が確実に取得されます
于 2012-04-27T17:46:42.627 に答える