2022年C#网络编程- .pdf
《2022年C#网络编程- .pdf》由会员分享,可在线阅读,更多相关《2022年C#网络编程- .pdf(22页珍藏版)》请在得力文库 - 分享文档赚钱的网站上搜索。
1、C#网络编程(订立协议和发送文件)-Part.4 文件传输前面两篇文章所使用的范例都是传输字符串,有的时候我们可能会想在服务端和客户端之间传递文件。比如,考虑这样一种情况,假如客户端显示了一个菜单,当我们输入S1、S2或 S3(S为 Send缩写)时,分别向服务端发送文件Client01.jpg、Client02.jpg、Client03.jpg;当我们输入R1、R2或 R3时(R为 Receive 缩写),则分别从服务端接收文件 Server01.jpg、Server02.jpg、Server03.jpg。那么,我们该如何完成这件事呢?此时可能有这样两种做法:类似于 FTP协议,服务端开辟两
2、个端口,并持续对这两个端口侦听:一个用于接收字符串,类似于FTP的控制端口,它接收各种命令(接收或发送文件);一个用于传输数据,也就是发送和接收文件。服务端只开辟一个端口,用于接收字符串,我们称之为控制端口。当接到请求之后,根据请求内容在客户端开辟一个端口专用于文件传输,并在传输结束后关闭端口。现在我们只关注于上面的数据端口,回忆一下在第二篇中我们所总结的,可以得出:当我们使用上面的方法一时,服务端的数据端口可以为多个客户端的多次请求服务;当我们使用方法二时,服务端只为一个客户端的一次请求服务,但是因为每次请求都会重新开辟端口,所以实际上还是相当于可以为多个客户端的多次请求服务。同时,因为它只
3、为一次请求服务,所以我们在数据端口上传输文件时无需采用异步传输方式。但在控制端口我们仍然需要使用异步方式。从上面看出,第一种方式要好得多,但是我们将采用第二种方式。至于原因,你可以回顾一下Part.1(基本概念和操作)中关于聊天程序模式的讲述,因为接下来一篇文章我们将创建一个聊天程序,而这个聊天程序采用第三种模式,所以本文的练习实际是对下一篇的一个铺垫。名师资料总结-精品资料欢迎下载-名师精心整理-第 1 页,共 22 页 -1.订立协议1.1 发送文件我们先看一下发送文件的情况,如果我们想将文件client01.jpg由客户端发往客户端,那么流程是什么:1.客户端开辟数据端口用于侦听,并获取
4、端口号,假设为8005。2.假设客户端输入了S1,则发送下面的控制字符串到服务端:file=Client01.jpg,mode=send,port=8005。3.服务端收到以后,根据客户端ip 和端口号与该客户端建立连接。4.客户端侦听到服务端的连接,开始发送文件。5.传送完毕后客户端、服务端分别关闭连接。此时,我们订立的发送文件协议为:file=Client01.jpg,mode=send,port=8005。但是,由于它是一个普通的字符串,在上一篇中,我们采用了正则表达式来获取其中的有效值,但这显然不是一种好办法。因此,在本文及下一篇文章中,我们采用一种新的方式来编写协议:XML。对于上面
5、的语句,我们可以写成这样的XML:这样我们在服务端就会好处理得多,接下来我们来看一下接收文件的流程及其协议。NOTE:这里说发送、接收文件是站在客户端的立场说的,当客户端发送文件时,对于服务器来收,则是接收文件。1.2 接收文件接收文件与发送文件实际上完全类似,区别只是由客户端向网络流写入数据,还是由服务端向网络流写入数据。1.客户端开辟数据端口用于侦听,假设为8006。名师资料总结-精品资料欢迎下载-名师精心整理-第 2 页,共 22 页 -2.假设客户端输入了R1,则发送控制字符串:到服务端。3.服务端收到以后,根据客户端ip 和端口号与该客户端建立连接。4.客户端建立起与服务端的连接,服
6、务端开始网络流中写入数据。5.传送完毕后服务端、客户端分别关闭连接。2.协议处理类的实现和上面一章一样,在开始编写实际的服务端客户端代码之前,我们首先要编写处理协议的类,它需要提供这样两个功能:1、方便地帮我们获取完整的协议信息,因为前面我们说过,服务端可能将客户端的多次独立请求拆分或合并。比如,客户端连续发送了两条控制信息到服务端,而服务端将它们合并了,那么则需要先拆开再分别处理。2、方便地获取我们所想要的属性信息,因为协议是XML格式,所以还需要一个类专门对XML进行处理,获得字符串的属性值。2.1 ProtocalHandler辅助类我们先看下ProtocalHandler,它与上一篇中
7、的RequestHandler作用相同。需要注意的是必须将它声明为实例的,而非静态的,这是因为每个TcpClient都需要对应一个ProtocalHandler,因为它内部维护的patialProtocal不能共享,在协议发送不完整的情况下,这个变量用于临时保存被截断的字符串。publicclassProtocolHandler privatestring partialProtocal;/保存不完整的协议public ProtocolHandler()partialProtocal=;名师资料总结-精品资料欢迎下载-名师精心整理-第 3 页,共 22 页 -publicstring GetP
8、rotocol(string input)return GetProtocol(input,null);/获得协议privatestring GetProtocol(string input,List outputList)if (outputList=null)outputList=new List();if (String.IsNullOrEmpty(input)return outputList.ToArray();if (!String.IsNullOrEmpty(partialProtocal)input=partialProtocal+input;string pattern=(.
9、*?);/如果有匹配,说明已经找到了,是完整的协议if (Regex.IsMatch(input,pattern)/获取匹配的值string match=Regex.Match(input,pattern).Groups0.Value;outputList.Add(match);partialProtocal=;/缩短 input的长度 input=input.Substring(match.Length);名师资料总结-精品资料欢迎下载-名师精心整理-第 4 页,共 22 页 -/递归调用 GetProtocol(input,outputList);else /如果不匹配,说明协议的长度不够
10、,/那么先缓存,然后等待下一次请求 partialProtocal=input;return outputList.ToArray();因为现在它已经不是本文的重点了,所以我就不演示对于它的测试了,本文所附带的代码中含有它的测试代码(我在ProtocolHandler中添加了一个静态类Test())。2.2 FileRequestType枚举和 FileProtocol结构因为 XML是以字符串的形式在进行传输,为了方便使用,我们最好构建一个强类型来对它们进行操作,这样会方便很多。我们首先可以定义FileRequestMode枚举,它代表是发送还是接收文件:publicenum FileReq
11、uestMode Send=0,Receive 接下来我们再定义一个FileProtocol结构,用来为整个协议字符串提供强类型的访问,注意这里覆盖了基类的ToString()方法,这样在客户端我们就不需要再手工去编写XML,只要在结构值上调用ToString()就 OK了,会方便很多。名师资料总结-精品资料欢迎下载-名师精心整理-第 5 页,共 22 页 -publicstructFileProtocol privatereadonlyFileRequestMode mode;privatereadonlyint port;privatereadonlystring fileName;pub
12、lic FileProtocol (FileRequestMode mode,int port,string fileName)this.mode=mode;this.port=port;this.fileName=fileName;public FileRequestMode Mode get return mode;publicint Port get return port;publicstring FileName get return fileName;publicoverridestring ToString()returnString.Format(,fileName,mode,
13、port);名师资料总结-精品资料欢迎下载-名师精心整理-第 6 页,共 22 页 -2.3 ProtocolHelper辅助类这个类专用于将XML格式的协议映射为我们上面定义的强类型对象,这里我没有加入try/catch异常处理,因为协议对用户来说是不可见的,而且客户端应该总是发送正确的协议,我觉得这样可以让代码更加清晰:publicclassProtocolHelper privateXmlNode fileNode;privateXmlNode root;public ProtocolHelper(string protocol)XmlDocument doc=new XmlDocume
14、nt();doc.LoadXml(protocol);root=doc.DocumentElement;fileNode=root.SelectSingleNode(file);/此时的 protocal一定为单条完整protocal private FileRequestMode GetFileMode()string mode=fileNode.Attributesmode.Value;mode=mode.ToLower();if (mode=send)return FileRequestMode.Send;elsereturn FileRequestMode.Receive;名师资料总结
15、-精品资料欢迎下载-名师精心整理-第 7 页,共 22 页 -/获取单条协议包含的信息public FileProtocol GetProtocol()FileRequestMode mode=GetFileMode();string fileName=;int port=0;fileName=fileNode.Attributesname.Value;port=Convert.ToInt32(fileNode.Attributesport.Value);returnnew FileProtocol(mode,port,fileName);OK,我们又耽误了点时间,下面就让我们进入正题吧。3.
16、客户端发送数据3.1 服务端的实现我们还是将一个问题分成两部分来处理,先是发送数据,然后是接收数据。我们先看发送数据部分的服务端。如果你从第一篇文章看到了现在,那么我觉得更多的不是技术上的问题而是思路,所以我们不再将重点放到代码上,这些应该很容易就看懂了。classServer staticvoid Main(string args)Console.WriteLine(Server is running.);IPAddress ip=IPAddress.Parse(127.0.0.1);名师资料总结-精品资料欢迎下载-名师精心整理-第 8 页,共 22 页 -TcpListener liste
17、ner=new TcpListener(ip,8500);listener.Start();/开启对控制端口 8500 的侦听Console.WriteLine(Start Listening.);while (true)/获取一个连接,同步方法,在此处中断TcpClient client=listener.AcceptTcpClient();RemoteClient wapper=new RemoteClient(client);wapper.BeginRead();publicclassRemoteClient privateTcpClient client;privateNetworkS
18、tream streamToClient;privateconstint BufferSize=8192;privatebyte buffer;privateProtocolHandler handler;public RemoteClient(TcpClient client)this.client=client;/打印连接到的客户端信息Console.WriteLine(nClient Connected!0 0,endpoint);return;/获取发送文件的流NetworkStream streamToClient=localClient.GetStream();/随机生成一个在当前
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 2022年C#网络编程- 2022 C# 网络 编程
限制150内