前面我們說,TcpClient類建立在Socket之上,在Tcp服務方面提供了更高層次的抽象,體現在網路資料的發送和接受方面,是TcpClient使用標準的Stream流處理技術,使得它讀寫資料更加方便直觀,同時,.Net架構負責提供更豐富的結構來處理流,貫穿於整個.Net架構中的流具有更廣泛的相容性,構建在更一般化的流操作上的通用方法使我們不再需要困惑於檔案的實際內容(HTML、XML 或其他任何內容),應用程式都將使用一致的方法(Stream.Write、Stream.Read) 發送和接收資料。另外,流在資料從 Internet 下載的過程中提供對資料的即時訪問,可以在部分資料到達時立即開始處理,而不需要等待應用程式下載完整個資料集。.Net中通過NetworkStream類實現了這些處理技術。 NetworkStream 類包含在.Net架構的System.Net.Sockets 命名空間裡,該類專門提供用於網路訪問的基礎資料流。NetworkStream 實現通過網路通訊端發送和接收資料的標準.Net 架構流機制。NetworkStream 支援對網路資料流的同步和非同步訪問。NetworkStream 從 Stream 繼承,後者提供了一組豐富的用於方便網路通訊的方法和屬性。 同其它繼承自抽象基類Stream的所有流一樣,NetworkStream網路流也可以被視為一個資料通道,架設在資料來源端(客戶Client)和接收端(服務Server)之間,而後的資料讀取及寫入均針對這個通道來進行。 .Net架構中,NetworkStream流支援兩方面的操作: 1、 寫入流。寫入是從資料結構到流的資料轉送。 示 意 圖 2、讀取流。讀取是從流到資料結構(如位元組數組)的資料轉送。 示 意 圖 與普通流Stream不同的是,網路流沒有當前位置的統一概念,因此不支援尋找和對資料流的隨機訪問。相應屬性CanSeek 始終返回 false,而 Seek 和 Position 方法也將引發 NotSupportedException。 基於Socket上的應用協議方面,你可以通過以下兩種方式擷取NetworkStream網路資料流: 1、使用NetworkStream建構函式:public NetworkStream(Socket, FileAccess, bool);(有重載方法),它用指定的存取權限和指定的 Socket 所屬權為指定的 Socket 建立 NetworkStream 類的新執行個體,使用前你需要建立Socket對象執行個體,並通過Socket.Connect方法建立與遠程服務端的串連,而後才可以使用該方法得到網路傳輸串流。樣本如下: Socket s=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);//建立用戶端Socket對象執行個體 try{ s.Connect("www.tuha.net",4088);//建立與遠程主機的串連 } catch(Exception e){ MessageBox.show("串連錯誤:" +e.Message); } try{ NetworkStream stream=new NetworkStream(s,FileAccess.ReadWrite,false);//取得網路傳輸串流 } 2、通過TcpClient.GetStream方法:public NetworkStream etStream();它返回用於發送和接收資料的基礎網路流NetworkStream。GetStream 通過將基礎 Socket 用作它的建構函式參數來建立 NetworkStream 類的執行個體。使用前你需要先創TcpClient對象執行個體並建立與遠程主機的串連,樣本如下: TcpClient tcpClient = new TcpClient();//建立TcpClient對象執行個體 Try{ tcpClient.Connect("www.tuha.net",4088);//嘗試與遠程主機相連 } catch(Exception e){ MessageBox.Show("串連錯誤:"+e.Message); } try{ NetworkStream stream=tcpClient.GetStream();//擷取網路傳輸串流 } catch(Exception e) { MessageBox.Show("TcpClient錯誤:"+e.Message); } 通過以上方法得到NetworkStream網路流之後,你就可以使用標準流讀寫方法Write和Read來發送和接受資料了。 以上是.Net下使用TcpClient類實現用戶端編程的技術資料,為了向用戶端提供這些服務,我們還需要編製相應的服務端程式,前一篇《Visual C#.Net網路程式開發-Socket篇》上曾經提到, Socket作為其他網路通訊協定的基礎,既可以面向用戶端開發,也可以面向服務端開發,在傳輸層面上使用較多,而在應用協議層面上,用戶端我們採用構建於Socket類之上的TcpClient取代Socket;相應地,構建於Socket之上的TcpListener提供了更高理念層級的 TCP 服務,使得我們能更方便地編寫服務端應用程式。正是因為這樣的原因,像FTP 和 HTTP 這樣的應用程式層協議都是在 TcpListener 類的基礎上建立的。 .Net中的TCPListener 用於監視TCP 通訊埠上的傳入請求,通過綁定本機IP地址和相應連接埠(這兩者應與用戶端的請求一致)建立TcpListener對象執行個體,並由Start方法啟動偵聽;當TcpListener偵聽到使用者端的串連後,視用戶端的不同請求方式,通過AcceptTcpClient 方法接受傳入的串連請求並建立 TcpClient 以處理請求,或者通過AcceptSocket 方法接受傳入的串連請求並建立 Socket 以處理請求。最後,你需要使用 Stop 關閉用於偵聽傳入串連的 Socket,你必須也關閉從 AcceptSocket 或 AcceptTcpClient 返回的任何執行個體。這個過程詳細解說如下: 首先,建立TcpListener對象執行個體,這通過TcpListener類的構造方法來實現: public TcpListener(port);//指定本機連接埠 public TcpListener(IPEndPoint)//指定本機終結點 public TcpListener(IPAddress,port)//指定本機IP地址及連接埠 以上方法中的參數在前面多次提到,這裡不再細述,唯一需要提醒的是,這些參數均針對服務端主機。下面的樣本示範建立 TcpListener 類的執行個體: IPHostEntry ipInfo=Dns.Resolve("127.0.0.1");//主機資訊 IPAddressList[] ipList=ipInfo.IPAddressList;//IP數組 IPAddress ip=ipList[0];//IP try{ TcpListener tcpListener = new TcpListener(ipAddress, 4088);//建立TcpListener對象執行個體以偵聽使用者端串連 } catch ( Exception e){ MessageBox.Show("TcpListener錯誤:"+e.Message); } 隨後,你需要調用Start方法啟動偵聽: public void Start(); 其次,當偵聽到有使用者端串連時,需要接受掛起的串連請求,這通過調用以下兩方法之一來完成串連: public Socket AcceptSocket(); public TcpClient AcceptTcpClient(); 前一個方法返回代表用戶端的Socket對象,隨後可以通過Socket 類的 Send 和 Receive 方法與遠端電腦通訊;後一個方法返回代表用戶端的TcpClient對象,隨後使用上面介紹的 TcpClient.GetStream 方法擷取 TcpClient 的基礎網路流 NetworkStream,並使用流讀寫Read/Write方法與遠端電腦通訊。 最後,請記住關閉接聽程式:public void Stop(); 同時關閉其他串連執行個體:public void Close(); 下面的樣本完整體現了上面的過程: bool done = false; TcpListener listener = new TcpListener(13);// 建立TcpListener對象執行個體(13號連接埠提供時間服務) listener.Start();//啟動偵聽 while (!done) {//進入無限迴圈以偵聽使用者串連 TcpClient client = listener.AcceptTcpClient();//偵聽到串連後建立用戶端串連TcpClient NetworkStream ns = client.GetStream();//得到網路傳輸串流 byte[] byteTime = Encoding.ASCII.GetBytes(DateTime.Now.ToString());//預發送的內容(此為服務端時間)轉換為位元組數組以便寫入流 try { ns.Write(byteTime, 0, byteTime.Length);//寫入流 ns.Close();//關閉流 client.Close();//關閉用戶端串連 } catch (Exception e) { MessageBox.Show("流錯誤:"+e.Message) } |