天天看點

用C#實作基于TCP協定的網絡通訊

TCP協定是一個基本的網絡協定,基本上所有的網絡服務都是基于TCP協定的,如HTTP,FTP等等,是以要了解網絡程式設計就必須了解基于TCP協定的程式設計。然而TCP協定是一個龐雜的體系,要徹底的弄清楚它的實作不是一天兩天的功夫,所幸的是在.Net framework環境下,我們不必要去追究TCP協定底層的實作,一樣可以很友善的編寫出基于TCP協定進行網絡通訊的程式。

要進行基于TCP協定的網絡通訊,首先必須建立同遠端主機的連接配接,連接配接位址通常包括兩部分——主機名和端口,如www.yesky.com:80中,www.yesky.com就是主機名,80指主機的80端口,當然,主機名也可以用IP位址代替。當連接配接建立之後,就可以使用這個連接配接去發送和接收資料包,TCP協定的作用就是保證這些資料包能到達終點并且能按照正确的順序組裝起來。

在.Net framework的類庫(Class Library)中,提供了兩個用于TCP網絡通訊的類,分别是TcpClient和TcpListener。由其英文意義顯而易見,TcpClient類是基于TCP協定的用戶端類,而TcpListener是伺服器端,監聽(Listen)用戶端傳來的連接配接請求。TcpClient類通過TCP協定與伺服器進行通訊并擷取資訊,它的内部封裝了一個Socket類的執行個體,這個Socket對象被用來使用TCP協定向伺服器請求和擷取資料。因為與遠端主機的互動是以資料流的形式出現的,是以傳輸的資料可以使用.Net framework中流處理技術讀寫。在我們下邊的例子中,你可以看到使.NetworkStream類操作資料流的方法。

在下面的例子中,我們将建立一個時間伺服器,包括伺服器端程式和用戶端程式。伺服器端監聽用戶端的連接配接請求,建立連接配接以後向用戶端發送目前的系統時間。

先運作伺服器端程式,下面截圖顯示了伺服器端程式運作的狀況:

然後運作用戶端程式,用戶端首先發送連接配接請求到伺服器端,伺服器端回應後發送目前時間到用戶端,這是用戶端程式的截圖:

發送完成後,伺服器端繼續等待下一次連接配接:

通過這個例子我們可以了解TcpClient類的基本用法,要使用這個類,必須使用System.Net.Socket命名空間,本例用到的三個命名空間如下:

using System;

using System.Net.Sockets;

using System.Text;//從位元組數組中擷取字元串時使用該命名空間中的類

首先讨論一下用戶端程式,開始我們必須初始化一個TcpClient類的執行個體:

TcpClient client = new TcpClient(hostName, portNum);

然後使用TcpClient類的GetStream()方法擷取資料流,并且用它初始化一.NetworkStream類的執行個體:

.NetworkStream ns = client.GetStream();

注意,當使用主機名和端口号初始化TcpClient類的執行個體時,直到跟伺服器建立了連接配接,這個執行個體才算真正建立,程式才能往下執行。如果因為網絡不通,伺服器不存在,伺服器端口未開放等等原因而不能連接配接,程式将抛出異常并且中斷執行。

建立資料流之後,我們可以使.NetworkStream類的Read()方法從流中讀取資料,使用Write()方法向流中寫入資料。讀取資料時,首先應該建立一個緩沖區,具體的說,就是建立一個byte型的數組用來存放從流中讀取的資料。Read()方法的原型描述如下:

public override int Read(in byte[] buffer,int offset,int size)

buffer是緩沖數組,offset是資料(位元組流)在緩沖數組中存放的開始位置,size是讀取的位元組數目,傳回值是讀取的位元組數。在本例中,簡單地使用該方法來讀取伺服器回報的資訊:

byte[] bytes = new byte[1024];//建立緩沖區

int bytesRead = ns.Read(bytes, 0, bytes.Length);//讀取位元組流

然後顯示到螢幕上:

Console.WriteLine(Encoding.ASCII.GetString(bytes,0,bytesRead));

最後不要忘記關閉連接配接:

client.Close();

下面是本例完整的程式清單:

using System.Text;

namespace TcpClientExample

{

public class TcpTimeClient

{

private const int portNum = 13;//伺服器端口,可以随意修改

private const string hostName = "127.0.0.1";//伺服器位址,127.0.0.1指本機

[STAThread]

static void Main(string[] args)

{

try

{

Console.Write("Try to connect to "+hostName+":"+portNum.ToString()+"\r\n");

TcpClient client = new TcpClient(hostName, portNum);

.NetworkStream ns = client.GetStream();

byte[] bytes = new byte[1024];

int bytesRead = ns.Read(bytes, 0, bytes.Length);

Console.WriteLine(Encoding.ASCII.GetString(bytes,0,bytesRead));

client.Close();

Console.ReadLine();//由于是控制台程式,故為了清楚的看到結果,可以加上這句

}

catch (Exception e)

Console.WriteLine(e.ToString());

}

}

}

上面這個例子清晰地示範了用戶端程式的編寫要點,下面我們讨論一下如何建立伺服器程式。這個例子将使用TcpListener類,在13号端口監聽,一旦有用戶端連接配接,将立即向用戶端發送目前伺服器的時間資訊。

TcpListener的關鍵在于AcceptTcpClient()方法,該方法将檢測端口是否有未處理的連接配接請求,如果有未處理的連接配接請求,該方法将使伺服器同用戶端建立連接配接,并且傳回一個TcpClient對象,通過這個對象的GetStream方法建立同用戶端通訊的資料流。事實上,TcpListener類還提供一個更為靈活的方法AcceptSocket(),當然靈活的代價是複雜,對于比較簡單的程式,AcceptTcpClient()已經足夠用了。此外,TcpListener類提供Start()方法開始監聽,提供Stop()方法停止監聽。

首先我們使用端口初始化一個TcpListener執行個體,并且開始在13端口監聽:

private const int portNum = 13;

TcpListener listener = new TcpListener(portNum);

listener.Start();//開始監聽

如果有未處理的連接配接請求,使用AcceptTcpClient方法進行處理,并且擷取資料流:

TcpClient client = listener.AcceptTcpClient();

然後,擷取本機時間,并儲存在位元組數組中,使.NetworkStream.Write()方法寫入資料流,然後用戶端就可以通過Read()方法從資料流中擷取這段資訊:

 byte[] byteTime = Encoding.ASCII.GetBytes(DateTime.Now.ToString());

ns.Write(byteTime, 0, byteTime.Length);

ns.Close();//不要忘記關閉資料流和連接配接

伺服器端程式完整的程式清單如下:

namespace TimeServer

class TimeServer

private const int portNum = 13;

bool done = false;

TcpListener listener = new TcpListener(portNum);

listener.Start();

while (!done)

Console.Write("Waiting for connection...");

TcpClient client = listener.AcceptTcpClient();

Console.WriteLine("Connection accepted.");

byte[] byteTime = Encoding.ASCII.GetBytes(DateTime.Now.ToString());

try

{

ns.Write(byteTime, 0, byteTime.Length);

ns.Close();

client.Close();

}

catch (Exception e)

Console.WriteLine(e.ToString());

listener.Stop();

把上面兩段程式分别編譯運作,OK,我們已經用C#實作了基于TCP協定的網絡通訊,怎麼樣?很簡單吧!

使用上面介紹的基本方法,我們可以很容易的編寫出一些很有用的程式,如FTP,電子郵件收發,點對點即時通訊等等,你甚至可以自己編制一個QQ來!

繼續閱讀