可以看到,盡管上面將消息分成了三條單獨發送,但是服務端卻將後兩條合並成了一條。對於這些情 況,我們可以這樣處理:就好像HTTP協議一樣,在實際的請求和應答內容之前包含了HTTP頭,其中是一些 與請求相關的信息。我們也可以訂立自己的協議,來解決這個問題,比如說,對於上面的情況,我們就可 以定義這樣一個協議:
[length=XXX]:其中xxx是實際發送的字符串長度(注意不是字節數組buffer的長度),那麼對於上面 的請求,則我們發送的數據為:“[length=25]Welcome to TraceFact.Net!”。而服務端接收字符串之後 ,首先讀取這個“元數據”的內容,然後再根據“元數據”內容來讀取實際的數據,它可能有下面這樣兩 種情況:
NOTE:我覺得這裡借用“元數據”這個術語還算比較恰當,因為“元數據”就是用來描述數據的數據 。
“[“”]”中括號是完整的,可以讀取到length的字節數。然後根據這個數值與後面的字符串長度相 比,如果相等,則說明發來了一條完整信息;如果多了,那麼說明接收的字節數多了,取出合適的長度, 並將剩余的進行緩存;如果少了,說明接收的不夠,那麼將收到的進行一個緩存,等待下次請求,然後將 兩條合並。
“[”“]”中括號本身就不完整,此時讀不到length的值,因為中括號裡的內容被截斷了,那麼將讀 到的數據進行緩存,等待讀取下次發送來的數據,然後將兩次合並之後再按上面的方式進行處理。
接下來我們來看下如何來進行實際的操作,實際上,這個問題已經不屬於C#網絡編程的內容了,而完 全是對字符串的處理。所以我們不再編寫服務端/客戶端代碼,直接編寫處理這幾種情況的方法:
public class RequestHandler {
private string temp = string.Empty;
public string[] GetActualString(string input) {
return GetActualString(input, null);
}
private string[] GetActualString(string input, List<string> outputList) {
if (outputList == null)
outputList = new List<string>();
if (!String.IsNullOrEmpty(temp))
input = temp + input;
string output = "";
string pattern = @"(?<=^\[length=)(\d+)(?=\])";
int length;
if (Regex.IsMatch(input, pattern)) {
Match m = Regex.Match(input, pattern);
// 獲取消息字符串實際應有的長度
length = Convert.ToInt32(m.Groups[0].Value);
// 獲取需要進行截取的位置
int startIndex = input.IndexOf(']') + 1;
// 獲取從此位置開始後所有字符的長度
output = input.Substring(startIndex);
if (output.Length == length) {
// 如果output的長度與消息字符串的應有長度相等
// 說明剛好是完整的一條信息
outputList.Add(output);
temp = "";
} else if (output.Length < length) {
// 如果之後的長度小於應有的長度,
// 說明沒有發完整,則應將整條信息,包括元數據,全部緩存
// 與下一條數據合並起來再進行處理
temp = input;
// 此時程序應該退出,因為需要等待下一條數據到來才能繼續處理
} else if (output.Length > length) {
// 如果之後的長度大於應有的長度,
// 說明消息發完整了,但是有多余的數據
// 多余的數據可能是截斷消息,也可能是多條完整消息
// 截取字符串
output = output.Substring(0, length);
outputList.Add(output);
temp = "";
// 縮短input的長度
input = input.Substring(startIndex + length);
// 遞歸調用
GetActualString(input, outputList);
}
} else { // 說明“[”,“]”就不完整
temp = input;
}
return outputList.ToArray();
}
}