.Net下HTTP访问穿越多层代理的方法以及代理服务器的验证

C#, WebService, 技术心得 Add comments

  我们学校的网络环境是,所有的出校访问均须通过代理服务器(我们叫作sproxy),并且不能连接国外网站。为此,我想要做一个自动抓取和验证出国代理列表的WebService。我们所有的验证代理的请求,都需要通过两层代理,最终通到用来测试的网站(我使用了www.redhat.com)

  技术上的思路是,通过连接第一层代理sproxy(支持http tunnel),给第二层代理发送GET指令,从而完成对目标网页的访问。

  首先,通过普通的匿名透明代理的方法,是直接使用Socket发送GET命令,只不过与GET普通网站稍有不同罢了

直接访问:

GET / HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/msword, application/vnd.ms-excel, application/vnd.ms-powerpoint, */*
Accept-Language: zh-cn
UA-CPU: x86
Accept-Encoding: gzip, deflate
If-Modified-Since: Thu, 06 Jul 2006 15:39:53 GMT
If-None-Match: "1172d9-381c-44ad2ec9"
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)
Host: www.redhat.com
Connection: Keep-Alive
Cookie: s_vi=[CS]v1|44AAA05400004577-A170C060000008A[CE]; Apache=61.147.159.196.23241152032846747

通过本机CCproxy:

GET / HTTP/1.0
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/msword, application/vnd.ms-excel, application/vnd.ms-powerpoint, */*
Accept-Language: zh-cn
UA-CPU: x86
Proxy-Connection: Keep-Alive
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)
Host: www.redhat.com
Cookie: s_vi=[CS]v1|44AAA05400004577-A170C060000008A[CE]; Apache=61.147.159.196.23241152032846747

  可以看出来,只要给代理服务器发送正确的请求地址即可,不需要程序上特殊的变化。
  事实上,对于不使用代理服务器的场合,你可以直接向某HTTP服务器发送GET /,而这在使用代理时不行,需要说明详细地址或者标明Host: www.redhat.com

对于两层代理,就有所不同了

  首先要给sproxy发送一个CONNECT指令,令其去连接某公众网代理(这里称为proxy2),然后通过这个Socket连接发送GET指令给proxy2。这里对sproxy(以后称proxy1)有一个要求,即必须支持CONNECT,也就是说必须是一个HTTP Tunnel,或者说支持HTTPS。
  由于一开始设置上的问题,我本以为返回HTTP 1.0的CCProxy不支持这样的方式,因为据说HTTP 1.0就不能支持HTTPS,连接一次,发送了数据一定会断开(一开始的测试中的确如此)。后来查阅资料才知道,HTTP 1.0只是默认不使用Connect: Keep-Alive的参数,事实上也是支持的,只要置上这个参数即可。
  但实际使用中,发现不加上这个参数也是可以的(我对proxy1没有使用这个参数,可能是.net下的Socket类自动加上了)。

  经过连续数天的测试,一直不能通过,问题集中在,给proxy2发送一次CONNECT命令,Socket就会断开,无法收到数据。这是典型的不支持HTTP Tunnel的表现,但是我一直怀疑是CCproxy和学校代理服务器的问题,没有发现根源所在。
  因为我们访问sproxy需要用户名密码验证,我不得已才使用了CCProxy。后来,我又安装了一个代理服务器软件,发现其中只要使用“代理嵌套”(CCproxy叫作二级代理)的时候都需要HTTPS,我突然想到,会不会需要在CCproxy中设置?
  于是,在CCproxy的设置-高级-二级代理中把代理类型设为HTTPS,使用proxy1做上级代理,果然成功。事实上,直接连接学校代理应该也是可以的,只是我还不知道如何在代理请求中发送用户验证信息(需要将验证信息截断为多个数据包,不能直接发送)。

代码如下,调试通过并成功运行了十多天了:

后台一个类:

public string Get_Socket_Request_uip(string ip)
        {
 
            Encoding ASCII = Encoding.Default;
 
            //给Proxy2发送命令
            string Con = "CONNECT " + ip + " HTTP/1.1
Connection: Keep-Alive
 
";
            string Get = "HEAD http://www.RedHat.com/ HTTP/1.1
Host: www.RedHat.com
Pragma: no-cache
Connection: Close
 
";
            Byte[] ByteCon = ASCII.GetBytes(Con);
            Byte[] ByteGet = ASCII.GetBytes(Get);
            Byte[] RecvBytes = new Byte[256];
            Byte[] RecvBytes2 = new Byte[256]; 
            string strRetPage = null;
            string strRetCon = null;
 
            //建立Socket
            Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 
            //连接本地CCProxy
            IPAddress hostip = IPAddress.Parse("127.0.0.1");
            IPEndPoint ipend = new IPEndPoint(hostip, 808);
            //设置超时,超过了就证明这个代理无效
            s.SendTimeout = 20000;
            s.ReceiveTimeout = 20000;
 
            //这句话就是socket的Keep-Alive,我没有使用
            //s.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.KeepAlive, true);
            s.Connect(ipend);
 
            try
            {
                //发送CONNECT命令
                s.Send(ByteCon, ByteCon.Length, 0);
 
                Int32 bytes = s.Receive(RecvBytes, RecvBytes.Length, 0);
                strRetCon = strRetCon + ASCII.GetString(RecvBytes, 0, bytes);
 
                if (strRetCon.Contains("200"))
                {
                    s.Send(ByteGet, ByteGet.Length, 0);
 
                    Int32 bytes2 = s.Receive(RecvBytes2, RecvBytes2.Length, 0);
                    strRetPage = strRetPage + ASCII.GetString(RecvBytes2, 0, bytes2);
 
                    //本来这里会循环接收数据(本来发送的不是HEAD而是GET,取整个页面),后来发现有时候这会导致断线,就去掉了,只用HEAD命令取返回的HTTP Head
                    /*while (bytes2 > 0)
                    {
                        bytes2 = s.Receive(RecvBytes2, RecvBytes2.Length, 0);
                        strRetPage = strRetPage + ASCII.GetString(RecvBytes2, 0, bytes2);
                    }*/
                }
            }
            catch
            {
                break;
            }
            finally 
            {
               s.Shutdown(SocketShutdown.Both);
               s.Close();
            }
            return strRetPage;
        }

前台的调用方法(已经使用证则表达式在别处的网页中抓取到待检测的代理列表,在matches数组中):

public void TestProxy()
        {
            int r = 0;
 
            try
            {
                foreach (Match i in matches)
                {
                    string ip = i.Result("${ip}");
                    string port = i.Result("${port}");
                    try
                    {
                        //这里的hc是后台那个类,myhttpclient
                        string result = hc.Get_Socket_Request_uip(ip + ":" + port);
 
                        if (result.Contains("200 OK"))
                        {
                            checkedproxy[r] = ip + ":" + port;
                            r = r + 1;
                        }
                    }
                    catch (TimeoutException)
                    {
                        continue;
                    }
                    catch
                    {
                        continue;
                    }
                }
            }
            catch
            {
                break;
            }
        }

  WebService代码就不给出了,就是调用这个方法而已。
  
  大家应该可以看出,这里实际上通过了三层代理(CCproxy,proxy1,proxy2),所以,如果要通过n层代理,方法也是一样的,只要一层层的CONNECT下去就行,但要求前n-1层代理都要支持HTTP tunnel。
  另外,在实际部署中使用这个程序,需要在Web.config中的system.web项下添加一行

<httpruntime executionTimeout="600"/>

  否则超过10项的代理列表,几乎一定会超时的

Related posts:

  1. 宇瞻钢铁侠优盘(AH321,群联Phsion UP14 PS2232)量产经历和教训
  2. 秒速5厘米:飞向云端的初恋,随风飘落的初恋,你选哪一个?
  3. WordPress+WP-SuperCache之中文tag的Permalink问题全解析

2 Responses to “.Net下HTTP访问穿越多层代理的方法以及代理服务器的验证”

  1. zhl Says:

    四片叶子的三叶草,你好。
    你说的很好,不过我还有一些不明白。
    我用学校代理上网的时候,无法用一般软件验证代理IP,因为无法链接。
    不知道你有没有具体的解决这个问题,就是在学校代理上网,又能用代理软件验证搜集到的代理IP。

  2. WG Says:

    我叫WilliamGates,你可以叫我WG,四片叶子的三叶草是我blog的副标题,谢谢

    我的文章是说明,如何通过编写程序,让软件通过学校代理再访问搜集到的代理,进而验证这些代理是否有效
    我给出的是原理、思路和程序代码
    我不是十分清楚你的问题是什么,如果我没理解错的话,你希望通过一个现存的网上可以下载的程序,来完成通过学校代理访问公网代理并且验证的功能?
    三件套就是这样一个功能,CCProxy+SocksCap+MultiProxy,曾经是南京大学上网的必备工具,你可以去了解一下这三个软件
    原理还是一样,CCProxy用来将学校的代理生成为本地的代理,SocksCap让MultiProxy能够使用CCproxy提供的代理,MultiProxy用来验证并且使用那些公众网的代理
    如果你只是要验证,据我所知,一些代理验证软件似乎可以设置代理,例如花刺代理,不过那是多年前的事情了,现在这软件还存不存在我都不知道了
    原理也都是一样,只是因为MProxy不支持设置代理才需要三件套的

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">


LilyStudio & WordPress & N.Design Studio
Entries RSS Comments RSS Log in