zoukankan      html  css  js  c++  java
  • C#网络编程:5接收文件

    这篇文章将完成Part.4中剩余的部分,它们本来是一篇完整的文章,但是因为上一篇比较长,合并起来页数太多,浏览起来可能会比较不方便,我就将它拆为两篇了,本文便是它的后半部分。我们继续进行上一篇没有完成的步骤:客户端接收来自服务端的文件。

    4.客户端接收文件

    4.1服务端的实现

    对于服务端,我们只需要实现上一章遗留的sendFile()方法就可以了,它起初在handleProtocol中是注释掉的。另外,由于创建连接、获取流等操作与receiveFile()是没有区别的,所以我们将它提出来作为一个公共方法getStreamToClient()。下面是服务端的代码,只包含新增改过的代码,对于原有方法我只给出了签名:

    1. class Server { 
    2. static void Main(string[] args) { 
    3.         Console.WriteLine("Server is running ... "); 
    4.         IPAddress ip = IPAddress.Parse("127.0.0.1"); 
    5.         TcpListener listener = new TcpListener(ip, 8500); 
    6.         listener.Start();           // 开启对控制端口 8500 的侦听
    7.         Console.WriteLine("Start Listening ..."); 
    8. while (true) { 
    9. // 获取一个连接,同步方法,在此处中断
    10.             TcpClient client = listener.AcceptTcpClient(); 
    11.             RemoteClient wapper = new RemoteClient(client); 
    12.             wapper.BeginRead(); 
    13.         } 
    14.     } 
    15. public class RemoteClient { 
    16. // 字段 略
    17. public RemoteClient(TcpClient client) {} 
    18. // 开始进行读取
    19. public void BeginRead() { } 
    20. // 再读取完成时进行回调
    21. private void OnReadComplete(IAsyncResult ar) { } 
    22. // 处理protocol
    23. private void handleProtocol(object obj) { 
    24. string pro = obj as string; 
    25.         ProtocolHelper helper = new ProtocolHelper(pro); 
    26.         FileProtocol protocol = helper.GetProtocol(); 
    27. if (protocol.Mode == FileRequestMode.Send) { 
    28. // 客户端发送文件,对服务端来说则是接收文件
    29.             receiveFile(protocol); 
    30.         } else if (protocol.Mode == FileRequestMode.Receive) { 
    31. // 客户端接收文件,对服务端来说则是发送文件
    32.             sendFile(protocol); 
    33.         } 
    34.     } 
    35. // 发送文件
    36. private void sendFile(FileProtocol protocol) { 
    37.         TcpClient localClient; 
    38.         NetworkStream streamToClient = getStreamToClient(protocol, out localClient); 
    39. // 获得文件的路径
    40. string filePath = Environment.CurrentDirectory + "/" + protocol.FileName; 
    41. // 创建文件流
    42.         FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read); 
    43. byte[] fileBuffer = new byte[1024];     // 每次传1KB
    44. int bytesRead; 
    45. int totalBytes = 0; 
    46. // 创建获取文件发送状态的类
    47.         SendStatus status = new SendStatus(filePath); 
    48. // 将文件流转写入网络流
    49. try { 
    50. do { 
    51.                 Thread.Sleep(10);           // 为了更好的视觉效果,暂停10毫秒:-)
    52.                 bytesRead = fs.Read(fileBuffer, 0, fileBuffer.Length); 
    53.                 streamToClient.Write(fileBuffer, 0, bytesRead); 
    54.                 totalBytes += bytesRead;            // 发送了的字节数
    55.                 status.PrintStatus(totalBytes); // 打印发送状态
    56.             } while (bytesRead > 0); 
    57.             Console.WriteLine("Total {0} bytes sent, Done!", totalBytes); 
    58.         } catch { 
    59.             Console.WriteLine("Server has lost..."); 
    60.         } 
    61.         streamToClient.Dispose(); 
    62.         fs.Dispose(); 
    63.         localClient.Close(); 
    64.     } 
    65. // 接收文件
    66. private void receiveFile(FileProtocol protocol) { } 
    67. // 获取连接到远程的流 -- 公共方法
    68. private NetworkStream getStreamToClient(FileProtocol protocol, out TcpClient localClient) { 
    69. // 获取远程客户端的位置
    70.         IPEndPoint endpoint = client.Client.RemoteEndPoint as IPEndPoint; 
    71.         IPAddress ip = endpoint.Address; 
    72. // 使用新端口号,获得远程用于接收文件的端口
    73.         endpoint = new IPEndPoint(ip, protocol.Port); 
    74. // 连接到远程客户端
    75. try { 
    76.             localClient = new TcpClient(); 
    77.             localClient.Connect(endpoint); 
    78.         } catch { 
    79.             Console.WriteLine("无法连接到客户端 --> {0}", endpoint); 
    80.             localClient = null; 
    81. return null; 
    82.         } 
    83. // 获取发送文件的流
    84.         NetworkStream streamToClient = localClient.GetStream(); 
    85. return streamToClient; 
    86.     } 
    87. // 随机获取一个图片名称
    88. private string generateFileName(string fileName) {} 

    服务端的sendFile方法和客户端的SendFile()方法完全类似,上面的代码几乎是一次编写成功的。另外注意我将客户端使用的SendStatus类也拷贝到了服务端。接下来我们看下客户端。

    4.2客户端的实现

    首先要注意的是客户端的SendFile()接收的参数是文件全路径,但是在写入到协议时只获取了路径中的文件名称。这是因为服务端不需要知道文件在客户端的路径,所以协议中只写文件名;而为了使客户端的SendFile()方法更通用,所以它接收本地文件的全路径。

    客户端的ReceiveFile()的实现也和服务端的receiveFile()方法类似,同样,由于要保存到本地,为了避免文件名重复,我将服务端的generateFileName()方法复制了过来。

    1. public class ServerClient :IDisposable { 
    2. // 字段略
    3. public ServerClient() {} 
    4. // 发送消息到服务端
    5. public void SendMessage(string msg) {} 
    6. // 发送文件 - 异步方法
    7. public void BeginSendFile(string filePath) {    } 
    8. private void SendFile(object obj) { } 
    9. // 发送文件 -- 同步方法
    10. public void SendFile(string filePath) {} 
    11. // 接收文件 -- 异步方法
    12. public void BeginReceiveFile(string fileName) { 
    13.         ParameterizedThreadStart start = 
    14. new ParameterizedThreadStart(ReceiveFile); 
    15.         start.BeginInvoke(fileName, null, null); 
    16.     } 
    17. public void ReceiveFile(object obj) { 
    18. string fileName = obj as string; 
    19.         ReceiveFile(fileName); 
    20.     } 
    21. // 接收文件 -- 同步方法
    22. public void ReceiveFile(string fileName) { 
    23.         IPAddress ip = IPAddress.Parse("127.0.0.1"); 
    24.         TcpListener listener = new TcpListener(ip, 0); 
    25.         listener.Start(); 
    26. // 获取本地侦听的端口号
    27.         IPEndPoint endPoint = listener.LocalEndpoint as IPEndPoint; 
    28. int listeningPort = endPoint.Port; 
    29. // 获取发送的协议字符串
    30.         FileProtocol protocol = 
    31. new FileProtocol(FileRequestMode.Receive, listeningPort, fileName); 
    32. string pro = protocol.ToString(); 
    33.         SendMessage(pro);       // 发送协议到服务端
    34. // 中断,等待远程连接
    35.         TcpClient localClient = listener.AcceptTcpClient(); 
    36.         Console.WriteLine("Start sending file..."); 
    37.         NetworkStream stream = localClient.GetStream(); 
    38. // 获取文件保存的路劲
    39. string filePath = 
    40.             Environment.CurrentDirectory + "/" + generateFileName(fileName); 
    41. // 创建文件流
    42.         FileStream fs = new FileStream(filePath, FileMode.CreateNew, FileAccess.Write); 
    43. byte[] fileBuffer = new byte[1024];     // 每次传1KB
    44. int bytesRead; 
    45. int totalBytes = 0; 
    46. // 从缓存buffer中读入到文件流中
    47. do { 
    48.             bytesRead = stream.Read(buffer, 0, BufferSize); 
    49.             fs.Write(buffer, 0, bytesRead); 
    50.             totalBytes += bytesRead; 
    51.             Console.WriteLine("Receiving {0} bytes ...", totalBytes); 
    52.         } while (bytesRead > 0); 
    53.         Console.WriteLine("Total {0} bytes received, Done!", totalBytes); 
    54.         fs.Dispose();          
    55.         stream.Dispose(); 
    56.         localClient.Close(); 
    57.         listener.Stop(); 
    58.     } 
    59. // 随机获取一个图片名称
    60. private string generateFileName(string fileName) {} 
    61. public void Dispose() { 
    62. if (streamToServer != null) 
    63.             streamToServer.Dispose(); 
    64. if (client != null) 
    65.             client.Close(); 
    66.     } 

    上面关键的一句就是创建协议那句,注意到将mode由Send改为了Receive,同时传去了想要接收的服务端的文件名称。

    4.3程序测试

    现在我们已经完成了所有收发文件的步骤,可以看到服务端的所有操作都是被动的,接下来我们修改客户端的Main()程序,创建一个菜单,然后根据用户输入发送或者接收文件。

    1. class Program { 
    2. static void Main(string[] args) { 
    3.         ServerClient client = new ServerClient(); 
    4. string input; 
    5. string path = Environment.CurrentDirectory + "/"; 
    6. do { 
    7.             Console.WriteLine("Send File:    S1 - Client01.jpg, S2 - Client02.jpg, S3 - Client03.jpg"); 
    8.             Console.WriteLine("Receive File: R1 - Server01.jpg, R1 - Server02.jpg, R3- Server03.jpg"); 
    9.             Console.WriteLine("Press 'Q' to exit. \n"); 
    10.             Console.Write("Enter your choice: "); 
    11.             input = Console.ReadLine(); 
    12. switch(input.ToUpper()){ 
    13. case "S1": 
    14.                     client.BeginSendFile(path + "Client01.jpg"); 
    15. break; 
    16. case "S2": 
    17.                     client.BeginSendFile(path + "Client02.jpg"); 
    18. break; 
    19. case "S3": 
    20.                     client.BeginSendFile(path + "Client02.jpg"); 
    21. break; 
    22. case "R1": 
    23.                     client.BeginReceiveFile("Server01.jpg"); 
    24. break; 
    25. case "R2": 
    26.                     client.BeginReceiveFile("Server01.jpg"); 
    27. break; 
    28. case "R3": 
    29.                     client.BeginReceiveFile("Server01.jpg"); 
    30. break; 
    31.             }              
    32.         } while (input.ToUpper() != "Q"); 
    33.         client.Dispose(); 
    34.     } 

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

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

  • 相关阅读:
    【NLP CS224N笔记】汇总
    【NLP CS224N笔记】Lecture 2
    论文摘记 2017.5
    FAST UA API
    FAST Hello World
    NetMagic Simple Overview
    论文摘记 2017.4.25-4.30
    LLDP协议、STP协议 笔记
    FAST:通过Floodlight控制器下发流表
    FAST:NetMagic交换机 与 Floodlight控制器 连接实战
  • 原文地址:https://www.cnblogs.com/bennylam/p/1784280.html
Copyright © 2011-2022 走看看