C#利用WCF改进文件流传输的三种方式
- 摘要:本文介绍C#利用WCF改进文件流传输的三种方式:MTOM模型、基于同步传输的异步回调模型、基于异步传输的异步模型,并提供相应的实现代码供参考。
- -
WCF在跨域传输使用了两种模型的方法调用:一种是同步模型,这种模型显然对那些需要大量操作时间的方法调用(如从数据库中获取大量数据时)是一种痛苦的选择。另一种是异步模型的方法调用,这种模型是一种非阻塞方法,其方法调用期间并不等到方法调用结束获得结果才返回,而是方法调用一经开始就马上返回,程序可以继续向前执行,被调用方法和主程序同时执行,在调用方法结束才返回结果。显然这种模型给了我们很好的编程和使用体验。
基于WCF在普通的编码是以文本编码方式在信道之间传输信息的,这种编码会把所有的二进制信息以字节数组的形式存储,并以Base64进行编码,而Base64则是用三个字节来储存4 个字符信息。使得数据量增大约30%以上。在WCF中引入了一种专门针对数据流进行优化编码的MTOM模型。下面我们使用编码模型和调用模型三种方式来改写文件流的传输,以提高WCF应用程序的性能。
1、 MTOM模型:
这模型在于将SOAP消息编码成SOAP MT OM(消息传输优化机制)编码。这种编码是为那些包含大量的二进制数据的SOAP消息而做的,它是把数据流作为SOAP消息的附件而添加的。所以利用这种编码在传输信道之间传输可以显著提高传输性能。在WCF中MTOM模型的操作契约中只能使用单个Stream对象作为参数或者返回类型。
这种模型的特点如图所示:
1.1实现服务契约
服务契约是服务所支持的操作、使用的消息交换模式和每一则消息的格式,它控制消息被格式化的方式,在这里由于要使用MTOM编码消息,所以在操作契约中必须要以单一的Stream对象为输入输出参数。所以这儿我们把服务定义为如下的形式:
[ServiceContract]
public interface ISendStreamService
{
[OperationContract]
void SendStream(Stream stream);
//这个方法的是为了传递文件的参数而设的
[OperationContract]
void FileNameSetting(string filename, string destinationpath);
}
另外我们还定义了一个传输文件路径的名称的辅助方法:FileNameSetting();
1.2实现服务器方法
在上面定义了公共的接口后,接下来我们就实现接口的方法,主要的方法的目的是为了传输Stream对象,由于Stream是一个抽象类,所以这儿以文件流为操作对象来使用SendStream()这个方法。
public class SendStreamService : ISendStreamService
{
static FileStream outStream = null;
static int startLength;
static int fileLength;
static int maxBytesCount=4096;
static byte[] bytes = new byte[int maxBytesCount];
string filePath;
static string fileName;
public void SendStream(System.IO.Stream stream)
{
string file = filePath + "//" + fileName;
outStream = new FileStream(file, FileMode.OpenOrCreate, FileAccess.Write);
try {
while ((startLength = stream.Read(bytes, 0, int maxBytesCount)) > 0){
outStream.Write(bytes, 0, startLength);
fileLength += startLength;
}
}
catch (Exception e) { }
}
public void FileNameSetting(string filename,string destinationpath)
{
fileName = filename;
filePath = destinationpath;
}
}
1.3客户通过接口调用服务器方法
客户端调用服务器方法至少有三种,这里我们选择工厂方法来实现,System.ServiceMode.Channel.ChannelFactory<T>类是这个信道工厂类,它的方法CreateChannel()可以创建T的实例。
ISendStreamService proxy=new
ChannelFactory<ISendStreamService>(“WSHttpBinding_ISendStreamService”).Create-
Channel();
proxy.FileNameSetting(file.Substring (file.LastIndexOf ("\\")+1), filePath);
proxy.SendStream(inStream);
1.4服务器和客户端的配置信息
配置信息定义了双方通信的终结点、绑定、契约行为及其他的配置如安全,可靠性等。服务器的配置如:
<service behaviorConfiguration="SendStreamServiceBehavior"
name="SendStreamService">
<endpoint address=" http://localhost:5504/WebSite2/ISendStreamService "
binding="wsHttpBinding" bindingConfiguration="MTMOBinding"
contract="ISendStreamService">
</endpoint>
<bindings>
<wsHttpBinding>
<binding name="MTMOBinding" messageEncoding="Mtom">
</wsHttpBinding>
</bindings>
</service>
同样客户端的配置如:
<client>
<endpoint address="http://localhost:5504/WebSite2/ISendStreamService"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ISendStreamService"
contract="ServiceReference1.ISendStreamService" name="WSHttpBinding_ISendStreamService">
</endpoint>
</client>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_ISendStreamService"
messageEncoding="Mtom" textEncoding="utf-8" >
</binding>
</wsHttpBinding>
</bindings>
注意:在这种方式下使用同步和异步方法没有明显的差别,后来我在分析了Windows Trace Viewer的消息包,发现在用异步方法时,整个过程只用两个消息来回,这就意味着第一次的SOAP包是在把SOAP消息加上MTOM编码的文件流作为附件一起发送的,在等待文件传输完成后才会返回一个加高消息给方法。也就是说异步方法IAsyncResult Begin*(params parameters,AsyncCallback callback,object state)是在发送第一个SOAP包,并等待服务器接收完第一个包后回应消息包才会返回的。由于在发送文件流时,因为文本字符始终不会超过一个SOAP包而必须等待。所在在这种编码方式下异步调用和同步调用没有差别。
2、 基于同步传输的异步回调模型:
同步传输是指方法在调用过程中一直阻塞到方法调用结束返回结果才会让程序继续向前执行,这种行为比较耗费资源,因为网络访问在等待方法完成的时间内是阻塞的。而且如果远程对象的调用时花费的时间会更长,所以这种时间的浪费让人是不可接受的,这在大文件传输中尤为明显。于是一种让方法的异步调用的机制便产生了。这种方法的内部处理中使用线程池中的一个线程接管这个调用,程序可以获得异步调用的返回信息而继续向前执行。
WCF编程模型中采用了一种让同步传输中使用异步回调的方式来提高应用程序的响应。具体是在每个操作契约中可以选择生成异步方法的调用,具体是在同步方法的前面加上
IAsyncResult Begin…..(params param,AsyncCallback,object state)形式表明这是一个异步调用。并且生成相应的void End……(IAsyncResult state)来返回结果。
2.1定义契约和实现相应的同步方法
这里在服务契约中定义了相应的同步方法,用这个调用FileStream类的同步方法Read()和Write()方法对文件进行读写操作,以实现将文件传输到服务的机器上。这里在服务契约中通过设置属性CallbackContract来实现客户端的回调功能。来其相应的代码如下:
[ServiceContract(CallbackContract = typeof(IUploadCallback))]
public interface IUploadFileService
{
//同步传输的接口
[OperationContract]
void FileUpload(string localFilePath, string netPath);
}
public interface IUploadCallback
{
[OperationContract(IsOneWay = true)]
void ReportFileUpload(int length);//这个回调函数是文件传输完成时发布一个通知
}
//实现文件读写的服务器方法
public class UploadFileService : IUploadFileService
{
static int startLength;
static int fileLength;
static int bytesLength;
static byte[] bytes;
static int maxLength=4096;
static FileStream inStream = null;
static FileStream outStream = null;
IUploadCallback client = null;
public void FileUpload(string localFilePath, string netPath)
{
//获得客户端代理的回调
client = OperationContext.Current.GetCallbackChannel<IUploadCallback>();
//得到原始文件名
string fileName = localFilePath.Substring(localFilePath.LastIndexOf("\\") + 1);
string netFile = netPath + fileName;
bytes = new byte[maxLength];//设置缓冲区
try
{
outStream = new FileStream(netFile, FileMode.OpenOrCreate, FileAccess.Write);
inStream = File.OpenRead(localFilePath);//打开文件读
int length;
while ((length = inStream.Read(bytes, 0, maxLength)) > 0)
{
fileLength += length;
outStream.Write(bytes, 0, length);
}
}
catch (Exception e)
{
Console.WriteLine("文件上传错误:" + e.Message);
inStream.Close();
outStream.Close();
}
finally
{
client.ReportFileUpload(fileLength);//使用回调报告文件的状态
inStream.Close();
outStream.Close();
}
}
2.2在客户端调用方法
在客户端调用BeginFileUpload()和EndFileUpload()方法来实现客户端的异步回调。并在这些方法完成后服务调用客户回调ReportFileUpload()报告给客户端相应的信息。
3、 基于异步传输的异步模型:
在同步方式处理中,文件传输的时间是和文件的长度密切相关的,对于一个大容量的文件传输,如果全部在主线程中执行,那么应用程序可能会等待很长的时间,因此我们给予文件流以异步方法读写的方法来实现性能的改进。这只调用了文件操作的异步处理。第二种模式一样这也是采用线程池来完成的。这实际上是利用了文件流的异步方法。
在这儿我们仍然使用第二种模型的WCF框架,只是我们这儿使用了FileStream对象BeginWrite();BeginRead()方法及相应的EndWrite();EndRead()方法。这儿我们只给出了服务器的方法实现:
public void AsyncFileUpload(string localFilePath, string netPath)
{
//获得客户端代理的回调
client = OperationContext.Current.GetCallbackChannel<IUploadCallback>();
//得到原始文件名
string fileName = localFilePath.Substring(localFilePath.LastIndexOf("\\") + 1);
string netFile = netPath + fileName;
bytes = new byte[maxLength];//设置缓冲区
try{
outStream = new
FileStream(netFile, FileMode.OpenOrCreate, FileAccess.Write);
inStream = File.OpenRead(localFilePath);//打开文件读
inStream.BeginRead(bytes, 0, maxLength, CallbackOnRead, null);
}
catch (Exception e){
inStream.Close();
outStream.Close();
}
}
void CallbackOnRead(IAsyncResult result)
{
int length =inStream.EndRead(result);
if (length >= 0){
fileLength += length;
outStream.BeginWrite(bytes, 0, length, CallbackOnWrite, null);
}
else{
client.ReportFileUpload(fileLength);//使用回调
inStream.Close();
outStream.Close();
}
}
void CallbackOnWrite(IAsyncResult result){
outStream.EndWrite(result);
inStream.BeginRead(bytes, 0, maxLength, CallbackOnRead, null);
}
通过以上的分析, 基于MTOM编码的文件流传输时,可以提高传输性能,而对于后两种方式的前提是必须是普通的文本消息编码才会有效果,才可以提高程序的响应性能。也就是说后两种方式只是一种提高WCF应用程序响应性能的方式,它的传输数据量会有明显的膨胀。具体设计中要看在传输效率和响应性能两者取舍来选取其一而用。
C#利用WCF改进文件流传输的三种方式相关推荐
- 文件上传的三种方式-Java
前言:负责,因为该项目他(jetty嵌入式开始SpringMvc)实现文件上传的必要性,并拥有java文件上传这一块还没有被曝光.并 Http 更多晦涩协议.因此,这种渐进的方式来学习和实践上载文件的 ...
- 使用git下载项目到本地,指定本地文件夹位置的三种方式
使用git下载项目到本地,指定本地文件夹位置的三种方式 使用VSCODE里的"克隆"功能直接粘贴项目链接即可选择本地想保存的位置. 使用git bash窗口下载项目之前,先切换到你 ...
- 使用apache-poi生成excel文件与同步请求三种方式与文件下载
使用apache-poi生成excel文件 a)添加依赖: <!-- poi依赖--><dependency><groupId>org.apache.poi< ...
- 小程序瀑布流的实现三种方式
实现思路:把图片分成两排,创建两个数组,计算总数组中图片的宽高,对比上一个图片和当前的图片高度,低的就给另一个数组添加. 效果图: 第一种:如果后端返回了图片高度 处理过程如下: js: Page({ ...
- python中判断文件是否存在的三种方式_Python中判断文件是否存在的方法
Python中判断文件是否存在的方法 发布时间:2020-08-06 11:15:42 来源:亿速云 阅读:58 作者:小新 这篇文章主要介绍Python中判断文件是否存在的方法,文中介绍的非常详细, ...
- Mybatis笔记整理1(基本文件与配置,三种方式完成数据库操作)
基本文件与配置 pojo类,省略setget,tostring方法 public class User implements Serializable {/*** */private static f ...
- php 声音文件链 处理,PHP 三种方式实现链式操作
在php中有很多字符串函数,例如要先过滤字符串收尾的空格,再求出其长度,一般的写法是: strlen(trim($str)) 如果要实现类似js中的链式操作,比如像下面这样应该怎么写? $str-&g ...
- 怎么把tiff文件转换为jpg?教你三种方式
tiff格式转化成jpg格式?相信很多人对于tiff格式较为陌生,我们需要对其进行了解,从而发挥出tiff文件格式的重要作用.通常来说,tiff是一种非失真的压缩格式.tiff文件的压缩主要是应用于文 ...
- Java利用TCP进行文件的传输
采用TCP进行通讯,需要服务器和客户端两个部分,因此程序包含SendFileServer.java和SendFileClient.java两个部分. 两个文件的IP,端口都在程序中指定 传输的文件路径 ...
最新文章
- html5+开发window桌面图标,js模仿windows桌面图标排列算法具体实现(附图)
- 笔记本电脑如何强制关机_长按电源键强制关机会损害笔记本硬件吗?联想:不会但不建议...
- html怎样做登录页面,使用HTML 5和CSS3制作登录页面完整步骤
- 项目中的异常处理应不应该获取后重新抛?
- UIImageView 圆角
- Laravel + MongoDB 数组字段相关查询
- (转载) MTK之NVRAM研究[三]
- 同等条件下,杂交水稻是否增产?
- 斐讯k2php环境,斐讯K2 刷华硕固件 实现 单线复用(网络、IPTV走一根网线)
- MBR和 GPT互转导致Windows无法开机,镜像备份找不到
- jasper报表格式化bigdecimal(decimal128)数据千分位
- 欧美html游戏,欧美HTML社区服务游戏
- 同一块芯片,同时使用多个SOCKET工作,应该注意什么问题?
- Android实现NFC芯片制造商识别
- Oracle错误08s01,Java7 sqljdbc4 – getConnection()上的SQL错误08S01
- Android11.0下应用管控实现解决方案(家长管理)(一)
- 硬件MSB最高位优先、LSB最低位优先的CRC计算原理详细解释和程序,正算反算成功等效,DS18B20和HTU31D传感器CRC
- Flowable入门系列文章35 - Activity解读 11
- ios获取UUID的方法
- Excel根据关键词批量查找提取
热门文章
- matlab 游标 精度,如何在MATLAB数据光标中显示更高精度的数字?
- linux内核按键驱动,嵌入式Linux按键驱动框架
- 收藏~10年软件测试人员的工作方法进阶汇总
- java监听表变化_「Java Web开发」Filte(过滤器)、Listener(监听器)
- PHP中的方形按钮怎么敲,php 魔术方法使用说明
- python语言折半查找_C语言折半查找 - 胡若晨的个人空间 - OSCHINA - 中文开源技术交流社区...
- Java小项目:坦克大战(二)
- Lesson 29 SVD decomposition
- 基于深度卷积神经网络的农作物病害识别
- oracle列传行函数,oracle行转列和列转行(pivot 和 unpivot 函数,wm_concat函数 )