Android Socks5代理伺服器程式開發
阿新 • • 發佈:2019-01-30
原理
為處理移動端網路斷連問題,實現應用無關。我們考慮採用client<->proxy<->Internet
的三層架構。從client->proxy
這一環節,ProxyDroid已經能夠實現。
因此接下來主要需要完成的工作是
proxy
的開發proxy
與Internet
互相之間的資訊轉發,以及剩餘的從proxy->client
端資訊傳輸。
在ProxyDroid端我們採用了Socks5協議。它的優勢是:無需proxy
從報文內容中解析目的IP以及埠號,而是可以從正式資料傳輸前的握手資訊獲取。
Socks5代理工作模式
client
連線Socks5 proxy
client
端傳送命令{5,1,0}
proxy
返回應答{5,0}
,表示可以進行代理client
傳送:{5,1,0,1}
+目的地址(4位元組的16進製表示)+目的埠(2位元組的16進製表示)proxy
提取出IP地址、埠號與外網建立socket
proxy
向client
返回應答:{5,0,0,1}
+外網套接字繫結的IP地址(4位元組的16進製表示)+外網套接字繫結的埠號(2位元組的16進製表示)proxy
不斷檢測client
套接字,讀出資料傳送給外網proxy
不斷檢測外網套接字,讀出資料傳送給client
程式碼
基本思想如下:
- 主活動類中會設定一個
ServerSocket
proxydroid
傳送過來的socks5
報文從而建立socket
,然後將該socket
傳遞給伺服器執行緒; - 伺服器執行緒與
client
端通訊,建立從client
與外網之間的互動鏈路。其中伺服器執行緒自己作為中間媒介。
主活動
public class MainActivity extends AppCompatActivity { private String TAG = "SocketServer"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread(new Runnable() { @Override public void run() { try { ServerSocket serverSocket = new ServerSocket(34500); //這裡隨機選擇了一個埠,需與proxydroid中設定的埠一致 Log.d(TAG, "Port=" + serverSocket.getLocalPort()); while (true) { Socket socket = serverSocket.accept();//若獲取不到會一直阻塞 new Thread(new ServerThread(socket)).start();//觸發伺服器執行緒 } }catch (Exception e){ e.printStackTrace(); } } }).start(); } }
伺服器執行緒
public class ServerThread implements Runnable {
private Socket socket;
private String TAG = this.getClass().getName();
private int BUFF_SIZE = 1024 * 100;
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
InputStream innerInputStream = socket.getInputStream();
OutputStream innerOutputStream = socket.getOutputStream();
byte[] buff = new byte[BUFF_SIZE];
int rc;
ByteArrayOutputStream byteArrayOutputStream;
/**
* client會向proxy傳送510,所以這裡執行的結果是buff={5,1,0}
* Caution: 這裡不能跟下面的innerInputStream.read(buff, 0, 10);合併成innerInputStream.read(buff, 0, 13);
* 我試過,大部分情況沒影響,但是偶爾會出現重大bug(讀不出外網ip),至於原因暫不詳
* 看來這種input和output型別的操作還是穩重一點,不要太心急
*/
innerInputStream.read(buff, 0, 3);
/**
* proxy向client傳送應答{5,0}
*/
byte[] firstAckMessage = new byte[]{5, 0};
byte[] secondAckMessage = new byte[10];
innerOutputStream.write(firstAckMessage);
innerOutputStream.flush();
/**
* client傳送命令5101+目的地址(4Bytes)+目的埠(2Bytes)
* 即{5,1,0,1,IPx1,IPx2,IPx3,IPx4,PORTx1,PORTx2} 一共10位
* 例如傳送給52.88.216.252伺服器的80埠,那麼這裡buff就是{5,1,0,1,52,88,-40,-4,0,80}(這裡每位都是byte,所以在-128~127之間,可以自己換算成0~255)
*/
innerInputStream.read(buff, 0, 10);
String IP = byte2int(buff[4]) + "." + byte2int(buff[5]) + "." + byte2int(buff[6]) + "." + byte2int(buff[7]);
int port = byte2int(buff[8]) * 256 + byte2int(buff[9]);
Log.e("ServerThread", "Connected to " + IP + ":" + port);
Socket outerSocket = new Socket(IP, port);
InputStream outerInputStream = outerSocket.getInputStream();
OutputStream outerOutputStream = outerSocket.getOutputStream();
/**
* proxy 向 client 返回應答5+0+0+1+因特網套接字繫結的IP地址(4位元組的16進製表示)+因特網套接字繫結的埠號(2位元組的16進製表示)
*/
byte ip1[] = new byte[4];
int port1 = 0;
ip1 = outerSocket.getLocalAddress().getAddress();
port1 = outerSocket.getLocalPort();
secondAckMessage[0] = 5;
secondAckMessage[1] = 0;
secondAckMessage[2] = 0;
secondAckMessage[3] = 1;
secondAckMessage[4] = ip1[0];
secondAckMessage[5] = ip1[1];
secondAckMessage[6] = ip1[2];
secondAckMessage[7] = ip1[3];
secondAckMessage[8] = (byte) (port1 >> 8);
secondAckMessage[9] = (byte) (port1 & 0xff);
innerOutputStream.write(secondAckMessage, 0, 10);
innerOutputStream.flush();
/**
* 應答執行緒:從外網不斷讀資料發到client
*/
SocksResponseThread responseThread = new SocksResponseThread(outerInputStream, innerOutputStream);
responseThread.start();
/**
* 本執行緒:從client不斷讀資料發到外網
*/
byteArrayOutputStream = new ByteArrayOutputStream();
while ((rc = innerInputStream.read(buff, 0, BUFF_SIZE)) > 0) {
outerOutputStream.write(buff, 0, rc);
byteArrayOutputStream.write(buff, 0, rc);
outerOutputStream.flush();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (socket != null) socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public int byte2int(byte b) {
return b & 0xff;
}
}
應答執行緒
public class SocksResponseThread extends Thread {
private InputStream in;
private OutputStream out;
private int BUFF_SIZE = 1024 * 100;
public SocksResponseThread(InputStream in, OutputStream out) {
this.in = in;
this.out = out;
}
@Override
public void run() {
int readbytes = 0;
byte buf[] = new byte[BUFF_SIZE];
while (true) {
try {
if (readbytes == -1) break;
readbytes = in.read(buf, 0, BUFF_SIZE);
if (readbytes > 0) {
out.write(buf, 0, readbytes);
}
out.flush();
} catch (Exception e) {
break;
}
}
}
}