`
king_tt
  • 浏览: 2098260 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

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

 
阅读更多

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

4.客户端接收文件

4.1服务端的实现

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

classServer{
staticvoidMain(string[] args) {
Console.WriteLine("Serverisrunning ... ");
IPAddressip = IPAddress.Parse("127.0.0.1");
TcpListenerlistener =newTcpListener(ip, 8500);

listener.Start();// 开启对控制端口 8500 的侦听
Console.WriteLine("Start Listening ...");

while(true) {
// 获取一个连接,同步方法,在此处中断
TcpClientclient = listener.AcceptTcpClient();
RemoteClientwapper =newRemoteClient(client);
wapper.BeginRead();
}
}
}

publicclassRemoteClient{
// 字段 略

publicRemoteClient(TcpClient client) {}

// 开始进行读取
publicvoidBeginRead() { }

// 再读取完成时进行回调
privatevoidOnReadComplete(IAsyncResult ar) { }

// 处理protocol
privatevoidhandleProtocol(objectobj) {
stringpro = objasstring;
ProtocolHelperhelper =newProtocolHelper(pro);
FileProtocolprotocol = helper.GetProtocol();

if(protocol.Mode == FileRequestMode.Send) {
// 客户端发送文件,对服务端来说则是接收文件
receiveFile(protocol);
}elseif(protocol.Mode == FileRequestMode.Receive) {
// 客户端接收文件,对服务端来说则是发送文件
sendFile(protocol);
}
}

// 发送文件
privatevoidsendFile(FileProtocol protocol) {
TcpClientlocalClient;
NetworkStreamstreamToClient = getStreamToClient(protocol,outlocalClient);

// 获得文件的路径
stringfilePath = Environment.CurrentDirectory +"/"+ protocol.FileName;

// 创建文件流
FileStreamfs =newFileStream(filePath, FileMode.Open, FileAccess.Read);
byte[]fileBuffer =newbyte[1024];// 每次传1KB
intbytesRead;
inttotalBytes = 0;

// 创建获取文件发送状态的类
SendStatusstatus =newSendStatus(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();
}

// 接收文件
privatevoidreceiveFile(FileProtocol protocol) { }

// 获取连接到远程的流 -- 公共方法
privateNetworkStream getStreamToClient(FileProtocol protocol,outTcpClient localClient) {
// 获取远程客户端的位置
IPEndPointendpoint = client.Client.RemoteEndPointasIPEndPoint;
IPAddressip = endpoint.Address;

// 使用新端口号,获得远程用于接收文件的端口
endpoint =newIPEndPoint(ip, protocol.Port);

// 连接到远程客户端
try{
localClient =newTcpClient();
localClient.Connect(endpoint);
}catch{
Console.WriteLine("无法连接到客户端 --> {0}", endpoint);
localClient =null;
returnnull;
}

// 获取发送文件的流
NetworkStreamstreamToClient = localClient.GetStream();
returnstreamToClient;
}

// 随机获取一个图片名称
privatestringgenerateFileName(stringfileName) {}
}

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

4.2客户端的实现

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

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

publicclassServerClient:IDisposable{
// 字段略

publicServerClient() {}

// 发送消息到服务端
publicvoidSendMessage(stringmsg) {}

// 发送文件 - 异步方法
publicvoidBeginSendFile(stringfilePath) { }

privatevoidSendFile(objectobj) { }

// 发送文件 -- 同步方法
publicvoidSendFile(stringfilePath) {}

// 接收文件 -- 异步方法
publicvoidBeginReceiveFile(stringfileName) {
ParameterizedThreadStartstart =
newParameterizedThreadStart(ReceiveFile);
start.BeginInvoke(fileName,null,null);
}

publicvoidReceiveFile(objectobj) {
stringfileName = objasstring;
ReceiveFile(fileName);
}

// 接收文件 -- 同步方法
publicvoidReceiveFile(stringfileName) {

IPAddressip = IPAddress.Parse("127.0.0.1");
TcpListenerlistener =newTcpListener(ip, 0);
listener.Start();

// 获取本地侦听的端口号
IPEndPointendPoint = listener.LocalEndpointasIPEndPoint;
intlisteningPort = endPoint.Port;

// 获取发送的协议字符串
FileProtocolprotocol =
newFileProtocol(FileRequestMode.Receive, listeningPort, fileName);
stringpro = protocol.ToString();

SendMessage(pro);// 发送协议到服务端

// 中断,等待远程连接
TcpClientlocalClient = listener.AcceptTcpClient();
Console.WriteLine("Start sending file...");
NetworkStreamstream = localClient.GetStream();

// 获取文件保存的路劲
stringfilePath =
Environment.CurrentDirectory +"/"+ generateFileName(fileName);

// 创建文件流
FileStreamfs =newFileStream(filePath, FileMode.CreateNew, FileAccess.Write);
byte[]fileBuffer =newbyte[1024];// 每次传1KB
intbytesRead;
inttotalBytes = 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();
}


// 随机获取一个图片名称
privatestringgenerateFileName(stringfileName) {}

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

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

4.3程序测试

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

classProgram{
staticvoidMain(string[] args) {

ServerClientclient =newServerClient();
stringinput;
stringpath = Environment.CurrentDirectory +"/";

do{
Console.WriteLine("SendFile: S1 - Client01.jpg, S2 - Client02.jpg, S3 - Client03.jpg");
Console.WriteLine("ReceiveFile: 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();
}
}

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

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

分享到:
评论

相关推荐

    明日科技C#示例源码.part01

    实例074 使用C#操作XML文件 257 实例075 创建PDF文档 261 实例076 批量将Word文档转换为HTML网页 263 实例077 Word目录提取工具 265 实例078 文件批量解压缩 269 第8章 网络开发应用 实例079 ...

    CLR.via.C#.(中文第3版)(自制详细书签)Part2

    CLR.via.C#.(中文第3版)(自制详细书签)Part2 CLR via C#(第3版) Jeffrey Richter 著 周靖 译 出版时间:2010年09月 页数:800 介绍 享有全球盛誉的编程专家Jeffrey Richter,这位与Microsoft .NET开发团队合作...

    ASP.NET网络编程标准教程 光盘part2

    ASP.NET+SQL Server 2000制作留言本、ASP.NET制作文件上传与下载、ASP.NET制作电子邮件发送和接收、ASP.NET制作无刷新模式聊天室、ASP.NET+SQL Server 2000制作网络硬盘、ASP.NET+SQL Server 2000制作在线论坛BBS,...

    ASP.NET网络编程标准教程 光盘part1

    ASP.NET+SQL Server 2000制作留言本、ASP.NET制作文件上传与下载、ASP.NET制作电子邮件发送和接收、ASP.NET制作无刷新模式聊天室、ASP.NET+SQL Server 2000制作网络硬盘、ASP.NET+SQL Server 2000制作在线论坛BBS,...

    CLR.via.C#.(中文第3版)(自制详细书签)Part3

    CLR.via.C#.(中文第3版)(自制详细书签)Part3 CLR via C#(第3版) Jeffrey Richter 著 周靖 译 出版时间:2010年09月 页数:800 介绍 享有全球盛誉的编程专家Jeffrey Richter,这位与Microsoft .NET开发团队合作...

    CLR.via.C#.(中文第3版)(自制详细书签)Part1

    22.6.5 更多的用法只局限于你自己的想象力 22.7 高级宿主控制 22.7.1 使用托管代码管理CLR 22.7.2 编写健壮的宿主应用程序 22.7.3 宿主如何拿回它的线程 第23章 程序集加载和反射 23.1 程序集加载 23.2 使用...

    CLR.via.C#.(中文第3版)(自制详细书签)

    CLR.via.C#.(中文第3版)(自制详细书签)Part2 CLR via C#(第3版) Jeffrey Richter 著 周靖 译 出版时间:2010年09月 页数:800 介绍 享有全球盛誉的编程专家Jeffrey Richter,这位与Microsoft .NET开发团队...

    《软件工程师入门 ASP.NET 2.0 网络编程自学手册》随书光盘 Part 2

    第1篇为起步篇(第1章-第5章),主要介绍ASP.NET 2.0从零起步、ASP.NET 2.0开发基础、C#语言基础、ASP.NET内置对象,ASP.NET常用开发服务器控件等内容,第2篇为提高篇(第6章-第13章),主要介绍面向对象的程序设计,...

    CLR via C# 3rd Edition

    新增了关于如何部署包含内嵌依赖程序集的单一文件的一节,以及比较反射调用、bind/invode、bind/create、delegate/invoke、C#动态类型的一节。 Chapter 24-Runtime Serialization 全新的一章。 Part V – ...

    asp.net知识库

    常用编码工具类,支持base64,md5,des,crc32 也谈谈技术面试 在C#里把ArrayList转换为Array 或 把Array转换为ArrayList C# 2.0 在.NET 2.0中,让你的组件也可以绑定 .NET20 一种简单的窗口控件UI状态控制方法 翻译MSDN...

    ASP.NET从入门到精通part2

    全书共分29章,包括ASP.NET 2.0开发入门、C#语言基础、ASP.NET的内置对象、ASP.NET 2.0 Web常用控件、数据验证技术、母版页、主题、数据绑定、使用ADO.NET操作数据库、数据控件、站点导航控件、Web用户常用控件、ASP...

Global site tag (gtag.js) - Google Analytics