天天看點

Net下HTTP通路穿越多層代理的方法以及代理伺服器的驗證

我們學校的網絡環境是,所有的出校通路均須通過代理伺服器(我們叫作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

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

    }

    }

    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項的代理清單,幾乎一定會逾時的