C#網路編程(接收檔案) - Part.5

來源:互聯網
上載者:User

標籤:http   使用   os   檔案   art   re   

C#網路編程(接收檔案) - Part.5

這篇文章將完成Part.4中剩餘的部分,它們本來是一篇完整的文章,但是因為上一篇比較長,合并起來頁數太多,瀏覽起來可能會比較不方便,我就將它拆為兩篇了,本文便是它的後半部分。我們繼續進行上一篇沒有完成的步驟:用戶端接收來自服務端的檔案。

4.用戶端接收檔案4.1服務端的實現

對於服務端,我們只需要實現上一章遺留的sendFile()方法就可以了,它起初在handleProtocol中是注釋掉的。另外,由於建立串連、擷取流等操作與receiveFile()是沒有區別的,所以我們將它提出來作為一個公用方法getStreamToClient()。下面是服務端的代碼,只包含新增改過的代碼,對於原有方法我只給出了簽名:

class Server {
    static void Main(string[] args) {
        Console.WriteLine("Server is running ... ");
        IPAddress ip = IPAddress.Parse("127.0.0.1");
        TcpListener listener = new TcpListener(ip, 8500);

        listener.Start();           // 開啟對控制連接埠 8500 的偵聽
        Console.WriteLine("Start Listening ...");

        while (true) {
            // 擷取一個串連,同步方法,在此處中斷
            TcpClient client = listener.AcceptTcpClient();  
            RemoteClient wapper = new RemoteClient(client);
            wapper.BeginRead();
        }
    }
}

public class RemoteClient {
    // 欄位 略

    public RemoteClient(TcpClient client) {}

    // 開始進行讀取
    public void BeginRead() { }

    // 再讀取完成時進行回調
    private void OnReadComplete(IAsyncResult ar) { }

    // 處理protocol
    private void handleProtocol(object obj) {
        string pro = obj as string;
        ProtocolHelper helper = new ProtocolHelper(pro);
        FileProtocol protocol = helper.GetProtocol();

        if (protocol.Mode == FileRequestMode.Send) {
            // 用戶端傳送檔案,對服務端來說則是接收檔案
            receiveFile(protocol);
        } else if (protocol.Mode == FileRequestMode.Receive) {
            // 用戶端接收檔案,對服務端來說則是傳送檔案
            sendFile(protocol);
        }
    }

    // 傳送檔案
    private void sendFile(FileProtocol protocol) {
        TcpClient localClient;
        NetworkStream streamToClient = getStreamToClient(protocol, out localClient);

        // 獲得檔案的路徑
        string filePath = Environment.CurrentDirectory + "/" + protocol.FileName;

        // 建立檔案流
        FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
        byte[] fileBuffer = new byte[1024];     // 每次傳1KB
        int bytesRead;
        int totalBytes = 0;

        // 建立擷取檔案發送狀態的類
        SendStatus status = new SendStatus(filePath);

        // 將檔案流轉寫入網路流
        try {
            do {
                Thread.Sleep(10);           // 為了更好的視覺效果,暫停10毫秒:-)
                bytesRead = fs.Read(fileBuffer, 0, fileBuffer.Length);
                streamToClient.Write(fileBuffer, 0, bytesRead);
                totalBytes += bytesRead;            // 發送了的位元組數
                status.PrintStatus(totalBytes); // 列印發送狀態
            } while (bytesRead > 0);
            Console.WriteLine("Total {0} bytes sent, Done!", totalBytes);
        } catch {
            Console.WriteLine("Server has lost...");
        }

        streamToClient.Dispose();
        fs.Dispose();
        localClient.Close();
    }

    // 接收檔案
    private void receiveFile(FileProtocol protocol) { }

    // 擷取串連到遠端流 -- 公用方法
    private NetworkStream getStreamToClient(FileProtocol protocol, out TcpClient localClient) {
        // 擷取遠程用戶端的位置
        IPEndPoint endpoint = client.Client.RemoteEndPoint as IPEndPoint;
        IPAddress ip = endpoint.Address;

        // 使用新連接埠號碼,獲得遠程用於接收檔案的連接埠
        endpoint = new IPEndPoint(ip, protocol.Port);

        // 串連到遠程用戶端
        try {
            localClient = new TcpClient();
            localClient.Connect(endpoint);
        } catch {
            Console.WriteLine("無法串連到用戶端 --> {0}", endpoint);
            localClient = null;
            return null;
        }

        // 擷取傳送檔案的流
        NetworkStream streamToClient = localClient.GetStream();
        return streamToClient;
    }

    // 隨機擷取一個圖片名稱
    private string generateFileName(string fileName) {}
}

服務端的sendFile方法和用戶端的SendFile()方法完全類似,上面的代碼幾乎是一次編寫成功的。另外注意我將用戶端使用的SendStatus類也拷貝到了服務端。接下來我們看下用戶端。

4.2用戶端的實現

首先要注意的是用戶端的SendFile()接收的參數是檔案全路徑,但是在寫入到協議時只擷取了路徑中的檔案名稱。這是因為服務端不需要知道檔案在用戶端的路徑,所以協議中唯寫檔案名稱;而為了使用戶端的SendFile()方法更通用,所以它接收本地檔案的全路徑。

用戶端的ReceiveFile()的實現也和服務端的receiveFile()方法類似,同樣,由於要儲存到本地,為了避免檔案名稱重複,我將服務端的generateFileName()方法複製了過來。

public class ServerClient :IDisposable {
    // 欄位略

    public ServerClient() {}

    // 發送訊息到服務端
    public void SendMessage(string msg) {}

    // 傳送檔案 - 非同步方法呼叫
    public void BeginSendFile(string filePath) {    }

    private void SendFile(object obj) { }
    
    // 傳送檔案 -- 同步方法
    public void SendFile(string filePath) {}
    
    // 接收檔案 -- 非同步方法呼叫
    public void BeginReceiveFile(string fileName) {
        ParameterizedThreadStart start =
            new ParameterizedThreadStart(ReceiveFile);
        start.BeginInvoke(fileName, null, null);
    }

    public void ReceiveFile(object obj) {
        string fileName = obj as string;
        ReceiveFile(fileName);
    }

    // 接收檔案 -- 同步方法
    public void ReceiveFile(string fileName) {

        IPAddress ip = IPAddress.Parse("127.0.0.1");
        TcpListener listener = new TcpListener(ip, 0);
        listener.Start();

        // 擷取本地偵聽的連接埠號碼
        IPEndPoint endPoint = listener.LocalEndpoint as IPEndPoint;
        int listeningPort = endPoint.Port;

        // 擷取發送的協議字串
        FileProtocol protocol =
            new FileProtocol(FileRequestMode.Receive, listeningPort, fileName);
        string pro = protocol.ToString();

        SendMessage(pro);       // 發送協議到服務端

        // 中斷,等待遠端連線
        TcpClient localClient = listener.AcceptTcpClient();
        Console.WriteLine("Start sending file...");
        NetworkStream stream = localClient.GetStream();

        // 擷取檔案儲存的路勁
        string filePath =
            Environment.CurrentDirectory + "/" + generateFileName(fileName);

        // 建立檔案流
        FileStream fs = new FileStream(filePath, FileMode.CreateNew, FileAccess.Write);
        byte[] fileBuffer = new byte[1024];     // 每次傳1KB
        int bytesRead;
        int totalBytes = 0;

        // 從緩衝buffer中讀入到檔案流中
        do {
            bytesRead = stream.Read(buffer, 0, BufferSize);
            fs.Write(buffer, 0, bytesRead);
            totalBytes += bytesRead;
            Console.WriteLine("Receiving {0} bytes ...", totalBytes);
        } while (bytesRead > 0);

        Console.WriteLine("Total {0} bytes received, Done!", totalBytes);

        fs.Dispose();           
        stream.Dispose();
        localClient.Close();
        listener.Stop();
    }


    // 隨機擷取一個圖片名稱
    private string generateFileName(string fileName) {}

    public void Dispose() {
        if (streamToServer != null)
            streamToServer.Dispose();
        if (client != null)
            client.Close();
    }
}

上面關鍵的一句就是建立協議那句,注意到將mode由Send改為了Receive,同時傳去了想要接收的服務端的檔案名稱。

4.3程式測試

現在我們已經完成了所有收發檔案的步驟,可以看到服務端的所有操作都是被動的,接下來我們修改用戶端的Main()程式,建立一個菜單,然後根據使用者輸入發送或者接收檔案。

class Program {
    static void Main(string[] args) {

        ServerClient client = new ServerClient();
        string input;
        string path = Environment.CurrentDirectory + "/";

        do {
            Console.WriteLine("Send File:    S1 - Client01.jpg, S2 - Client02.jpg, S3 - Client03.jpg");
            Console.WriteLine("Receive File: R1 - Server01.jpg, R1 - Server02.jpg, R3- Server03.jpg");
            Console.WriteLine("Press ‘Q‘ to exit. \n");
            Console.Write("Enter your choice: ");
            input = Console.ReadLine();
            switch(input.ToUpper()){
                case "S1":
                    client.BeginSendFile(path + "Client01.jpg");
                    break;
                case "S2":
                    client.BeginSendFile(path + "Client02.jpg");
                    break;
                case "S3":
                    client.BeginSendFile(path + "Client02.jpg");
                    break;
                case "R1":
                    client.BeginReceiveFile("Server01.jpg");
                    break;
                case "R2":
                    client.BeginReceiveFile("Server01.jpg");
                    break;
                case "R3":
                    client.BeginReceiveFile("Server01.jpg");
                    break;
            }               
        } while (input.ToUpper() != "Q");

        client.Dispose();
    }
}

由於這是一個控制台應用程式,並且採用了非同步作業,所以這個菜單的出現順序有點混亂。我這裡描述起來比較困難,你將代碼下載下來後運行一下就知道了:-)

程式的運行結果和上一節類似,這裡我就不再貼圖了。接下來是本系列的最後一篇,將發送字串與傳輸檔案的功能結合起來,建立一個可以發送訊息並能收發檔案的聊天程式,至於語音交談嘛...等我學習了再告訴你 >_<、

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.