c#網絡編程學習筆記02_Tcp編程(上)

/*關於多線程的一些知識和概念,會在從此之後的一些聯繫中講解*/

/*這一節主要是概念性和相關方法的介紹,在下一篇會使用例子來演示tcp的同步和異步編程*/

一.TCP簡介

TCP是一種面向連接的, 可靠的, 給予字節流的傳輸層通信協議,位於ip層之上,應用層之下。由於應用層之間經常需要可靠的,像管道一樣的連接,但是ip層並不提供這樣的流機制,故需要由TCP完成傳輸管道的任務。

1. TCP的工作過程

1.1 連接的建立

用TCP編寫的程序,必須先建立TCP連接。這個建立過程稱爲“三次握手”:

第一次: 建立連接時,客戶端發送SYN(不用管他是什麼東西)包到服務器,客戶端進入SYN_SEND狀態,等待確認。

第二次: 服務器收到SYN包,必須確認客戶的SYN,同時自己也發送一個SYN, 即SYN+ACK包,同時服務器進入SYN_RECV狀態。

第三次: 客戶端收到SYN+ ACK包,同時向服務器發送ACK,此包發送完畢,客戶端和服務器進入Established狀態,完成三次握手。

1.2 傳輸數據

TCP協議是字節流發送,將字節流按照一定的格式和長度組成多個數據報發送,然後接收方,收到數據報後,按照分解順序重新組裝,恢復數據。

1.3 連接的終止

TCP連接需要三次握手,但是想終止連接,則需要四次握手,這是由於TCP的版關閉造成的。

2. Tcp的主要特點

1.tcp是面向協議的連接。

2. 端到端的通信,只能一對一,不能一對多。

3. 高可靠性,保證數據無差錯,不丟失,不重複,能準確到達接收方,並且到達順序和發出順序相同。

4.全雙工方式傳輸。

5. 數據以字節流方式傳輸。

6. 傳輸的數據沒有消息邊界(不懂)。

二.同步和異步

不想過多解釋。.NET封裝好的類,TcpClient類和TcpListener類都有同步與異步相對應的方法。

三. C#中的TCP編程類

TcpLiistener類用於監聽客戶端的連接請求, TcpClient類用於提供本地主機和遠程主機的連接信息。

1.1 TcpListener類

該類用於監聽和接受傳入的連接請求。該類的構造函數常用的有以下兩種重在形式:

1)TcpListener(IPEndPoint iep)

2) TcpListener(IPAddress localAddr, int port)

第一種構造是通過IPEndPoint類型的對象,在指定的Ip地址與端口監聽客戶端的鏈接請求,iep包含了本機的ip地址和端口號。

第二種構造是直接指定本機的ip和端口,並通過指定的本機ip和端口號監聽傳入的連接請求。也可以將本機ip設置爲IPAdrress.Any,將本機的端口指定爲0,這種形式表示ip地址和端口號都有系統自動分配。


在創建了TcpListener對象後,就可以監聽客戶端的連接請求了。其工作方式,分爲同步和異步。

同步:

1)Start方法。 方法原型爲:

public void Start()

public void Start(int backlog)

整形backlog代表了請求隊列的最大長度,即最多允許的客戶端連接的個數。在調用Start方法後,系統自動將LocalEndPoint和底層套接字綁定,並自動監聽來自客戶端的請求, 如果接受了一個客戶端的請求,則把該請求插入隊列,然後繼續監聽下一個請求,知道調用Stop方法爲止。如果超出最大長度,泡醋SocketExceptioni類型異常。

2) Stop方法。方法原型爲:

public void Stop()

執行stop方法後,立即停止監聽客戶端的連接請求,此時隊列中的所有爲接受的請求全部丟失,導致等待鏈接的客戶端引發SocketException異常,從而使得服務器的AcceptTcpClient方法也會產生異常。但是要注意的是,該方法不會關閉已經接受的連接請求。

Stop方法還會關閉基礎Socket, 並未TcpListener創建新的Socket,如果在調用stop之前已經在socket上設置屬性,這些屬性不會傳遞到新的socket中。

3) AcceptSocket方法。

用於在 同步阻塞方式下獲得並返回一個用來接收和發送數據的Socket對象,同時從傳入的連接隊列中刪除該客戶端的連接請求,該套接字包含了本地和遠程主機的ip地址和端口號,得到該對象後,就可以通過調用Socke對象的Send和Reseive方法和遠程主機通信。

4) AcceptTcpClient方法

該方法用於在同步阻塞方式下獲取並且返回一個封裝了Socket的TcpClient對象,同時傳入的連接隊列中刪除改客戶端的連接請求。得到該對象後,就可以通過該對象的GetStream方法生成NetworkStream對象,通過NetworkStream對象與客戶端通信。

如果應用程序僅需要IO,調用AccepteTcpClient方法即可,但是如果進行更加細化的行爲控制,需要AcceptSocket方法。

注意: 當程序之執行AcceptTcpClient方法的時候,線程會處於阻塞狀態,知道接受客戶端向服務器發送的連接請求後,纔會繼續執行嚇一跳語句。

TcpListener類中的常用方法:

AcceptSocket 從端口處接受一個鏈接並賦予他socket對象

AcceptTcpClient 蔥段扣除接受一個連接並賦予他TcpClient對象

Equals 判斷兩個TcpListener對象是否相等

GetHashCode  特定類型的哈希函數

GetType 得到當前實例類型

Pending 確定是否有掛起的請求

Start 開始偵聽傳入請求

Stop 關閉偵聽器

ToString 呵呵

1.2 TcpClient 類

Tcplient類也位於 System.Net.Socket命名空間下, 該類主要用於客戶端編程,而服務器程序是通過TcpListener對象的AcceptTcpClient方法得到的TcpClient對象的。

TcpClient 類的構造函數有以下四種:

1) TcpClient()

自動分配本機(客戶端)的ip和端口號,利用此構造函數創建對象後,還必須調用Connet方法與服務器連接,比如:


<span style="white-space:pre">		</span>TcpClient tcpClient = new TcpClient();
		tcpClient.Connet("www.abcd.com", 51888);

2) TcpClient(AddressFamily family)

這個構造方法也能自動分配本機(客戶端)的ip和端口號,但是使用AddressFamily枚舉指定使用那種網絡協議。創建該對象,同樣要調用Connet方法與服務器連接。例如:

<span style="white-space:pre">		</span>TcpClient tcpClient =new TcpClient(AddressFamily.InterNetwork);
		tcpClient.Connet("www.abcd.com", 51888);

3) TcpClient(IPEndPoint iep)

改構造函數的參數iep指定了本機(客戶端)Ip地址和端口號,當客戶端有一個以上的ip地址是,而且程序希望直接指定ip地址和端口號,可以使用這種方式,如果使用這種方式,必須使用Connet方法與服務器對接。例如:

<span style="white-space:pre">		</span>IPAddress[] address = Dns.GetHostAddress(Dns.GetHostName());
		IPEndPoint iep = new IPEndPoint(Address[0],51888);
		TcpClient tcpClient = new TcpClient(iep);
		tcpClient.Connet("www.abcd.com", 51888);
4)TcpClient(string hostname, int port)

這是最方便的一種構造, hostname表示要連接的遠程主機的DNS名,port表示要連接的遠程主機端口號。該構造會自動分配最適合的本地主機ip和端口號,並對dns進行解析,然後與遠程服務器進行連接,例如:

<span style="white-space:pre">		</span>TcpClient tcpClient =new TcpClient("www.abcd.com", 51888);

他相當於:

<span style="white-space:pre">		</span>TcpClient tcpClient = new TcpClient();
		tcpClient.Connet("www.abcd.com", 51888);

一旦創建了TcpClient對象,就可以使用GetStream方法得到NetworkStream對象,然後再利用NetworkStream,向遠程主機發送數據,或接收數據。

值得注意的,使用NetworkStream仍然複雜,在之後,我們會用BinaryReader,BinaryWriter,StreamReader, StreamWriter方法進行io處理。

TcpClient類中的常用屬性:

Client 獲取或設置基礎套接字

LingerState 獲取或設置套接字保持連接的時間

NoDelay 獲取或設置一個值, 該值在發送或接受緩衝區未滿時禁用延遲

ReceiveBufferSize 獲取或設置Tcp接受緩衝區的大小

ReceiveTimeout 獲取或設置套接字接受數據的超時時間

SendBufferSize 獲取或設置Tcp發送緩衝區的大小

SendTimeout  獲取或設置套接字發送數據的超時時間


TcpClient類中的常用方法

Close 釋放TcpClient實例,而不是關閉基礎連接

Connet 用指定的主機名和端口號將客戶端連接到Tcp主機

BeginConnet 開始一個對遠程主機連接的異步請求

EndConnet 異步接收傳入的連接嘗試

GetStream 獲取能夠發送和接受數據的NetworkStream對象


四,Tcp基礎編程的一般步驟

1. 編寫服務器端程序的一般步驟

1)創建一個TcpListener的對象,然後調用Start方法開始監聽。

典型代碼如下:

	//聲明
		private IPAddress localAddress;
		private const int port = 51888;
		private TcpListener tcpListener;

		//初始化
		IPAddress [] listenIP = Dns,GetHosyAddress("");
		localAddress = listenIp[0];
		//創建TcpListener對象,開始監聽
		tcpListener = new TcpListener(localAddress, port);
		tcpListener.Start();

2) 再單獨的線程中,首先循環調用AcceptTcpClient方法接受客戶端的連接請求,從該方法的返回結果中得到與改客戶端相對應的TcpClient對象,並利用GetStream方法得到NetworkStream對象,然後再利用該對像得到其他使用更方便的對象如:Binary Reader , BinaryWriter,爲進一步通信做準備。典型代碼:

//啓動一個線程來接受請求
		Thread threadAccept = new Thread(AcceptClientConnet);
		threadAccept.Start();
		//線程執行AcceptClientConnet方法
		private void AcceptClientConnet()
		{
			while(true)
			{
				try{
					tcpClient = tcpListener.AcceptTcpClient();
					if(tcpClient != null)
					{
						statueStripInfo,Invoke(shwStatusInfoCallBack,"接受了一個連接“);
						networkStream = tcpClient.GetStream();
						br = new BinaryReader(networkStream);
						bw = new BinaryWriter(networkStream);
					}
					catch
					{
							...
					}
			}
		}
3)沒得到一個新的TcpClient對象,就創建一個與該客戶對應的線程,在線程中與對應的客戶進行通信。

		read threadReceive  = new Thread (ReceiveMessage);
		threadReceive.Start();

其中ReceiveMessage是接受消息的方法。

4)根據傳送信息的情況確定是否關閉和客戶的連接

關鍵代碼如下

		(br != null)
		{
			br.close();
		}
		if(bw != null)
		{
			bw.Close();
		}
		if(tcpClient != null)
		{
			tcpClient.Close();
		}
		toolStripStatusInfo,Text = "斷開連接";
在連接關閉前,要停止流。

在停止服務後,服務器可以斷開監聽

	tcpListener.Stop();
2.編寫客戶端的一般步驟

1)利用TcpClient構造一個對象

		private TcpClient tcpClient;
		tcpClient = new TcpClient();


2) 使用Connet方法進行連接

		tcpClient.Connet(remoteHost.HostName, int,Parse(tbxPort.Text));


3) 利用tcpClient對象的GetStream得到NetworkStream,然後得到更爲實用的流方法

		if(tcpClient != null)
		{
			networkStream = tcpClient.GetStream();
			br. = new BinaryReader(networkStream);
			bw = new BinaryWriter(networkStream);
		}


4) 創建一個線程監聽指定的端口,循環接受並處理服務器發來的信息

		read threadReceive = new Thread(ReceiveMessage);
		threadReceive.Start();

這裏的ReveiveMessage方法是線程執行的,用於循環接受信息,典型代碼結構如下:

	private void ReceiveMessage()
		{
			while(true)
			{
				try
				{
					string rcvMsgStr = br.ReadString();//從流中讀取信息
					if(rcvMsgStr != null)
					{
						lstbxMsgView,Invoke(shwMsgforViewCallBack, rcvNsgStr);
					}
				}
				catch
				{
					break;
				}

			}
	

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章