前面兩篇文章所使用的范例都是傳輸字符串,有的時候我們可能會想在服務端和客戶端之間傳遞文件。比如,考慮這樣一種情況,假如客戶端顯示了一個菜單,當我們輸入S1、S2或S3(S為Send縮寫)時,分別向服務端發送文件Client01.jpg、Client02.jpg、Client03.jpg;當我們輸入R1、R2或R3時(R為Receive縮寫),則分別從服務端接收文件Server01.jpg、Server02.jpg、Server03.jpg。那麼,我們該如何完成這件事呢?此時可能有這樣兩種做法:
現在我們只關注於上面的數據端口,回憶一下在第二篇中我們所總結的,可以得出:當我們使用上面的方法一時,服務端的數據端口可以為多個客戶端的多次請求服務;當我們使用方法二時,服務端只為一個客戶端的一次請求服務,但是因為每次請求都會重新開辟端口,所以實際上還是相當於可以為多個客戶端的多次請求服務。同時,因為它只為一次請求服務,所以我們在數據端口上傳輸文件時無需采用異步傳輸方式。但在控制端口我們仍然需要使用異步方式。
從上面看出,第一種方式要好得多,但是我們將采用第二種方式。至於原因,你可以回顧一下C#網絡編程(基本概念和操作) 一中關於聊天程序模式的講述,因為接下來一篇文章我們將創建一個聊天程序,而這個聊天程序采用第三種模式,所以本文的練習實際是對下一篇的一個鋪墊。
我們先看一下發送文件的情況,如果我們想將文件client01.jpg由客戶端發往客戶端,那麼流程是什麼:
此時,我們訂立的發送文件協議為:[file=Client01.jpg, mode=send, port=8005]。但是,由於它是一個普通的字符串,在上一篇中,我們采用了正則表達式來獲取其中的有效值,但這顯然不是一種好辦法。因此,在本文及下一篇文章中,我們采用一種新的方式來編寫協議:XML。對於上面的語句,我們可以寫成這樣的XML:
<protocol><file name="client01.jpg" mode="send" port="8005" /></protocol>
這樣我們在服務端就會好處理得多,接下來我們來看一下接收文件的流程及其協議。
NOTE:這裡說發送、接收文件是站在客戶端的立場說的,當客戶端發送文件時,對於服務器來收,則是接收文件。
接收文件與發送文件實際上完全類似,區別只是由客戶端向網絡流寫入數據,還是由服務端向網絡流寫入數據。
和上面一章一樣,在開始編寫實際的服務端客戶端代碼之前,我們首先要編寫處理協議的類,它需要提供這樣兩個功能:1、方便地幫我們獲取完整的協議信息,因為前面我們說過,服務端可能將客戶端的多次獨立請求拆分或合並。比如,客戶端連續發送了兩條控制信息到服務端,而服務端將它們合並了,那麼則需要先拆開再分別處理。2、方便地獲取我們所想要的屬性信息,因為協議是XML格式,所以還需要一個類專門對XML進行處理,獲得字符串的屬性值。
我們先看下ProtocalHandler,它與上一篇中的RequestHandler作用相同。需要注意的是必須將它聲明為實例的,而非靜態的,這是因為每個TcpClient都需要對應一個ProtocalHandler,因為它內部維護的patialProtocal不能共享,在協議發送不完整的情況下,這個變量用於臨時保存被截斷的字符串。
public class ProtocolHandler {
private string partialProtocal; // 保存不完整的協議
public ProtocolHandler() {
partialProtocal = "";
}
public string[] GetProtocol(string input) {
return GetProtocol(input, null);
}
// 獲得協議
private string[] GetProtocol(string input, List<string> outputList) {
if (outputList == null)
outputList = new List<string>();
if (String.IsNullOrEmpty(input))
return outputList.ToArray();
if (!String.IsNullOrEmpty(partialProtocal))
input = partialProtocal + input;
string pattern = "(^<protocol>.*?</protocol>)";
// 如果有匹配,說明已經找到了,是完整的協議
if (Regex.IsMatch(input, pattern)) {
// 獲取匹配的值
string match = Regex.Match(input, pattern).Groups[0].Value;
outputList.Add(match);
partialProtocal = "";
// 縮短input的長度
input = input.Substring(match.Length);
// 遞歸調用
GetProtocol(input, outputList);
&