├── .DS_Store ├── .idea ├── .gitignore ├── artifacts │ ├── MyNatClient.xml │ └── MyNatServer.xml ├── libraries │ ├── commons_io_2_7.xml │ └── org_dtools_javaini_v1_1_1.xml ├── misc.xml ├── modules.xml ├── uiDesigner.xml └── vcs.xml ├── LocalConfig.json ├── MyNat.iml ├── README.md ├── RemoteConfig.json ├── lib ├── commons-io-2.7.jar └── fastjson-1.2.73.jar ├── out └── artifacts │ ├── MyNatClient │ ├── LocalConfig.json │ ├── MyNatClient.jar │ ├── mac_close_proxy.sh │ ├── mac_open_proxy.sh │ ├── win_close_proxy.bat │ └── win_open_proxy.bat │ └── MyNatServer │ ├── MyNatServer.jar │ └── RemoteConfig.json └── src ├── .DS_Store ├── META-INF-CLIENT ├── MANIFEST.MF └── META-INF │ └── MANIFEST.MF ├── META-INF-SERVER ├── MANIFEST.MF └── META-INF │ └── MANIFEST.MF ├── META-INF └── MANIFEST.MF └── com ├── .DS_Store └── proxy ├── .DS_Store ├── common ├── Constant.java ├── DataFilter.java ├── DeqMap.java ├── NetContentGrabber.java ├── OnBrokenCallBack.java ├── OnBrokenCallBackInconnctor.java ├── OnDataReceiveCallBack.java ├── ReadDataRunnable.java ├── ReadDataThread.java └── RequestBannerMethodAna.java ├── domain ├── ForWard.java ├── ServerConfig.java └── Shuff.java ├── local ├── LocalManager.java └── starter │ └── LocalStarter.java ├── middleware ├── ConnectorRunnable.java └── ConnectorThread.java ├── remote ├── ProxyServerManager.java ├── RemoteProxyServer.java └── starter │ └── RemoteStarter.java ├── test ├── ConnectorThreadTest.java ├── LocalManagerTest.java ├── LocalProxyServerConnectorTest.java ├── Test.java ├── TestConnectQtServer.java └── TestIniTool.java └── utils ├── Base64Coder.java ├── Log.java ├── ProjectTool.java ├── ShuffTool.java └── ZipUtil.java /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woshiwzy/OpenProxyJ/86e6f29d7b4e5fa9b9393bcbf6771abc8b095063/.DS_Store -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/artifacts/MyNatClient.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | $PROJECT_DIR$/out/artifacts/MyNatClient 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/artifacts/MyNatServer.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | $PROJECT_DIR$/out/artifacts/MyNatServer 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/libraries/commons_io_2_7.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/libraries/org_dtools_javaini_v1_1_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LocalConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "remoteServer": "xx.xx.xx.xx", 3 | "remotePort": "1667", 4 | "host": "127.0.0.1", 5 | "port": "1666" 6 | } -------------------------------------------------------------------------------- /MyNat.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # 申明 3 | 4 | 本项目只是作者记录和分享Java网络编程学习心得,请勿用于非法用途,否则后果自负! 5 | 6 | 原理介绍博客: 7 | 8 | https://blog.csdn.net/wang382758656/article/details/123098032 9 | 10 | https://juejin.cn/post/7069218800226271239 11 | 12 | # 介绍 13 | 14 | 纯Java实现的网络代理小工具,可以实现http网络协议隐藏,突破局域网拦截,上班就可以开心的摸鱼了... 15 | 16 | 当然 你也可以用来学习Socket通信,多线程编程,也可以用于简单的生产环境,实现正向代理,流量监控,上网行为监控等。 17 | 18 | 本项目原始基于idea,可导入idea直接运行。 19 | 20 | # 如何使用 21 | 22 | ## 直接运行已经打包好的jar(快速使用) 23 | [已经打包好的jar在这里](./out/artifacts/) 24 | 25 | ### 1.设置系统代理 26 | 27 | 要想看到效果,需要把你操作系统代理设置到 你LocalConfig.json文件设置的host和port上 28 | 29 | 设置代理方法,以win10 为例(Mac 自己搜索一下,也很简单,移动设备要使用这个代理的话,需要和运行这个代理程序客户端的主机在同一个局域网络 30 | ,并且把代理设置到这个主机的IP和配置端口上) 31 | 32 |     1.右击桌面右下角的wifi符号:选择弹出菜单“打开网络和internet设置“ 33 | 34 |     2.接着在打开的“设置“窗口中,点击”代理“ 35 | 36 |     3.在右侧的代理设置面板开启"使用代理服务器"开关 37 | 38 |     4.并且在地址一栏填写: 127.0.0.1,端口一栏填写,LocalConfig.json中配置的port值 39 | 40 | 41 | 如果是PC 或者Mac 上,上述步骤可以通过bat脚本完成 ,前提需要配置JRE环境变量, 42 | 43 | **当然你也可以把JRE拷贝出来做一个整体包,直接点击脚本直接运行** 44 | 45 | Win shell:open_proxy.bat,close_proxy.bat 46 | 47 | Mac shell: open_proxy.sh,close_proxy.sh 48 | 49 | ### 2.运行Jar包 50 | 51 | 分别将配置文件和jar放在同一个目录 52 | 53 | 在服务器上,运行server: java -jar MyNatServer.jar(如果在一台电脑上既运行客户端又运行服务器端,仅仅相当于charles抓包效果) 54 | 55 | 在你的电脑上,运行client: java -jar MyNatClient.jar 56 | 57 | 到此,你可以打开浏览器访问"一些网站"试试看, 58 | 59 | 60 | #源码运行 61 | 62 | ##客户端 63 | 运行本地代理端主方法 64 | 65 | com.proxy.local.starter.LocalStarter 66 | 67 | 以这个类为主类打包可执行jar,LocalConfig.json文件放在和这个jar同一个目录下 68 | 运行时直接 java -jar MyNatClient.jar 即可. 69 | 70 | ###客户端配置文件详解 71 | LocalConfig.json 这个文件和jar包放在同一个目录下 72 | 73 | 74 | { 75 | "remoteServer": "xx.xx.xx.xx", 76 | "remotePort": "1667", 77 | "host": "127.0.0.1", 78 | "port": "1666" 79 | } 80 | 81 | remoteServer:远程代理服务器的IP 82 | 83 | remotePort:远程代理服务器的端口,必须和RemoteConfig.json 配置的端口一致 84 | 85 | host:本地代理ip,永远为127.0.0.1(设置到系统代理的Ip) 86 | 87 | port:本地代理端口,可以是任意本地没有使用的端口(设置到系统代理的端口) 88 | 89 | *** 90 | 91 | ##服务器端 92 | 运行在服务器的主类 93 | 94 | com.proxy.remote.starter.RemoteStarter 95 | 96 | 以这个类为主类打包可执行jar,RemoteConfig.json文件放在和这个jar同一个目录下 97 | 运行时直接 java -jar MyNatServer.jar 即可. 98 | 99 | 100 | 101 | ###服务器端的配置详解 102 | { 103 | "host": "127.0.0.1", 104 | "port": 1666, 105 | "responseHttps": "true", 106 | "enc": "true" 107 | } 108 | 109 | 110 | host:远程代理服务器IP,因为是本地所以就是127.0.0.1" 永远不变,实际上你配置了也不会有任何影响 111 | 112 | port:服务器接受客户端端网络请求端口(服务器端配置文件唯一需要修改的) 113 | 114 | responseHttps:是否需要响应Https,永远是true(只有当做中间件的时候为false) 115 | 116 | enc:是否加密,解密,建议永远是true 117 | 118 | 119 | ### 欢迎fork star !!! 120 | -------------------------------------------------------------------------------- /RemoteConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "host": "127.0.0.1", 3 | "port": 1666, 4 | "responseHttps": "true", 5 | "enc": "true" 6 | } -------------------------------------------------------------------------------- /lib/commons-io-2.7.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woshiwzy/OpenProxyJ/86e6f29d7b4e5fa9b9393bcbf6771abc8b095063/lib/commons-io-2.7.jar -------------------------------------------------------------------------------- /lib/fastjson-1.2.73.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woshiwzy/OpenProxyJ/86e6f29d7b4e5fa9b9393bcbf6771abc8b095063/lib/fastjson-1.2.73.jar -------------------------------------------------------------------------------- /out/artifacts/MyNatClient/LocalConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "remoteServer": "127.0.0.1", 3 | "remotePort": "9999", 4 | "host": "127.0.0.1", 5 | "port": "8888" 6 | } -------------------------------------------------------------------------------- /out/artifacts/MyNatClient/MyNatClient.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woshiwzy/OpenProxyJ/86e6f29d7b4e5fa9b9393bcbf6771abc8b095063/out/artifacts/MyNatClient/MyNatClient.jar -------------------------------------------------------------------------------- /out/artifacts/MyNatClient/mac_close_proxy.sh: -------------------------------------------------------------------------------- 1 | 2 | 3 | networksetup -setsecurewebproxystate "Wi-Fi" off 4 | networksetup -setwebproxystate "Wi-Fi" off -------------------------------------------------------------------------------- /out/artifacts/MyNatClient/mac_open_proxy.sh: -------------------------------------------------------------------------------- 1 | 2 | trim(){ 3 | trimmed=$1 4 | trimmed=${trimmed%% } 5 | trimmed=${trimmed## } 6 | echo $trimmed 7 | } 8 | 9 | row=$(lsof -i:8888 | wc -l) 10 | irow=$(trim $row) 11 | # echo ${irow} 12 | 13 | if [ ${irow} -eq 0 ]; then 14 | echo "start jar....." 15 | nohup java -jar OPC_v1.7.jar & 16 | echo "jar starting..." 17 | else 18 | echo "jar has started" 19 | fi 20 | 21 | networksetup -setwebproxy "Wi-Fi" 127.0.0.1 8888 22 | networksetup -setsecurewebproxy "Wi-Fi" 127.0.0.1 8888 23 | 24 | networksetup -setsecurewebproxystate "Wi-Fi" on 25 | networksetup -setwebproxystate "Wi-Fi" on 26 | 27 | echo "ok you can surfing now..." 28 | 29 | -------------------------------------------------------------------------------- /out/artifacts/MyNatClient/win_close_proxy.bat: -------------------------------------------------------------------------------- 1 | chcp 65001 2 | @echo off 3 | echo “关闭代理” 4 | reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyEnable /t REG_DWORD /d 0 /f 5 | reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyServer /d "" /f 6 | echo “代理已经关闭,可以推出!” 7 | pause>nul 8 | 9 | -------------------------------------------------------------------------------- /out/artifacts/MyNatClient/win_open_proxy.bat: -------------------------------------------------------------------------------- 1 | chcp 65001 2 | @echo off 3 | echo "请确保代理ip和端口和配置文件一致" 4 | echo start set proxy 5 | reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyEnable /t REG_DWORD /d 1 /f 6 | reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyServer /d "127.0.0.1:8888" /f 7 | echo "代理已经设置,本地程序已经开启,请勿关闭这个窗口,否则无法访问网络!\n 如需关闭代理,请用双击关闭脚本 close_proxy脚本" 8 | java -jar MyNatClient.jar 9 | pause>nul 10 | 11 | -------------------------------------------------------------------------------- /out/artifacts/MyNatServer/MyNatServer.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woshiwzy/OpenProxyJ/86e6f29d7b4e5fa9b9393bcbf6771abc8b095063/out/artifacts/MyNatServer/MyNatServer.jar -------------------------------------------------------------------------------- /out/artifacts/MyNatServer/RemoteConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "host": "127.0.0.1", 3 | "port": 9999, 4 | "responseHttps": "true", 5 | "enc": "true" 6 | } -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woshiwzy/OpenProxyJ/86e6f29d7b4e5fa9b9393bcbf6771abc8b095063/src/.DS_Store -------------------------------------------------------------------------------- /src/META-INF-CLIENT/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: com.proxy.local.starter.LocalStarter 3 | 4 | -------------------------------------------------------------------------------- /src/META-INF-CLIENT/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: com.proxy.local.starter.LocalStarter 3 | 4 | -------------------------------------------------------------------------------- /src/META-INF-SERVER/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: com.proxy.remote.starter.RemoteStarter 3 | 4 | -------------------------------------------------------------------------------- /src/META-INF-SERVER/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: com.proxy.local.starter.LocalStarter 3 | 4 | -------------------------------------------------------------------------------- /src/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: com.proxy.remote.starter.RemoteStarter 3 | 4 | -------------------------------------------------------------------------------- /src/com/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woshiwzy/OpenProxyJ/86e6f29d7b4e5fa9b9393bcbf6771abc8b095063/src/com/.DS_Store -------------------------------------------------------------------------------- /src/com/proxy/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woshiwzy/OpenProxyJ/86e6f29d7b4e5fa9b9393bcbf6771abc8b095063/src/com/proxy/.DS_Store -------------------------------------------------------------------------------- /src/com/proxy/common/Constant.java: -------------------------------------------------------------------------------- 1 | package com.proxy.common; 2 | 3 | public class Constant { 4 | 5 | public static final String SPLITER = "<分隔符>"; 6 | public static final int HEADER_BAND = 50;// 头部宽度 7 | public static final char SPACE = '~';// 头部填充符 8 | 9 | public static final String PASSBOOK_TAG_START = "["; 10 | public static final String PASSBOOK_TAG_END = "]"; 11 | 12 | public static final String ENC_DEC_TAG = "-EC-";// 密码协议 13 | 14 | public static final String HTTPS_CONNECT_MSG = "HTTP/1.1 200 Connection Established\r\n\r\n"; 15 | public static final int BUFFER_SIZE = 512*2; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/com/proxy/common/DataFilter.java: -------------------------------------------------------------------------------- 1 | package com.proxy.common; 2 | 3 | import java.io.Serializable; 4 | 5 | import com.proxy.utils.ShuffTool; 6 | 7 | public class DataFilter implements Serializable { 8 | 9 | /** 10 | * 11 | */ 12 | private static final long serialVersionUID = 1L; 13 | 14 | public static boolean isDataProtect = true;// 会否进行数据保护 15 | 16 | private DeqMap deqMap; 17 | 18 | public DataFilter() { 19 | deqMap = ShuffTool.createPassWordBook(); 20 | } 21 | 22 | public DataFilter(DeqMap deqMap) { 23 | this.deqMap = deqMap; 24 | } 25 | 26 | public void encodeData(byte[] buffer, int start, int offset) { 27 | if (isDataProtect) { 28 | for (int i = start; i < offset; i++) { 29 | buffer[i] = deqMap.getVByK(buffer[i]); 30 | } 31 | } 32 | } 33 | 34 | public void decodeData(byte[] buffer, int start, int offset) { 35 | if (isDataProtect) { 36 | for (int i = start; i < offset; i++) { 37 | buffer[i] = deqMap.getKByV(buffer[i]); 38 | } 39 | } 40 | 41 | } 42 | 43 | 44 | /** 45 | * 获取序列化内容,方便分发传输 46 | * 47 | * @return 48 | */ 49 | public String getStringInChannel() { 50 | return Constant.PASSBOOK_TAG_START + this.deqMap.toString() + Constant.PASSBOOK_TAG_END; 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return deqMap.toString(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/com/proxy/common/DeqMap.java: -------------------------------------------------------------------------------- 1 | package com.proxy.common; 2 | 3 | import java.io.Serializable; 4 | import java.util.HashMap; 5 | import java.util.Map.Entry; 6 | 7 | /** 8 | * 双向HashMap 9 | * 既可以用用key获取value,也可以把value当做key,获取原始的Key 10 | * 仅限于没有重复的key-value键值对 11 | * @param 12 | * @param 13 | */ 14 | public class DeqMap implements Serializable { 15 | 16 | /** 17 | * 18 | */ 19 | private static final long serialVersionUID = 1L; 20 | private HashMap map1; 21 | private HashMap map2; 22 | 23 | public DeqMap() { 24 | map1 = new HashMap<>(); 25 | map2 = new HashMap<>(); 26 | } 27 | 28 | public void put(K k1, V v1, V v2, K k2) { 29 | map1.put(k1, v1); 30 | map2.put(v2, k2); 31 | } 32 | 33 | 34 | public void put(K k, V v) { 35 | map1.put(k, v); 36 | map2.put(v, k); 37 | } 38 | 39 | public V getVByK(K key) { 40 | return map1.get(key); 41 | } 42 | 43 | public K getKByV(V v) { 44 | return map2.get(v); 45 | } 46 | 47 | 48 | @Override 49 | public String toString() { 50 | 51 | StringBuilder sbf = new StringBuilder(); 52 | 53 | // System.out.println(this.map1.size()); 54 | for (Entry entry1 : this.map1.entrySet()) { 55 | sbf.append(entry1.getKey() + "=" + entry1.getValue() + ","); 56 | } 57 | 58 | sbf.append(Constant.ENC_DEC_TAG); 59 | 60 | // System.out.println(this.map2.size()); 61 | for (Entry entry2 : this.map2.entrySet()) { 62 | sbf.append(entry2.getKey() + "=" + entry2.getValue() + ","); 63 | } 64 | 65 | return sbf.toString(); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/com/proxy/common/NetContentGrabber.java: -------------------------------------------------------------------------------- 1 | package com.proxy.common; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.net.Socket; 7 | 8 | import com.alibaba.fastjson.util.IOUtils; 9 | import com.proxy.domain.ForWard; 10 | import com.proxy.utils.Log; 11 | 12 | /** 13 | * 代理通道 14 | */ 15 | public class NetContentGrabber extends Thread { 16 | 17 | private String host; 18 | private int port; 19 | private Socket socket; 20 | private InputStream inputStream; 21 | private OutputStream outputStream; 22 | 23 | private boolean isCloseIO = false; 24 | 25 | private OnDataReceiveCallBack onDataReceiveCallBack; 26 | 27 | public NetContentGrabber(ForWard forWard, OnDataReceiveCallBack onDataReceiveCallBack) { 28 | this.host = forWard.getHost(); 29 | this.port = forWard.getPort(); 30 | try { 31 | Log.println("准备JONIT链接到====>: " + host + ":" + port); 32 | this.socket = new Socket(host, port); 33 | Log.println("已经JONIT链接到====>: " + host + ":" + port); 34 | this.inputStream = this.socket.getInputStream(); 35 | this.outputStream = this.socket.getOutputStream(); 36 | this.onDataReceiveCallBack = onDataReceiveCallBack; 37 | 38 | } catch (Exception e) { 39 | closeConnect(); 40 | Log.redPrint("链接故障:" + forWard.getHost() + " " + forWard.getPort() + " " + e.getLocalizedMessage()); 41 | if (null != onDataReceiveCallBack) { 42 | onDataReceiveCallBack.onBrokenInGrabber(e, "NetContentGrabber.NetContentGrabber"); 43 | } 44 | }finally { 45 | 46 | } 47 | 48 | } 49 | 50 | public void closeConnect() { 51 | IOUtils.close(outputStream); 52 | IOUtils.close(inputStream); 53 | IOUtils.close(socket); 54 | isCloseIO = true; 55 | } 56 | 57 | /** 58 | * 向代理服务器发送数据 59 | * 60 | * @param data 61 | * @param start 62 | * @param offset 63 | * @throws IOException 64 | */ 65 | public void sendData(byte[] data, int start, int offset) throws IOException { 66 | if (null != this.outputStream && !isCloseIO) { 67 | this.outputStream.write(data, start, offset); 68 | this.outputStream.flush(); 69 | } 70 | } 71 | 72 | /** 73 | * 接受数据 74 | * 75 | * @param buffer 76 | * @param start 77 | * @param offset 78 | */ 79 | private void onReceiveData(byte[] buffer, int start, int offset) { 80 | if (null != onDataReceiveCallBack) { 81 | onDataReceiveCallBack.onReceiveData(buffer, start, offset); 82 | } 83 | } 84 | 85 | @Override 86 | public void run() { 87 | if (null != this.inputStream) { 88 | byte[] buffer = new byte[Constant.BUFFER_SIZE]; 89 | int ret = -1; 90 | try { 91 | while ((ret = this.inputStream.read(buffer)) != -1) { 92 | onReceiveData(buffer, 0, ret); 93 | } 94 | } catch (IOException e) { 95 | closeConnect(); 96 | if (null != onDataReceiveCallBack) { 97 | onDataReceiveCallBack.onBrokenInGrabber(e, "NetContentGrabber.run"); 98 | } 99 | } finally { 100 | 101 | } 102 | if (null != onDataReceiveCallBack) { 103 | onDataReceiveCallBack.onBrokenInGrabber(null, "NetContentGrabber.run"); 104 | } 105 | 106 | } 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/com/proxy/common/OnBrokenCallBack.java: -------------------------------------------------------------------------------- 1 | package com.proxy.common; 2 | 3 | /** 4 | * 连接断开回调方法 5 | */ 6 | public interface OnBrokenCallBack { 7 | void onBroken(Exception e, String tag,ReadDataThread readDataThread); 8 | } -------------------------------------------------------------------------------- /src/com/proxy/common/OnBrokenCallBackInconnctor.java: -------------------------------------------------------------------------------- 1 | package com.proxy.common; 2 | 3 | 4 | /** 5 | * 连接断开回调方法 6 | */ 7 | public interface OnBrokenCallBackInconnctor { 8 | void onBroken(Exception e, String tag,Thread connector); 9 | } -------------------------------------------------------------------------------- /src/com/proxy/common/OnDataReceiveCallBack.java: -------------------------------------------------------------------------------- 1 | package com.proxy.common; 2 | 3 | 4 | public interface OnDataReceiveCallBack { 5 | 6 | 7 | public void onReceiveData(byte[] buffer, int start, int offset); 8 | public void onBrokenInGrabber(Exception e,String tag); 9 | } 10 | -------------------------------------------------------------------------------- /src/com/proxy/common/ReadDataRunnable.java: -------------------------------------------------------------------------------- 1 | package com.proxy.common; 2 | 3 | import com.alibaba.fastjson.util.IOUtils; 4 | import com.proxy.domain.ForWard; 5 | import com.proxy.utils.Log; 6 | 7 | import java.io.ByteArrayOutputStream; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.io.OutputStream; 11 | import java.net.Socket; 12 | 13 | /** 14 | * 数据读取线程,适用于 Http和Https 代理请求转发到真实的主机 15 | */ 16 | public class ReadDataRunnable extends Thread { 17 | 18 | private Socket socket; 19 | private InputStream inputStream; 20 | private OutputStream outputStream; 21 | private NetContentGrabber netGrabber; 22 | private boolean responseHttpsProtocol;// 是否需要在本服务器响应Https协议 23 | 24 | private DataFilter dataFilter; 25 | 26 | public ReadDataRunnable(Socket socket, boolean responseHttpsProtocol) throws IOException { 27 | this.socket = socket; 28 | this.dataFilter = new DataFilter(); 29 | this.responseHttpsProtocol = responseHttpsProtocol; 30 | this.inputStream = this.socket.getInputStream(); 31 | this.outputStream = this.socket.getOutputStream(); 32 | } 33 | 34 | /** 35 | * 断网回调 36 | * @param e 37 | * @param tag 38 | */ 39 | private void onBroken(Exception e, String tag) { 40 | 41 | IOUtils.close(outputStream); 42 | IOUtils.close(inputStream); 43 | IOUtils.close(socket); 44 | 45 | if (null != netGrabber) { 46 | netGrabber.closeConnect(); 47 | } 48 | } 49 | 50 | private NetContentGrabber getNetContentGrabber(ForWard forward) { 51 | 52 | if (null == netGrabber) { 53 | netGrabber = new NetContentGrabber(forward, new OnDataReceiveCallBack() { 54 | 55 | @Override 56 | public void onBrokenInGrabber(Exception e, String tag) { 57 | onBroken(e, tag); 58 | } 59 | 60 | @Override 61 | public void onReceiveData(byte[] buffer, int start, int offset) { 62 | try { 63 | if (null != dataFilter) { 64 | dataFilter.encodeData(buffer, start, offset);// 加密 65 | } 66 | outputStream.write(buffer, start, offset); 67 | } catch (IOException e) { 68 | Log.redPrint("转发数据异常:" + e.getLocalizedMessage()); 69 | onBroken(e, "getNetContentGrabber"); 70 | } 71 | } 72 | }); 73 | } 74 | 75 | return netGrabber; 76 | } 77 | 78 | @Override 79 | public void run() { 80 | if (null != inputStream) { 81 | try { 82 | ByteArrayOutputStream byo = new ByteArrayOutputStream(); 83 | // 只要能读取到目标地址就停止读取,建立代理连接 84 | // 继续读取信息写入到目标服务器 85 | NetContentGrabber netGrabber = null; 86 | int ret = -1; 87 | byte[] dataBuffer = new byte[512]; 88 | boolean getHeader = false; 89 | while ((ret = inputStream.read(dataBuffer)) != -1) { 90 | if (null != dataFilter) {// 解密 91 | dataFilter.decodeData(dataBuffer, 0, ret); 92 | } 93 | 94 | if (!getHeader) { 95 | byo.write(dataBuffer, 0, ret);// 保存本次转发的头部信息 96 | byte[] nowData = byo.toByteArray(); 97 | // System.out.println(new String(nowData)); 98 | String header = new String(nowData); 99 | ForWard forward = RequestBannerMethodAna.anaForward(header); 100 | // System.out.println("头信息:" + forward.toString()); 101 | if (null != forward && forward.isParseSuccess()) { 102 | netGrabber = getNetContentGrabber(forward); 103 | netGrabber.start(); 104 | if (forward.isHttps()) { 105 | // 如果读到这个头不要转发给目标服务器 106 | // 如果是Https协议需要告诉浏览器一个固定的头部 107 | if (responseHttpsProtocol) { 108 | 109 | byte[] httpsResonseBytes = Constant.HTTPS_CONNECT_MSG.getBytes(); 110 | if (null != dataFilter) { 111 | dataFilter.encodeData(httpsResonseBytes, 0, httpsResonseBytes.length);// 加密 112 | } 113 | outputStream.write(httpsResonseBytes); 114 | outputStream.flush(); 115 | 116 | Log.redPrint("回应:" + Constant.HTTPS_CONNECT_MSG + " ====>>>" + forward.getHost()); 117 | } else { 118 | Log.redPrint("无须回应:" + Constant.HTTPS_CONNECT_MSG + " ====>>>" + forward.getHost()); 119 | } 120 | 121 | } else { 122 | // 如果是Http原封原样的写到目标通道中 123 | netGrabber.sendData(nowData, 0, nowData.length); 124 | } 125 | getHeader = true; 126 | } 127 | } else { 128 | // 得到头以后的数据就正常盲转发 129 | netGrabber.sendData(dataBuffer, 0, ret); 130 | } 131 | } 132 | 133 | } catch (IOException e) { 134 | onBroken(e, "异常结束:"+e.getLocalizedMessage()); 135 | Log.println("IO异常:" + e.getLocalizedMessage()); 136 | }finally { 137 | onBroken(null, "Normal end"); 138 | } 139 | } 140 | } 141 | 142 | 143 | } 144 | -------------------------------------------------------------------------------- /src/com/proxy/common/ReadDataThread.java: -------------------------------------------------------------------------------- 1 | package com.proxy.common; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.OutputStream; 7 | import java.net.Socket; 8 | 9 | import com.alibaba.fastjson.util.IOUtils; 10 | import com.proxy.domain.ForWard; 11 | import com.proxy.utils.Log; 12 | 13 | /** 14 | * 数据读取线程,适用于 Http和Https 代理请求转发到真实的主机 15 | */ 16 | public class ReadDataThread extends Thread { 17 | 18 | private Socket socket; 19 | private InputStream inputStream; 20 | private OutputStream outputStream; 21 | private NetContentGrabber netGrabber; 22 | private OnBrokenCallBack onBrokenCallBack; 23 | private boolean responseHttpsProtocol;// 是否需要在本服务器响应Https协议 24 | 25 | private DataFilter dataFilter; 26 | 27 | public ReadDataThread(Socket socket, boolean responseHttpsProtocol) throws IOException { 28 | this.socket = socket; 29 | this.dataFilter = new DataFilter(); 30 | this.responseHttpsProtocol = responseHttpsProtocol; 31 | this.inputStream = this.socket.getInputStream(); 32 | this.outputStream = this.socket.getOutputStream(); 33 | } 34 | 35 | /** 36 | * 断网回调 37 | * 38 | * @param e 39 | * @param tag 40 | */ 41 | private void onBroken(Exception e, String tag) { 42 | if (null != onBrokenCallBack) { 43 | onBrokenCallBack.onBroken(e, tag, this); 44 | } 45 | 46 | IOUtils.close(outputStream); 47 | IOUtils.close(inputStream); 48 | IOUtils.close(socket); 49 | 50 | if (null != netGrabber) { 51 | netGrabber.closeConnect(); 52 | } 53 | } 54 | 55 | private NetContentGrabber getNetContentGrabber(ForWard forward) { 56 | 57 | if (null == netGrabber) { 58 | netGrabber = new NetContentGrabber(forward, new OnDataReceiveCallBack() { 59 | 60 | @Override 61 | public void onBrokenInGrabber(Exception e, String tag) { 62 | onBroken(e, tag); 63 | } 64 | 65 | @Override 66 | public void onReceiveData(byte[] buffer, int start, int offset) { 67 | try { 68 | if (null != dataFilter) { 69 | dataFilter.encodeData(buffer, start, offset);// 加密 70 | } 71 | outputStream.write(buffer, start, offset); 72 | } catch (IOException e) { 73 | Log.redPrint("转发数据异常:" + e.getLocalizedMessage()); 74 | onBroken(e, "getNetContentGrabber"); 75 | } 76 | } 77 | }); 78 | } 79 | 80 | return netGrabber; 81 | } 82 | 83 | @Override 84 | public void run() { 85 | if (null != inputStream) { 86 | try { 87 | ByteArrayOutputStream byo = new ByteArrayOutputStream(); 88 | // 只要能读取到目标地址就停止读取,建立代理连接 89 | // 继续读取信息写入到目标服务器 90 | NetContentGrabber netGrabber = null; 91 | int ret = -1; 92 | byte[] dataBuffer = new byte[512]; 93 | boolean getHeader = false; 94 | while ((ret = inputStream.read(dataBuffer)) != -1) { 95 | if (null != dataFilter) {// 解密 96 | dataFilter.decodeData(dataBuffer, 0, ret); 97 | } 98 | 99 | if (!getHeader) { 100 | byo.write(dataBuffer, 0, ret);// 保存本次转发的头部信息 101 | byte[] nowData = byo.toByteArray(); 102 | // System.out.println(new String(nowData)); 103 | String header = new String(nowData); 104 | ForWard forward = RequestBannerMethodAna.anaForward(header); 105 | // System.out.println("头信息:" + forward.toString()); 106 | if (null != forward && forward.isParseSuccess()) { 107 | netGrabber = getNetContentGrabber(forward); 108 | netGrabber.start(); 109 | if (forward.isHttps()) { 110 | // 如果读到这个头不要转发给目标服务器 111 | // 如果是Https协议需要告诉浏览器一个固定的头部 112 | if (responseHttpsProtocol) { 113 | 114 | byte[] httpsResonseBytes = Constant.HTTPS_CONNECT_MSG.getBytes(); 115 | if (null != dataFilter) { 116 | dataFilter.encodeData(httpsResonseBytes, 0, httpsResonseBytes.length);// 加密 117 | } 118 | outputStream.write(httpsResonseBytes); 119 | outputStream.flush(); 120 | 121 | Log.redPrint("回应:" + Constant.HTTPS_CONNECT_MSG + " ====>>>" + forward.getHost()); 122 | } else { 123 | Log.redPrint("无须回应:" + Constant.HTTPS_CONNECT_MSG + " ====>>>" + forward.getHost()); 124 | } 125 | 126 | } else { 127 | // 如果是Http原封原样的写到目标通道中 128 | netGrabber.sendData(nowData, 0, nowData.length); 129 | } 130 | getHeader = true; 131 | } 132 | } else { 133 | // 得到头以后的数据就正常盲转发 134 | netGrabber.sendData(dataBuffer, 0, ret); 135 | } 136 | } 137 | 138 | } catch (IOException e) { 139 | onBroken(e, "异常结束:"+e.getLocalizedMessage()); 140 | Log.println("IO异常:" + e.getLocalizedMessage()); 141 | }finally { 142 | onBroken(null, "Normal end"); 143 | } 144 | } 145 | } 146 | 147 | public OnBrokenCallBack getOnBrokenCallBack() { 148 | return onBrokenCallBack; 149 | } 150 | 151 | public void setOnBrokenCallBack(OnBrokenCallBack onBrokenCallBack) { 152 | this.onBrokenCallBack = onBrokenCallBack; 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /src/com/proxy/common/RequestBannerMethodAna.java: -------------------------------------------------------------------------------- 1 | package com.proxy.common; 2 | 3 | import com.proxy.domain.ForWard; 4 | 5 | /** 6 | * 请求分析工具 7 | */ 8 | public class RequestBannerMethodAna { 9 | 10 | public static final String HTTPS_CONNECT = "CONNECT"; 11 | 12 | public static final String HTTP_GET = "GET"; 13 | public static final String HTTP_POST = "POST"; 14 | public static final String HTTP_PUT = "PUT"; 15 | public static final String HTTP_HEAD = "HEAD"; 16 | public static final String HTTP_DELETE = "DELETE"; 17 | public static final String HTTP_OPTIONS = "OPTIONS"; 18 | public static final String HTTP_TRACE = "TRACE"; 19 | public static final String HTTP_PATCH = "PATCH"; 20 | 21 | public static final String HTTP_HEADER_HOST = "Host"; 22 | 23 | public static ForWard anaForward(byte header[], int start, int offset) { 24 | return anaForward(new String(header, start, offset)); 25 | } 26 | 27 | public static ForWard anaForward(String header) { 28 | // System.out.println("需要分析的头数据:===================="); 29 | // System.out.println("需要分析的头数据:" + header); 30 | // System.out.println("==================================="); 31 | 32 | ForWard forward = new ForWard(); 33 | if (null != header) { 34 | forward.setHeaderContent(header); 35 | String[] metas = header.split("\r\n"); 36 | // System.out.println("内容长度:" + metas.length); 37 | // http 38 | // GET http://login.psvr.wps.cn/api/v1/connectors?protocol=udp&usertype=10 HTTP/1.1 39 | // Host: login.psvr.wps.cn 40 | // https 41 | // 0==> CONNECT mtalk.google.com:5228 HTTP/1.1 42 | // 1==> Host: mtalk.google.com:5228 43 | 44 | if (metas.length >= 2) { 45 | for (String headerMeta : metas) { 46 | if (null == headerMeta || headerMeta.length() == 0) { 47 | continue; 48 | } 49 | if (headerMeta.startsWith(HTTPS_CONNECT)) { 50 | forward.setHttps(true); 51 | forward.setPort(443); 52 | continue; 53 | } 54 | 55 | if (headerMeta.toLowerCase().startsWith(HTTP_HEADER_HOST.toLowerCase())) { 56 | String[] hostMeta = headerMeta.split(":"); 57 | if (null != hostMeta && hostMeta.length >= 2) { 58 | forward.setHost(hostMeta[1]); 59 | 60 | if (hostMeta.length == 3) { 61 | forward.setPort(Integer.parseInt(hostMeta[2])); 62 | } else if (hostMeta.length == 2 && !forward.isHttps()) { 63 | forward.setPort(80); 64 | } 65 | } 66 | continue; 67 | } 68 | 69 | if (forward.isParseSuccess()) { 70 | break; 71 | } 72 | 73 | } 74 | 75 | } 76 | } 77 | 78 | return forward; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/com/proxy/domain/ForWard.java: -------------------------------------------------------------------------------- 1 | package com.proxy.domain; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | 6 | import com.proxy.common.Constant; 7 | 8 | /** 9 | * 解析后的转发地址 10 | */ 11 | public class ForWard { 12 | 13 | private String headerContent; 14 | private boolean isHttps; 15 | private String host; 16 | private int port; 17 | 18 | public ForWard() { 19 | } 20 | 21 | public ForWard(String host, int port) { 22 | super(); 23 | this.host = host; 24 | this.port = port; 25 | } 26 | 27 | public boolean isHttps() { 28 | return isHttps; 29 | } 30 | 31 | public void setHttps(boolean isHttps) { 32 | this.isHttps = isHttps; 33 | } 34 | 35 | public String getHost() { 36 | return host.trim(); 37 | } 38 | 39 | public void setHost(String host) { 40 | this.host = host; 41 | } 42 | 43 | public int getPort() { 44 | return port; 45 | } 46 | 47 | public void setPort(int port) { 48 | this.port = port; 49 | } 50 | 51 | public boolean isParseSuccess() { 52 | if (null != host && -1 != port) { 53 | return true; 54 | } 55 | return false; 56 | } 57 | 58 | public String getHeaderContent() { 59 | return headerContent; 60 | } 61 | 62 | public void setHeaderContent(String headerContent) { 63 | this.headerContent = headerContent; 64 | } 65 | 66 | public byte[] getHeaderBanner() { 67 | 68 | ByteArrayOutputStream byo = new ByteArrayOutputStream(); 69 | String headerBanner = host + ":" + port; 70 | byte[] targetBannerByte = headerBanner.getBytes(); 71 | int len = targetBannerByte.length; 72 | try { 73 | byo.write(targetBannerByte); 74 | // 凑够50个字节 75 | for (int i = 0, isize = (Constant.HEADER_BAND - len); i < isize; i++) { 76 | byo.write(Constant.SPACE); 77 | } 78 | } catch (IOException e) { 79 | e.printStackTrace(); 80 | } 81 | byte[] header = byo.toByteArray(); 82 | System.out.println("头部长度:" + header.length + " 头部内容:" + (new String(header))); 83 | 84 | return header; 85 | } 86 | 87 | 88 | 89 | @Override 90 | public String toString() { 91 | return "ForWard [isHttps=" + isHttps + ", host=" + host + ", port=" + port + "]"; 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/com/proxy/domain/ServerConfig.java: -------------------------------------------------------------------------------- 1 | package com.proxy.domain; 2 | 3 | public class ServerConfig { 4 | 5 | private String host; 6 | private int port; 7 | private String remoteServer; 8 | private int remotePort; 9 | private boolean enc; 10 | 11 | 12 | //是否需要响应Https协议,如果被代理端直接连接到代理服务器,就需要响应,如果是通过中间件连接过来的,中间件必须先响应Https协议代理服务器此时不需要再次响应 13 | private boolean responseHttps; 14 | 15 | public String getHost() { 16 | return host; 17 | } 18 | public void setHost(String host) { 19 | this.host = host; 20 | } 21 | public int getPort() { 22 | return port; 23 | } 24 | public void setPort(int port) { 25 | this.port = port; 26 | } 27 | 28 | public boolean isResponseHttps() { 29 | return responseHttps; 30 | } 31 | public void setResponseHttps(boolean responseHttps) { 32 | this.responseHttps = responseHttps; 33 | } 34 | 35 | 36 | public String getRemoteServer() { 37 | return remoteServer; 38 | } 39 | public void setRemoteServer(String remoteServer) { 40 | this.remoteServer = remoteServer; 41 | } 42 | public int getRemotePort() { 43 | return remotePort; 44 | } 45 | public void setRemotePort(int remotePort) { 46 | this.remotePort = remotePort; 47 | } 48 | public boolean isEnc() { 49 | return enc; 50 | } 51 | public void setEnc(boolean enc) { 52 | this.enc = enc; 53 | } 54 | @Override 55 | public String toString() { 56 | return "ServerConfig [host=" + host + ", port=" + port + ", remoteServer=" + remoteServer + ", remotePort=" 57 | + remotePort + ", enc=" + enc + ", responseHttps=" + responseHttps + "]"; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/com/proxy/domain/Shuff.java: -------------------------------------------------------------------------------- 1 | package com.proxy.domain; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.Random; 6 | 7 | import com.proxy.utils.Log; 8 | 9 | public class Shuff { 10 | 11 | 12 | public static Shuff create() { 13 | return new Shuff(); 14 | } 15 | 16 | private ArrayList encList; 17 | private ArrayList decList; 18 | 19 | private Shuff() { 20 | encList = new ArrayList<>(); 21 | decList = new ArrayList<>(); 22 | for (byte b = Byte.MIN_VALUE; b <= Byte.MAX_VALUE; b++) { 23 | byte enc = 0; 24 | enc = (byte) -b; 25 | encList.add(b); 26 | decList.add(enc); 27 | 28 | if (b == Byte.MAX_VALUE) { 29 | break; 30 | } 31 | } 32 | 33 | Random rand = new Random((int) System.currentTimeMillis()); 34 | Collections.shuffle(encList, rand); 35 | Collections.shuffle(decList, rand); 36 | } 37 | 38 | public String getEncDecString() { 39 | StringBuffer sbf = new StringBuffer(); 40 | String connectChar = "="; 41 | for (int i = 0, isize = encList.size(); i < isize; i++) { 42 | if (i != isize - 1) { 43 | sbf.append(encList.get(i) + connectChar + decList.get(i) + ","); 44 | } else { 45 | sbf.append(encList.get(i) + connectChar + decList.get(i)); 46 | } 47 | } 48 | return sbf.toString(); 49 | } 50 | 51 | public void printShuffs() { 52 | Log.redPrint(encList.toString()); 53 | Log.redPrint(decList.toString()); 54 | } 55 | 56 | public static void main(String[] args) { 57 | 58 | Shuff shuff = Shuff.create(); 59 | shuff.printShuffs(); 60 | System.out.println(shuff.getEncDecString()); 61 | 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/com/proxy/local/LocalManager.java: -------------------------------------------------------------------------------- 1 | package com.proxy.local; 2 | 3 | import java.io.IOException; 4 | import java.net.InetAddress; 5 | import java.net.ServerSocket; 6 | import java.net.Socket; 7 | import java.util.HashMap; 8 | import java.util.concurrent.ExecutorService; 9 | import java.util.concurrent.Executors; 10 | import java.util.concurrent.ThreadPoolExecutor; 11 | 12 | import com.proxy.common.DataFilter; 13 | import com.proxy.common.OnBrokenCallBackInconnctor; 14 | import com.proxy.domain.ForWard; 15 | import com.proxy.domain.ServerConfig; 16 | import com.proxy.middleware.ConnectorRunnable; 17 | import com.proxy.middleware.ConnectorThread; 18 | import com.proxy.utils.Log; 19 | 20 | public class LocalManager { 21 | 22 | private static ThreadPoolExecutor pool; 23 | 24 | static { 25 | // pool =(ThreadPoolExecutor) Executors.newFixedThreadPool(30); 26 | pool =(ThreadPoolExecutor) Executors.newCachedThreadPool(); 27 | // pool =(ThreadPoolExecutor) Executors.newSingleThreadExecutor();//error 28 | } 29 | 30 | public static void startLocalService(ServerConfig config) throws IOException, InterruptedException { 31 | // 代理服务器地址 32 | String proxyHost = config.getRemoteServer(); 33 | // 代理服务器端口 34 | int proxyPort = config.getRemotePort(); 35 | // 本地服务永远是localhost 或者 127.0.0.1 36 | String localhost = config.getHost(); 37 | // 本地代理端口可以是任意没有被占用的端口 38 | int localport = config.getPort(); 39 | 40 | ServerSocket serverSocket = new ServerSocket(localport); 41 | Socket clientSocket = null; 42 | System.out.println("代理转发服务已开启:" + localhost + ":" + localport); 43 | ForWard forWardTarget = new ForWard(proxyHost, proxyPort);// 转发目标(代理服务器地址或者中专服务器地址) 44 | 45 | while ((clientSocket = serverSocket.accept()) != null) { 46 | InetAddress clientAddress = clientSocket.getInetAddress(); 47 | ConnectorRunnable connectorRunnable=new ConnectorRunnable(clientSocket,forWardTarget); 48 | pool.submit(connectorRunnable); 49 | Log.redPrint("收到本地代理请求:" + clientAddress.toString() + " 线程池大小:" + pool.getPoolSize()+" 核心线程:"+pool.getCorePoolSize()+" 活动线程:"+pool.getActiveCount()); 50 | } 51 | 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/com/proxy/local/starter/LocalStarter.java: -------------------------------------------------------------------------------- 1 | package com.proxy.local.starter; 2 | 3 | import com.proxy.domain.ServerConfig; 4 | import com.proxy.local.LocalManager; 5 | import com.proxy.utils.Log; 6 | import com.proxy.utils.ProjectTool; 7 | 8 | import java.io.File; 9 | import java.io.IOException; 10 | 11 | /** 12 | * 需要代理的电脑运行程序 13 | */ 14 | public class LocalStarter { 15 | 16 | public static void main(String[] args) throws IOException, InterruptedException { 17 | 18 | String configPath = System.getProperty("user.dir") + File.separator + "LocalConfig.json"; 19 | ServerConfig config = ProjectTool.readConfigFile(configPath); 20 | Log.println("工作目录:" + configPath); 21 | Log.println("配置信息:" + config.toString()); 22 | LocalManager.startLocalService(config); 23 | 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/com/proxy/middleware/ConnectorRunnable.java: -------------------------------------------------------------------------------- 1 | package com.proxy.middleware; 2 | 3 | import com.alibaba.fastjson.util.IOUtils; 4 | import com.proxy.common.Constant; 5 | import com.proxy.common.DataFilter; 6 | import com.proxy.common.OnBrokenCallBackInconnctor; 7 | import com.proxy.domain.ForWard; 8 | import com.proxy.utils.Log; 9 | 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.io.OutputStream; 13 | import java.net.Socket; 14 | 15 | /** 16 | * 连接件,把来自浏览器等被代理端的请求盲转发 17 | */ 18 | public class ConnectorRunnable implements Runnable { 19 | private Socket localSocket;// 接受本地浏览器代理输入Socket 20 | private InputStream proxyedInputStream;// 被代理的输入数据(把这个输入流的东西转发到代理) 21 | private OutputStream proxyedOutputStream;// 被代理的输出流(把代理服务器的返回写入到被代理的服务器) 22 | 23 | private ForWard proxedServerForward;// 目标代理服务器 24 | private Thread readDataFromProxyServerThread;// 从代理服务器读取数据线程 25 | private volatile Socket connectProxyServerSocket;// 链接到远程代理服务器的Socket 26 | 27 | private OutputStream proxyServerOutputStream;// 代理服务器的输出流(把浏览器等被代理端的数据写入这个流) 28 | private InputStream proxyServerInputStream;// 代理服务器的输入流(把这个流数写给浏览器) 29 | 30 | private DataFilter dataFilter; 31 | // private boolean readBanner = false; 32 | 33 | public ConnectorRunnable(Socket socket, ForWard forward) { 34 | this.localSocket = socket; 35 | this.proxedServerForward = forward; 36 | this.dataFilter = new DataFilter(); 37 | 38 | try { 39 | this.proxyedInputStream = this.localSocket.getInputStream(); 40 | this.proxyedOutputStream = this.localSocket.getOutputStream(); 41 | // 链接代理服务器 42 | this.connectProxyServerSocket = new Socket(proxedServerForward.getHost(), proxedServerForward.getPort()); 43 | this.proxyServerOutputStream = this.connectProxyServerSocket.getOutputStream(); 44 | this.proxyServerInputStream = this.connectProxyServerSocket.getInputStream(); 45 | this.readDataFromProxyServerThread = new Thread(new Runnable() { 46 | @Override 47 | public void run() { 48 | try { 49 | byte[] proxedBuffer = new byte[Constant.BUFFER_SIZE]; 50 | int ret = -1; 51 | // 读取代理服务器的数据返回给浏览器 52 | while ( (ret = proxyServerInputStream.read(proxedBuffer)) != -1 ) { 53 | if (null != ConnectorRunnable.this.dataFilter) { 54 | ConnectorRunnable.this.dataFilter.decodeData(proxedBuffer, 0, ret);// 解密 55 | } 56 | proxyedOutputStream.write(proxedBuffer, 0, ret);// 发送给浏览器 57 | proxyedOutputStream.flush(); 58 | } 59 | } catch (IOException e) { 60 | closeConnect(); 61 | // e.printStackTrace(); 62 | Log.redPrint("数据读取异常1:" + forward.toString() + "==>" + e.getLocalizedMessage()); 63 | } finally { 64 | 65 | } 66 | } 67 | }); 68 | this.readDataFromProxyServerThread.start(); 69 | 70 | } catch (IOException e) { 71 | closeConnect(); 72 | // e.printStackTrace(); 73 | Log.redPrint("本地转发器错误2:" + e.getLocalizedMessage()); 74 | } finally { 75 | 76 | } 77 | } 78 | 79 | @Override 80 | public void run() { 81 | if (null != proxyedInputStream) { 82 | try { 83 | int readSize = -1; 84 | // 读取浏览器数据,发送给代理服务器 85 | byte[] buffer = new byte[Constant.BUFFER_SIZE]; 86 | while ((readSize = proxyedInputStream.read(buffer)) != -1) { 87 | if (null == this.proxyServerOutputStream) { 88 | break; 89 | } 90 | if (null != dataFilter) {// 加密 91 | dataFilter.encodeData(buffer, 0, readSize); 92 | } 93 | this.proxyServerOutputStream.write(buffer, 0, readSize); 94 | this.proxyServerOutputStream.flush(); 95 | } 96 | } catch (IOException e) { 97 | // e.printStackTrace(); 98 | Log.redPrint("本地转发器异常结束3:" + e.getLocalizedMessage()); 99 | } finally { 100 | closeConnect(); 101 | } 102 | 103 | } 104 | } 105 | 106 | public void closeConnect() { 107 | IOUtils.close(proxyedInputStream); 108 | IOUtils.close(proxyedOutputStream); 109 | IOUtils.close(proxyServerInputStream); 110 | IOUtils.close(proxyServerOutputStream); 111 | IOUtils.close(localSocket); 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /src/com/proxy/middleware/ConnectorThread.java: -------------------------------------------------------------------------------- 1 | package com.proxy.middleware; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.net.Socket; 7 | 8 | import com.alibaba.fastjson.util.IOUtils; 9 | import com.proxy.common.Constant; 10 | import com.proxy.common.DataFilter; 11 | import com.proxy.common.OnBrokenCallBackInconnctor; 12 | import com.proxy.domain.ForWard; 13 | import com.proxy.utils.Log; 14 | 15 | /** 16 | * 连接件,把来自浏览器等被代理端的请求盲转发 17 | */ 18 | public class ConnectorThread extends Thread { 19 | 20 | private OnBrokenCallBackInconnctor onBrokenCallBackInconnctor;// 处理异常回调 21 | private Socket localSocket;// 接受本地浏览器代理输入Socket 22 | private InputStream proxyedInputStream;// 被代理的输入数据(把这个输入流的东西转发到代理) 23 | private OutputStream proxyedOutputStream;// 被代理的输出流(把代理服务器的返回写入到被代理的服务器) 24 | 25 | private ForWard proxedServerForward;// 目标代理服务器 26 | private Thread readDataFromProxyServerThread;// 从代理服务器读取数据线程 27 | private Socket connectProxyServerSocket;// 链接到远程代理服务器的Socket 28 | 29 | private OutputStream proxyServerOutputStream;// 代理服务器的输出流(把浏览器等被代理端的数据写入这个流) 30 | private InputStream proxyServerInputStream;// 代理服务器的输入流(把这个流数写给浏览器) 31 | 32 | private DataFilter dataFilter; 33 | // private boolean readBanner = false; 34 | 35 | public ConnectorThread(Socket socket, ForWard forward, OnBrokenCallBackInconnctor onBrokenCallBackInconnctor) { 36 | this.localSocket = socket; 37 | this.onBrokenCallBackInconnctor = onBrokenCallBackInconnctor; 38 | this.proxedServerForward = forward; 39 | this.dataFilter = new DataFilter(); 40 | 41 | try { 42 | this.proxyedInputStream = this.localSocket.getInputStream(); 43 | this.proxyedOutputStream = this.localSocket.getOutputStream(); 44 | // 链接代理服务器 45 | this.connectProxyServerSocket = new Socket(proxedServerForward.getHost(), proxedServerForward.getPort()); 46 | this.proxyServerOutputStream = this.connectProxyServerSocket.getOutputStream(); 47 | this.proxyServerInputStream = this.connectProxyServerSocket.getInputStream(); 48 | this.readDataFromProxyServerThread = new Thread(new Runnable() { 49 | @Override 50 | public void run() { 51 | try { 52 | byte[] proxedBuffer = new byte[Constant.BUFFER_SIZE]; 53 | int ret = -1; 54 | // 读取代理服务器的数据返回给浏览器 55 | while ((ret = proxyServerInputStream.read(proxedBuffer)) != -1) { 56 | if (null != ConnectorThread.this.dataFilter) { 57 | ConnectorThread.this.dataFilter.decodeData(proxedBuffer, 0, ret);// 解密 58 | } 59 | proxyedOutputStream.write(proxedBuffer, 0, ret);// 发送给浏览器 60 | proxyedOutputStream.flush(); 61 | } 62 | } catch (IOException e) { 63 | closeConnect(); 64 | Log.redPrint("数据读取异常:" + forward.toString() + "==>" + e.getLocalizedMessage()); 65 | } finally { 66 | if (null != onBrokenCallBackInconnctor) { 67 | onBrokenCallBackInconnctor.onBroken(new Exception("在ConnectorThread中通信结束"), "Connector.Run", 68 | ConnectorThread.this); 69 | } 70 | } 71 | 72 | } 73 | }); 74 | this.readDataFromProxyServerThread.start(); 75 | } catch (IOException e) { 76 | closeConnect(); 77 | Log.redPrint("本地转发器错误:" + e.getLocalizedMessage()); 78 | } finally { 79 | if (null != onBrokenCallBackInconnctor) { 80 | onBrokenCallBackInconnctor.onBroken(new Exception("在ConnectorThread构造中通信结束"), "Connector.Run", 81 | ConnectorThread.this); 82 | } 83 | } 84 | start(); 85 | } 86 | 87 | @Override 88 | public void run() { 89 | if (null != proxyedInputStream) { 90 | try { 91 | int readSize = -1; 92 | // 读取浏览器数据,发送给代理服务器 93 | byte[] buffer = new byte[Constant.BUFFER_SIZE]; 94 | while ((readSize = proxyedInputStream.read(buffer)) != -1) { 95 | if (null == this.proxyServerOutputStream) { 96 | break; 97 | } 98 | if (null != dataFilter) {// 加密 99 | dataFilter.encodeData(buffer, 0, readSize); 100 | } 101 | this.proxyServerOutputStream.write(buffer, 0, readSize); 102 | this.proxyServerOutputStream.flush(); 103 | } 104 | } catch (IOException e) { 105 | Log.redPrint("本地转发器异常结束:" + e.getLocalizedMessage()); 106 | } finally { 107 | closeConnect(); 108 | if (null != onBrokenCallBackInconnctor) { 109 | onBrokenCallBackInconnctor.onBroken(new Exception("正常数据连接结束"), "Connector.Run", 110 | ConnectorThread.this); 111 | } 112 | } 113 | 114 | } 115 | } 116 | 117 | public void closeConnect() { 118 | IOUtils.close(proxyedInputStream); 119 | IOUtils.close(proxyedOutputStream); 120 | IOUtils.close(proxyServerInputStream); 121 | IOUtils.close(proxyServerOutputStream); 122 | IOUtils.close(localSocket); 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /src/com/proxy/remote/ProxyServerManager.java: -------------------------------------------------------------------------------- 1 | package com.proxy.remote; 2 | 3 | import java.io.IOException; 4 | import java.net.InetAddress; 5 | import java.net.ServerSocket; 6 | import java.net.Socket; 7 | import java.util.HashMap; 8 | import java.util.concurrent.Executors; 9 | import java.util.concurrent.ThreadPoolExecutor; 10 | 11 | import com.proxy.common.DataFilter; 12 | import com.proxy.common.OnBrokenCallBack; 13 | import com.proxy.common.ReadDataRunnable; 14 | import com.proxy.common.ReadDataThread; 15 | import com.proxy.domain.ForWard; 16 | import com.proxy.utils.Log; 17 | 18 | /** 19 | * 代理服务器端运行的程序 20 | */ 21 | public class ProxyServerManager { 22 | 23 | private static ThreadPoolExecutor pool; 24 | static { 25 | pool= (ThreadPoolExecutor)Executors.newCachedThreadPool(); 26 | } 27 | 28 | public static void startProxyServer(ForWard forWard,boolean responseHttpsProtocol) throws IOException { 29 | ServerSocket serverSocket = new ServerSocket(forWard.getPort()); 30 | Socket clientSocket = null; 31 | Log.println("LocalProxy Server Ready On:" + forWard.getHost() + ":" + forWard.getPort()); 32 | 33 | while ((clientSocket = serverSocket.accept()) != null) { 34 | InetAddress clientAddress = clientSocket.getInetAddress(); 35 | Log.println("新请求:" + clientAddress.getCanonicalHostName() + "线程池大小:" + pool.getPoolSize()+" 核心线程:"+pool.getCorePoolSize()+" 活动线程:"+pool.getActiveCount()); 36 | ReadDataRunnable readDataRunnable=new ReadDataRunnable(clientSocket,responseHttpsProtocol); 37 | pool.submit(readDataRunnable); 38 | } 39 | System.out.println("=======end======="); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/com/proxy/remote/RemoteProxyServer.java: -------------------------------------------------------------------------------- 1 | package com.proxy.remote; 2 | 3 | import java.io.IOException; 4 | 5 | import com.proxy.domain.ForWard; 6 | import com.proxy.domain.ServerConfig; 7 | import com.proxy.utils.ProjectTool; 8 | 9 | public class RemoteProxyServer { 10 | 11 | public static void main(String[] args) throws IOException, InterruptedException { 12 | 13 | ServerConfig config = ProjectTool.readConfigFile("/Users/wangzy/eclipse-workspace/MyNat/src/RemoteConfig.json"); 14 | 15 | System.out.println("服务器配置信息:" + config.toString()); 16 | String currentDir = System.getProperty("user.dir"); 17 | 18 | ProxyServerManager.startProxyServer(new ForWard("127.0.0.1", 1666),config.isResponseHttps()); 19 | 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/com/proxy/remote/starter/RemoteStarter.java: -------------------------------------------------------------------------------- 1 | package com.proxy.remote.starter; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import com.proxy.domain.ForWard; 7 | import com.proxy.domain.ServerConfig; 8 | import com.proxy.remote.ProxyServerManager; 9 | import com.proxy.utils.ProjectTool; 10 | 11 | public class RemoteStarter { 12 | 13 | public static void main(String[] args) throws IOException, InterruptedException { 14 | 15 | String configPath = System.getProperty("user.dir") + File.separator + "RemoteConfig.json"; 16 | ServerConfig config = ProjectTool.readConfigFile(configPath); 17 | 18 | System.out.println("工作目录:" + configPath); 19 | System.out.println("服务器配置信息:" + config.toString()); 20 | 21 | // config.setHost("127.0.0.1"); 22 | // config.setPort(1666); 23 | // config.setResponseHttps(true); 24 | 25 | ProxyServerManager.startProxyServer(new ForWard(config.getHost(), config.getPort()), config.isResponseHttps()); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/com/proxy/test/ConnectorThreadTest.java: -------------------------------------------------------------------------------- 1 | package com.proxy.test; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.net.Socket; 7 | 8 | import com.alibaba.fastjson.util.IOUtils; 9 | import com.proxy.common.Constant; 10 | import com.proxy.common.NetContentGrabber; 11 | import com.proxy.common.OnBrokenCallBackInconnctor; 12 | import com.proxy.common.OnDataReceiveCallBack; 13 | import com.proxy.common.RequestBannerMethodAna; 14 | import com.proxy.domain.ForWard; 15 | import com.proxy.utils.Log; 16 | 17 | /** 18 | * 连接件,把来自浏览器等被代理端的请求盲转发 19 | */ 20 | public class ConnectorThreadTest extends Thread { 21 | 22 | private OnBrokenCallBackInconnctor onBrokenCallBackInconnctor;// 处理异常回调 23 | 24 | private Socket localSocket;// 接受本地浏览器代理输入Socket 25 | private InputStream proxyedInputStream;// 被代理的输入数据(把这个输入流的东西转发到代理) 26 | private OutputStream proxyedOutputStream;// 被代理的输出流(把代理服务器的返回写入到被代理的服务器) 27 | private boolean isCloseIO = false; 28 | 29 | // private boolean readBanner = false; 30 | private NetContentGrabber netContentGrabber; 31 | 32 | public ConnectorThreadTest(Socket socket, ForWard forward, OnBrokenCallBackInconnctor onBrokenCallBackInconnctor) { 33 | this.localSocket = socket; 34 | this.onBrokenCallBackInconnctor = onBrokenCallBackInconnctor; 35 | 36 | try { 37 | this.proxyedInputStream = this.localSocket.getInputStream(); 38 | this.proxyedOutputStream = this.localSocket.getOutputStream(); 39 | // 链接代理服务器 40 | // proxyedOutputStream.write(Constant.HTTPS_CONNECT_MSG.getBytes("utf-8")); 41 | } catch (IOException e) { 42 | closeConnect(); 43 | e.printStackTrace(); 44 | Log.redPrint("本地转发器错误:" + e.getLocalizedMessage()); 45 | } finally { 46 | if (null != onBrokenCallBackInconnctor) { 47 | onBrokenCallBackInconnctor.onBroken(new Exception("在ConnectorThread构造中通信结束"), "Connector.Run", 48 | ConnectorThreadTest.this); 49 | } 50 | } 51 | 52 | start(); 53 | } 54 | 55 | @Override 56 | public void run() { 57 | if (null != proxyedInputStream) { 58 | try { 59 | int readSize = -1; 60 | // 读取浏览器数据,发送给代理服务器 61 | byte[] buffer = new byte[Constant.BUFFER_SIZE * 2]; 62 | 63 | boolean getHeader = false; 64 | 65 | while ((readSize = proxyedInputStream.read(buffer)) != -1) { 66 | if (getHeader == false) { 67 | // Log.redPrint("首次收到数据:\n" + new String(buffer,0,readSize)); 68 | ForWard forward = RequestBannerMethodAna.anaForward(new String(buffer)); 69 | if (null != forward && null == netContentGrabber) { 70 | netContentGrabber = new NetContentGrabber(forward, new OnDataReceiveCallBack() { 71 | @Override 72 | public void onReceiveData(byte[] buffer, int start, int offset) { 73 | try { 74 | if (!isCloseIO) { 75 | proxyedOutputStream.write(buffer, start, offset); 76 | } 77 | } catch (IOException e) { 78 | e.printStackTrace(); 79 | } 80 | } 81 | 82 | @Override 83 | public void onBrokenInGrabber(Exception e, String tag) { 84 | 85 | } 86 | }); 87 | netContentGrabber.start(); 88 | if (forward.isHttps() && !isCloseIO) { 89 | proxyedOutputStream.write(Constant.HTTPS_CONNECT_MSG.getBytes("utf-8")); 90 | Log.println("回应Https"); 91 | } else { 92 | netContentGrabber.sendData(buffer, 0, readSize); 93 | } 94 | } 95 | getHeader = true; 96 | } else { 97 | netContentGrabber.sendData(buffer, 0, readSize); 98 | } 99 | } 100 | 101 | } catch (IOException e) { 102 | // e.printStackTrace(); 103 | closeConnect(); 104 | Log.redPrint("本地转发器异常结束:" + e.getLocalizedMessage()); 105 | } finally { 106 | if (null != onBrokenCallBackInconnctor) { 107 | onBrokenCallBackInconnctor.onBroken(new Exception("正常数据连接结束"), "Connector.Run", 108 | ConnectorThreadTest.this); 109 | } 110 | } 111 | } 112 | } 113 | 114 | public void closeConnect() { 115 | IOUtils.close(proxyedInputStream); 116 | IOUtils.close(proxyedOutputStream); 117 | IOUtils.close(localSocket); 118 | isCloseIO = true; 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /src/com/proxy/test/LocalManagerTest.java: -------------------------------------------------------------------------------- 1 | package com.proxy.test; 2 | 3 | import java.io.IOException; 4 | import java.net.InetAddress; 5 | import java.net.ServerSocket; 6 | import java.net.Socket; 7 | import java.util.HashMap; 8 | 9 | import com.proxy.common.DataFilter; 10 | import com.proxy.common.OnBrokenCallBackInconnctor; 11 | import com.proxy.domain.ForWard; 12 | import com.proxy.domain.ServerConfig; 13 | import com.proxy.middleware.ConnectorThread; 14 | import com.proxy.utils.Log; 15 | 16 | public class LocalManagerTest { 17 | 18 | private static HashMap connectorMapPool = new HashMap(); 19 | 20 | public static void startLocalService(ServerConfig config) throws IOException, InterruptedException { 21 | 22 | // 代理服务器地址 23 | String proxyHost = config.getRemoteServer(); 24 | // 代理服务器端口 25 | int proxyPort = config.getRemotePort(); 26 | 27 | // 本地服务永远是localhost 或者 127.0.0.1 28 | String localhost = config.getHost(); 29 | // 本地代理端口可以是任意没有被占用的端口 30 | int localport = config.getPort(); 31 | 32 | ServerSocket serverSocket = new ServerSocket(localport); 33 | Socket clientSocket = null; 34 | System.out.println("代理转发服务已开启:" + localhost + ":" + localport); 35 | 36 | ForWard forWardTarget = new ForWard(proxyHost, proxyPort);// 转发目标(代理服务器地址或者中专服务器地址) 37 | 38 | while ((clientSocket = serverSocket.accept()) != null) { 39 | 40 | InetAddress clientAddress = clientSocket.getInetAddress(); 41 | Log.redPrint("收到本地代理请求:" + clientAddress.toString() + " 线程池大小:" + connectorMapPool.size()); 42 | 43 | ConnectorThreadTest connector = new ConnectorThreadTest(clientSocket, forWardTarget, 44 | new OnBrokenCallBackInconnctor() { 45 | @Override 46 | public void onBroken(Exception e, String tag, Thread connector) { 47 | Log.println("删除前本地转发连接线程池池大小:" + connectorMapPool.size() + "==>关闭原因:" + e.getMessage()); 48 | ConnectorThreadTest gcThread = connectorMapPool.remove(connector.toString()); 49 | if (null != gcThread && !gcThread.isInterrupted()) { 50 | gcThread.interrupt(); 51 | } 52 | gcThread = null; 53 | Log.println("删除后本地转发连接线程池池大小:" + connectorMapPool.size()); 54 | } 55 | }); 56 | 57 | // connector.start(); 58 | connectorMapPool.put(connector.toString(), connector); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/com/proxy/test/LocalProxyServerConnectorTest.java: -------------------------------------------------------------------------------- 1 | package com.proxy.test; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import com.proxy.domain.ForWard; 7 | import com.proxy.domain.ServerConfig; 8 | import com.proxy.remote.ProxyServerManager; 9 | import com.proxy.utils.ProjectTool; 10 | 11 | public class LocalProxyServerConnectorTest { 12 | 13 | public static void main(String[] args) throws IOException, InterruptedException { 14 | 15 | String configPath = System.getProperty("user.dir") + File.separator + "RemoteConfig.json"; 16 | System.out.println(configPath + " 是否存在配置文件:" + (new File(configPath)).exists()); 17 | ServerConfig config = ProjectTool.readConfigFile(configPath); 18 | 19 | config.setHost("127.0.0.1"); 20 | config.setPort(1666); 21 | config.setResponseHttps(true); 22 | LocalManagerTest.startLocalService(config); 23 | 24 | // System.out.println("服务器配置信息:" + config.toString()); 25 | // ProxyServerManager.startProxyServer(new ForWard(config.getHost(),config.getPort()),config.isResponseHttps()); 26 | 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/com/proxy/test/Test.java: -------------------------------------------------------------------------------- 1 | package com.proxy.test; 2 | 3 | import java.io.IOException; 4 | import java.util.Calendar; 5 | import java.util.TimeZone; 6 | 7 | import com.proxy.domain.ServerConfig; 8 | 9 | /** 10 | * 本地测试代理 11 | */ 12 | public class Test { 13 | 14 | public static void main(String[] args) throws IOException, InterruptedException { 15 | 16 | ServerConfig config=new ServerConfig(); 17 | config.setHost("127.0.0.1"); 18 | config.setPort(1666); 19 | LocalManagerTest.startLocalService(config); 20 | 21 | // 22 | // ConnectorThreadTest connectorThreadTest=new ConnectorThreadTest(socket, forward, onBrokenCallBackInconnctor); 23 | // connectorThreadTest.start(); 24 | // String[] ids = TimeZone.getAvailableIDs(); 25 | // for (String id : ids) { 26 | // System.out.println(id); 27 | // } 28 | 29 | // Calendar ca1 = Calendar.getInstance(TimeZone.getTimeZone("UTC")); 30 | // long currentMiles = System.currentTimeMillis(); 31 | // 32 | // int after = Integer.parseInt(String.valueOf(currentMiles).substring(0, 6)); 33 | // System.out.println(currentMiles); 34 | // System.out.println(after); 35 | 36 | // Calendar.getInstance() 37 | 38 | // System.currentTimeMillis(); 39 | // 40 | // 41 | // DataFilter dataFilter = new DataFilter(true); 42 | // 43 | // String filter = dataFilter.getStringInChannel(); 44 | // 45 | // System.out.println("压缩前长度:" + filter.length()); 46 | // 47 | // String afteren = ZipUtil.compress(filter); 48 | // 49 | // System.out.println("压缩后长度:" + afteren.length()); 50 | // 51 | // DataFilter df = DataFilter.createFromBase64(ZipUtil.uncompress(afteren)); 52 | // 53 | // System.out.println(df); 54 | 55 | // dataFilter = DataFilter.createFromBase64(filter); 56 | // 57 | // System.out.println(dataFilter); 58 | // 59 | // String s = "Hello123..123"; 60 | // 61 | // byte[] sb = s.getBytes(); 62 | // 63 | // dataFilter.encodeData(sb, 0, sb.length); 64 | // System.out.println(new String(sb)); 65 | // dataFilter.decodeData(sb, 0, sb.length); 66 | // System.out.println(new String(sb)); 67 | 68 | // ByteArrayOutputStream byo = new ByteArrayOutputStream(); 69 | // 70 | // ObjectOutputStream obo = new ObjectOutputStream(byo); 71 | // obo.writeObject(dataFilter); 72 | // 73 | // String payCityMapBase64 = new String((byo.toByteArray())); 74 | // 75 | // System.out.println(payCityMapBase64); 76 | 77 | // String s = "Hello,123,-123abcdefghijklmnopzrstuvwxyz"; 78 | // 79 | // DataFilter dataFilter = new DataFilter(true); 80 | // 81 | // byte[] sbs = s.getBytes(); 82 | // 83 | // dataFilter.encodeData(sbs, 0, sbs.length); 84 | // 85 | // System.out.println(new String(sbs)); 86 | // 87 | // dataFilter.decodeData(sbs, 0, sbs.length); 88 | // 89 | // String dec=new String(sbs); 90 | // System.out.println(dec+" ==>"+dec.equals(s)); 91 | 92 | // Random rand=new Random((int)System.currentTimeMillis()); 93 | // Integer[] ia={0,1,2,3,4,5,6,7,8,9}; 94 | // ArrayList list=new ArrayList(Arrays.asList(ia)); 95 | // System.out.println("Before shufflig: "+list); 96 | // Collections.shuffle(list,rand); 97 | // System.out.println("After shuffling: "+list); 98 | // System.out.print(Byte.MAX_VALUE); 99 | // System.out.print(Byte.MIN_VALUE); 100 | 101 | // for (byte b = Byte.MIN_VALUE; b <= Byte.MAX_VALUE; b++) { 102 | // byte enc = 0; 103 | // enc=(byte) -b; 104 | // System.out.println(b + "=>" + enc); 105 | // if (b == Byte.MAX_VALUE) { 106 | // break; 107 | // } 108 | // } 109 | 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/com/proxy/test/TestConnectQtServer.java: -------------------------------------------------------------------------------- 1 | package com.proxy.test; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.net.Socket; 6 | 7 | public class TestConnectQtServer { 8 | 9 | public static void main(String[] args) throws IOException, InterruptedException { 10 | 11 | Socket socket = new Socket("localhost", 8712); 12 | InputStream in = socket.getInputStream(); 13 | byte[] buffer = new byte[2048]; 14 | int ret = in.read(buffer); 15 | 16 | String HTTPS_CONNECT_MSG = "HTTP/1.1 200 Connection Established\r\n\r\n"; 17 | String rrrr=new String(buffer,0,ret); 18 | rrrr.getBytes(); 19 | System.out.println("结果:"+" size :"+ret+" 是否相等:"+HTTPS_CONNECT_MSG.equals(rrrr)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/com/proxy/test/TestIniTool.java: -------------------------------------------------------------------------------- 1 | package com.proxy.test; 2 | import org.dtools.ini.*; 3 | 4 | import java.io.File; 5 | import java.io.IOException; 6 | 7 | public class TestIniTool { 8 | 9 | public static void main(String[] args) throws IOException { 10 | 11 | IniFile iniFile=new BasicIniFile(); 12 | File file=new File("c:\\test_config.ini"); 13 | IniFileReader reader=new IniFileReader(iniFile,file); 14 | 15 | IniFileWriter writer=new IniFileWriter(iniFile,file); 16 | 17 | IniSection iniSection=iniFile.addSection("local"); 18 | 19 | IniItem iniItem=new IniItem("host"); 20 | iniItem.setValue("127.0.0.1"); 21 | 22 | iniSection.addItem(iniItem); 23 | writer.write(); 24 | 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/com/proxy/utils/Base64Coder.java: -------------------------------------------------------------------------------- 1 | // Copyright 2003-2010 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland 2 | // www.source-code.biz, www.inventec.ch/chdh 3 | // 4 | // This module is multi-licensed and may be used under the terms 5 | // of any of the following licenses: 6 | // 7 | // EPL, Eclipse Public License, V1.0 or later, http://www.eclipse.org/legal 8 | // LGPL, GNU Lesser General Public License, V2.1 or later, http://www.gnu.org/licenses/lgpl.html 9 | // GPL, GNU General Public License, V2 or later, http://www.gnu.org/licenses/gpl.html 10 | // AL, Apache License, V2.0 or later, http://www.apache.org/licenses 11 | // BSD, BSD License, http://www.opensource.org/licenses/bsd-license.php 12 | // MIT, MIT License, http://www.opensource.org/licenses/MIT 13 | // 14 | // Please contact the author if you need another license. 15 | // This module is provided "as is", without warranties of any kind. 16 | 17 | package com.proxy.utils; 18 | 19 | /** 20 | * A Base64 encoder/decoder. 21 | * 22 | *

23 | * This class is used to encode and decode data in Base64 format as described in RFC 1521. 24 | * 25 | *

26 | * Project home page: www.source-code.biz/base64coder/java
27 | * Author: Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland
28 | * Multi-licensed: EPL / LGPL / GPL / AL / BSD / MIT. 29 | */ 30 | public class Base64Coder { 31 | 32 | // The line separator string of the operating system. 33 | private static final String systemLineSeparator = System.getProperty("line.separator"); 34 | 35 | // Mapping table from 6-bit nibbles to Base64 characters. 36 | private static final char[] map1 = new char[64]; 37 | static { 38 | int i=0; 39 | for (char c='A'; c<='Z'; c++) map1[i++] = c; 40 | for (char c='a'; c<='z'; c++) map1[i++] = c; 41 | for (char c='0'; c<='9'; c++) map1[i++] = c; 42 | map1[i++] = '+'; map1[i++] = '/'; } 43 | 44 | // Mapping table from Base64 characters to 6-bit nibbles. 45 | private static final byte[] map2 = new byte[128]; 46 | static { 47 | for (int i=0; isun.misc.BASE64Encoder.encodeBuffer(byte[]). 62 | * @param in An array containing the data bytes to be encoded. 63 | * @return A String containing the Base64 encoded data, broken into lines. 64 | */ 65 | public static String encodeLines (byte[] in) { 66 | return encodeLines(in, 0, in.length, 76, systemLineSeparator); } 67 | 68 | /** 69 | * Encodes a byte array into Base 64 format and breaks the output into lines. 70 | * @param in An array containing the data bytes to be encoded. 71 | * @param iOff Offset of the first byte in in to be processed. 72 | * @param iLen Number of bytes to be processed in in, starting at iOff. 73 | * @param lineLen Line length for the output data. Should be a multiple of 4. 74 | * @param lineSeparator The line separator to be used to separate the output lines. 75 | * @return A String containing the Base64 encoded data, broken into lines. 76 | */ 77 | public static String encodeLines (byte[] in, int iOff, int iLen, int lineLen, String lineSeparator) { 78 | int blockLen = (lineLen*3) / 4; 79 | if (blockLen <= 0) throw new IllegalArgumentException(); 80 | int lines = (iLen+blockLen-1) / blockLen; 81 | int bufLen = ((iLen+2)/3)*4 + lines*lineSeparator.length(); 82 | StringBuilder buf = new StringBuilder(bufLen); 83 | int ip = 0; 84 | while (ip < iLen) { 85 | int l = Math.min(iLen-ip, blockLen); 86 | buf.append (encode(in, iOff+ip, l)); 87 | buf.append (lineSeparator); 88 | ip += l; } 89 | return buf.toString(); } 90 | 91 | /** 92 | * Encodes a byte array into Base64 format. 93 | * No blanks or line breaks are inserted in the output. 94 | * @param in An array containing the data bytes to be encoded. 95 | * @return A character array containing the Base64 encoded data. 96 | */ 97 | public static char[] encode (byte[] in) { 98 | return encode(in, 0, in.length); } 99 | 100 | /** 101 | * Encodes a byte array into Base64 format. 102 | * No blanks or line breaks are inserted in the output. 103 | * @param in An array containing the data bytes to be encoded. 104 | * @param iLen Number of bytes to process in in. 105 | * @return A character array containing the Base64 encoded data. 106 | */ 107 | public static char[] encode (byte[] in, int iLen) { 108 | return encode(in, 0, iLen); } 109 | 110 | /** 111 | * Encodes a byte array into Base64 format. 112 | * No blanks or line breaks are inserted in the output. 113 | * @param in An array containing the data bytes to be encoded. 114 | * @param iOff Offset of the first byte in in to be processed. 115 | * @param iLen Number of bytes to process in in, starting at iOff. 116 | * @return A character array containing the Base64 encoded data. 117 | */ 118 | public static char[] encode (byte[] in, int iOff, int iLen) { 119 | int oDataLen = (iLen*4+2)/3; // output length without padding 120 | int oLen = ((iLen+2)/3)*4; // output length including padding 121 | char[] out = new char[oLen]; 122 | int ip = iOff; 123 | int iEnd = iOff + iLen; 124 | int op = 0; 125 | while (ip < iEnd) { 126 | int i0 = in[ip++] & 0xff; 127 | int i1 = ip < iEnd ? in[ip++] & 0xff : 0; 128 | int i2 = ip < iEnd ? in[ip++] & 0xff : 0; 129 | int o0 = i0 >>> 2; 130 | int o1 = ((i0 & 3) << 4) | (i1 >>> 4); 131 | int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); 132 | int o3 = i2 & 0x3F; 133 | out[op++] = map1[o0]; 134 | out[op++] = map1[o1]; 135 | out[op] = op < oDataLen ? map1[o2] : '='; op++; 136 | out[op] = op < oDataLen ? map1[o3] : '='; op++; } 137 | return out; } 138 | 139 | /** 140 | * Decodes a string from Base64 format. 141 | * No blanks or line breaks are allowed within the Base64 encoded input data. 142 | * @param s A Base64 String to be decoded. 143 | * @return A String containing the decoded data. 144 | * @throws IllegalArgumentException If the input is not valid Base64 encoded data. 145 | */ 146 | public static String decodeString (String s) { 147 | return new String(decode(s)); } 148 | 149 | /** 150 | * Decodes a byte array from Base64 format and ignores line separators, tabs and blanks. 151 | * CR, LF, Tab and Space characters are ignored in the input data. 152 | * This method is compatible with sun.misc.BASE64Decoder.decodeBuffer(String). 153 | * @param s A Base64 String to be decoded. 154 | * @return An array containing the decoded data bytes. 155 | * @throws IllegalArgumentException If the input is not valid Base64 encoded data. 156 | */ 157 | public static byte[] decodeLines (String s) { 158 | char[] buf = new char[s.length()]; 159 | int p = 0; 160 | for (int ip = 0; ip < s.length(); ip++) { 161 | char c = s.charAt(ip); 162 | if (c != ' ' && c != '\r' && c != '\n' && c != '\t') 163 | buf[p++] = c; } 164 | return decode(buf, 0, p); } 165 | 166 | /** 167 | * Decodes a byte array from Base64 format. 168 | * No blanks or line breaks are allowed within the Base64 encoded input data. 169 | * @param s A Base64 String to be decoded. 170 | * @return An array containing the decoded data bytes. 171 | * @throws IllegalArgumentException If the input is not valid Base64 encoded data. 172 | */ 173 | public static byte[] decode (String s) { 174 | return decode(s.toCharArray()); } 175 | 176 | /** 177 | * Decodes a byte array from Base64 format. 178 | * No blanks or line breaks are allowed within the Base64 encoded input data. 179 | * @param in A character array containing the Base64 encoded data. 180 | * @return An array containing the decoded data bytes. 181 | * @throws IllegalArgumentException If the input is not valid Base64 encoded data. 182 | */ 183 | public static byte[] decode (char[] in) { 184 | return decode(in, 0, in.length); } 185 | 186 | /** 187 | * Decodes a byte array from Base64 format. 188 | * No blanks or line breaks are allowed within the Base64 encoded input data. 189 | * @param in A character array containing the Base64 encoded data. 190 | * @param iOff Offset of the first character in in to be processed. 191 | * @param iLen Number of characters to process in in, starting at iOff. 192 | * @return An array containing the decoded data bytes. 193 | * @throws IllegalArgumentException If the input is not valid Base64 encoded data. 194 | */ 195 | public static byte[] decode (char[] in, int iOff, int iLen) { 196 | if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4."); 197 | while (iLen > 0 && in[iOff+iLen-1] == '=') iLen--; 198 | int oLen = (iLen*3) / 4; 199 | byte[] out = new byte[oLen]; 200 | int ip = iOff; 201 | int iEnd = iOff + iLen; 202 | int op = 0; 203 | while (ip < iEnd) { 204 | int i0 = in[ip++]; 205 | int i1 = in[ip++]; 206 | int i2 = ip < iEnd ? in[ip++] : 'A'; 207 | int i3 = ip < iEnd ? in[ip++] : 'A'; 208 | if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) 209 | throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); 210 | int b0 = map2[i0]; 211 | int b1 = map2[i1]; 212 | int b2 = map2[i2]; 213 | int b3 = map2[i3]; 214 | if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) 215 | throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); 216 | int o0 = ( b0 <<2) | (b1>>>4); 217 | int o1 = ((b1 & 0xf)<<4) | (b2>>>2); 218 | int o2 = ((b2 & 3)<<6) | b3; 219 | out[op++] = (byte)o0; 220 | if (op 0) { 34 | JSONObject json = JSONObject.parseObject(configContent); 35 | 36 | serverConfig.setRemotePort(json.getIntValue("remotePort")); 37 | serverConfig.setRemoteServer(json.getString("remoteServer")); 38 | serverConfig.setPort(json.getIntValue("port")); 39 | serverConfig.setHost(json.getString("host")); 40 | serverConfig.setEnc(json.getBooleanValue("enc")); 41 | serverConfig.setResponseHttps(json.getBooleanValue("responseHttps")); 42 | } 43 | 44 | return serverConfig; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/com/proxy/utils/ShuffTool.java: -------------------------------------------------------------------------------- 1 | package com.proxy.utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Calendar; 5 | import java.util.Collections; 6 | import java.util.Random; 7 | import java.util.TimeZone; 8 | 9 | import com.proxy.common.DeqMap; 10 | 11 | /** 12 | * 生成密码本的程序 13 | */ 14 | public class ShuffTool { 15 | 16 | private static int getSeed() { 17 | Calendar ca1 = Calendar.getInstance(TimeZone.getTimeZone("UTC")); 18 | long currentMiles = ca1.getTimeInMillis(); 19 | int after = Integer.parseInt(String.valueOf(currentMiles).substring(0, 6)); 20 | return after; 21 | } 22 | 23 | public static DeqMap createPassWordBook() { 24 | 25 | ArrayList bytes1 = new ArrayList<>(); 26 | ArrayList bytes2 = new ArrayList<>(); 27 | 28 | for (byte b = Byte.MIN_VALUE; b <= Byte.MAX_VALUE; b++) { 29 | byte enc = 0; 30 | enc = (byte) -b; 31 | bytes1.add(b); 32 | bytes2.add(enc); 33 | if (b == Byte.MAX_VALUE) { 34 | break; 35 | } 36 | } 37 | 38 | Random rand = new Random(getSeed()); 39 | 40 | Collections.shuffle(bytes1, rand); 41 | Collections.shuffle(bytes2, rand); 42 | 43 | DeqMap deqMap = new DeqMap<>(); 44 | 45 | for (int i = 0, isize = bytes1.size(); i < isize; i++) { 46 | deqMap.put(bytes1.get(i), bytes2.get(i)); 47 | } 48 | 49 | // System.out.println(bytes1); 50 | // System.out.println(bytes1.size()); 51 | 52 | // System.out.println(bytes2); 53 | // System.out.println(bytes2.size()); 54 | 55 | return deqMap; 56 | } 57 | 58 | // public static void main(String[] args) { 59 | // 60 | // DeqMap deqMap = createPassWordBook(); 61 | // System.out.println("----------"); 62 | // System.out.println(deqMap); 63 | // 64 | // byte bk = 10; 65 | // System.out.println(bk + "==>" + deqMap.getVByK(bk)); 66 | // byte bv = (byte) deqMap.getVByK(bk); 67 | // System.out.println(bv + "==>" + deqMap.getKByV(bv)); 68 | // 69 | // } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/com/proxy/utils/ZipUtil.java: -------------------------------------------------------------------------------- 1 | package com.proxy.utils; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.util.zip.GZIPInputStream; 7 | import java.util.zip.GZIPOutputStream; 8 | 9 | // 将一个字符串按照zip方式压缩和解压缩 10 | public class ZipUtil { 11 | 12 | // 压缩 13 | public static String compress(String str) throws IOException { 14 | if (str == null || str.length() == 0) { 15 | return str; 16 | } 17 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 18 | GZIPOutputStream gzip = new GZIPOutputStream(out); 19 | gzip.write(str.getBytes()); 20 | gzip.close(); 21 | return out.toString("ISO-8859-1"); 22 | } 23 | 24 | // 解压缩 25 | public static String uncompress(String str) throws IOException { 26 | if (str == null || str.length() == 0) { 27 | return str; 28 | } 29 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 30 | ByteArrayInputStream in = new ByteArrayInputStream(str.getBytes("ISO-8859-1")); 31 | GZIPInputStream gunzip = new GZIPInputStream(in); 32 | byte[] buffer = new byte[256]; 33 | int n; 34 | while ((n = gunzip.read(buffer)) >= 0) { 35 | out.write(buffer, 0, n); 36 | } 37 | // toString()使用平台默认编码,也可以显式的指定如toString("GBK") 38 | return out.toString(); 39 | } 40 | 41 | // 测试方法 42 | public static void main(String[] args) throws IOException { 43 | 44 | // 测试字符串 45 | String str = "55"; 46 | 47 | System.out.println("原长度:" + str.length()); 48 | 49 | System.out.println("压缩后:" + ZipUtil.compress(str).length()); 50 | 51 | System.out.println("解压缩:" + ZipUtil.uncompress(ZipUtil.compress(str))); 52 | } 53 | 54 | } --------------------------------------------------------------------------------