1. 程式人生 > >.net網路程式設計之一:Socket程式設計

.net網路程式設計之一:Socket程式設計

 在.net下進行網路程式設計其實也相對比較簡單,因為在.net類庫已經提供了大量封裝好的類。在.net下網路程式設計比較底層的類是System.Net.Sockets.Socket類,這個類提供了豐富的方法和屬性,並且還提供了非同步資料傳輸支援。
對Socket類做一個簡單的介紹,它有如下常見方法:
public Socket Accept ():為新建連線建立新的 Socket。   
public void Bind (EndPoint localEP):使 Socket 與一個本地終結點相關聯。 
public void Close ():關閉 Socket 連線並釋放所有關聯的資源。注意這個方法有衝載方法。
public void Connect (EndPoint remoteEP):建立與遠端主機的連線。注意這個方法有過載方法。
public void Disconnect (bool reuseSocket):關閉套接字連線並是否允許重用套接字。 
public void Listen (int backlog):將 Socket 置於偵聽狀態。 
public int Receive (byte[] buffer):接收來自繫結的 Socket 的資料。注意這個方法有過載方法。
public int ReceiveFrom (byte[] buffer,ref EndPoint remoteEP):接收資料報並存儲源終結點。注意這個方法有過載方法。
public int Send (byte[] buffer):將資料傳送到連線的 Socket。注意這個方法有過載方法。
public void SendFile (string fileName):將檔案和可選資料非同步傳送到連線的 Socket。注意這個方法有過載方法。
public int SendTo (byte[] buffer,EndPoint remoteEP):將資料傳送到特定終結點。注意這個方法有過載方法。
public void Shutdown (SocketShutdown how):禁用某 Socket 上的傳送和接收。
說明:
因為在網路傳輸時傳輸的資料都是二進位制形式的(表現為位元組陣列),所以如果要傳輸類似於中文這樣的雙位元組字元就需要在傳輸之前用合適的編碼轉換成位元組陣列,然後接收方按照發送方的編碼將接收到位元組陣列轉換成字串。另外,注意接收資料的時候是先聲明瞭一個位元組陣列,然後將接收到的資料儲存到位元組陣列中,這個方法有個返回值表示實際接收了多少位元組資料。這是因為陣列是不可變的,假如聲明瞭一個1024位元組大小的陣列,而傳送方僅傳送1位元組資料,而接收方並不直到傳送方傳送的資料量把整個1024位元組當作傳送傳送的資料的話,最終會發生錯誤。
要實現一個伺服器端的面向連線的Socket用於接收客戶端的請求的話,有如下步驟:
(1)首先根據IP地址和埠號例項化一個Socket,注意埠要要大於1024並且不要使用特殊埠號,要大於1024的原因是1024以下的埠號已經被指派了,而1433、3306這樣的埠號已經被用作SQL Server和MySQL的預設埠號了,若指定為這些埠號容易發生衝突。
(3)接著呼叫Bind()方法進行繫結,然後再呼叫Listen()方法用於監聽,Listen()方法的引數用於指定監聽的佇列大小,也就是最多可容納的等待接受的傳入連線數。
(4)再呼叫Accept()方法,呼叫這個方法之後會是程式處於阻塞狀態,直至有客戶端連線為止。當有客戶端連線,這個方法將會返回一個新的Socket,使用這個Socket與客戶端進行通訊。
(5)使用Accept()方法返回的新Socket的Send()方法就可以向客戶端傳送資料了,還可以使用這個新Socket的Receive()接收客戶端的資料。
(6)最後終止與客戶端會話時,注意使用ShutDown()方法關閉Socket連線,並且使用Close()方法釋放所佔用的資源。

使用Socket類編寫客戶端的Socket程式步驟如下:
(1)首先指定遠端主機和埠號例項化Socket類,注意連線的埠號一定要與伺服器監聽的埠號一致。
(2)接著呼叫Connect()方法連線遠端主機。
(3)連線到遠端主機之後就可以呼叫Send()方法向伺服器傳送請求了,然後可以呼叫Receive()方法接收伺服器響應資料,注意如果是傳送的類似於中文這樣的雙位元組字串的話,還需要按照伺服器響應的字串編碼將位元組陣列轉換成字串。
最後終止與客戶端會話時,注意使用ShutDown()方法關閉Socket連線,並且使用Close()方法釋放所佔用的資源。
需要特別說明的是上面是建立連線式Socket(如建立TCP協議Socket)的一般步驟,如果是建立非連線式Socket(如UDP協議Socket)就不需要進行監聽埠了,直接使用ReceiveFrom()方法就能接收來自指定主機埠的資料,用SendTo()方法就能向直接主機埠傳送資料。
下面一個例子來演示Socket的程式設計,在這個例子裡伺服器端可以處理多個客戶端請求並響應,具體的做法是每接收到一個客戶端請求就新建一個執行緒來與客戶端通訊,主程式依然繼續監聽,在例子中新建的與客戶端通訊的Socket類僅處理客戶端的日期時間請求(為了演示,這裡做了簡化),根據客戶端的請求的不同傳送日期時間中響應資訊到客戶端,客戶端接收到資料之後在控制檯顯示。
伺服器端主要由兩個類組成:ServerSocket類和SocketThread類,ServerSocket類用於監聽客戶端連線請求,SocketThread類用於接收日期時間顯示請求並做出應答。
ServerSocket類程式碼如下(ServerSocket類和SocketThread類位於同一個類庫專案中):

SocketThread類程式碼如下:

客戶端請求的Socket程式碼如下(與前面兩個類位於不同的控制檯專案中):

上面的三個類位於兩個控制檯專案中ServerSocket類和SocketThread類位於同一個類庫專案中,其中ClientSocket類位於另一個控制檯專案中,編譯之後會生成兩個exe檔案,執行的時候首先執行伺服器端exe程式以便監聽,再執行客戶端exe程式。這個程式執行的效果如下:

客戶端與伺服器端Socket互動的效果

注意,在終止程式的時候首先在客戶端向伺服器傳送一個自定義的字串“<EOF>”,在客戶端使用命令列引數“bye”就是傳送這個字串的,這樣就會正確終止伺服器端響應Socket及正確關閉客戶端請求的Socket,否則有可能丟擲異常。
以上僅僅是演示瞭如何使用Socket進行程式設計,如果要傳輸檔案的話,可能需要更多類似於關閉Socket的自定義字串用以控制了。另外使用Socket來傳輸資料是比較高效的,但是傳輸控制相對來說要麻煩一點,針對網路中一些特殊場合的資料傳輸,可以使用特定的傳輸協議,在這個系列的後續文章中會繼續介紹網路程式設計的有關知識。程式程式碼稍後整理之後提供下載。

目前有關在.net下進行網路程式設計的文章較少,而偶爾有需要進行網路程式設計的專案需求,所以打算研究一下.net下的網路程式設計,抱著交流技術的態度,整理成文章與大家分享。不過在網路程式設計方面,本人經驗幾乎沒有經驗,如果大家發現有不當之處,望不吝賜教。

下一篇將會介紹TCP程式設計。

周公
2009-3-18 1:03