Android Socks5代理服务器程序开发

原创 2016年07月16日 14:27:54

原理

为处理移动端网络断连问题,实现应用无关。我们考虑采用client<->proxy<->Internet的三层架构。从client->proxy这一环节,ProxyDroid已经能够实现。

因此接下来主要需要完成的工作是

  1. proxy的开发
  2. proxyInternet互相之间的信息转发,以及剩余的从proxy->client端信息传输。

ProxyDroid端我们采用了Socks5协议。它的优势是:无需proxy从报文内容中解析目的IP以及端口号,而是可以从正式数据传输前的握手信息获取。

Socks5代理工作模式

  1. client连接Socks5 proxy服务器端口
  2. client端发送命令{5,1,0}
  3. proxy返回应答{5,0},表示可以进行代理
  4. client发送:{5,1,0,1}+目的地址(4字节的16进制表示)+目的端口(2字节的16进制表示)
  5. proxy提取出IP地址、端口号与外网建立socket
  6. proxyclient返回应答:{5,0,0,1}+外网套接字绑定的IP地址(4字节的16进制表示)+外网套接字绑定的端口号(2字节的16进制表示)
  7. proxy不断检测client套接字,读出数据发送给外网
  8. proxy不断检测外网套接字,读出数据发送给client

代码

基本思想如下:

  1. 主活动类中会设置一个ServerSocket用于接收proxydroid发送过来的socks5报文从而建立socket,然后将该socket传递给服务器线程;
  2. 服务器线程与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;
            }
        }
    }
}
版权声明:本文为博主原创文章,未经博主允许不得转载。

安卓手机使用Shadowsocks(影梭)教程

请主要这个需要的是Shadowsocks帐号,不是SSH帐号,只有购买了本站的一号通帐号参考这个教程才有用 首先下载软件 点击此处下载 下载后安装到您的手机内。 然后安装,安装完成后有个叫影梭的...
  • hjh200507609
  • hjh200507609
  • 2017年03月29日 17:31
  • 59014

教你在Android手机上使用全局代理!

前言:在Android上使用系统自带的代理,限制灰常大,仅支持系统自带的浏览器。这样像QQ、飞信、微博等这些单独的App都不能使用系统的代理。如何让所有软件都能正常代理呢?ProxyDroid这个软件...
  • codezjx
  • codezjx
  • 2013年05月01日 15:08
  • 31189

Android分享之“始终”和“仅此一次”

几乎所有的App都有分享功能,除了使用第三方的分享SDK之外,我们还可以使用隐式Intent的分享方式,自动弹出一个包含系统中支持分享功能App的分享界面,用户可以选择一个希望使用的分享方式来进行分享...
  • u014738140
  • u014738140
  • 2014年09月02日 12:59
  • 2362

Shadowsocks(Sock5代理)的PAC模式与全局模式与VPN的区别

VPN,即虚拟专用网络 虚拟专用网络的功能是:在公用网络上建立专用网络,进行加密通讯。在企业网络中有广泛应用。VPN网关通过对数据包的加密和数据包目标地址的转换实现远程访问。VPN有多种分类方式,主要...
  • guyue35
  • guyue35
  • 2016年03月19日 19:47
  • 43052

Android 使用Socket实现服务器与手机客户端的长连接五:使用队列封装请求

一、概述:1、把发送者发送的信息全部封装在blockqueue队列里,然后使用connManager把队列里的信息取出,分发出去 2、原理图: 二、实现:/** * @描述 使用socket实现...
  • lovoo
  • lovoo
  • 2016年06月27日 22:54
  • 2270

Android:这是一份很详细的Socket使用攻略

前言 Socket的使用在Android的网络编程中非常重要 今天我将带大家全面了解Socket及其使用方法 目录1.网络基础1.1 计算机网络分层计算机网络分为五层:物理层、数据链路层、网络层、运输...
  • carson_ho
  • carson_ho
  • 2016年11月27日 21:09
  • 24709

ww socks5 代理服务器 安卓手机使用教程

WW socks5 代理教程     设置一:首先我们会发给你或者网盘下载APP安装好后,点击代理机器人打开   首次打开会提示ROOT 权限选择 永久运行就行     设置二:   以上...
  • qq_30796159
  • qq_30796159
  • 2017年12月11日 14:35
  • 1766

安卓socks5代理 apk

  • 2017年06月06日 18:04
  • 156KB
  • 下载

Sock5协议详解

Sock5协议详解 由于项目需求,最近需要了解一些代理的知识,因此看了一下sock5协议。主要还是RFC1928.也参考了网上的一些翻译。 防火墙的使用,有效的隔离了机构内部网络和外部网络,这种...
  • suifengdeshitou
  • suifengdeshitou
  • 2015年09月28日 11:41
  • 4330

【Android】给Android Studio设置代理

我们都知道Android Studio是基于IDEA开发的,而我们写的每一个程序又都是有Gradle构建的,Gradle的优点可以说是很多,被很多程序员夸得没边,但是它有一个特点还是值得我们注意的.我...
  • lchad
  • lchad
  • 2015年02月06日 18:56
  • 76652
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android Socks5代理服务器程序开发
举报原因:
原因补充:

(最多只允许输入30个字)