Microsoft.Net Framework爲應用程序訪問Internet提供了分層的、可擴展的以及受管轄的網絡服務,其名字空間System.Net和System.Net.Sockets包含豐富的類能夠開發多種網絡應用程序。.Net類採用的分層結構容許應用程序在不一樣的控制級別上訪問網絡,開發人員能夠根據須要選擇針對不一樣的級別編制程序,這些級別幾乎囊括了Internet的全部須要--從socket套接字到普通的請求/響應,更重要的是,這種分層是能夠擴展的,可以適應Internet不斷擴展的須要。
拋開ISO/OSI模型的7層構架,單從TCP/IP模型上的邏輯層面上看,.Net類能夠視爲包含3個層次:請求/響應層、應用協議層、傳輸層。WebReqeust和WebResponse 表明了請求/響應層,支持Http、Tcp和Udp的類組成了應用協議層,而Socket類處於傳輸層。
傳輸層位於這個結構的最底層,當其上面的應用協議層和請求/響應層不能知足應用程序的特殊須要時,就須要使用這一層進行Socket套接字編程。
而在.Net中,System.Net.Sockets 命名空間爲須要嚴密控制網絡訪問的開發人員提供了 Windows Sockets (Winsock) 接口的託管實現。System.Net 命名空間中的全部其餘網絡訪問類都創建在該套接字Socket實現之上,如TCPClient、TCPListener 和 UDPClient 類封裝有關建立到 Internet 的 TCP 和 UDP 鏈接的詳細信息;NetworkStream類則提供用於網絡訪問的基礎數據流等,常見的許多Internet服務均可以見到Socket的蹤跡,如Telnet、Http、Email、Echo等,這些服務儘管通信協議Protocol的定義不一樣,可是其基礎的傳輸都是採用的Socket。
其實,Socket能夠象流Stream同樣被視爲一個數據通道,這個通道架設在應用程序端(客戶端)和遠程服務器端之間,然後,數據的讀取(接收)和寫入(發送)均針對這個通道來進行。
可見,在應用程序端或者服務器端建立了Socket對象以後,就可使用Send/SentTo方法將數據發送到鏈接的Socket,或者使用Receive/ReceiveFrom方法接收來自鏈接Socket的數據;
針對Socket編程,.NET 框架的 Socket 類是 Winsock32 API 提供的套接字服務的託管代碼版本。其中爲實現網絡編程提供了大量的方法,大多數狀況下,Socket 類方法只是將數據封送到它們的本機 Win32 副本中並處理任何須要的安全檢查。若是你熟悉Winsock API函數,那麼用Socket類編寫網絡程序會很是容易,固然,若是你未曾接觸過,也不會太困難,跟隨下面的解說,你會發覺使用Socket類開發windows 網絡應用程序原來有規可尋,它們在大多數狀況下遵循大體相同的步驟。
在使用以前,你須要首先建立Socket對象的實例,這能夠經過Socket類的構造方法來實現:
public Socket(AddressFamily addressFamily,SocketType socketType,ProtocolType protocolType);
其中,addressFamily 參數指定 Socket 使用的尋址方案,socketType 參數指定 Socket 的類型,protocolType 參數指定 Socket 使用的協議。
下面的示例語句建立一個 Socket,它可用於在基於 TCP/IP 的網絡(如 Internet)上通信。
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
若要使用 UDP 而不是 TCP,須要更改協議類型,以下面的示例所示:
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
一旦建立 Socket,在客戶端,你將能夠經過Connect方法鏈接到指定的服務器,並經過Send/SendTo方法向遠程服務器發送數據,然後能夠經過Receive/ReceiveFrom從服務端接收數據;而在服務器端,你須要使用Bind方法綁定所指定的接口使Socket與一個本地終結點相聯,並經過Listen方法偵聽該接口上的請求,當偵聽到用戶端的鏈接時,調用Accept完成鏈接的操做,建立新的Socket以處理傳入的鏈接請求。使用完 Socket 後,記住使用 Shutdown 方法禁用 Socket,並使用 Close 方法關閉 Socket。其間用到的方法/函數有:
Socket.Connect方法:創建到遠程設備的鏈接
public void Connect(EndPoint remoteEP)(有重載方法)
Socket.Send 方法:從數據中的指示位置開始將數據發送到鏈接的 Socket。
public int Send(byte[], int, SocketFlags);(有重載方法)
Socket.SendTo 方法 將數據發送到特定終結點。
public int SendTo(byte[], EndPoint);(有重載方法)
Socket.Receive方法:將數據從鏈接的 Socket 接收到接收緩衝區的特定位置。
public int Receive(byte[],int,SocketFlags);
Socket.ReceiveFrom方法:接收數據緩衝區中特定位置的數據並存儲終結點。
public int ReceiveFrom(byte[], int, SocketFlags, ref EndPoint);
Socket.Bind 方法:使 Socket 與一個本地終結點相關聯:
public void Bind( EndPoint localEP );
Socket.Listen方法:將 Socket 置於偵聽狀態。
public void Listen( int backlog );
Socket.Accept方法:建立新的 Socket 以處理傳入的鏈接請求。
public Socket Accept();
Socket.Shutdown方法:禁用某 Socket 上的發送和接收
public void Shutdown( SocketShutdown how );
Socket.Close方法:強制 Socket 鏈接關閉
public void Close();
能夠看出,以上許多方法包含EndPoint類型的參數,在Internet中,TCP/IP 使用一個網絡地址和一個服務端口號來惟一標識設備。網絡地址標識網絡上的特定設備;端口號標識要鏈接到的該設備上的特定服務。網絡地址和服務端口的組合稱爲終結點,在 .NET 框架中正是由 EndPoint 類表示這個終結點,它提供表示網絡資源或服務的抽象,用以標誌網絡地址等信息。.Net同時也爲每一個受支持的地址族定義了 EndPoint 的子代;對於 IP 地址族,該類爲 IPEndPoint。IPEndPoint 類包含應用程序鏈接到主機上的服務所需的主機和端口信息,經過組合服務的主機IP地址和端口號,IPEndPoint 類造成到服務的鏈接點。
用到IPEndPoint類的時候就不可避免地涉及到計算機IP地址,.Net中有兩種類能夠獲得IP地址實例:
IPAddress類:IPAddress 類包含計算機在 IP 網絡上的地址。其Parse方法可將 IP 地址字符串轉換爲 IPAddress 實例。下面的語句建立一個 IPAddress 實例:
IPAddress myIP = IPAddress.Parse("192.168.1.2");
Dns 類:向使用 TCP/IP Internet 服務的應用程序提供域名服務。其Resolve 方法查詢 DNS 服務器以將用戶友好的域名(如"host.contoso.com")映射到數字形式的 Internet 地址(如 192.168.1.1)。Resolve方法 返回一個 IPHostEnty 實例,該實例包含所請求名稱的地址和別名的列表。大多數狀況下,可使用 AddressList 數組中返回的第一個地址。下面的代碼獲取一個 IPAddress 實例,該實例包含服務器 host.contoso.com 的 IP 地址。
IPHostEntry ipHostInfo = Dns.Resolve("host.contoso.com");
IPAddress ipAddress = ipHostInfo.AddressList[0];
你也可使用GetHostName方法獲得IPHostEntry實例:
IPHosntEntry hostInfo=Dns.GetHostByName("host.contoso.com")
在使用以上方法時,你將可能須要處理如下幾種異常:
SocketException異常:訪問Socket時操做系統發生錯誤引起
ArgumentNullException異常:參數爲空引用引起
ObjectDisposedException異常:Socket已經關閉引起
在掌握上面得知識後,下面的代碼將該服務器主機( host.contoso.com的 IP 地址與端口號組合,以便爲鏈接建立遠程終結點:
IPEndPoint ipe = new IPEndPoint(ipAddress,11000);
肯定了遠程設備的地址並選擇了用於鏈接的端口後,應用程序能夠嘗試創建與遠程設備的鏈接。下面的示例使用現有的 IPEndPoint 實例與遠程設備鏈接,並捕獲可能引起的異常:
try {
s.Connect(ipe);//嘗試鏈接
}
//處理參數爲空引用異常
catch(ArgumentNullException ae) {
Console.WriteLine("ArgumentNullException : {0}", ae.ToString());
}
//處理操做系統異常
catch(SocketException se) {
Console.WriteLine("SocketException : {0}", se.ToString());
}
catch(Exception e) {
Console.WriteLine("Unexpected exception : {0}", e.ToString());
}
須要知道的是:Socket 類支持兩種基本模式:同步和異步。其區別在於:在同步模式中,對執行網絡操做的函數(如 Send 和 Receive)的調用一直等到操做完成後纔將控制返回給調用程序。在異步模式中,這些調用當即返回。
另外,不少時候,Socket編程視狀況不一樣須要在客戶端和服務器端分別予以實現,在客戶端編制應用程序向服務端指定端口發送請求,同時編制服務端應用程序處理該請求,這個過程在上面的闡述中已經說起;固然,並不是全部的Socket編程都須要你嚴格編寫這兩端程序;視應用狀況不一樣,你能夠在客戶端構造出請求字符串,服務器相應端口捕獲這個請求,交由其公用服務程序進行處理。如下事例語句中的字符串就向遠程主機提出頁面請求:
string Get = "GET / HTTP/1.1\r\nHost: " + server + "\r\nConnection: Close\r\n\r\n";
遠程主機指定端口接受到這一請求後,就可利用其公用服務程序進行處理而不須要另行編制服務器端應用程序。
綜合運用以上闡述的使用Visual C#進行Socket網絡程序開發的知識,下面的程序段完整地實現了Web頁面下載功能。用戶只需在窗體上輸入遠程主機名(Dns 主機名或以點分隔的四部分表示法格式的 IP 地址)和預保存的本地文件名,並利用專門提供Http服務的80端口,就能夠獲取遠程主機頁面並保存在本地機指定文件中。若是保存格式是.htm格式,你就能夠在Internet瀏覽器中打開該頁面。適當添加代碼,你甚至能夠實現一個簡單的瀏覽器程序。
實現此功能的主要源代碼以下:
//"開始"按鈕事件
private void button1_Click(object sender, System.EventArgs e) {
//取得預保存的文件名
string fileName=textBox3.Text.Trim();
//遠程主機
string hostName=textBox1.Text.Trim();
//端口
int port=Int32.Parse(textBox2.Text.Trim());
//獲得主機信息
IPHostEntry ipInfo=Dns.GetHostByName(hostName);
//取得IPAddress[]
IPAddress[] ipAddr=ipInfo.AddressList;
//獲得ip
IPAddress ip=ipAddr[0];
//組合出遠程終結點
IPEndPoint hostEP=new IPEndPoint(ip,port);
//建立Socket 實例
Socket socket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
try
{
//嘗試鏈接
socket.Connect(hostEP);
}
catch(Exception se)
{
MessageBox.Show("鏈接錯誤"+se.Message,"提示信息
,MessageBoxButtons.RetryCancel,MessageBoxIcon.Information);
}
//發送給遠程主機的請求內容串
string sendStr="GET / HTTP/1.1\r\nHost: " + hostName +
"\r\nConnection: Close\r\n\r\n";
//建立bytes字節數組以轉換髮送串
byte[] bytesSendStr=new byte[1024];
//將發送內容字符串轉換成字節byte數組
bytesSendStr=Encoding.ASCII.GetBytes(sendStr);
try
{
//向主機發送請求
socket.Send(bytesSendStr,bytesSendStr.Length,0);
}
catch(Exception ce)
{
MessageBox.Show("發送錯誤:"+ce.Message,"提示信息
,MessageBoxButtons.RetryCancel,MessageBoxIcon.Information);
}
//聲明接收返回內容的字符串
string recvStr="";
//聲明字節數組,一次接收數據的長度爲1024字節
byte[] recvBytes=new byte[1024];
//返回實際接收內容的字節數
int bytes=0;
//循環讀取,直到接收完全部數據
while(true)
{
bytes=socket.Receive(recvBytes,recvBytes.Length,0);
//讀取完成後退出循環
if(bytes〈=0)
break;
//將讀取的字節數轉換爲字符串
recvStr+=Encoding.ASCII.GetString(recvBytes,0,bytes);
}
//將所讀取的字符串轉換爲字節數組
byte[] content=Encoding.ASCII.GetBytes(recvStr);
try
{
//建立文件流對象實例
FileStream fs=new FileStream(fileName,FileMode.OpenOrCreate,FileAccess.ReadWrite);
//寫入文件
fs.Write(content,0,content.Length);
}
catch(Exception fe)
{
MessageBox.Show("文件建立/寫入錯誤:"+fe.Message,"提示信息",MessageBoxButtons.RetryCancel,MessageBoxIcon.Information);
}
//禁用Socket
socket.Shutdown(SocketShutdown.Both);
//關閉Socket
socket.Close();
}
}
程序在WindowsXP中文版、.Net Frameworkd 中文正式版、Visual Studio.Net中文正式版下調試經過
關於做者
宋華,96年畢業於承德石油學院電子與電氣系計算機應用專業,同年進入中國石油自然氣集團公司吐哈油田工做,一直從事網絡規劃與建設、網站架構與設計,現專門從事Internet應用程序開發及Windows應用程序開發。
Visual C#.Net網絡程序開發-Tcp篇
前一篇《Visual C#.Net網絡程序開發-Socket篇》中說到:支持Http、Tcp和Udp的類組成了TCP/IP三層模型(請求響應層、應用協議層、傳輸層)的中間層-應用協議層,該層的類比位於最底層的Socket類提供了更高層次的抽象,它們封裝 TCP 和 UDP 套接字的建立,不須要處理鏈接的細節,這使得咱們在編寫套接字級別的協議時,能夠更多地嘗試使用 TCPClient 、 UDPClient和TcpListener,而不是直接向 Socket 中寫。它們之間的這種層次關係示意以下:
可見, TcpClient 類基於 Socket 類構建,這是它可以以更高的抽象程度提供 TCP 服務的基礎。正由於這樣,許多應用層上的通信協議,好比FTP(File Transfers Protocol)文件傳輸協議、HTTP(Hypertext Transfers Protocol)超文本傳輸協議等都直接建立在TcpClient等類之上。
TCPClient 類使用 TCP 從 Internet 資源請求數據。TCP 協議創建與遠程終結點的鏈接,而後使用此鏈接發送和接收數據包。TCP 負責確保將數據包發送到終結點並在數據包到達時以正確的順序對其進行組合。
從名字上就能夠看出,TcpClient類專爲客戶端設計,它爲 TCP 網絡服務提供客戶端鏈接。TcpClient 提供了經過網絡鏈接、發送和接收數據的簡單方法。
若要創建 TCP 鏈接,必須知道承載所需服務的網絡設備的地址(IPAddress)以及該服務用於通信的 TCP 端口 (Port)。Internet 分配號碼機構 (Internet Assigned Numbers Authority, IANA) 定義公共服務的端口號(你能夠訪問
http://www.iana.org/assignments/port-numbers得到這方面更詳細的資料)。IANA 列表中所沒有的服務可以使用 1,024 到 65,535 這一範圍中的端口號。要建立這種鏈接,你能夠選用TcpClient類的三種構造函數之一:
一、public TcpClient()當使用這種不帶任何參數的構造函數時,將使用本機默認的ip地址並將使用默認的通訊端口號0。這樣狀況下,若是本機不止一個ip地址,將沒法選擇使用。如下語句示例瞭如何使用默認構造函數來建立新的 TcpClient:
TcpClient tcpClientC = new TcpClient();
二、public TcpClient(IPEndPoint)使用本機IPEndPoint建立TcpClient的實例對象。上一篇介紹過了,IPEndPoint將網絡端點表示爲IP地址和端口號,在這裏它用於指定在創建遠程主機鏈接時所使用的本地網絡接口(IP 地址)和端口號,這個構造方法爲使用本機IPAddress和Port提供了選擇餘地。下面的語句示例瞭如何使用本地終結點建立 TcpClient 類的實例:
IPHostEntry ipInfo=Dns.GetHostByName("www.tuha.net");//主機信息
IPAddressList[] ipList=ipInfo.AddressList;//IP地址數組
IPAddress ip=ipList[0];//多IP地址時通常用第一個
IPEndPoint ipEP=new IPEndPoint(ip,4088);//獲得網絡終結點
try{
TcpClient tcpClientA = new TcpClient(ipLocalEndPoint);
}
catch (Exception e ) {
Console.WriteLine(e.ToString());
}
到這裏,你可能會感到困惑,客戶端要和服務端建立鏈接,所指定的IP地址及通訊端口號應該是遠程服務器的呀!事實上的確如此,使用以上兩種構造函數,你所實現的只是TcpClient實例對象與IP地址和Port端口的綁定,要完成鏈接,你還須要顯式指定與遠程主機的鏈接,這能夠經過TcpClient類的Connect方法來實現, Connet方法使用指定的主機名和端口號將客戶端鏈接到 遠程主機:
1)、public void Connect(IPEndPoint); 使用指定的遠程網絡終結點將客戶端鏈接到遠程 TCP 主機。
public void Connect(IPAddress, int); 使用指定的 IP 地址和端口號將客戶端鏈接到 TCP 主機。
public void Connect(string, int); 將客戶端鏈接到指定主機上的指定端口。
須要指出的是,Connect方法的全部重載形式中的參數IPEndPoint網絡終
結點、IPAddress以及表現爲string的Dns主機名和int指出的Port端口均指的是遠程服務器。
如下示例語句使用主機默認IP和Port端口號0與遠程主機創建鏈接:
TcpClient tcpClient = new TcpClient();//建立TcpClient對象實例
try{
tcpClient.Connect("www.contoso.com",11002);//創建鏈接
}
catch (Exception e ){
Console.WriteLine(e.ToString());
}
三、public TcpClient(string, int);初始化 TcpClient 類的新實例並鏈接到指定主機上的指定端口。與前兩個構造函數不同,這個構造函數將自動創建鏈接,你再也不須要額外調用Connect方法,其中string類型的參數表示遠程主機的Dns名,如:www.tuha.net。
如下示例語句調用這一方法實現與指定主機名和端口號的主機相連:
try{
TcpClient tcpClientB = new TcpClient("www.tuha.net", 4088);
}
catch (Exception e ) {
Console.WriteLine(e.ToString());
}
前面咱們說,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流支持兩方面的操做:
一、 寫入流。寫入是從數據結構到流的數據傳輸。
二、讀取流。讀取是從流到數據結構(如字節數組)的數據傳輸。
與普通流Stream不一樣的是,網絡流沒有當前位置的統一律念,所以不支持查找和對數據流的隨機訪問。相應屬性CanSeek 始終返回 false,而 Seek 和 Position 方法也將引起 NotSupportedException。
基於Socket上的應用協議方面,你能夠經過如下兩種方式獲取NetworkStream網絡數據流:
一、使用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);//取得網絡傳輸流
}
二、經過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)
}
綜合運用上面的知識,下面的實例實現了簡單的網絡通信-雙機互連,針對客戶端和服務端分別編制了應用程序。客戶端建立到服務端的鏈接,向遠程主機發送鏈接請求鏈接信號,併發送交談內容;遠程主機端接收來自客戶的鏈接,向客戶端發回確認鏈接的信號,同時接收並顯示客戶端的交談內容。在這個基礎上,發揮你的創造力,你徹底能夠開發出一個基於程序語言(C#)級的聊天室!
客戶端主要源代碼:
public void SendMeg()//發送信息
{
try
{
int port=Int32.Parse(textBox3.Text.ToString());//遠程主機端口
try
{
tcpClient=new TcpClient(textBox1.Text,port);//建立TcpClient對象實例 }
catch(Exception le)
{
MessageBox.Show("TcpClient Error:"+le.Message);
}
string strDateLine=DateTime.Now.ToShortDateString()+" "+DateTime.Now.ToLongTimeString();//獲得發送時客戶端時間
netStream=tcpClient.GetStream();//獲得網絡流
sw=new StreamWriter(netStream);//建立TextWriter,向流中寫字符
string words=textBox4.Text;//待發送的話
string content=strDateLine+words;//待發送內容
sw.Write(content);//寫入流
sw.Close();//關閉流寫入器
netStream.Close();//關閉網絡流
tcpClient.Close();//關閉客戶端鏈接
}
catch(Exception ex)
{
MessageBox.Show("Sending Message Failed!"+ex.Message);
}
textBox4.Text="";//清空
}
服務器端主要源代碼:
public void StartListen()//偵聽特定端口的用戶請求
{
//ReceiveMeg();
isLinked=false; //鏈接標誌
try
{
int port=Int32.Parse(textBox1.Text.ToString());//本地待偵聽端口
serverListener=new TcpListener(port);//建立TcpListener對象實例
serverListener.Start(); //啓動偵聽
}
catch(Exception ex)
{
MessageBox.Show("Can‘t Start Server"+ex.Message);
return;
}
isLinked=true;
while(true)//進入無限循環等待用戶端鏈接
{
try
{
tcpClient=serverListener.AcceptTcpClient();//建立客戶端鏈接對象
netStream=tcpClient.GetStream();//獲得網絡流
sr=new StreamReader(netStream);//流讀寫器
}
catch(Exception re)
{
MessageBox.Show(re.Message);
}
string buffer="";
string received="";
received+=sr.ReadLine();//讀流中一行
while(received.Length!=0)
{
buffer+=received;
buffer+="\r\n";
//received="";
received=sr.ReadLine();
}
listBox1.Items.Add(buffer);//顯示
//關閉
sr.Close();
netStream.Close();
tcpClient.Close();
}
}