java实现一个socks5代理 <一>了解nio Selector的基本用法

来源:互联网 发布:阿里云备案管理系统 编辑:程序博客网 时间:2024/06/06 11:02

上面浏览器可以放心的将数据交给代理了,接下来做的是怎样处理这些数据,以及面对浏览器突然过来的很多连接如何处理他们,这就要用到javax.nio包下的东西了,下面做简单介绍,不会nio可以先去学习下nio selector的基本用法,再回来接着看,下面先讲思路再上代码

1.创建ServiceSocketChannel,监听在5661端口。

2.像selector注册accept事件

3.堵塞式的从selector选择,选择后就开始执行

4.允许所有的连接,对获取到的连接都发送 5 0 表示接受无密码的连接

5.接受到ip port 的信息后,ip port 解析出来,异步连接到此ip port 并注册 connection事件

6。当连接事件触发后,完成连接,像浏览器返回 连接成功的标识。

7.像selector注册两个监听,分别监听client 和 外网 的socketchannel连接 read事件

8.当监听到其中一方的数据就读取并将数据写入另一方的channel中。

9.当读到流的末尾,管理连接,注销掉channel 等。

10.socks4的握手方式和socks5不同其他都相同,所以可以共用大部分代码

package com.proxydemo;import java.io.IOException;public class Main {//程序启动入口public static void main(String[] args) throws IOException {Reactor reactor=new Reactor();reactor.run();}}
package com.proxydemo;/*
容器类,selector的创建 以及做选择 并且执行的类
*/import java.io.IOException;import java.net.InetSocketAddress;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.util.Iterator;import java.util.Set;//容器类public class Reactor implements Runnable {private Selector selector;//容器private ServerSocketChannel ssc;//服务端socketchannelpublic Reactor() throws IOException{//初始化容器和服务器selector=Selector.open();ssc=ServerSocketChannel.open();//绑定事件 和 端口 设置异步ssc.bind(new InetSocketAddress(5661));ssc.configureBlocking(false);SelectionKey key = ssc.register(selector, SelectionKey.OP_ACCEPT);//创建accept事件,以及创建处理的线程key.attach(new AcceptAcceptor(selector,ssc));
}public void run() {while(true){try {int size = selector.select();if(size==0){continue;}} catch (IOException e) {e.printStackTrace();}Set<SelectionKey> keys = selector.selectedKeys();Iterator<SelectionKey> iterator = keys.iterator();while(iterator.hasNext()){SelectionKey next = iterator.next();consume(next);iterator.remove();}}}private void consume(SelectionKey key){Runnable r = (Runnable) key.attachment();r.run();}}


package com.proxydemo;import java.io.IOException;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;//负责接受连接的类,接受完立即注册   读操作 读操作出发 Handler551public class AcceptAcceptor implements Runnable {private Selector selector;private ServerSocketChannel ssc;public AcceptAcceptor(Selector selector,ServerSocketChannel ssc){this.selector=selector;this.ssc=ssc;}public void run() {try {SocketChannel socket = ssc.accept();socket.configureBlocking(false);SelectionKey keyclient = socket.register(selector, SelectionKey.OP_READ);keyclient.attach(new Handler551(keyclient));} catch (IOException e) {e.printStackTrace();}}}


package com.proxydemo;import java.io.IOException;import java.net.InetAddress;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.SocketChannel;import java.util.Arrays;/*出发了读操作触发此事件 目的读取 5 1 0 /或者4 1 0  * 出发写操作的事件,目的写入 5 0 * 根据事件的不同做出不同行为
根据是socks4 /socks5做不同处理 * */public class Handler551 implements Runnable {private Selector selector;private SocketChannel sc;private SelectionKey keyclient;private ByteBuffer bytebuffer=ByteBuffer.allocate(10);static byte bb510[]={5,0};static byte bb410[]={0,90,0,0,0,0,0,0}; //返回愿意连接public Handler551(SelectionKey keyclient){this.selector=keyclient.selector();this.keyclient=keyclient;sc=(SocketChannel) keyclient.channel();}public void run() {bytebuffer.clear();try {int read = sc.read(bytebuffer);if(read==-1){keyclient.cancel();return;}System.out.println("得到的结果"+Arrays.toString(bytebuffer.array()));int b = bytebuffer.get(0);if(b==5){ //协议5sc.write(ByteBuffer.wrap(bb510));keyclient.attach(new Handler501002( keyclient));}else if(b==4){//协议4int portflag=bytebuffer.get(2);int port=bytebuffer.get(3);if(port>0){port=256*portflag+port;}else{port=256*portflag+(256+port);}StringBuilder sb=new StringBuilder();int A=bytebuffer.get(4);int B=bytebuffer.get(5);int C=bytebuffer.get(6);int D=bytebuffer.get(7);System.out.println("原来的"+A+"."+B+"."+C+"."+D);if(A<0) A=256+A;if(B<0) B=256+B;if(C<0) C=256+C;if(D<0) D=256+D;sb.append(A);sb.append(".");sb.append(B);sb.append(".");sb.append(C);sb.append(".");sb.append(D);String host=sb.toString();sc.write(ByteBuffer.wrap(bb410));keyclient.attach(new Sockts4Handler( keyclient,host,port));}} catch (IOException e) {keyclient.cancel();selector.wakeup();e.printStackTrace();}}}




package com.proxydemo;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.SocketChannel;/* * 第一步初步握手完成后 第二步 进行验证握手 如果是socks5 则执行这一步 进行接受ip和端口信息 * */public class Handler501002 implements Runnable {private Selector selector;private SocketChannel sc;private SelectionKey keyclient;static byte bb501002[]={5,0,0,1,0,0,0,0,1,1};ByteBuffer bytebuffer=ByteBuffer.allocate(100);public Handler501002(SelectionKey keyclient){this.selector=keyclient.selector();this.keyclient=keyclient;this.sc=(SocketChannel) keyclient.channel();}/*1.读取数据 * 2.解析数据,建立连接,绑定下一个handler *  * */public void run() {int read=-1;try {read = sc.read(bytebuffer);} catch (IOException e) {e.printStackTrace();}if(read==-1){keyclient.cancel();return;}//sc.write(ok50001);//2.解析数据String host=getHost(bytebuffer.asReadOnlyBuffer(), read);int port=getPort(bytebuffer.asReadOnlyBuffer(), read);try{//4.不处理host为空的数据if(host==null||"".equals(host)){for(int i=0;i<read;i++){//***********************System.out.print(bytebuffer.get(i));System.out.println("host null");}sc.close();keyclient.cancel();selector.wakeup();return;}//3.创建channelSocketChannel scClient=SocketChannel.open();//5 不处理查找不到ip地址的域名InetSocketAddress isa=new InetSocketAddress(host, port);if(isa.isUnresolved() || isa.getAddress().equals("") ||isa.getAddress().equals("127.0.0.1")){sc.close();keyclient.cancel();return;}//5.设置异步scClient.configureBlocking(false);//6.异步连接scClient.connect(isa);//7.注册SelectionKey register = scClient.register(selector, SelectionKey.OP_CONNECT);//7 还没有连接上时 暂时不监测client、的是否有东西可读keyclient.interestOps(keyclient.interestOps() &~SelectionKey.OP_READ );//8添加连接事件/* 分别是与优酷之间的连接 || 与client之间的连接  */register.attach(new LinkFinish( register,keyclient)); //传进去代理和优酷之间的连接System.out.println(host+":"+port);}catch(IOException e){e.printStackTrace();}}//解析地址public String getHost(ByteBuffer a,int len){if(len<8){return null;}StringBuffer sb=new StringBuffer();if(a.get(3)==3){//说明是网址地址int size=a.get(4); //网址长度for(int i=5;i<(5+size);i++){sb.append((char)a.get(i));}}else if(a.get(3)==1){//说明是ip地址for(int i=4;i<=7;i++){int A=a.get(i);if(A<0) A=256+A;sb.append(A);sb.append(".");}sb.deleteCharAt(sb.length()-1);}return sb.toString();}//解析端口public int getPort(ByteBuffer a,int len){if(len<4){return 0;}int port = a.get(len-1);int thod=a.get(len-2);if(port>0){return 256*thod+port;}else{return 256*thod +(256+port);}}}

package com.proxydemo;import java.io.IOException;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.SocketChannel;import java.util.concurrent.TimeoutException;/* * 异步连接如果触发,则完成连接,注册读取事件 上一步获取了真正的ip和port并进行异步连接,这一步完成真正的连接* */public class LinkFinish implements Runnable {static byte bb501002[]={5,0,0,1,0,0,0,0,1,1};private Selector selector;private SocketChannel sc; //代理和实际网站的连接private SocketChannel cd; //client和代理之间private SelectionKey key551,keyWeb;public LinkFinish(SelectionKey keyWeb,SelectionKey key551){this.selector=key551.selector();this.key551=key551;this.keyWeb=keyWeb;sc=(SocketChannel) keyWeb.channel();cd=(SocketChannel) key551.channel();}public void run() {//1.完成连接boolean finishConnect =false;try {finishConnect = sc.finishConnect();//1  向客户端通知他连接成功了,可以发送数据了ByteBuffer ok50001 = ByteBuffer.wrap(bb501002);//返回愿意连接cd.write(ok50001);if(finishConnect){//2.转换成read//keyFor.interestOps(SelectionKey.OP_READ &~SelectionKey.OP_CONNECT);keyWeb.interestOps(keyWeb.interestOps() & 0 |SelectionKey.OP_READ);key551.interestOps(SelectionKey.OP_READ );//3.切换handlerkeyWeb.attach(new ReadHandler(keyWeb,key551));//设置Handler   代理to优酷      电脑to代理key551.attach(new ReadHandler2( keyWeb,key551));//代理to优酷      电脑to代理System.out.println("实际连接");//selector.wakeup();}} catch (IOException e1) {try {sc.close();cd.close();} catch (IOException e) {e.printStackTrace();}keyWeb.cancel();key551.cancel();System.out.println("超时一个"+sc.socket().getInetAddress());//selector.wakeup();return;}}}

package com.proxydemo;import java.io.IOException;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.SocketChannel;import java.util.Arrays;/* * 代理将数据打向浏览器 * */public class ReadHandler implements Runnable {private Selector selector;private SocketChannel sc; //代理和优酷网站之间的通道private SocketChannel cd;//浏览器和代理之间的通道private SelectionKey key551,keyWeb;ByteBuffer buffer=ByteBuffer.allocate(6144);public ReadHandler(SelectionKey keyWeb,SelectionKey key551){this.selector=key551.selector();this.keyWeb=keyWeb;this.key551=key551;sc=(SocketChannel) keyWeb.channel();cd=(SocketChannel) key551.channel();}/*负责交换数据,都的话放进缓冲区,写的话,从缓冲区中国写 * */public void run() {//ByteBuffer buffer=ByteBuffer.allocate(4096); //浏览器打向代理//1.清空缓冲区buffer.clear();//2.读取数据int read=-1;try {read = sc.read(buffer);} catch (IOException e1) {/*异常则证明连接已经断开了,直接断开就可以*/e1.printStackTrace();}//检查是否是-1 是的话说明没有数据读 或者已经关闭了if(read==-1){//没关闭就将他关闭if(sc.isOpen()){try {sc.close();} catch (IOException e) {e.printStackTrace();}}keyWeb.cancel();return;//selector.wakeup();}int write=-1;if(read>0){buffer.flip();if(cd.isOpen()){try { write = cd.write(buffer);} catch (IOException e) {e.printStackTrace();}}}if(write==-1){if(cd.isOpen()){try {cd.close();} catch (IOException e) {e.printStackTrace();}key551.cancel();}}buffer.clear();if(read==0){//keyFor2.interestOps(SelectionKey.OP_WRITE);//selector.wakeup();}System.out.println(read+"接收");}}

package com.proxydemo;import java.io.IOException;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.SocketChannel;//浏览器将数据打向代理//public class ReadHandler2 implements Runnable {private Selector selector;private SocketChannel sc; //代理和优酷网站之间的通道private SocketChannel cd;//浏览器和代理之间的通道private SelectionKey key551,keyWeb;ByteBuffer buffer=ByteBuffer.allocate(6144);public ReadHandler2(SelectionKey keyWeb,SelectionKey key551){this.selector=key551.selector();this.keyWeb=keyWeb;this.key551=key551;this.sc=(SocketChannel) keyWeb.channel();this.cd=(SocketChannel) key551.channel();}/*负责交换数据,都的话放进缓冲区,写的话,从缓冲区中国写 * */public void run() {//ByteBuffer buffer=ByteBuffer.allocate(10240);//1.清空缓冲区buffer.clear();//2.读取数据int read=-1;if(cd.isOpen()){try {read = cd.read(buffer);} catch (IOException e) {e.printStackTrace();}}if(read==-1){if(cd.isOpen()){try {cd.close();} catch (IOException e) {e.printStackTrace();}}key551.cancel();return;}int write=-1;if(read>0){buffer.flip();if(cd.isOpen()){try {write = sc.write(buffer);} catch (IOException e) {e.printStackTrace();}}}if(write==-1){if(cd.isOpen()){try {cd.close();} catch (IOException e) {e.printStackTrace();}}keyWeb.cancel();}buffer.clear();if(read==0){//keyFor.interestOps(SelectionKey.OP_WRITE);//selector.wakeup();}System.out.println("发送"+read);}}

package com.proxydemo;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.SocketChannel;//如果是sockets4 则执行这个public class Sockts4Handler implements Runnable {private Selector selector;private SocketChannel sc;private SelectionKey keyclient;String host;int port;public Sockts4Handler(SelectionKey keyclient,String host,int port){this.selector=keyclient.selector();this.keyclient=keyclient;this.sc=(SocketChannel) keyclient.channel();this.host=host;this.port=port;}/*1.读取数据 * 2.解析数据,建立连接,绑定下一个handler *  * */public void run() {try{//4.不处理host为空的数据if(host==null||"".equals(host)){sc.close();keyclient.cancel();selector.wakeup();return;}//3.创建channelSocketChannel scClient=SocketChannel.open();//5 不处理查找不到ip地址的域名InetSocketAddress isa=new InetSocketAddress(host, port);if(isa.isUnresolved() || isa.getAddress().equals("") ||isa.getAddress().equals("127.0.0.1")){sc.close();keyclient.cancel();return;}//5.设置异步scClient.configureBlocking(false);//6.异步连接scClient.connect(isa);//7.注册SelectionKey register = scClient.register(selector, SelectionKey.OP_CONNECT);//7 还没有连接上时 暂时不监测client、的是否有东西可读keyclient.interestOps(keyclient.interestOps() &~SelectionKey.OP_READ );//8添加连接事件/* 分别是与优酷之间的连接 || 与client之间的连接  */register.attach(new Sockets4LinkFinish( register,keyclient)); //传进去代理和优酷之间的连接System.out.println(host+":"+port);}catch(IOException e){e.printStackTrace();}}}

package com.proxydemo;import java.io.IOException;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.SocketChannel;import java.util.concurrent.TimeoutException;/* * 异步连接如果触发,则完成连接,注册读取事件 sockets4 执行这个* */public class Sockets4LinkFinish implements Runnable {static byte bb501002[]={5,0,0,1,0,0,0,0,1,1};static byte bb401002[]={0,90,0,0,0,0,0,0};private Selector selector;private SocketChannel sc; //代理和实际网站的连接private SocketChannel cd; //client和代理之间private SelectionKey key551,keyWeb;public Sockets4LinkFinish(SelectionKey keyWeb,SelectionKey key551){this.selector=key551.selector();this.key551=key551;this.keyWeb=keyWeb;sc=(SocketChannel) keyWeb.channel();cd=(SocketChannel) key551.channel();}public void run() {//1.完成连接boolean finishConnect =false;try {finishConnect = sc.finishConnect();//1  向客户端通知他连接成功了,可以发送数据了ByteBuffer ok50001 = ByteBuffer.wrap(bb401002);//返回愿意连接//cd.write(ok50001);if(finishConnect){//2.转换成read//keyFor.interestOps(SelectionKey.OP_READ &~SelectionKey.OP_CONNECT);keyWeb.interestOps(keyWeb.interestOps() & 0 |SelectionKey.OP_READ);key551.interestOps(SelectionKey.OP_READ );//3.切换handlerkeyWeb.attach(new ReadHandler(keyWeb,key551));//设置Handler   代理to优酷      电脑to代理key551.attach(new ReadHandler2( keyWeb,key551));//代理to优酷      电脑to代理System.out.println("实际连接");//selector.wakeup();}} catch (IOException e1) {try {sc.close();cd.close();} catch (IOException e) {e.printStackTrace();}keyWeb.cancel();key551.cancel();System.out.println("超时一个"+sc.socket().getInetAddress());//selector.wakeup();return;}}}


这个demo主要是练习下nio的使用,兼容socks4 后,可以将internate选项中设置套接字,那个直接就是socks4的代理,这样能看到很多数据包在代理中流动,本地使用对浏览器打开网页的速度影响非常小,几乎看不出来


这里的源码下载可以导入eclipse 直接执行看结果了 源码地址



原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 大班安全教案遇到小偷怎么办 小班孩子舞台表演找不到位置怎么办 懂你英语学完了怎么办 小班社会走丢了怎么办 帮小老鼠搬鸡蛋怎么办 小老鼠还能怎么办鸡蛋 中班教案走丢了怎么办 走丢了怎么办可后反思 社会走丢了怎么办教案 孩子一直不吃幼儿园的饭怎么办 大班安全游泳抽筋怎么办反思 汤洒了怎么办教学反思 迷路了怎么办小班详案 大班牙又痛又摇怎么办 大班安全教案着火了怎么办 小班社会生病了怎么办反思 脚扭伤了怎么办的反思 中班安全迷了眼怎么办 15个月的小孩长泡疹怎么办 墨盒加错颜色墨水怎么办 墨盒颜色加错了怎么办 uV打印有色差是怎么办 cmyk和rgb有色差怎么办 染头发新长出来怎么办 白色衣服弄上口红怎么办 口红弄到牛仔裤上怎么办 口红弄到沙发上怎么办 做了拔罐后背疼怎么办 拔罐拔出血水后怎么办 拨了罐浑身疼怎么办 拔完火罐洗澡疼怎么办 拔罐之后脊背疼怎么办? 五个月的狗胆小怎么办 电脑颜色不正常分辨率调不了怎么办 宝宝认人怎么办 五个月 新生儿42天听力筛查未通过怎么办 幼儿大便干燥拉不下粑粑怎么办 宝宝大便干燥拉粑粑带浓血怎么办 新生儿便秘4天了怎么办 写卷子的题目不认识怎么办? 2个月小猫尿床怎么办