C#网络编程(接收文件)

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

}

}

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

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

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-12-30 04:58:11

C#网络编程(接收文件)的相关文章

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

C#网络编程(接收文件) - Part.5 这篇文章将完成Part.4中剩余的部分,它们本来是一篇完整的文章,但是因为上一篇比较长,合并起来页数太多,浏览起来可能会比较不方便,我就将它拆为两篇了,本文便是它的后半部分.我们继续进行上一篇没有完成的步骤:客户端接收来自服务端的文件. 4.客户端接收文件 4.1服务端的实现 对于服务端,我们只需要实现上一章遗留的sendFile()方法就可以了,它起初在handleProtocol中是注释掉的.另外,由于创建连接.获取流等操作与receiveFile

网络编程练习 -- 文件上传

LWTViewController.m // // LWTViewController.m // 网络编程练习 -- 文件上传 // // Created by apple on 14-6-30. // Copyright (c) 2014年 lwt. All rights reserved. // #import "LWTViewController.h" #define KStringToData(string) [string dataUsingEncoding:NSUTF8St

黑马程序员---网络编程之文件上传

------- android培训.java培训.期待与您交流! ----------. 网络编程 (1)用编程语言来实现计算机的资源共享和信息传递. 计算机网络:多台独立的计算机用网络通信设备连接起来的网络.实现资源共享和数据传递. (2)网络通信的三要素 A:IP地址 计算机在网络中的唯一标识. 现在使用的是:"点分十进制" B:端口 应用程序的的标记. C:协议 通信的规则. UDP:不建立连接,数据打包传输,数据有限制,数据不可靠,速度快. TCP:建立连接,数据无限制,数据可

iOS开发之网络编程--获取文件的MIMEType

前言:有时候我们需要获取文件的MIMEType的信息,下面就介绍关于获取MIMEType的方法. 1.直接百度搜索关键字"MIMEType",你会找到,然后查吧: 2.用代码获取文件的MIMEType信息: 1 #import "GetMIMEType.h" 2 3 #import <MobileCoreServices/MobileCoreServices.h> 4 5 @implementation GetMIMEType 6 7 #pragma m

Android网络编程之——文件断点下载(暂停/继续/重新下载)

开头还是不说废话了直接进入主题吧! 一:关于断点下载所涉及到的知识点 1.对SQLite的增删改查(主要用来保存当前任务的一些信息) 2.HttpURLConnection的请求配置 HttpURLConnection connection = null; //设置下载请求属性 connection.setRequestProperty(); 3.RandomAccessFile 对文件进行写入 RandomAccessFile rwd = null; //从文件的某一位置写入 rwd.seek

record-11 网络编程 打开文件

#open() #urlopen() #方法名称不同 #参数不同 #只能以只读模式打开网络资源文件 from urllib.request import urlopen from urllib.parse import quote from json import loads from time import time,sleep print('请输入要查询的城市名称:') s=input() s1=quote(s) #print(s1) url='http://www.sojson.com/o

第13篇-JAVA 网络编程

第13篇-JAVA 网络编程 每篇一句 比我强大的人都在努力我还有什么理由不拼命 初学心得 不去追逐你所渴求你将永远不会拥有 (笔者JEEP/711)[JAVA笔记 | 时间:2017-04-22| JAVA 网络编程 ] 1.网络编程基本概念 1.什么是计算机网络 计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备通过通信线路连接起来在网络操作系统网络管理软件及网络通讯协议的管理和协调下实现资源共享和信息传递的计算机系统 把分布在不同地理区域的计算机与专门的外部设备用通讯线路互

详解C# 网络编程系列:实现类似QQ的即时通信程序

引言: 前面专题中介绍了UDP.TCP和P2P编程,并且通过一些小的示例来让大家更好的理解它们的工作原理以及怎样.Net类库去实现它们的.为了让大家更好的理解我们平常中常见的软件QQ的工作原理,所以在本专题中将利用前面专题介绍的知识来实现一个类似QQ的聊天程序.  一.即时通信系统 在我们的生活中经常使用即时通信的软件,我们经常接触到的有:QQ.阿里旺旺.MSN等等.这些都是属于即时通信(Instant Messenger,IM)软件,IM是指所有能够即时发送和接收互联网消息的软件. 在前面专题

arpa/inet.h所引起的Segmentation fault及网络编程常见的头文件

最近在学习Linux网络编程方面的知识,感觉还是有些困难.主要是对协议过程的理解,还有socket的API的理解不够深刻.今天复习编写了一个TCP的服务端和客户端的程序实现client.c从命令行参数中获得一个字符串发给服务器,然后接收服务器返回的已处理的字符串并打印. server.c 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h>