原理
为处理移动端网络断连问题,实现应用无关。我们考虑采用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地址、端口号与外网建立socketproxy向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; } } } }
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/208406.html原文链接:https://javaforall.net
