套接字异常:远程主机强制关闭现有连接

时间:2012-04-27 17:25:40

标签: c# sockets proxy asyncsocket serversocket

最近,在我的学校有关于http代理类的项目。因此,它的目的是当您在浏览器上键入url时,请求到达您的代理,在解压缩主机后,您重新创建新请求并请求到目标服务器,然后,您将从目标服务器接收数据并发送回浏览器渲染页面。

我创建了一个异步的http代理服务器类。在StateObject类中,我有两个socket,一个是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时,加载网站后,应用程序会自动退出。 但例外仍然存在。

2 个答案:

答案 0 :(得分:0)

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

 telnet ipaddr port

答案 1 :(得分:0)

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

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

然后代理执行依赖:

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

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

但部分代码:
        套接字侦听器=(套接字)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始终为null,因此在ReceiveFromDestinationServer:line2中,您应该使用 state.destinationSocket ;
  2. 在ReadCallback:line29中你应该使用 destinationSocket.Send(state.buffer,bytesRead,SocketFlags.None); ,或者你将整个缓冲区发送到目的地,那么你肯定会得到一个'400错误请求'