迫切需要帮助构建Android代理服务器

时间:2012-07-18 19:33:10

标签: android multithreading proxy

我正在为Android构建一个代理服务器而且我已经碰壁了。我拼命地需要帮助。我对Android很新。 所以,起初,我在Java SE中创建了代理服务器。代理服务器的工作方式如下:

  1. 它侦听本地端口

  2. 当客户端连接时,会创建一个新线程。每个帖子也都有一个名字。

  3. 从请求中提取HOST。

  4. 然后将客户端的请求转发到远程地址(从HOST获取)。

  5. 然后,代理会读取远程服务器的地址,直到找到-1。

  6. 然后将此响应转发给客户端。

  7. 代理服务器在笔记本电脑中运行。客户端是Android手机的Youtube APP。 手机已植根,我已经安装了ProxyDroid来告诉客户端Proxy Server的位置。 使用ProxyDroid的“全局代理”选项。

    Java SE中的代码:

        import java.io.IOException;
        import java.io.InputStream;
        import java.io.OutputStream;
        import java.net.InetAddress;
        import java.net.NetworkInterface;
        import java.net.ServerSocket;
        import java.net.Socket;
        import java.util.Enumeration;
    
        class clientThread implements Runnable
        {
            Socket clientSocket = null;//each client socket.
    
            Socket connectYoutubeServerSock = null;//This socket is used to write client's request to the 
                                            //YouTube Server.
            int videoFlag = 0;//this flag determines whether the GET request is a video request or not. 
    
            final int BufferSize = 8019;
            public String hostURL;
            int youtubePort = 80;
            //public DataOutputStream sendClientResponse = null;
    
            public clientThread(Socket clientSocket)
            {
                this.clientSocket = clientSocket;
            }
    
    
            public void run()
            {
                System.out.println("Client connected: "+clientSocket.toString());
                System.out.println("Current Thread Name: "+Thread.currentThread().getName());
    
                try
                {
                    byte youtubeAppReqArray[] = new byte[BufferSize];//this byte array will house youtube app's request!
                    InputStream youtubeAppReq = clientSocket.getInputStream();
    
                    // reading the request and put it into youtubeAppReqArray.
                    int bytesRead = youtubeAppReq.read(youtubeAppReqArray,0,BufferSize);
                    String youtubeAppReqString = new String(youtubeAppReqArray, 0, bytesRead);
                    System.out.println(youtubeAppReqString);
    
                    // extract the url of the GET request!
                    int gStart = youtubeAppReqString.indexOf("GET: ") + 4;
                    int gEnd = youtubeAppReqString.indexOf('\n', gStart);
                    String gURL = youtubeAppReqString.substring(gStart, gEnd - 9);
                    System.out.println("URL of GET: " + gURL);
                    //test if videoplayback is there or not?
                    if(gURL.indexOf("videoplayback") > -1  )
                    {
                        System.out.println("This is a video request!");
                        videoFlag = 1;
                    }
    
    
                    // extract the host to connect to
                    int hStart = youtubeAppReqString.indexOf("Host: ") + 6;
                    int hEnd = youtubeAppReqString.indexOf('\n', hStart);
                    String host = youtubeAppReqString.substring(hStart, hEnd - 1);
                    System.out.println("Connecting to host " + host);
    
                    //forward the youtube app request from proxy to the youtube server
                    Socket youtubeServerSocket = new Socket(host, 80);
                    OutputStream writeToYoutubeServerStream = youtubeServerSocket.getOutputStream();
                    System.out.println("Forwarding request to server");
                    writeToYoutubeServerStream.write(youtubeAppReqArray, 0, bytesRead);
                    writeToYoutubeServerStream.flush(); 
    
                    // forward the response from the server to the browser
                    byte youtubeServerResArray[] = new byte[BufferSize];//this byte array will house youtube server's response.
    
                    InputStream readFromYoutubeServerStream = youtubeServerSocket.getInputStream();
                    OutputStream writeToYoutubeAppStream = clientSocket.getOutputStream();
    
                    System.out.println("Forwarding request from server");
    
                    int remoteBytesRead;
                    do 
                    {
                        remoteBytesRead = readFromYoutubeServerStream.read(youtubeServerResArray,0,BufferSize);
                        System.out.println("Receiving " + remoteBytesRead + " bytes");
                        if (remoteBytesRead > 0) 
                        {
                            writeToYoutubeAppStream.write(youtubeServerResArray, 0, remoteBytesRead);
                            String rData = new String(youtubeServerResArray,0,remoteBytesRead);
                            System.out.println("Remote data:  "+rData);
                        }
                    } while (remoteBytesRead > 0); 
    
                    writeToYoutubeAppStream.flush();
                    youtubeServerSocket.close();
                    clientSocket.close();
                    System.out.println("End of communication");
                }
                catch(IOException ioe)
                {
                    System.out.println("Error"+ioe.getMessage());
                }
            }
        }
    
        public class ChallengeProxy extends IOException 
        {
            public static final int SERVERPORT = 4447;
            public static int threadName = 0;
            public static void main(String[] args) 
            {
                ChallengeProxy proxyObj = new ChallengeProxy();
                proxyObj.knowIP();//get the IP address of the local machine!
    
                 try 
                 {
                     ServerSocket serverSocket = new ServerSocket(SERVERPORT);
                     System.out.println("Started on: "+SERVERPORT);
    
                     while(true)//Non stop listen for clients!
                     {
                         Socket clientSocket = serverSocket.accept();//blocks until client is connected!
                         System.out.println("Client connected: "+clientSocket.toString());
    
                         Thread t = new Thread(new clientThread(clientSocket), Integer.toString(threadName));
                         t.start();
    
                         threadName++;
                     }
                 } 
                 catch (IOException ioe) 
                 {
                    System.out.println("Could not listen on port: "+SERVERPORT);
                    System.out.println("Error"+ioe.getMessage());
                 }
                 catch(Exception e)
                 {
                     System.out.println("Error"+e.getMessage());
                 }
            }
    
            public void knowIP()
            {
                try 
                {
                     Enumeration e = NetworkInterface.getNetworkInterfaces();
    
                     while(e.hasMoreElements()) 
                     {
                        NetworkInterface ni = (NetworkInterface) e.nextElement();
                        System.out.println("Net interface: "+ni.getName());
    
                        Enumeration e2 = ni.getInetAddresses();
    
                        while (e2.hasMoreElements())
                        {
                           InetAddress ip = (InetAddress) e2.nextElement();
                           System.out.println("IP address: "+ ip.toString());
                        }
                     }
                 }
                 catch (Exception e) 
                 {
                     e.printStackTrace();
                 }
            }
    
    }
    

    这正是我想要的。现在,我将代码转换一点,以便在Android中运行。 但是,下面的逻辑是相同的。此外,当新客户端连接时,会创建一个新线程。 Youtube APP仍然是客户端。现在,YouTube APP将请求驻留在同一Android机器中的代理服务器。和以前一样,配置ProxyDroid,以便YouTube APP知道我的代理服务器在哪里运行 Android的代码看起来像(这个代码在我得到第一个答案之后就相应地修改了并且它没有完整,因为在完成代码之前已经开始显示严重的错误!):

    public class ChallengeAndroidProxyActivity extends Activity 
    {
        public static final int SERVERPORT = 4453;
        public static int threadName = 0;
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) 
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
    
            (new Thread(new Runnable() 
                                     {
                                        public void run() 
                                        {
                                            startSocketServer();
                                        }
                                     }
                         )).start();
        }
    
        /*
         * Here we will create an infinite loop that will listen
         * for client connections.
         * 
         * */
        public void startSocketServer() 
        {
            knowIP();// Now we find the IP address of the native machine.
            String LOG_TAG = "SocketServerThread";
            Log.i(LOG_TAG,"Inside the server thread! ");
    
            try 
            {
                ServerSocket serverSocket = new ServerSocket(SERVERPORT);
                Log.i(LOG_TAG, "Started on : " + SERVERPORT);
    
                threadName = 0;
                while (true)// Non stop listen for clients!
                {
                    Socket clientSocket = serverSocket.accept();
                    Log.d(LOG_TAG , "Client connected : " + clientSocket.toString());
    
                    Thread t = new Thread(new clientThread(clientSocket), Integer.toString(threadName));
                    t.start();
    
                    threadName++;
                }
            }
            catch (IOException ioe) 
            {
                Log.i(LOG_TAG, "Could not listen on port: " + SERVERPORT);
                Log.i(LOG_TAG, "Error : " + ioe.getMessage());
            } 
            catch (Exception e) 
            {
                Log.i(LOG_TAG, "Error : " + e.getMessage());
            }
        }
    
    class clientThread implements Runnable
        {
            String LOG_TAG = "clientSocketThread";
            Socket clientSocket = null;//each client socket.
    
            Socket connectYoutubeServerSock = null;//This socket is used to write client's request to the 
                                            //YouTube Server.
            int videoFlag = 0;//this flag determines whether the GET request is a video request or not. 
    
            final int BufferSize = 8019;
            public String hostURL;
            int youtubePort = 80;
    
            public clientThread(Socket clientSocket)
            {
                this.clientSocket = clientSocket;
            }
    
            public void run()
            {
                Log.i(LOG_TAG,"Client connected: "+clientSocket.toString());
                Log.i(LOG_TAG,"Current Thread Name: "+Thread.currentThread().getName());
    
    
                try
                {//1
                    byte youtubeAppReqArray[] = new byte[BufferSize];//this byte array will house youtube app's request!
                    InputStream youtubeAppReq = clientSocket.getInputStream();
    
                    // reading the request and put it into youtubeAppReqArray.
                    int bytesRead = youtubeAppReq.read(youtubeAppReqArray,0,BufferSize);
                    String youtubeAppReqString = new String(youtubeAppReqArray, 0, bytesRead);
                    Log.i("Youtube App Request: ",youtubeAppReqString);
    
                    // extract the host to connect to
                    int hStart = youtubeAppReqString.indexOf("Host: ") + 6;
                    int hEnd = youtubeAppReqString.indexOf('\n', hStart);
                    String host = youtubeAppReqString.substring(hStart, hEnd - 1);
                    Log.i("Connecting to host ",host);
    
                    InetAddress addr = InetAddress.getByName(host);
                    int port = 80;
                    SocketAddress sockaddr = new InetSocketAddress(addr, port);
    
                    Socket youtubeServerSocket = new Socket();
                    youtubeServerSocket.connect(sockaddr);
    
                    OutputStream writeToYoutubeServerStream = youtubeServerSocket.getOutputStream();
                    //writeToYoutubeServerStream.write(youtubeAppReqArray, 0, bytesRead);
    
                }//1
                catch(IOException ioe)
                {//1
                    Log.i("Error",ioe.getMessage());
                }//1
            }
        }   
    
    
        /************************************************************************************/
        public void knowIP()
        {
                This function just gets the IP of the native machine.
        }
    }
    

    代码已根据第一个答案建议的更改进行了修改。当代码行

    //writeToYoutubeServerStream.write(youtubeAppReqArray, 0, bytesRead);  
    

    已注释(通过此行,我的代理将客户端的请求写入远程服务器),我从客户端获取的请求如下所示:

    07-19 22:30:16.309: I/ApplicationPackageManager(2843): cscCountry is not German : NEE
    07-19 22:30:16.399: I/Net interface:(2843): wlan0
    07-19 22:30:16.429: I/IP address:(2843): /192.168.1.129
    07-19 22:30:16.429: I/Net interface:(2843): lo
    07-19 22:30:16.439: I/IP address:(2843): /127.0.0.1
    07-19 22:30:16.439: I/SocketServerThread(2843): Inside the server thread! 
    07-19 22:30:16.439: I/SocketServerThread(2843): Started on : 4453
    07-19 22:30:20.929: D/SocketServerThread(2843): Client connected : Socket[addr=/192.168.1.129,port=44276,localport=4453]
    07-19 22:30:20.959: I/clientSocketThread(2843): Client connected: Socket[addr=/192.168.1.129,port=44276,localport=4453]
    07-19 22:30:20.959: I/clientSocketThread(2843): Current Thread Name: 0
    07-19 22:30:20.969: I/Youtube App Request:(2843): GET http://gdata.youtube.com/feeds/api/users/asadfffx/newsubscriptionvideos?format=2%2C3%2C9&start-index=1&max-results=10&safeSearch=none HTTP/1.1
    
    07-19 22:30:20.969: I/Youtube App Request:(2843): GData-Version: 2
    
    07-19 22:30:20.969: I/Youtube App Request:(2843): X-GData-Device: device-id="AOuj_RqmoX-WkCNNaJKieF2mmwMlkOMFRk7sQsKP_wmdrL1BB1N9V_iVIJAUBkvvyzGdpxWVS83wE7UkGPYjWf0BWvbPa0Uoo0cmgKfxzEqOog8EC-Rm1Wg", data="H1NEdDZ+FuL4U9v3Bx1hlVIAm1s="
    
    07-19 22:30:20.969: I/Youtube App Request:(2843): Authorization: GoogleLogin auth="DQAAAKcAAAC5aU6IN0t6yUkoiU9OmjU8fYjKm1m7MLKleHLhtR-uwCclnZGcKGm-6gYfkjvAGKGXK88dbKnB708CVCQkvJofL6smBXp7TEFj1FqGaRasdOx2iVYrbaTbWtIc8sBaga7v2P1BfctMiSTtEU2HWCqqCiubMvpfspnl9wS284SLgk-Se4jjFsZTo-84la_2wJy_Wmap8OSTdif7JoaxpxYLAWECByrJO50iMTj__6cN3A"
    
    07-19 22:30:20.969: I/Youtube App Request:(2843): Host: gdata.youtube.com
    
    07-19 22:30:20.969: I/Youtube App Request:(2843): User-Agent: Android-YouTube/2
    
    07-19 22:30:20.969: I/Youtube App Request:(2843): Proxy-Connection: close
    
    07-19 22:30:20.969: I/Youtube App Request:(2843): Connection: close
    
    07-19 22:30:20.969: I/Youtube App Request:(2843): 
    
    07-19 22:30:20.979: I/Connecting to host(2843): gdata.youtube.com
    

    这是客户端请求的样子。但是当我取消注释以下行时(即它被执行):

    writeToYoutubeServerStream.write(youtubeAppReqArray, 0, bytesRead);   
    
    一切都崩溃了。特别是,来自YouTube应用程序的请求没有任何意义。创建了数百个线程。我的GET请求开始看起来像:

    07-19 22:36:21.969: I/Youtube App Request:(2916): GData-Version: 2
    
    07-19 22:36:21.969: I/Youtube App Request:(2916): X-GData-Device: device-id="AOuj_RqmoX-WkCNNaJKieF2mmwMlkOMFRk7sQsKP_wmdrL1BB1N9V_iVIJAUBkvvyzGdpxWVS83wE7UkGPYjWf0BWvbPa0Uoo0cmgKfxzEqOog8EC-Rm1Wg", data="H1NEdDZ+FuL4U9v3Bx1hlVIAm1s="
    
    07-19 22:36:21.969: I/Youtube App Request:(2916): Authorization: GoogleLogin auth="DQAAAKcAAAC5aU6IN0t6yUkoiU9OmjU8fYjKm1m7MLKleHLhtR-uwCclnZGcKGm-6gYfkjvAGKGXK88dbKnB708CVCQkvJofL6smBXp7TEFj1FqGaRasdOx2iVYrbaTbWtIc8sBaga7v2P1BfctMiSTtEU2HWCqqCiubMvpfspnl9wS284SLgk-Se4jjFsZTo-84la_2wJy_Wmap8OSTdif7JoaxpxYLAWECByrJO50iMTj__6cN3A"
    
    07-19 22:36:21.969: I/Youtube App Request:(2916): Host: gdat
    07-19 22:36:21.969: I/Connecting to host(2916): gdata.youtube.com
    07-19 22:36:21.979: D/SocketServerThread(2916): Client connected : Socket[addr=/192.168.1.129,port=55126,localport=4453]
    07-19 22:36:21.979: I/clientSocketThread(2916): Client connected: Socket[addr=/192.168.1.129,port=55125,localport=4453]
    07-19 22:36:21.979: I/clientSocketThread(2916): Current Thread Name: 463
    07-19 22:36:21.979: I/Youtube App Request:(2916): GET http://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.comhttp://gdata.youtube.com/feeds/api/users/asadfffx/newsubscriptionvideos?format=2%2C3%2C9&start-index=1&max-results=10&safeSearch=none HTTP/1.1
    
    07-19 22:36:21.979: I/Youtube App Request:(2916): GData-Version: 2
    
    07-19 22:36:21.979: I/Youtube App Request:(2916): X-GData-Device: device-id="AOuj_RqmoX-WkCNNaJKieF2mmwMlkOMFRk7sQsKP_wmdrL1BB1N9V_iVIJAUBkvvyzGdpxWVS83wE7UkGPYjWf0BWvbPa0Uoo0cmgKfxzEqOog8EC-Rm1Wg", data="H1NEdDZ+FuL4U9v3Bx1hlVIAm1s="
    
    07-19 22:36:21.979: I/Youtube App Request:(2916): Authorization: GoogleLogin auth="DQAAAKcAAAC5aU6IN0t6yUkoiU9OmjU8fYjKm1m7MLKleHLhtR-uwCclnZGcKGm-6gYfkjvAGKGXK88dbKnB708CVCQkvJofL6smBXp7TEFj1FqGaRasdOx2iVYrbaTbWtIc8sBaga7v2P1BfctMiSTtEU2HWCqqCiubMvpfspnl9wS284SLgk-Se4jjFsZTo-84la_2wJy_Wmap8OSTdif7JoaxpxYLAWECByrJO50iMTj__6cN3A"
    
    07-19 22:36:21.979: I/Youtube App Request:(2916): Host: gdata.youtube.com
    

    为什么GET请求看起来像这样?我的猜测是,线程有问题。 创建套接字,读/写线程安全吗?代码是否在线程之间跳转?我究竟做错了什么?相同的代码适用于Java SE;那为什么它会在Android中崩溃?

    任何建议,评论和代码段都将不胜感激。

1 个答案:

答案 0 :(得分:3)

您不应该在活动的onCreate()方法中运行服务器循环,因为您将阻止UI线程。启动另一个运行套接字服务器的线程。 在onCreate(),onStart()和onResume()方法完成之前,您的应用程序不会被视为正在运行。如果您阻止onCreate(),您的应用程序将永远无法启动。

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    (new Thread(new Runnable() {

        public void run() {
            startSocketServer();
        }
    })).start();
}

private void startSocketServer() {
    final String LOG_TAG = "SocketServerThread";
    knowIP();

    try {

        ServerSocket serverSocket = new ServerSocket(SERVERPORT);
        Log.i(LOG_TAG, "Started on : " + SERVERPORT);

        while (true)// Non stop listen for clients!
        {
            Socket clientSocket = serverSocket.accept();!
            Log.d(LOG_TAG , "Client connected : " + clientSocket.toString());

            Thread t = new Thread(new clientThread(clientSocket),
                    Integer.toString(threadName));
            t.start();

            threadName++;
        }
    } catch (IOException ioe) {
        Log.i(LOG_TAG, "Could not listen on port: " + SERVERPORT);
        Log.i(LOG_TAG, "Error : " + ioe.getMessage());
    } catch (Exception e) {
        Log.i(LOG_TAG, "Error : " + e.getMessage());
    }
}

阅读Android应用程序生命周期 http://developer.android.com/reference/android/app/Activity.html Activity lifecycle

您还可以考虑将套接字服务器作为服务运行。 http://developer.android.com/guide/components/services.html