2012-07-18 53 views
2

我正在建立一個代理服務器的android和我已經打了一堵牆。我絕望地需要幫助。我對Android非常陌生。 因此,我首先在Java SE中創建了代理服務器。代理服務器的工作原理如下:拼命地需要幫助建立一個代理服務器的Android

  1. 它監聽本地端口,

  2. 當客戶端連接,一個新的線程被創建。每個線程也被賦予一個名字。

  3. HOST從請求中提取。

  4. 然後客戶端的請求被轉發到遠程地址(從主機獲取)。

  5. 代理然後讀取遠程服務器的地址,直到找到-1。

  6. 該響應然後被轉發給客戶端。

代理服務器運行在筆記本電腦上。客戶端是Android手機的Youtube APP。 手機已經植根,我已經安裝了ProxyDroid以告訴客戶端代理服務器在哪裏。 使用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應用程序會請求駐留在同一臺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中崩潰?

任何建議,意見和代碼段將不勝感激。

回答

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

+0

嗨,謝謝你的回覆。根據您的建議,我已對代碼進行了更改。但現在我看到了一些問題,這可能是線程相關的。我已經按照你的建議編輯的代碼,並把它放在這裏。請看看並給我一些建議。 – user1185396 2012-07-19 19:47:41