大家好,欢迎来到IT知识分享网。
原理
为处理移动端网络断连问题,实现应用无关。我们考虑采用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();
}
}
IT知识分享网
服务器线程
IT知识分享网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://yundeesoft.com/7474.html