1

最近,在我的学校有一个关于http代理类的项目。所以它的目的是当你在浏览器上输入url时,请求到达你的代理,提取主机后,你重新创建一个新的请求并请求目标服务器,然后,你将从目标服务器接收数据并发送回浏览器到渲染一个页面。

我创建了一个异步 http 代理服务器类。在 StateObject 类中,我有两个套接字,一个是 workSocket 来处理来自浏览器的新请求,第二个是 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)。我认为睡眠很重要,当设置为10时,有些站点加载速度很快,有些站点只加载一些信息,当设置为100时,加载站点后,应用程序会自动退出。但例外仍然存在。

4

2 回答 2

0

我认为您的代码用于在客户端和服务器之间进行交互。“AcceptCallback”中的“处理程序”是用于将字节发送回浏览器而不是发送到目的地的套接字。
因此,您需要编写另一个工作对(异步)/一个(同步)套接字来实现 http 代理。首先,您应该明确请求和响应的样子。
浏览器首先发送给您:

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

然后代理做依赖:

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

在您的代理收到 domainname.com 的响应后,将其发送回 browserIP。这就是你要做的,在 .NET 中,你还需要处理一些特定于代理的 http 标头,例如 X-forwarded-by。等等

但在您的代码部分:
Socket listener = (Socket) ar.AsyncState;
Socket handler = listener.EndAccept(ar);//这行创建一个新的套接字只是为了将字节发送回浏览器:所以你的浏览器将得到与其请求相同的回复。

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

所以你需要创建另一个新的 Socket 和一对新的处理程序。才能真正将请求发送到目的地。在你创建它们之前。您需要获取目标的真实 IP 地址(您的代理不知道,您的客户端不知道目标的 IP 地址[domainname.com])

改变这两个,看看它是否有效:

  1. 您的 StateObject 中有两个套接字destinationServer 和destinationSocket ???并且似乎 destinationServer 始终为空,因此在 ReceiveFromDestinationServer:line2 您应该使用state.destinationSocket
  2. 在 ReadCallback:line29 你应该使用destinationSocket.Send(state.buffer,bytesRead,SocketFlags.None); 相反,否则您会将整个缓冲区发送到目的地,那么您肯定会收到“400 错误请求”
于 2012-04-27T17:46:42.627 回答
0

可能是远程主机未运行或未侦听您尝试连接的端口。您可能需要使用 telnet 检查服务器应用程序的状态

 telnet ipaddr port
于 2012-04-27T17:44:14.003 回答