文档章节

JAVA实现网络请求代理之Socket篇

jlcao
 jlcao
发布于 2015/09/10 14:57
字数 1245
阅读 685
收藏 2

JAVA实现网络请求代理之HTTP篇 (一)

JAVA实现网络请求代理之Socket篇(二)

Java代理服务器之截取,篡改HTTP请求(应用篇)


用Java实现Socket代理服务,原理上和http代理有些相似,只是两种协议的格式不一样。socket是对tcp/ip协议的抽象封装,所以socket的格式和tcp/ip的协议是不一样的。

具体详细的Socket 和tcp/ip 的关系大家可以参考 http://www.2cto.com/net/201211/166537.html 这篇博文。

下面还是继续贴上代码,供大家研究

package com.mato.proxy.socket;


import java.net.ServerSocket;
import java.net.Socket;

/**
 * Created by cjl on 2015/9/9.
 */
public class SOCKETProxy extends Thread{
    private ServerSocket server;
    public SOCKETProxy(ServerSocket _server){
        server=_server;
        start();
    }
    public void run(){ // 线程运行函数
        Socket connection;
        while(true){
            try{
                connection=server.accept();
                SOCKSServerThread handler =new SOCKSServerThread(connection);
            }catch(Exception e){}
        }
    }
}

接下来就是socket 连接的具体处理类。

package com.mato.proxy.socket;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

/**
 * Created by cjl on 2015/9/8.
 */
public class SOCKSServerThread extends Thread{
    private Socket client;
    int bytes2int(byte b){ // 将 byte 类型转换为 int 类型
        int mask=0xff;
        int temp=0;
        int res=0;
        res<<=8;
        temp=b&mask;
        res|=temp;
        return res;
    }
    public SOCKSServerThread(Socket _connection){
// 构造函数
        client =_connection;
        start();
    }

    public void run(){ // 线程运行函数
        byte creadBuf[]=new byte[10000],cwriteBuf[]=new byte[10000],buf2[]=new byte[10000];
        int creadlen=0,sreadlen=0,readbytes2=0;
        DataInputStream cin=null,sin=null;
        DataOutputStream cout=null,sout=null;
        String s=null,s1=null,s2=null;
        int i;
        int port=0,port1=0;
        String ip=null;
        Socket server=null;
        byte ip1[]=new byte[4],ip2[]=new byte[4];
        try{
            cin=new DataInputStream(client.getInputStream());
            cout =new DataOutputStream (client.getOutputStream());
            if(cin!=null&&cout!=null){
                creadlen=cin.read(creadBuf,0,10000); // 从客户端读数据
                if(creadlen>0){ // 读到数据
                    if(creadBuf[0]==5){ // 读到 SOCK5 请求
                        // 发送 SOCK5 应答,第一次
                        cwriteBuf[0]=5;cwriteBuf[1]=0;
                        cout.write(cwriteBuf,0,2);
                        cout.flush();
                        creadlen=cin.read(creadBuf,0,10000);
                        // 继续读 SOCK5 请求
                        if(creadlen>0){ // 读到 SOCK5 请求
                            if(creadBuf[0]==5&&creadBuf[1]==1&&creadBuf[2]==0&&creadBuf[3]== 1){//TCP 请求
                                // 从该请求中取要连接的 IP 地址和端口号 , 并建立TCP 套接字
                                ip=bytes2int(creadBuf[4])+"."+bytes2int(creadBuf[5])+"."+ bytes2int(creadBuf[6])+"."+bytes2int(creadBuf[7]);
                                port=creadBuf[8]*256+creadBuf[9];
                                server=new Socket(ip,port);//创建到远程服务端的套接字
                                sin = new DataInputStream(server.getInputStream());
                                sout = new DataOutputStream(server.getOutputStream());
                                // 发送 SOCK5 应答
                                ip1 = server.getLocalAddress().getAddress();
                                port1 = server.getLocalPort();
                                creadBuf[1] = 0;
                                creadBuf[4] = ip1[0];
                                creadBuf[5] = ip1[1];
                                creadBuf[6] = ip1[2];
                                creadBuf[7] = ip1[3];
                                creadBuf[8] = (byte) (port1 >> 8);
                                creadBuf[9] = (byte) (port1 & 0xff);
                                cout.write(creadBuf, 0, 10);//回复应答,第二次
                                cout.flush();
                                // 建立线程 , 用于给客户端返回数据
                                SOCKETChannel channel = new SOCKETChannel(sin, cout);
                                while(true){ // 循环读数据
                                    try {
                                        if (sreadlen == -1) break; // 无数据则退出循环
                                        sreadlen = cin.read(cwriteBuf, 0, 10000);
                                        // 从客户端读数据
                                        if (sreadlen > 0) { // 读到数据 , 则发送给外网
                                            sout.write(cwriteBuf, 0, sreadlen);
                                            sout.flush();
                                        }
                                    }catch(Exception e1){break;}
                                }
                            }
                        }
                    }
                    if(creadBuf[0]==4){ // 读到 SOCK4 请求
                        port=creadBuf[2]*256+creadBuf[3]; // 从请求中取端口号
                        if(creadBuf[4]==0&&creadBuf[5]==0&&creadBuf[6]==0&&creadBuf[7]!= 0&&creadBuf[8]==0){
                            // 如请求中为域名
                            s=new String(creadBuf);
                            s=s.substring(9);
                            s=s.substring(0,s.indexOf("\0"));
                        }
                        else{ // 如请求中为 IP 地址
                            ip=bytes2int(creadBuf[4])+"."+bytes2int(creadBuf[5])+"."+bytes2int(creadBuf[6])+"."+bytes2int(creadBuf[7]);
                            s=ip;
                        }
                        for(i=1;i<=9;i++)
                            creadBuf[i-1]=0;
                        server=new Socket(s,port);
                        // 根据 SOCK4 请求中的地址建立 TCP 套接字
                        sin=new DataInputStream(server.getInputStream());
                        sout =new DataOutputStream(server.getOutputStream());
                        // 返回 SOCK4 应答,第二次
                        ip1=server.getLocalAddress().getAddress();
                        port1=server.getLocalPort();
                        creadBuf[0]=0;
                        creadBuf[1]=0x5a;
                        creadBuf[2]=ip1[0];
                        creadBuf[3]=ip1[1];
                        creadBuf[4]=(byte)(port1>>8);
                        creadBuf[5]=(byte)(port1&0xff);
                        cout.write(creadBuf,0,8);
                        cout.flush();
                        // 建立线程 , 用于给客户端返回数据
                        SOCKETChannel thread1 = new SOCKETChannel(sin, cout);
                        while(true){ // 循环读数据
                            try {
                                if (sreadlen == -1) break; // 无数据则退出循环
                                sreadlen = cin.read(cwriteBuf, 0, 10000);
                                // 从客户端读数据
                                if (sreadlen > 0) { // 读到数据 , 则发送给外网
                                    sout.write(cwriteBuf, 0, sreadlen);
                                    sout.flush();
                                }
                            }catch(Exception e1){break;}
                        }
                    }
                }
            }
            // 执行关闭操作
            if(sin!=null) sin.close();
            if(sout!=null) sout.close();
            if(server!=null) server.close();
            if(cin!=null) cin.close();
            if(cout!=null) cout.close();
            if(client !=null) client.close();
        }catch(IOException e){}
    }

}

再接下来就是socket 建立传输通道的线程类

package com.mato.proxy.socket;

import java.io.DataInputStream;
import java.io.DataOutputStream;

/**
 * Created by cjl on 2015/9/8.
 */
public class SOCKETChannel extends Thread{
    private DataInputStream in; // 读数据
    private DataOutputStream out; // 写数据
    public SOCKETChannel(DataInputStream _in, DataOutputStream _out){
        in=_in;
        out=_out;
        start();
    }
    public void run(){
        // 线程运行函数 , 循环读取返回数据 , 并发送给相关客户端
        int readbytes=0;
        byte buf[]=new byte[10000];
        while(true){ // 循环
            try{
                if(readbytes==-1)
                    break; // 无数据则退出循环
                readbytes=in.read(buf,0,10000);
                if(readbytes>0){
                    out.write(buf,0,readbytes);
                    out.flush();
                }
            }catch(Exception e){break;} // 异常则退出循环
        }
    }
}

最后就是一个Main启动

package com.mato.proxy;

import com.mato.proxy.http.HttpProxy;
import com.mato.proxy.socket.SOCKETProxy;

import java.io.IOException;
import java.net.ServerSocket;

/**
 * Created by cjl on 2015/9/8.
 */
public class Proxy {

    public static void main(String[] args) {
        try{
            ServerSocket httpserver=new ServerSocket(808);
            // 建立 HTTP 侦听套接字
            System.out.println ("HTTP Proxy started on "+httpserver.getLocalPort());
            ServerSocket socksserver=new ServerSocket(888);
            // 建立 SOCKS 侦听套接字
            System.out.println ("SOCKS Proxy started on "+socksserver.
                    getLocalPort());
            HttpProxy httpproxy=new HttpProxy(httpserver); // 建立HTTP 侦听线程
            SOCKETProxy socksproxy=new SOCKETProxy(socksserver);
            // 建立 SOCKS 侦听线程
        }catch(IOException e){}
    }
}

其中包括昨天的http代理服务的启动,socket代理只是实现了tcp协议的,如果要实现UDP协议的话,按照其协议格式进行解析,socket类替换成DatagramSocket 即可,大家不妨下来试一试。


© 著作权归作者所有

jlcao
粉丝 2
博文 6
码字总数 4620
作品 0
南岸
程序员
私信 提问
求教!!!Java编写网络代理,完全没有思路

直接进行socket编程,有这方面的资料吗?求推荐! 要用Java做一个网络代理,能实现接收浏览器请求并用这个代理访问服务器,最后再把服务器返还的网页内容给浏览器。 各位有啥想法指导指导啊。...

BeasTea
2015/10/22
619
9
JAVA RPC:从上手到爱不释手

文首,思考一个问题:为什么需要 RPC 服务? 在传统的开发模式中,我们通常将系统的各个服务部署在单台机器,随着服务的扩展,这种方式已经完全无法满足系统大规模的扩展需要,分布式系统由此...

编程SHA
03/28
46
0
Tomcat剖析之架构篇(一)

前言 早在之前写过一些http玩具服务器,总感觉无法继续前进了,期间花了比较多的时间在基础知识上,前段时间想着直接从用的比较多的服务器开始,对于Java开发者来说,自然Tomcat是首选,但有...

丶legend
07/14
0
0
分布式消息通信框架RMI原理分析

什么是RPC RPC(Remote Procedure Call,远程过程调用) 一般用来实现部署在不同机器上的系统之间的方法调用, 使得程序能够像访问本地系统资源一样,通过网络传输去访问远端系统资源;(!!...

Java搬砖工程师
2018/11/13
138
0
解决Fiddler不能监听Java HttpURLConnection请求的方法

在默认情况下,Fiddler不能监听Java HttpURLConnection请求。究其原因,Java的网络通信协议栈可能浏览器的通信协议栈略有区别,Fiddler监听Http请求的原理是在应用程序和操作系统网络通信层之...

小克898
2013/05/18
346
0

没有更多内容

加载失败,请刷新页面

加载更多

webGL和three.js的关系

本文转载于:专业的前端网站➤webGL和three.js的关系 如今浏览器的功能越来越强大,而且这些功能可能通过JavaScript直接调用。你可以用HTML5标签轻松地添加音频和视频,而且可以在HTML5画布上...

前端老手
31分钟前
6
0
Spring如何实现AOP,请不要再说cglib了!

1. 从注解入手找到对应核心类 最近工作中我都是基于注解实现AOP功能,常用的开启AOP的注解是@EnableAspectJAutoProxy,我们就从它入手。 上面的动图的流程的步骤就是: @EnableAspectJAutoPr...

温安适
33分钟前
31
0
Mybatis之ParameterHandler

mybatis-3.4.6.release. ParementerHandler是个接口,如下List-1 List-1 public interface ParameterHandler { Object getParameterObject(); void setParameters(PreparedStateme......

克虏伯
47分钟前
6
0
Spark sql的批处理物理计划BatchScanExec

BatchScanExec是batch类的物理计划,对应的逻辑计划是DataSourceV2Relation,是Datasource。 它的入参是Scan类,Scan类有两个重要方法,一个获取分区列表信息;另一个方法获取读取器工厂。 ...

守望者之父
51分钟前
10
0
for循环与while循环

循环结构三要素 a. 初始值 b. 循环条件 c. 改变条件 for循环 声明格式 for(表达式1;表达式2;表达式3){ 循环体语句 } 表达式1:用来循环的变量初始值。(开始值) 表达式2:用来判断循环的条件。...

Lenat
57分钟前
12
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部