程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> Visual C#網絡編程之TCP

Visual C#網絡編程之TCP

編輯:關於C#

前一篇《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類的三種構造函數之一:

1、public TcpClient()當使用這種不帶任何參數的構造函數時,將使用本機默認的ip地址並將使用默認的通信端口號0。這樣情況下,如果本機不止一個ip地址,將無法選擇使用。以下語句示例了如何使用默認構造函數來創建新的 TcpClient:

TcpClient tcpClientC = new TcpClient();

2、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());
}

3、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流支持兩方面的操作:

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)
}

綜合運用上面的知識,下面的實例實現了簡單的網絡通訊-雙機互連,針對客戶端和服務端分別編制了應用程序。客戶端創建到服務端的連接,向遠程主機發送連接請求連接信號,並發送交談內容;遠程主機端接收來自客戶的連接,向客戶端發回確認連接的信號,同時接收並顯示客戶端的交談內容。在這個基礎上,發揮你的創造力,你完全可以開發出一個基於程序語言(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();
}
}

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved