構造.NET環境下的網頁下載器 (1)
2024-07-10 13:00:29
供稿:網友
一.前言:
微軟的.net框架為我們進行網絡編程提供了以下兩個名字空間:system.net以及system.net.sockets。通過合理運用其中的類和方法,我們可以很容易地編寫出各種網絡應用程序。這種網絡應用程序既可以是基于流套接字的,也可以是基于數據報套接字的。而基于流套接字的通訊中采用最廣泛的協議就是tcp協議,基于數據報套接字的通訊中采用最廣泛的自然就是udp協議了。下面我將重點向大家介紹.net網絡編程中的一些類:dns類、iphostentry類、ipendpoint類以及socket類,并會在最后給出一個網頁下載器的實例以加深讀者對.net網絡編程的理解。
二.網絡編程類介紹:
dns 類:
向使用 tcp/ip internet 服務的應用程序提供域名服務。其resolve()方法查詢dns服務器以將用戶友好的域名(如"www.google.com")映射到數字形式的 internet 地址(如 192.168.1.1)。resolve()方法返回一個iphostenty實例,該實例包含所請求名稱的地址和別名的列表。大多數情況下,可以使用 addresslist 數組中返回的第一個地址。
resolve()方法的函數原型如下:
public static iphostentry resolve(string hostname);
下面的代碼獲取一個 ipaddress 實例,該實例包含服務器 www.google.com 的ip地址:
iphostentry iphostinfo = dns.resolve("www.google.com");
ipaddress ipaddress = iphostinfo.addresslist[0];
不過在dns類中,除了通過resolve()方法,你還可以通過gethostbyaddress()方法以及gethostbyname()方法來得到相應的iphostentry實例,函數原型如下:
public static iphostentry gethostbyaddress(string ipaddress);
public static iphostentry gethostbyname(string hostname);
下面的代碼顯示了如何分別運用以上兩種方法獲得包含服務器www.google.com的相關信息的iphostentry實例:
iphostentry hostinfo=dns.gethostbyaddress("192.168.1.1");
iphostentry hostinfo=dns.gethostbyname("www.google.com");
在使用以上方法時,你將可能需要處理以下幾種異常:
socketexception異常:訪問socket時操作系統發生錯誤引發
argumentnullexception異常:參數為空引用引發
objectdisposedexception異常:socket已經關閉引發
以上,我向大家簡要地介紹了dns類中一些方法以及其用法,并列舉出了可能出現的異常,下面就讓我們轉到和dns類密切相關的iphostentry類。
iphostentry類:
該類的實例對象中包含了internet主機的地址相關信息。此類型的所有公共靜態成員對多線程操作而言都是安全的,但不保證任何實例成員是線程安全的。其中主要的一些屬性有:addresslist屬性、aliases屬性以及hostname屬性。
addresslist屬性和aliases屬性的作用分別是獲取或設置與主機關聯的ip地址列表以及獲取或設置與主機關聯的別名列表。其中addresslist屬性值是一個ipaddress類型的數組,包含解析為aliases屬性中包含的主機名的ip地址;aliases屬性值是一組字符串,包含解析為addresslist 屬性中的ip地址的dns名。而hostname屬性比較好理解,它包含了服務器的主要主機名,這光從名稱上就可以知道了。如果服務器的dns項定義了附加別名,則可在aliases屬性中使用這些別名。
下面的代碼列出了服務器www.google.com的相關別名列表以及ip地址列表的長度并將所有的ip地址列出:
iphostentry iphost = dns.resolve("www.google.com/");
string[] aliases = iphost.aliases;
console.writeline(aliases.length);
ipaddress[] addr = iphost.addresslist;
console.writeline(addr.length);
for(int i= 0; i < addr.length ; i++)
{
console.writeline(addr[i]);
}
介紹完iphostentry類,我們能獲得了所要連接的主機的相關ip地址以及別名列表,但是真正要和主機取得連接還需要一個很重要的類-ipendpoint類。
ipendpoint類:
在internet中,tcp/ip使用一個網絡地址和一個服務端口號來唯一標識設備。網絡地址標識網絡上的特定設備;端口號標識要連接到的該設備上的特定服務。網絡地址和服務端口的組合稱為終結點,在.net框架中正是由endpoint類表示這個終結點,它提供表示網絡資源或服務的抽象,用以標志網絡地址等信息。.net同時也為每個受支持的地址族定義了 endpoint的子代;對于ip地址族,該類為ipendpoint。ipendpoint類包含應用程序連接到主機上的服務所需的主機和端口信息,通過組合服務的主機ip地址和端口號,ipendpoint類形成到服務的連接點。
在ipendpoint類中有兩個很有用的構造函數:
public ipendpoint(long, int);
public ipendpoint(ipaddress, int);
它們的作用就是用指定的地址和端口號初始化 ipendpoint 類的新實例。該類中的屬性有:address屬性、addressfamily屬性以及port屬性,這些屬性相對比較容易理解,這里就不作多介紹。下面的代碼顯示了如何取得服務器www.google.com的終結點:
iphostentry iphost = dns.resolve("www.google.com");
ipaddress[] addr = iphost.addresslist;
ipendpoint ep = new ipendpoint(addr[0],80);
這樣,我們已經了解了和主機取得連接的一些必要的基本類,有了這些知識,我們就可以運用下面的socket類真正地和主機取得連接并進行通訊了。
socket類:
socket類是包含在system.net.sockets名字空間中的一個非常重要的類。一個socket實例包含了一個本地以及一個遠程的終結點,就像上面介紹的那樣,該終結點包含了該socket實例的一些相關信息。
需要知道的是socket 類支持兩種基本模式:同步和異步。其區別在于:在同步模式中,對執行網絡操作的函數(如send和receive)的調用一直等到操作完成后才將控制返回給調用程序。在異步模式中,這些調用立即返回。
下面我們重點討論同步模式的socket編程。首先,同步模式的socket編程的基本過程如下:
1.創建一個socket實例對象。
2.將上述實例對象連接到一個具體的終結點(endpoint)。
3.連接完畢,就可以和服務器進行通訊:接收并發送信息。
4.通訊完畢,用shutdown()方法來禁用socket。
5.最后用close()方法來關閉socket。
知道了以上基本過程,我們就開始進一步實現連接并通訊了。在使用之前,你需要首先創建socket對象的實例,這可以通過socket類的構造方法來實現:
public socket(addressfamily addressfamily,sockettype
sockettype,protocoltype protocoltype);
其中,addressfamily 參數指定socket使用的尋址方案,比如addressfamily.internetwork表明為ip版本4的地址;sockettype參數指定socket的類型,比如sockettype.stream表明連接是基于流套接字的,而sockettype.dgram表示連接是基于數據報套接字的。protocoltype參數指定socket使用的協議,比如protocoltype.tcp表明連接協議是運用tcp協議的,而protocol.udp則表明連接協議是運用udp協議的。
在創建了socket實例后,我們就可以通過一個遠程主機的終結點和它取得連接,運用的方法就是connect()方法:
public connect (endpoint ep);
該方法只可以被運用在客戶端。進行連接后,我們可以運用套接字的connected屬性來驗證連接是否成功。如果返回的值為true,則表示連接成功,否則就是失敗。下面的代碼就顯示了如何創建socket實例并通過終結點與之取得連接的過程:
iphostentry iphost = dns.resolve("http://www.google.com/");
string []aliases = iphost.aliases;
ipaddress[] addr = iphost.addresslist;
endpoint ep = new ipendpoint(addr[0],80);
socket sock = newsocket(addressfamily.internetwork,
sockettype.stream,protocoltype.tcp);
sock.connect(ep);
if(sock.connected)
console.writeline("ok");
一旦連接成功,我們就可以運用send()和receive()方法來進行通訊。
send()方法的函數原型如下:
public int send (byte[] buffer, int size, socketflags flags);
其中,參數buffer包含了要發送的數據,參數size表示要發送數據的大小,而參數flags則可以是以下一些值:socketflags.none、socketflags.dontroute、socketflags.outofbnd。
該方法返回的是一個system.int32類型的值,它表明了已發送數據的大小。同時,該方法還有以下幾種已被重載了的函數實現:
public int send (byte[] buffer);
public int send (byte[] buffer, socketflags flags);
public int send (byte[] buffer,int offset, int size,
socketflags flags);
介紹完send()方法,下面是receive()方法,其函數原型如下:
public int receive(byte[] buffer, int size, socketflags flags);
其中的參數和send()方法的參數類似,在這里就不再贅述。
同樣,該方法還有以下一些已被重載了的函數實現:
public int receive (byte[] buffer);
public int receive (byte[] buffer, socketflags flags);
public int receive (byte[] buffer,int offset, int size,
socketflags flags);
在通訊完成后,我們就通過shutdown()方法來禁用socket,函數原型如下:
public void shutdown(socketshutdown how);
其中的參數how表明了禁用的類型,soketshutdown.send表明關閉用于發送的套接字;soketshutdown.receive表明關閉用于接收的套接字;而soketshutdown.both則表明發送和接收的套接字同時被關閉。
應該注意的是在調用close()方法以前必須調用shutdown()方法以確保在socket關閉之前已發送或接收所有掛起的數據。一旦shutdown()調用完畢,就調用close()方法來關閉socket,其函數原型如下:
public void close();
該方法強制關閉一個socket連接并釋放所有托管資源和非托管資源。該方法在內部其實是調用了方法dispose(),該函數是受保護類型的,其函數原型如下:
protected virtual void dispose(bool disposing);
其中,參數disposing為true或是false,如果為true,則同時釋放托管資源和非托管資源;如果為false,則僅釋放非托管資源。因為close()方法調用dispose()方法時的參數是true,所以它釋放了所有托管資源和非托管資源。
這樣,一個socket從創建到連接到通訊最后的關閉的過程就完成了。雖然整個過程比較復雜,但相對以前在sdk或是其他環境下進行socket編程,這個過程就顯得相當輕松了。