基於C# Winform的簡易聊天程序[socket-文件發送]

程序簡介

基於網友的提議,最近有點時間,便打算給之前的聊天程序增加一個功能-文件發送.

 

原理

文件發送跟字符串信息發送的原理其實是一樣的,都是通過將需要發送的數據轉換成計算機可以識別的字節數組來發送.當然,計算機本身並不知道你發送的是字符串信息還是文件,所以我們首先需要告訴計算機哪個發送的是文件,哪個是字符串信息;這裏分別給它們的字節數組附加了一個類型標識符:字符串信息的字節數組標識符爲0,文件的字節數組標識符爲1.當一端將文件發送過去後,另一端則首先判斷髮送過來的類型標識符(1或者0),然後再調用相應的方法將獲取的字節數組轉換成人可以看懂的字符串信息或文件.

 

界面設計 - 客戶端

這裏新增了3個控件,用於實現文件發送功能.

Textbox: 文件名name: txtFileName

Button:  選擇文件name: btnSelectFile  發送文件name: btnSendFile

01-界面設計

 

代碼實施 - 客戶端

首先,我們需要寫一個選擇發送文件的方法,這裏使用了最常見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;     //獲取包含文件名的全路徑
    }
}

選取文件之後,我們先發送文件的名稱和長度, 然後再發送文件.

        /// <summary>
        /// 發送文件的方法
        /// </summary>
        /// <param name="fileFullPath">文件全路徑(包含文件名稱)</param>
        private void SendFile(string fileFullPath)
        {
            if (string.IsNullOrEmpty(fileFullPath))
            {
                MessageBox.Show(@"請選擇需要發送的文件!");
                return;
            }

            //發送文件之前 將文件名字和長度發送過去
            long fileLength = new FileInfo(fileFullPath).Length;
            string totalMsg = string.Format("{0}-{1}", fileName, fileLength);
            ClientSendMsg(totalMsg, 2);


            //發送文件
            byte[] buffer = new byte[SendBufferSize];

            using (FileStream fs = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read))
            {
                int readLength = 0;
                bool firstRead = true;
                long sentFileLength = 0;
                while ((readLength = fs.Read(buffer, 0, buffer.Length)) > 0 && sentFileLength < fileLength)
                {
                    sentFileLength += readLength;
                    //在第一次發送的字節流上加個前綴1
                    if (firstRead)
                    {
                        byte[] firstBuffer = new byte[readLength + 1];
                        firstBuffer[0] = 1; //告訴機器該發送的字節數組爲文件
                        Buffer.BlockCopy(buffer, 0, firstBuffer, 1, readLength);

                        socketClient.Send(firstBuffer, 0, readLength + 1, SocketFlags.None);

                        firstRead = false;
                        continue;
                    }
                    //之後發送的均爲直接讀取的字節流
                    socketClient.Send(buffer, 0, readLength, SocketFlags.None);
                }
                fs.Close();
            }
            txtMsg.AppendText("SoFlash:" + GetCurrentTime() + "\r\n您發送了文件:" + fileName + "\r\n");
        }

代碼實施 - 服務端

服務端接受字符串信息,文件名稱和長度以及文件

        string strSRecMsg = null;
        /// <summary>
        /// 接收客戶端發來的信息
        /// </summary>
        private void ServerRecMsg(object socketClientPara)
        {
            Socket socketServer = socketClientPara as Socket;

            long fileLength = 0;
            while (true)
            {
                int firstReceived = 0;
                byte[] buffer = new byte[ReceiveBufferSize];
                try
                {
                    //獲取接收的數據,並存入內存緩衝區  返回一個字節數組的長度
                    if (socketServer != null) firstReceived = socketServer.Receive(buffer);

                    if (firstReceived > 0) //接受到的長度大於0 說明有信息或文件傳來
                    {
                        if (buffer[0] == 0) //0爲文字信息
                        {
                            strSRecMsg = Encoding.UTF8.GetString(buffer, 1, firstReceived - 1);//真實有用的文本信息要比接收到的少1(標識符)
                            txtMsg.AppendText("SoFlash:" + GetCurrentTime() + "\r\n" + strSRecMsg + "\r\n");
                        }
                        if (buffer[0] == 2)//2爲文件名字和長度
                        {
                            string fileNameWithLength = Encoding.UTF8.GetString(buffer, 1, firstReceived - 1);
                            strSRecMsg = fileNameWithLength.Split('-').First(); //文件名
                            fileLength = Convert.ToInt64(fileNameWithLength.Split('-').Last());//文件長度
                        }
                        if (buffer[0] == 1)//1爲文件
                        {
                            string fileNameSuffix = strSRecMsg.Substring(strSRecMsg.LastIndexOf('.')); //文件後綴
                            SaveFileDialog sfDialog = new SaveFileDialog()
                            {
                                Filter = "(*" + fileNameSuffix + ")|*" + fileNameSuffix + "", //文件類型
                                FileName = strSRecMsg
                            };

                            //如果點擊了對話框中的保存文件按鈕 
                            if (sfDialog.ShowDialog(this) == DialogResult.OK)
                            {
                                string savePath = sfDialog.FileName; //獲取文件的全路徑
                                //保存文件
                                int received = 0;
                                long receivedTotalFilelength = 0;
                                bool firstWrite = true;
                                using (FileStream fs = new FileStream(savePath, FileMode.Create, FileAccess.Write))
                                {
                                    while (receivedTotalFilelength < fileLength) //之後收到的文件字節數組
                                    {
                                        if (firstWrite)
                                        {
                                            fs.Write(buffer, 1, firstReceived - 1); //第一次收到的文件字節數組 需要移除標識符1 後寫入文件
                                            fs.Flush();

                                            receivedTotalFilelength += firstReceived - 1;

                                            firstWrite = false;
                                            continue;
                                        }
                                        received = socketServer.Receive(buffer); //之後每次收到的文件字節數組 可以直接寫入文件
                                        fs.Write(buffer, 0, received);
                                        fs.Flush();

                                        receivedTotalFilelength += received;
                                    }
                                    fs.Close();
                                }

                                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");
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    txtMsg.AppendText("系統異常消息:" + ex.Message);
                    break;
                }
            }
        }

運行程序

首先,啓動服務端並持續監聽客戶端對其的連接,當客戶端成功連接上服務端之後,兩端便可以開始通信了.

02-啓動服務端

 

02-連接服務端

 

02-開始通信

 

兩端建立連接之後,便可以開始互相通信了.

03-程序運行

 

03-程序運行02

 

簡單的兩端對聊之後, 本人便打算髮送個文件過去.

03-程序運行03

 

選取了一本張道真的語法書,後綴爲.pdf(文件類型)

03-程序運行04

 

當點擊發送文件按鈕後,客戶端聊天內容中顯示"您發送了文件:張道真實用英語語法.pdf".

03-程序運行05

 

這時服務端收到文件後,程序彈出一個另存爲對話框,用於保存接收到的文件.這裏我們可以看到系統自動附加上了文件名和保存類型.

03-程序運行06

 

當服務端用戶接收並保存文件之後,聊天內容裏顯示"您成功接收了文件張道真實用英語語法.pdf" 以及文件的保存路徑.

03-程序運行08

發佈了49 篇原創文章 · 獲贊 55 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章