Visual C#實(shí)現(xiàn)HTTP代理服務(wù)程序

2010-08-28 10:50:44來(lái)源:西部e網(wǎng)作者:

網(wǎng)絡(luò)代理程序的種類非常多,根據(jù)代理服務(wù)程序代理的協(xié)議不同,分成HTTP代理服務(wù)程序、FTP代理服務(wù)程序等,運(yùn)行代理服務(wù)程序的服務(wù)器也就稱為HTTP代理服務(wù)器和FTP代理服務(wù)器。在本節(jié)中介紹的Web代理服務(wù)程序代理的就是HTTP協(xié)議。

  一.網(wǎng)絡(luò)代理的類型及實(shí)現(xiàn)原理:

  網(wǎng)絡(luò)代理服務(wù)根據(jù)工作層次,一般可分為應(yīng)用層代理、傳輸層代理和SOCKS代理。應(yīng)用層代理是工作在TCP/IP參考模型的應(yīng)用層之上,它支持對(duì)應(yīng)用層協(xié)議(如HTTP,F(xiàn)TP)的代理。它提供的控制最多,但是不靈活,必須要有相應(yīng)的協(xié)議支持。如果協(xié)議不支持代理(如SMTP和POP),那就只能在應(yīng)用層以下代理,也即傳輸層代理。傳輸層代理直接與TCP層交互,更加靈活。要求代理服務(wù)器具有部分真正服務(wù)器的功能:監(jiān)聽特定TCP或UDP端口,接收客戶端的請(qǐng)求同時(shí)向客戶端發(fā)出相應(yīng)的響應(yīng)。另一種代理需要改變客戶端的IP棧,即SOCKS代理。它是可用的最強(qiáng)大、最靈活的代理標(biāo)準(zhǔn)協(xié)議。SOCK V4允許代理服務(wù)器內(nèi)部的客戶端完全地連接到外部的服務(wù)器,SOCK V5增加了對(duì)客戶端的授權(quán)和認(rèn)證,因此它是一種安全性較高的代理。本節(jié)后面介紹的代理是一種應(yīng)用層上面的代理,所代理的協(xié)議是HTTP,也就是經(jīng)常見(jiàn)到的Web代理。

  網(wǎng)絡(luò)代理就是一個(gè)連接客戶端(設(shè)定需要代理的計(jì)算機(jī))和服務(wù)器端(需要訪問(wèn)資源的服務(wù)器)的橋。要實(shí)現(xiàn)這種橋,網(wǎng)絡(luò)代理就必須滿足下列條件,其實(shí)也是代理服務(wù)的運(yùn)行的流程:

 。1). 能夠接收并解析客戶端的請(qǐng)求。

 。2). 創(chuàng)建到服務(wù)器的新連接,并根據(jù)轉(zhuǎn)發(fā)客戶端的請(qǐng)求信息。

  (3). 接收服務(wù)器反饋的信息。

 。4). 能夠發(fā)出或解釋服務(wù)器的響應(yīng)并將該響應(yīng)傳回給客戶端。

  圖01是網(wǎng)絡(luò)代理服務(wù)的一個(gè)典型模型圖:

\
圖01:代理服務(wù)的模型

  二.Visual C#實(shí)現(xiàn)Web代理服務(wù)程序

  Web代理服務(wù)是代理服務(wù)中最常用的一種代理服務(wù),按照代理服務(wù)的層次,它屬于應(yīng)用層代理,是對(duì)TCP/IP參考模型中的應(yīng)用層的HTTP協(xié)議的代理。

  Web代理服務(wù)也是代理服務(wù)中的一種,所以它也要滿足代理服務(wù)的基本條件。在下面介紹的代理服務(wù)程序中,是按照下列的順序來(lái)實(shí)現(xiàn)其功能的。

 。1). 代理服務(wù)器程序偵聽端口,接收客戶端瀏覽器發(fā)送來(lái)的Web請(qǐng)求信息。

 。2). 代理服務(wù)器程序接收到客戶端Web請(qǐng)求信息后,解析出Web服務(wù)器的地址,并創(chuàng)建一個(gè)Socket實(shí)例,并以此實(shí)例連接Web服務(wù)器上。

  (3). 通過(guò)創(chuàng)建的Socket傳送客戶端的Web請(qǐng)求數(shù)據(jù)包到Web服務(wù)器的80端口。

 。4). 代理服務(wù)器程序接收Web服務(wù)器返回頁(yè)面數(shù)據(jù)。

 。5). 代理服務(wù)器程序把接收來(lái)的數(shù)據(jù)傳送到客戶端,實(shí)現(xiàn)Web代理。

  由于客戶端的對(duì)一個(gè)地址的瀏覽,要傳送很多的Web請(qǐng)求信息,為了更快、更準(zhǔn)確的處理這些信息,Web代理服務(wù)程序采用了多線程來(lái)處理每一個(gè)Web請(qǐng)求。細(xì)心的讀者可能會(huì)發(fā)現(xiàn),處理每一個(gè)客戶端的Web請(qǐng)求信息,代理服務(wù)器軟件都要使用二個(gè)Socket,一個(gè)是用來(lái)接收/傳送客戶機(jī)的信息,一個(gè)是和Web服務(wù)器進(jìn)行交流。為了區(qū)分這二個(gè)Socket,我們把他們都命名,和服務(wù)器對(duì)話的Socket,稱為服務(wù)Socket;和客戶端機(jī)器對(duì)話的Scoket,稱為客戶Socket。

  下面就開始Web代理服務(wù)程序的編寫工作。

  這個(gè)示例主要包含三個(gè)部分內(nèi)容:

  ·創(chuàng)建一個(gè)Web代理類。

  ·Web代理服務(wù)的類的實(shí)例化。

  ·如何通過(guò)這個(gè)Web代理類的實(shí)例實(shí)現(xiàn)Web代理服務(wù)。

  下面就是第一部分的具體的實(shí)現(xiàn)步驟。

  (一).創(chuàng)建一個(gè)Web代理類

  以下是具體的操作步驟如下:

  1. 首先啟動(dòng)Visual Studio .Net,依次選擇"文件"、"新建"、"項(xiàng)目"菜單后,在彈出"新建項(xiàng)目"對(duì)話框中將"項(xiàng)目類型"設(shè)置為"Visual C#項(xiàng)目",將"模板"設(shè)置為"Windows應(yīng)用程序",在"名稱"文本框中輸入"WebProxy",在"位置"的文本框中輸入"E:\VS.NET項(xiàng)目",然后單擊"確定"按鈕。這樣在"E:\VS.NET項(xiàng)目"目錄中就創(chuàng)建了一個(gè)新名稱為"WebProxy"文件夾,里面存放的就是"WebProxy"的項(xiàng)目文件。

  2. 選擇菜單【項(xiàng)目】|【添加類】,彈出【添加新項(xiàng)】對(duì)話框

  3. 將【模板】設(shè)置【類】

  4. 在【名稱】文本框中輸入【Proxy】,單擊【打開】按鈕,具體如圖02所示。

\
圖02:Web代理項(xiàng)目中【添加新項(xiàng)】對(duì)話框

  5. 在【解決方案資源管理器】窗口中,雙擊Proxy.cs文件,進(jìn)入Proxy.cs文件的編輯界面。

  6. 在Proxy.cs源文件的開頭,添加下列代碼,下列代碼是導(dǎo)入Proxy.cs中要使用到的命名空間:

using System ;
using System.Net ;
using System.Net.Sockets ;
using System.Text ;
using System.IO ;

 

7. 用下列構(gòu)造函數(shù)替代默認(rèn)的構(gòu)造函數(shù)。下面的代碼是在Proxy類中創(chuàng)建一個(gè)構(gòu)造函數(shù)。 Proxy類只有一個(gè)構(gòu)造函數(shù),并且這個(gè)構(gòu)造函數(shù)只有一個(gè)參數(shù),這個(gè)參數(shù)是Socket對(duì)象,它主要用來(lái)和客戶端進(jìn)行數(shù)據(jù)交換,是一個(gè)客戶Socket.。

public Proxy ( Socket socket )
{
 //
 // TODO: 在此處添加構(gòu)造函數(shù)邏輯
 //
 this.clientSocket = socket ;
}

  8. 創(chuàng)建Proxy類中的Run方法,Run方法是Proxy類中唯一的方法。其功能是從客戶端接收HTTP請(qǐng)求,并傳送到Web服務(wù)器,然后從Web服務(wù)器接收反饋來(lái)的數(shù)據(jù),并傳送到客戶端。為了實(shí)現(xiàn)這二個(gè)不同方面的數(shù)據(jù)傳送,Run方法中是通過(guò)二個(gè)Socket實(shí)例來(lái)實(shí)現(xiàn)的。在編寫Run方法的時(shí)候,要注意下面幾點(diǎn):

 。1). 由于HTTP是TCP/IP參考模型中的應(yīng)用層協(xié)議,它建立于TCP協(xié)議之上,所以創(chuàng)建的Socket實(shí)例使用的協(xié)議類型應(yīng)該為TCP協(xié)議。下面代碼是創(chuàng)建可以傳送HTTP請(qǐng)求命令到Web服務(wù)器和接收來(lái)自Web服務(wù)器反饋來(lái)信息的Socket實(shí)例:

Socket IPsocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Tcp);

 。2). 另外一個(gè)Socket是在代理服務(wù)程序偵聽端口號(hào),接收掛起的連接請(qǐng)求時(shí)候得到的,以此Socket為參數(shù),利用Proxy類中的構(gòu)造函數(shù),來(lái)創(chuàng)建一個(gè)Proxy實(shí)例的。此Socket實(shí)現(xiàn)從客戶端接收HTTP請(qǐng)求信息,并傳送數(shù)據(jù)到客戶端。

  Socket創(chuàng)建和使用是實(shí)現(xiàn)Web代理軟件的關(guān)鍵,具體實(shí)現(xiàn)方法是在構(gòu)造函數(shù)代碼后面,輸入下列代碼,創(chuàng)建Proxy類的Run方法:

public void Run ( )
{
 string clientmessage = " " ;
 //存放來(lái)自客戶端的HTTP請(qǐng)求字符串
 string URL = " " ;
 //存放解析出地址請(qǐng)求信息
 int bytes = ReadMessage ( read , ref clientSocket , ref clientmessage ) ;
 if ( bytes == 0 )
 {
  return ;
 }

 int index1 = clientmessage.IndexOf ( ' ' ) ;
 int index2 = clientmessage.IndexOf ( ' ' , index1 + 1 ) ;
 if ( ( index1 == -1 ) || ( index2 == -1 ) )
 {
  throw new IOException ( ) ;
 }
 string part1 = clientmessage.Substring ( index1 + 1 , index2 - index1 ) ;
 int index3 = part1.IndexOf ( '/' , index1 + 8 ) ;
 int index4 = part1.IndexOf ( ' ' , index1 + 8 ) ;
 int index5 = index4 - index3 ;
 URL = part1.Substring ( index1 + 4 , ( part1.Length - index5 ) - 8 ) ;

 try
 {
  IPHostEntry IPHost = Dns.Resolve ( URL ) ;
  Console.WriteLine ( "遠(yuǎn)程主機(jī)名: " + IPHost.HostName ) ;
  string [] aliases = IPHost.Aliases ;
  IPAddress[] address = IPHost.AddressList ;
  Console.WriteLine ( "Web服務(wù)器IP地址:" + address[0] ) ;
  //解析出要訪問(wèn)的服務(wù)器地址
  IPEndPoint ipEndpoint = new IPEndPoint ( address[0] , 80 ) ;
  Socket IPsocket = new Socket ( AddressFamily.InterNetwork , SocketType.Stream , ProtocolType.Tcp ) ;
  //創(chuàng)建連接Web服務(wù)器端的Socket對(duì)象
  IPsocket.Connect ( ipEndpoint ) ;
  //Socket連Web接服務(wù)器
  if ( IPsocket.Connected )
   Console.WriteLine ( "Socket 正確連接!" ) ;
  string GET = clientmessage ;
  Byte[] ByteGet = ASCII.GetBytes ( GET ) ;
  IPsocket.Send ( ByteGet , ByteGet.Length , 0 ) ;
  //代理訪問(wèn)軟件對(duì)服務(wù)器端傳送HTTP請(qǐng)求命令
  Int32 rBytes = IPsocket.Receive ( RecvBytes , RecvBytes.Length , 0 ) ;
  //代理訪問(wèn)軟件接收來(lái)自Web服務(wù)器端的反饋信息
  Console.WriteLine ( "接收字節(jié)數(shù):" + rBytes.ToString ( ) ) ;
  String strRetPage = null ;
  strRetPage = strRetPage + ASCII.GetString ( RecvBytes , 0 , rBytes ) ;
  while ( rBytes > 0 )
  {
   rBytes = IPsocket.Receive ( RecvBytes , RecvBytes.Length , 0 ) ;
   strRetPage = strRetPage + ASCII.GetString ( RecvBytes , 0 , rBytes ) ;
  }
  IPsocket.Shutdown ( SocketShutdown.Both ) ;
  IPsocket.Close ( ) ;
  SendMessage ( clientSocket , strRetPage ) ;
  //代理服務(wù)軟件往客戶端傳送接收到的信息
 }
 catch ( Exception exc2 )
 {
  Console.WriteLine ( exc2.ToString ( ) ) ;
 }
}
//接收客戶端的HTTP請(qǐng)求數(shù)據(jù)
private int ReadMessage ( byte [ ] ByteArray , ref Socket s , ref String clientmessage )
{
 int bytes = s.Receive ( ByteArray , 1024 , 0 ) ;
 string messagefromclient = Encoding.ASCII.GetString ( ByteArray ) ;
 clientmessage = ( String )messagefromclient ;
 return bytes ;
}
//傳送從Web服務(wù)器反饋的數(shù)據(jù)到客戶端
private void SendMessage ( Socket s , string message )
{
 Buffer = new Byte[message.Length + 1] ;
 int length = ASCII.GetBytes ( message , 0 , message.Length , Buffer , 0 ) ;
 Console.WriteLine ( "傳送字節(jié)數(shù):" + length.ToString ( ) ) ;
 s.Send ( Buffer , length , 0 ) ;
}

  9. 在定義Proxy類代碼區(qū)中加入下列代碼,下列代碼是定義Proxy類中的使用的一些變量,這些變量主要是在后面的定義Run方法中使用。

Socket clientSocket ;
Byte[] read = new byte[1024] ;
//定義一個(gè)空間,存儲(chǔ)來(lái)自客戶端請(qǐng)求數(shù)據(jù)包
Byte [] Buffer = null ;
Encoding ASCII = Encoding.ASCII ;
//設(shè)定編碼
Byte[] RecvBytes = new Byte[4096] ;
//定義一個(gè)空間,存儲(chǔ)Web服務(wù)器返回的數(shù)據(jù)

  10. 至此,Proxy類的定義過(guò)程就完成了。把Proxy類實(shí)例化非常簡(jiǎn)單,和以前用的其他完全一樣,具體語(yǔ)法如下:

public Proxy ( Socket socket );

  參數(shù):socket為一個(gè)Scoket實(shí)例

  下面代碼是創(chuàng)建一個(gè)Proxy實(shí)例:

Proxy proxy = new Proxy ( socket ) ;

 

(二). 利用Proxy類,實(shí)現(xiàn)Web代理的具體示例:

  下面是利用上面創(chuàng)建的Proxy類,實(shí)現(xiàn)Web代理程序的具體實(shí)現(xiàn)步驟,Proxy類被定義在命名空間WebProxy中。

  1. 在Visual Studio .Net的代碼編輯器中打開Class1.cs文件,進(jìn)入Class1.cs的代碼編輯界面。

  2. 在Class1.cs源文件的開頭導(dǎo)入下列命名空間:

using System ;
using System.Net ;
using System.Net.Sockets ;
using System.Text ;
using System.IO ;
using System.Threading ;
using WebProxy ;
//其中命名空間WebProxy是Proxy類所處的位置,具體可以參閱Proxy.cs源文件
//中命名空間的定義。

  3. 在Main函數(shù)中添加下列代碼,下列代碼是利用Proxy類,來(lái)實(shí)現(xiàn)Web代理程序。

const int port = 8000 ;
//定義端口號(hào)
TcpListener tcplistener = new TcpListener ( port ) ;
Console.WriteLine ( "偵聽端口號(hào): " + port.ToString ( ) ) ;
tcplistener.Start ( ) ;
//偵聽端口號(hào)
while ( true )
{
 Socket socket = tcplistener.AcceptSocket ( ) ;
 //并獲取傳送和接收數(shù)據(jù)的Scoket實(shí)例
 Proxy proxy = new Proxy ( socket ) ;
 //Proxy類實(shí)例化
 Thread thread = new Thread ( new ThreadStart ( proxy.Run ) ) ;
 //創(chuàng)建線程
 thread.Start ( ) ;
 //啟動(dòng)線程
}

  保存上面的所有步驟,這樣一個(gè)簡(jiǎn)單Web代理程序就算是完成了。此Web代理程序偵聽的是8000端口號(hào)。

 。ㄈ.測(cè)試Web代碼程序:

  Web代理程序要通過(guò)二臺(tái)計(jì)算機(jī)才能夠?qū)崿F(xiàn)。其中的一臺(tái)計(jì)算機(jī)運(yùn)行Web代理程序,充當(dāng)Web代理服務(wù)器。另外一臺(tái)計(jì)算機(jī)充當(dāng)客戶機(jī),通過(guò)Web代理服務(wù)器來(lái)瀏覽網(wǎng)頁(yè)。在確定Web代理軟件運(yùn)行后,下面是對(duì)客戶機(jī)進(jìn)行必要的設(shè)置。

  1. 打開IE瀏覽器。

  2. 選擇【工具】|【Internet選項(xiàng)】,彈出【Internet選項(xiàng)】對(duì)話框。在此對(duì)話框中選擇【連接】頁(yè)面,單擊其中的【局域網(wǎng)設(shè)置】按鈕。彈出【局域網(wǎng)(LAN)設(shè)置】對(duì)話框。選擇【為L(zhǎng)AN使用代理服務(wù)器(X),(這些設(shè)置不會(huì)應(yīng)用于撥號(hào)和VPN連接)】多選框。并在其中的【地址】文本框中輸入代理服務(wù)器的IP地址,由于測(cè)試的代理服務(wù)器的IP地址為"10.138.198.213",所有也輸入此IP地址,在【端口】文本框中輸入"8000"。具體如圖03所示:

\
圖03:客戶端設(shè)定Web代理服務(wù)器對(duì)話框

  此時(shí)客戶端的設(shè)置就完成了,在確定IP地址為"10.138.198.213"的這臺(tái)計(jì)算機(jī)已經(jīng)運(yùn)行上面介紹的Web代理程序后。打開客戶端的IE瀏覽器,并輸入要瀏覽的網(wǎng)址,就可以通過(guò)Web代理服務(wù)器來(lái)瀏覽網(wǎng)頁(yè)了,圖04是Web代理服務(wù)程序在服務(wù)器端運(yùn)行時(shí)的界面。

\
圖04:Web代理服務(wù)程序在服務(wù)器端的運(yùn)行界面

  四.總結(jié)

  至此一個(gè)簡(jiǎn)單的Web代理服務(wù)軟件就算基本完成了,通過(guò)上面內(nèi)容的介紹可見(jiàn),雖然代理服務(wù)的實(shí)現(xiàn)原理相對(duì)簡(jiǎn)單,但具體實(shí)現(xiàn)其實(shí)還是很繁瑣的。網(wǎng)絡(luò)代理是一個(gè)內(nèi)容豐富,實(shí)現(xiàn)復(fù)雜的論題,本節(jié)介紹的代理服務(wù)軟件,無(wú)論在實(shí)現(xiàn)的協(xié)議種類,還是實(shí)現(xiàn)的功能,都只能算很小的一部分。希望各位能夠通過(guò)本文的介紹,結(jié)合其他相關(guān)的知識(shí),創(chuàng)造出功能更強(qiáng)大、安全性更高,使用更穩(wěn)定的網(wǎng)絡(luò)代理服務(wù)程序來(lái)。

關(guān)鍵詞:C#