C#.Net網(wǎng)絡程序開發(fā)基礎之TCP篇

字號:

《Visual C#.Net網(wǎng)絡程序開發(fā)-Socket篇》中說到:支持Http、Tcp和Udp的類組成了TCP/IP三層模型(請求響應層、應用協(xié)議層、傳輸層)的中間層-應用協(xié)議層,該層的類比位于最底層的Socket類提供了更高層次的抽象,它們封裝 TCP 和 UDP 套接字的創(chuàng)建,不需要處理連接的細節(jié),這使得我們在編寫套接字級別的協(xié)議時,可以更多地嘗試使用 TCPClient 、 UDPClient和TcpListener,而不是直接向 Socket 中寫。它們之間的這種層次關系示意如下:
    ???
    ??可見, TcpClient 類基于 Socket 類構建,這是它能夠以更高的抽象程度提供 TCP 服務的基礎。正因為這樣,許多應用層上的通訊協(xié)議,比如FTP(File Transfers Protocol)文件傳輸協(xié)議、HTTP(Hypertext Transfers Protocol)超文本傳輸協(xié)議等都直接創(chuàng)建在TcpClient等類之上。
    ???
    ??TCPClient 類使用 TCP 從 Internet 資源請求數(shù)據(jù)。TCP 協(xié)議建立與遠程終結點的連接,然后使用此連接發(fā)送和接收數(shù)據(jù)包。TCP 負責確保將數(shù)據(jù)包發(fā)送到終結點并在數(shù)據(jù)包到達時以正確的順序對其進行組合。
    ???
    ??從名字上就可以看出,TcpClient類專為客戶端設計,它為 TCP 網(wǎng)絡服務提供客戶端連接。TcpClient 提供了通過網(wǎng)絡連接、發(fā)送和接收數(shù)據(jù)的簡單方法。
    ???
    ??若要建立 TCP 連接,必須知道承載所需服務的網(wǎng)絡設備的地址(IPAddress)以及該服務用于通訊的 TCP 端口 (Port)。Internet 分配號碼機構 (Internet Assigned Numbers Authority, IANA) 定義公共服務的端口號(你可以訪問 http://www.iana.org/assignments/port-numbers獲得這方面更詳細的資料)。IANA 列表中所沒有的服務可使用 1,024 到 65,535 這一范圍中的端口號。要創(chuàng)建這種連接,你可以選用TcpClient類的三種構造函數(shù)之一:
    ???
    ??1、public TcpClient()當使用這種不帶任何參數(shù)的構造函數(shù)時,將使用本機默認的ip地址并將使用默認的通信端口號0。這樣情況下,如果本機不止一個ip地址,將無法選擇使用。以下語句示例了如何使用默認構造函數(shù)來創(chuàng)建新的 TcpClient:
    ???
    ??TcpClient tcpClientC = new TcpClient();
    ???
    ???
    ??2、public TcpClient(IPEndPoint)使用本機IPEndPoint創(chuàng)建TcpClient的實例對象。上一篇介紹過了,IPEndPoint將網(wǎng)絡端點表示為IP地址和端口號,在這里它用于指定在建立遠程主機連接時所使用的本地網(wǎng)絡接口(IP 地址)和端口號,這個構造方法為使用本機IPAddress和Port提供了選擇余地。下面的語句示例了如何使用本地終結點創(chuàng)建 TcpClient 類的實例:
    ???
    ??IPHostEntry ipInfo=Dns.GetHostByName("www.tuha.net");//主機信息
    ???IPAddressList[] ipList=ipInfo.AddressList;//IP地址數(shù)組
    ???IPAddress ip=ipList[0];//多IP地址時一般用第一個
    ???IPEndPoint ipEP=new IPEndPoint(ip,4088);//得到網(wǎng)絡終結點
    ???try{
    ???TcpClient tcpClientA = new TcpClient(ipLocalEndPoint);
    ???}
    ??catch (Exception e ) {
    ???Console.WriteLine(e.ToString());
    ???}
    ???
    ???
    ??到這里,你可能會感到困惑,客戶端要和服務端創(chuàng)建連接,所指定的IP地址及通信端口號應該是遠程服務器的呀!事實上的確如此,使用以上兩種構造函數(shù),你所實現(xiàn)的只是TcpClient實例對象與IP地址和Port端口的綁定,要完成連接,你還需要顯式指定與遠程主機的連接,這可以通過TcpClient類的Connect方法來實現(xiàn), Connet方法使用指定的主機名和端口號將客戶端連接到 遠程主機:
    ???
    ??1)、public void Connect(IPEndPoint); 使用指定的遠程網(wǎng)絡終結點將客戶端連接到遠程 TCP 主機。
    ???
    ??public void Connect(IPAddress, int); 使用指定的 IP 地址和端口號將客戶端連接到 TCP 主機。
    ???
    ??public void Connect(string, int); 將客戶端連接到指定主機上的指定端口。
    ???
    ??需要指出的是,Connect方法的所有重載形式中的參數(shù)IPEndPoint網(wǎng)絡終
    ???
    ??結點、IPAddress以及表現(xiàn)為string的Dns主機名和int指出的Port端口均指的是遠程服務器。
    ???
    ??以下示例語句使用主機默認IP和Port端口號0與遠程主機建立連接:
    ???
    ??TcpClient tcpClient = new TcpClient();//創(chuàng)建TcpClient對象實例
    ???try{
    ???tcpClient.Connect("www.contoso.com",11002);//建立連接
    ???}
    ???catch (Exception e ){
    ???Console.WriteLine(e.ToString());
    ???}
    ???
    ???
    ??3、public TcpClient(string, int);初始化 TcpClient 類的新實例并連接到指定主機上的指定端口。與前兩個構造函數(shù)不一樣,這個構造函數(shù)將自動建立連接,你不再需要額外調用Connect方法,其中string類型的參數(shù)表示遠程主機的Dns名,如:www.tuha.net。
    ???
    ??以下示例語句調用這一方法實現(xiàn)與指定主機名和端口號的主機相連:
    ???
    ??try{
    ???TcpClient tcpClientB = new TcpClient("www.tuha.net", 4088);
    ???}
    ???catch (Exception e ) {
    ???Console.WriteLine(e.ToString());
    ???}
    ??前面我們說,TcpClient類創(chuàng)建在Socket之上,在Tcp服務方面提供了更高層次的抽象,體現(xiàn)在網(wǎng)絡數(shù)據(jù)的發(fā)送和接受方面,是TcpClient使用標準的Stream流處理技術,使得它讀寫數(shù)據(jù)更加方便直觀,同時,.Net框架負責提供更豐富的結構來處理流,貫穿于整個.Net框架中的流具有更廣泛的兼容性,構建在更一般化的流操作上的通用方法使我們不再需要困惑于文件的實際內容(HTML、XML 或其他任何內容),應用程序都將使用一致的方法(Stream.Write、Stream.Read) 發(fā)送和接收數(shù)據(jù)。另外,流在數(shù)據(jù)從 Internet 下載的過程中提供對數(shù)據(jù)的即時訪問,可以在部分數(shù)據(jù)到達時立即開始處理,而不需要等待應用程序下載完整個數(shù)據(jù)集。.Net中通過NetworkStream類實現(xiàn)了這些處理技術。
    ???
    ??NetworkStream 類包含在.Net框架的System.Net.Sockets 命名空間里,該類專門提供用于網(wǎng)絡訪問的基礎數(shù)據(jù)流。NetworkStream 實現(xiàn)通過網(wǎng)絡套接字發(fā)送和接收數(shù)據(jù)的標準.Net 框架流機制。NetworkStream 支持對網(wǎng)絡數(shù)據(jù)流的同步和異步訪問。NetworkStream 從 Stream 繼承,后者提供了一組豐富的用于方便網(wǎng)絡通訊的方法和屬性。
    ???
    ??同其它繼承自抽象基類Stream的所有流一樣,NetworkStream網(wǎng)絡流也可以被視為一個數(shù)據(jù)通道,架設在數(shù)據(jù)來源端(客戶Client)和接收端(服務Server)之間,而后的數(shù)據(jù)讀取及寫入均針對這個通道來進行。
    ???
    ??.Net框架中,NetworkStream流支持兩方面的操作:
    ???
    ??1、 寫入流。寫入是從數(shù)據(jù)結構到流的數(shù)據(jù)傳輸。
    ???
    ??2、讀取流。讀取是從流到數(shù)據(jù)結構(如字節(jié)數(shù)組)的數(shù)據(jù)傳輸。
    ???
    ??與普通流Stream不同的是,網(wǎng)絡流沒有當前位置的統(tǒng)一概念,因此不支持查找和對數(shù)據(jù)流的隨機訪問。相應屬性CanSeek 始終返回 false,而 Seek 和 Position 方法也將引發(fā) NotSupportedException。
    ???
    ??基于Socket上的應用協(xié)議方面,你可以通過以下兩種方式獲取NetworkStream網(wǎng)絡數(shù)據(jù)流:
    ???
    ??1、使用NetworkStream構造函數(shù):public NetworkStream(Socket, FileAccess, bool);(有重載方法),它用指定的訪問權限和指定的 Socket 所屬權為指定的 Socket 創(chuàng)建 NetworkStream 類的新實例,使用前你需要創(chuàng)建Socket對象實例,并通過Socket.Connect方法建立與遠程服務端的連接,而后才可以使用該方法得到網(wǎng)絡傳輸流。示例如下:
    ???
    ??Socket s=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);//創(chuàng)建客戶端Socket對象實例
    ???try{
    ???s.Connect("www.tuha.net",4088);//建立與遠程主機的連接
    ???}
    ???catch(Exception e){
    ???MessageBox.show("連接錯誤:" +e.Message);
    ???}
    ???try{
    ???NetworkStream stream=new NetworkStream(s,FileAccess.ReadWrite,false);//取得網(wǎng)絡傳輸流
    ???}
    ???
    ???
    ??2、通過TcpClient.GetStream方法:public NetworkStream etStream();它返回用于發(fā)送和接收數(shù)據(jù)的基礎網(wǎng)絡流NetworkStream。GetStream 通過將基礎 Socket 用作它的構造函數(shù)參數(shù)來創(chuàng)建 NetworkStream 類的實例。使用前你需要先創(chuàng)TcpClient對象實例并建立與遠程主機的連接,示例如下:
    ???
    ??TcpClient tcpClient = new TcpClient();//創(chuàng)建TcpClient對象實例
    ???Try{
    ???tcpClient.Connect("www.tuha.net",4088);//嘗試與遠程主機相連
    ???}
    ???catch(Exception e){
    ???MessageBox.Show("連接錯誤:"+e.Message);
    ???}
    ???try{
    ???NetworkStream stream=tcpClient.GetStream();//獲取網(wǎng)絡傳輸流
    ???}
    ???catch(Exception e)
    ???{
    ???MessageBox.Show("TcpClient錯誤:"+e.Message);
    ???}
    ???
    ???
    ??通過以上方法得到NetworkStream網(wǎng)絡流之后,你就可以使用標準流讀寫方法Write和Read來發(fā)送和接受數(shù)據(jù)了。