在前面的幾篇中,講了關於套接字Socket以及利用套接字助手類來進行服務 端和客戶端之間的通信,在此中間並沒有對發送的信息進行任何的處理。在本篇 中將會講一下TCP通信時的信息邊界問題。
通過套接字或其助手類來接收信息時,是從緩存區裡一次性把全部的緣存都 讀取出來,只要你設置的緩存夠大,它就能讀取這麼多,這樣就會導致這樣的情 況出現。如果服務端連續發送信息到客戶端,如我連續發送字符串 “message 1”、“message 2”、“message 3”、“message 4”、“message 5”,我預想的是 在客戶端也是能夠收到這樣的五個完整的字符串,如果用前二篇中講的方法,在 同台機子上測試的話,是正常的,因為同台機子上網絡信息傳送出現的異常會比 較少,但如果把客戶端與服務端部署在不同的機器上,則會出現一些異想不到的 現象。你會發現接收到的字符都被打亂了,會出現如“3message 4” 的字符串,這樣的話,我們就不能把服務端發送的信息正常的還原。這個就是消 息的邊界問題,要解決這個問題,方法有很多,現抽取其中幾個來講一下:
1、固定尺寸的消息
這是最簡單但也是最昂貴的解決TCP消息問題的方案。就是要設計一種協議, 永遠以固定的長度傳遞消息,通過將所有的消息都設置為固定的尺寸,在從遠程 設備中接收到完整的消息時,TCP接收程序就能夠了解發送的情況了。用這各地 意味著必須將短消息加長,造成網絡帶寬資源的浪費。
2、使用消息尺寸信息
這個方案允許使用可變長度的消息,惟一的不足就是接收端的遠程設置必須 了解每一個變長消息的確切長度。具體的方法是,在發送消息的時候,一起發送 該消息的長度。那麼在客戶端接收的時候就能知道該消息的長度是多少,再來讀 取消息。
3、使用消息標記
該方案使用預先確定的一個字符(或多個字符)來指定消息的結束,通過這 種方式來分隔不同的消息。但用這種方法必須對所接收到的每一個字符進行檢查 以便確定為結束標記,這對於大型消息來說,可能導致系統性能的下降,不過對 於C#語言來說,提供了一些類,能夠用於簡化這個過程,那就是System.IO命名 空間流類,下面我們也著重來講一下這各方法。至於第二種方法,將在下一篇中 與在消息中傳送實體類信息相結合來講述。
在上一篇中,我們已經提到NetworkStream類,利用該類來傳送和接收消息。 在這裡,再提一下另外的二個流類,那就是StreamReader和StreamWriter,這二 個類也可用於TCP協議發送和接收文本消息。
當我們得到Socket連接的一個NetworkStream對象時,可以通過下面的方法得 到StreamWriter和StreamReader對象。
1NetworkStream ns = s.GetStream();
2 StreamReader sr = new StreamReader(ns);
3 StreamWriter sw = new StreamWriter(ns);
這樣我們 就可以通過StreamWriter來發送消息,通過StreamReader來接收消息:
1//發送消息
2string welcome = "Welcome to my test sever ";
3
4 sw.WriteLine(welcome);
5 sw.Flush();
接收消息:
1//接收消息
2string data = "";
3data = sr.ReadLine();
這樣是不是比以前的做法更簡單了,而且同時也解決了TCP消息邊界問題了。
但是用這各方法必須得注意以下二點:
1、這種方法其實就是利用消息標記來解決邊界問題的,這裡的標記就是換行 符,也就是說,StreamWriter中的WriteLine()和StreamReader中的ReadLine() 一定要成對使用,不然如果發送的信息中沒有換行符,則客戶機中用ReadLine() 讀取信息時,將無法結束,將堵塞程序的執行,一直等待換行符。
2、另外還要保證在發送的消息本身不應該帶有換行符,如果消息本身帶有換 行符,則這些換行符將被ReadLine()方法錯誤地作為標記,影響數據的完整性。
關於TCP消息邊界處理就暫時講到這裡了,由於自己的理解也不夠深,難免會 出現錯誤,請各位及時糾正。在下一篇中,將講述傳送實體類方面的問題。