C#網絡編程

來源:互聯網
上載者:User

C#網路編程概述
 
微軟下一代互連網開發工具VS.Net已於三月份在全國範圍推出,其中的一門新興語言C#正被越來越多的開發人員所接受並運用。
  C#作為一門集眾家之長的語言,在各個方面尤其是網路編程方面有著很大的優勢。本文就向大家介紹一下用C#進行網路編程的一些基本知識和方法。
  微軟的.Net架構為我們進行網路編程提供了以下兩個名字空間:System.Net以及System.Net.Sockets。通過合理運用其中的類和方法,我們可以很容易地編寫出各種網路應用程式。這種網路應用程式既可以是基於流通訊端的,也可以是基於資料通訊端的。而基於流通訊端的通訊中採用最廣泛的協議就是TCP協議,基於資料通訊端的通訊中採用最廣泛的自然就是UDP協議了。

  下面我重點向大家介紹C#網路編程中的一些類:Dns類、IPHostEntry類、IPEndPoint類以及Socket類,最後我會給出相應的執行個體以加深讀者的理解。
Dns 類:

  向使用 TCP/IP 網際網路服務的應用程式提供網域名稱服務 (DNS)。其Resolve()方法查詢DNS伺服器以將方便使用的網域名稱(如"www.google.com")映射到數字形式的 網際網路位址(如 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 = new Socket(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編程,這個過程就顯得相當輕鬆了。

最後,我就綜合以上C#網路編程的一些知識,向大家展示一個很好的執行個體。該執行個體是一個運用Socket的基於同步模式的用戶端應用程式,它首先通過解析伺服器的IP地址建立一個終結點,同時建立一個基於流通訊端的Socket串連,其運用的協議是TCP協議。通過該Socket就可以發送擷取網頁的命令,再通過該Socket獲得伺服器上預設的網頁,最後通過檔案流將獲得的資料寫入本機檔案。這樣就完成了網頁的下載工作了,程式啟動並執行效果如下所示:

原始碼如下:(其中主要的函數為DoSocketGet())

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.IO;

namespace SocketSample
{

///
/// Form1 的摘要說明。
///

public class Form1 : System.Windows.Forms.Form
{

private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Button Download;
private System.Windows.Forms.TextBox ServerAddress;
private System.Windows.Forms.TextBox Filename;

///
/// 必需的設計器變數。
///

private System.ComponentModel.Container components = null;

public Form1()
{
//
// Windows 表單設計器支援所必需的
//

InitializeComponent();

//
// TODO: 在 InitializeComponent 調用後添加任何建構函式代碼
//

}

///
/// 清理所有正在使用的資源。
///
protected override void Dispose( bool disposing )
{

if( disposing )

{
if (components != null)
{

components.Dispose();

}

}

base.Dispose( disposing );

}

#region Windows Form Designer generated code

///

/// 設計器支援所需的方法 - 不要使用代碼編輯器修改

/// 此方法的內容。

///

private void InitializeComponent()

{

this.label1 = new System.Windows.Forms.Label();

this.label2 = new System.Windows.Forms.Label();

this.Download = new System.Windows.Forms.Button();

this.ServerAddress = new System.Windows.Forms.TextBox();

this.Filename = new System.Windows.Forms.TextBox();

this.SuspendLayout();

//

// label1

//

this.label1.Location = new System.Drawing.Point(16, 24);

this.label1.Name = "label1";

this.label1.Size = new System.Drawing.Size(80, 23);

this.label1.TabIndex = 0;

this.label1.Text = "伺服器位址:";

this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleRight;

//

// label2

//

this.label2.Location = new System.Drawing.Point(16, 64);

this.label2.Name = "label2";

this.label2.Size = new System.Drawing.Size(80, 23);

this.label2.TabIndex = 1;

this.label2.Text = "本地檔案名稱:";

this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleRight;

//

// Download

//

this.Download.Location = new System.Drawing.Point(288, 24);

this.Download.Name = "Download";

this.Download.TabIndex = 2;

this.Download.Text = "開始下載";

this.Download.Click += new System.EventHandler(this.Download_Click);

//

// ServerAddress

//

this.ServerAddress.Location = new System.Drawing.Point(96, 24);

this.ServerAddress.Name = "ServerAddress";

this.ServerAddress.Size = new System.Drawing.Size(176, 21);

this.ServerAddress.TabIndex = 3;

this.ServerAddress.Text = "";

//

// Filename

//

this.Filename.Location = new System.Drawing.Point(96, 64);

this.Filename.Name = "Filename";

this.Filename.Size = new System.Drawing.Size(176, 21);

this.Filename.TabIndex = 4;

this.Filename.Text = "";

//

// Form1

//

this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);

this.ClientSize = new System.Drawing.Size(376, 117);

this.Controls.AddRange(new System.Windows.Forms.Control[] {

this.Filename,

this.ServerAddress,

this.Download,

this.label2,

this.label1});

this.Name = "Form1";

this.Text = "網頁下載器";

this.ResumeLayout(false);

}

#endregion

///

/// 應用程式的主進入點。

///

[STAThread]

static void Main()

{

Application.Run(new Form1());

}

private string DoSocketGet(string server)

{

//定義一些必要的變數以及一條要發送到伺服器的字串

Encoding ASCII = Encoding.ASCII;

string Get = "GET / HTTP/1.1/r/nHost: " + server +

"/r/nConnection: Close/r/n/r/n";

Byte[] ByteGet = ASCII.GetBytes(Get);

Byte[] RecvBytes = new Byte[256];

String strRetPage = null;

//擷取伺服器相關的IP地址清單,其中第一項即為我們所需的

IPAddress hostadd = Dns.Resolve(server).AddressList[0];

//根據獲得的伺服器的IP地址建立一個終結點,連接埠為預設的80

IPEndPoint EPhost = new IPEndPoint(hostadd, 80);

//建立一個Socket執行個體

Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream,

ProtocolType.Tcp );

try

{

//用上面所取得的終結點串連到伺服器

s.Connect(EPhost);

}

catch(Exception se)

{

MessageBox.Show("串連錯誤:"+se.Message,"提示資訊",

MessageBoxButtons.RetryCancel,MessageBoxIcon.Information);

}

if (!s.Connected)

{

strRetPage = "不能串連到伺服器!";

return strRetPage;

}

try

{

//向伺服器發送GET命令

s.Send(ByteGet, ByteGet.Length, SocketFlags.None);

}

catch(Exception ce)

{

MessageBox.Show("發送錯誤:"+ce.Message,"提示資訊",

MessageBoxButtons.RetryCancel,MessageBoxIcon.Information);

}

//接收頁面資料,直到所有位元組接收完畢

Int32 bytes = s.Receive(RecvBytes, RecvBytes.Length, 0);

strRetPage = "以下是在伺服器" + server + "上的預設網頁:/r/n";

strRetPage = strRetPage + ASCII.GetString(RecvBytes, 0, bytes);

while (bytes > 0)

{

bytes = s.Receive(RecvBytes, RecvBytes.Length, SocketFlags.None);

strRetPage = strRetPage + ASCII.GetString(RecvBytes, 0, bytes);

}

//禁用並關閉Socket執行個體

s.Shutdown(SocketShutdown.Both);

s.Close();

return strRetPage;

}

private void Download_Click(object sender, System.EventArgs e)

{

//將所讀取的字串轉換為位元組數組

byte[] content=Encoding.ASCII.GetBytes(DoSocketGet(ServerAddress.Text));

try

{

//建立檔案流對象執行個體

FileStream fs=new FileStream(Filename.Text,FileMode.OpenOrCreate,FileAccess.ReadWrite);

//寫入檔案

fs.Write(content,0,content.Length);

}

catch(Exception fe)

{

MessageBox.Show("檔案建立/寫入錯誤:"+fe.Message,"提示資訊",MessageBoxButtons.RetryCancel,MessageBoxIcon.Information);

}

}

}

}

以上程式在Windows 2000伺服器版、Visual Studio.Net中文正式版下調試通過

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.