程式簡介
基於網友的提議,最近有點時間,便打算給之前的聊天程式增加一個功能-檔案發送.
原理
檔案發送跟字串資訊發送的原理其實是一樣的,都是通過將需要發送的資料轉換成電腦可以識別的位元組數組來發送.當然,電腦本身並不知道你發送的是字串資訊還是檔案,所以我們首先需要告訴電腦哪個發送的是檔案,哪個是字串資訊;這裡分別給它們的位元組數組附加了一個類型標識符:字串資訊的位元組數群組識別碼為0,檔案的位元組數群組識別碼為1.當一端將檔案發送過去後,另一端則首先判斷髮送過來的類型標識符(1或者0),然後再調用相應的方法將擷取的位元組數群組轉換成人可以看懂的字串資訊或檔案.
介面設計 - 用戶端
這裡新增了3個控制項,用於實現檔案發送功能.
Textbox: 檔案名稱name: txtFileName
Button: 選擇檔案name: btnSelectFile 傳送檔案name: btnSendFile
代碼實施 - 用戶端
首先,我們需要寫一個選擇傳送檔案的方法,這裡使用了最常見OpenFileDialog方法,用於選取需要發送的檔案.
string filePath = null; //檔案的全路徑string fileName = null; //檔案名稱(不包含路徑) //選擇要發送的檔案private void btnSelectFile_Click(object sender, EventArgs e){ OpenFileDialog ofDialog = new OpenFileDialog(); if (ofDialog.ShowDialog(this) == DialogResult.OK) { fileName = ofDialog.SafeFileName; //擷取選取檔案的檔案名稱 txtFileName.Text = fileName; //將檔案名稱顯示在文字框上 filePath = ofDialog.FileName; //擷取包含檔案名稱的全路徑 }}
選取檔案之後,通過FileStream來讀取檔案位元組數組,然後在讀到的檔案位元組數組的索引為0的位置上增加了一個檔案標識符1,目的是告知電腦該位元組數組為檔案位元組數組.這裡在向服務端傳送檔案的同時也發送了一個檔案名稱(字串資訊),目的是在服務端成功接收檔案後,自動將原檔案名稱附加上去.
/// <summary> /// 傳送檔案的方法 /// </summary> /// <param name="fileFullPath">檔案全路徑(包含檔案名稱)</param> private void SendFile(string fileFullPath) { if (fileFullPath == null) { MessageBox.Show("請選擇需要發送的檔案!"); return; } else if (fileFullPath != null) { //建立檔案流 FileStream fs = new FileStream(fileFullPath, FileMode.Open); //建立一個記憶體緩衝區 用於臨時儲存讀取到的檔案位元組數組 byte[] arrClientFile = new byte[10 * 1024 * 1024]; //從檔案流中讀取檔案的位元組數組 並將其存入到緩衝區arrClientFile中 int realLength = fs.Read(arrClientFile, 0, arrClientFile.Length); //realLength 為檔案的真實長度 byte[] arrClientSendedFile = new byte[realLength + 1]; //給新增標識符(實際要發送的)位元組數組的索引為0的位置上增加一個標識符1 arrClientSendedFile[0] = 1; //告訴機器該發送的位元組數組為檔案 //將真實的檔案位元組數組完全拷貝到需要發送的檔案位元組數組中,從索引為1的位置開始存放,存放的位元組長度為realLength. //實際發送的檔案位元組數組 arrSendedFile包含了2部分 索引為0位置上的標識符1 以及 後面的真實檔案位元組數組 Buffer.BlockCopy(arrClientFile, 0, arrClientSendedFile, 1, realLength); //調用發送資訊的方法 將檔案名稱發送出去 ClientSendMsg(fileName); socketClient.Send(arrClientSendedFile); txtMsg.AppendText("SoFlash:" + GetCurrentTime() + "\r\n您發送了檔案:" + fileName + "\r\n"); } }
代碼實施 - 服務端
由於新增了一個類型標識符,這裡便將之前服務端接收資訊的方法稍微改了下. 當服務端接收到含有標識符為0的位元組數組,則直接將位元組數群組轉換成字串,並附加到聊天資訊文字框上.若接收到的位元組數組含有標識符1(即檔案),則調用儲存檔案的方法SaveFile()將其儲存為原檔案;
string strSRecMsg = null; /// <summary> /// 接收用戶端發來的資訊 /// </summary> /// <param name="socketClientPara">用戶端通訊端的委派物件</param> private void ServerRecMsg(object socketClientPara) { Socket socketServer = socketClientPara as Socket; while (true) { int length = 0; //建立一個接收用的記憶體緩衝區 大小為10M位元組數組 byte[] arrServerRecMsg = new byte[10 * 1024 * 1024]; try { //擷取接收的資料,並存入記憶體緩衝區 返回一個位元組數組的長度 length = socketServer.Receive(arrServerRecMsg); } catch (Exception ex) { txtMsg.AppendText("系統異常訊息:" + ex.Message); break; } //判斷髮送過來的資料是檔案還是普通文字資訊 if (arrServerRecMsg[0] == 0) //0為文字資訊 { //將位元組數組 轉換為人可以讀懂的字串 strSRecMsg = Encoding.UTF8.GetString(arrServerRecMsg, 1, length - 1);//真實有用的文本資訊要比接收到的少1(標識符) //將接收到的資訊附加到文字框txtMsg上 txtMsg.AppendText("SoFlash:" + GetCurrentTime() + "\r\n" + strSRecMsg + "\r\n"); } //如果發送過來的資料是檔案 if (arrServerRecMsg[0] == 1) { SaveFile(arrServerRecMsg, length - 1);//同樣實際檔案長度需要-1(減去標識符) } } }
SaveFile()方法裡包含了FileStream的Write()方法,用於將已接收的檔案位元組數組儲存為實際檔案,這裡Write()方法傳入了3個參數,檔案的位元組數組,需要拷貝檔案位元組數組的初始位置以及拷貝的位元組數組的長度[具體介紹可以看這裡].在擷取到檔案的同時,這裡也擷取了檔案名稱(字串資訊),用於附加到另存新檔對話方塊的檔案名稱上;同時截取了檔案名稱的尾碼,作為需要儲存的檔案類型.最後,在檔案成功儲存到服務端所在的電腦的同時,在聊天內容文字框中附加了成功接收的檔案名稱和檔案的儲存路徑.
/// <summary>/// 儲存接收檔案的方法 包含一個位元組數組參數 和 檔案的長度/// </summary>/// <param name="arrFile">位元組數組參數</param>/// <param name="fileLength">檔案的長度</param>private void SaveFile(byte[] arrFile, int fileLength){ SaveFileDialog sfDialog = new SaveFileDialog(); //建立一個用於儲存檔案的對話方塊 sfDialog = new SaveFileDialog(); //擷取檔案名稱的尾碼 比如文字檔尾碼 .txt string fileNameSuffix = strSRecMsg.Substring(strSRecMsg.LastIndexOf(".")); sfDialog.Filter = "(*" + fileNameSuffix + ")|*" + fileNameSuffix + ""; //檔案類型 sfDialog.FileName = strSRecMsg; //檔案名稱 //如果點擊了對話方塊中的儲存檔案按鈕 if (sfDialog.ShowDialog(this) == DialogResult.OK) { string savePath = sfDialog.FileName; //擷取檔案的全路徑 //儲存檔案 FileStream fs = new FileStream(savePath, FileMode.Create); //Write()方法需要傳入3個參數,檔案的位元組數組,開始寫入位元組的索引位置以及位元組數組的長度 fs.Write(arrFile, 1, fileLength); string fName = savePath.Substring(savePath.LastIndexOf("\\") + 1); //檔案名稱 不帶路徑 string fPath = savePath.Substring(0, savePath.LastIndexOf("\\")); //檔案路徑 不帶檔案名稱 txtMsg.AppendText("天之涯:" + GetCurrentTime() + "\r\n您成功接收了檔案" + fName + "\r\n儲存路徑為:" + fPath + "\r\n"); }}
運行程式
首先,啟動服務端並持續監聽用戶端對其的串連,當用戶端成功串連上服務端之後,兩端便可以開始通訊了.
兩端建立串連之後,便可以開始互相通訊了.
簡單的兩端對聊之後, 本人便打算髮送個檔案過去.
選取了一本張道真的文法書,尾碼為.pdf(檔案類型)
當點擊傳送檔案按鈕後,用戶端聊天內容中顯示"您發送了檔案:張道真實用英語文法.pdf".
這時服務端收到檔案後,程式彈出一個另存新檔對話方塊,用於儲存已接收的檔案.這裡我們可以看到系統自動附加上了檔案名稱和檔案類型.
當服務端使用者接收並儲存檔案之後,聊天內容裡顯示"您成功接收了檔案張道真實用英語文法.pdf" 以及檔案的儲存路徑.
附上原始碼
服務端 ChatServer2.zip 用戶端 ChatClient2.zip
相關推薦
- 基於C# Winform的簡易聊天程式[第一篇-兩端通訊]
- 基於C# Winform的簡易聊天程式[第三篇-資訊群發]