C#网络编程:5接收文件

it2025-10-20  9

这篇文章将完成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();      }  } 

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

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

转载于:https://www.cnblogs.com/bennylam/archive/2010/07/24/1784280.html

最新回复(0)