• (转)C#网络编程(接收文件) Part.5


    源码下载:http://www.tracefact.net/SourceCode/Network-Part5.rar

    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. ");
                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();
        }
    }

    由于这是一个控制台应用程序,并且采用了异步操作,所以这个菜单的出现顺序有点混乱。我这里描述起来比较困难,你将代码下载下来后运行一下就知道了:-)

    程序的运行结果和上一节类似,这里我就不再贴图了。接下来是本系列的最后一篇,将发送字符串与传输文件的功能结合起来,创建一个可以发送消息并能收发文件的聊天程序,至于语音聊天嘛...等我学习了再告诉你 >_<、

  • 相关阅读:
    POJ 1887 Testing the CATCHER
    HDU 3374 String Problem
    HDU 2609 How many
    POJ 1509 Glass Beads
    POJ 1458 Common Subsequence
    POJ 1159 Palindrome
    POJ 1056 IMMEDIATE DECODABILITY
    POJ 3080 Blue Jeans
    POJ 1200 Crazy Search
    软件体系结构的艺术阅读笔记1
  • 原文地址:https://www.cnblogs.com/ColdFish_Pegasus/p/1932122.html
Copyright © 2020-2023  润新知