学会WCF之试错法——数据传输
服务契约
[ServiceContract]
public interface IService
{
[OperationContract]
string GetData(int value); [OperationContract]
string GetString(string value); [OperationContract]
void Upload(Request request);
} [MessageContract]
public class Request
{
[MessageHeader(MustUnderstand = true)]
public string FileName { get; set; } [MessageBodyMember(Order = )]
public Stream Content {get;set;}
}
服务
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Reentrant)]
public class Service : IService
{
public string GetData(int value)
{
//Thread.Sleep(120000);
return string.Format("You entered: {0}", value);
} public string GetString(string value)
{
//Thread.Sleep(120000);
return string.Format("You entered: {0}", value);
} public void Upload(Request request)
{
try
{
StreamReader sr = new StreamReader(request.Content, Encoding.GetEncoding("GB2312"));
StreamWriter sw = new StreamWriter("E:\\" + request.FileName + ".txt", false, Encoding.GetEncoding("GB2312"));
while (!sr.EndOfStream)
{
sw.WriteLine(sr.ReadLine());
//Thread.Sleep(5000);
}
sr.Close();
sw.Close();
}
catch (Exception ex)
{ } }
}
服务配置
<system.serviceModel>
<services>
<service name="WCF_Find_Error_Lib.Service">
<endpoint address="" binding="basicHttpBinding" contract="WCF_Find_Error_Lib.IService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<host>
<baseAddresses>
<add baseAddress="http://localhost/S" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="True" httpsGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
客户端代理
public class ServiceProxy
{
public string GetData(int value)
{
string ret = null;
ServiceClient client = null;
try
{
client = new ServiceClient();
ret = client.GetData(value);
client.Close();
}
catch
{
if (client != null)
{
client.Abort();
}
throw;
}
return ret;
} public string GetString(string value)
{
string ret = null;
ServiceClient client = null;
try
{
client = new ServiceClient();
ret = client.GetString(value);
client.Close();
}
catch
{
if (client != null)
{
client.Abort();
}
throw;
}
return ret;
}
public void Upload(Request request)
{
ServiceClient client = null;
try
{
client = new ServiceClient();
client.Upload(request);
client.Close();
}
catch
{
if (client != null)
{
client.Abort();
}
throw;
}
} } [ServiceContractAttribute(ConfigurationName = "IService")]
public interface IService
{ [System.ServiceModel.OperationContractAttribute(Action = "http://tempuri.org/IService/GetData", ReplyAction = "http://tempuri.org/IService/GetDataResponse")]
string GetData(int value); [System.ServiceModel.OperationContractAttribute(Action = "http://tempuri.org/IService/GetString", ReplyAction = "http://tempuri.org/IService/GetStringResponse")]
string GetString(string value); [System.ServiceModel.OperationContractAttribute(Action = "http://tempuri.org/IService/Upload", ReplyAction = "http://tempuri.org/IService/UploadResponse")]
void Upload(Request request);
}
[MessageContract]
public class Request
{
[MessageHeader(MustUnderstand = true)]
public string FileName { get; set; } [MessageBodyMember(Order = )]
public Stream Content { get; set; }
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Reentrant)]
public class ServiceClient : System.ServiceModel.ClientBase<IService>, IService
{ public ServiceClient()
{
} public string GetData(int value)
{
return base.Channel.GetData(value);
} public string GetString(string value)
{
return base.Channel.GetString(value);
} public void Upload(Request request)
{
base.Channel.Upload(request);
}
}
客户端配置
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService" />
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost/S" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IService" contract="IService"
name="BasicHttpBinding_IService" />
</client>
</system.serviceModel>
配置参数:
MaxBufferPoolSize :从通道接收消息的消息缓冲区管理器分配并供其使用的最大内存量,BasicHttpBinding.MaxBufferPoolSize默认值为 524288 个字节。WSHttpBindingBase.MaxBufferPoolSize默认值为 65,536个字节,配置它可提高性能。
MaxBufferSize :从通道接收消息的缓冲区最大大小,默认值为 65,536 个字节。
MaxReceivedMessageSize:此绑定配置的通道上接收的消息的最大值,默认值为 65,536 个字节。
TransferMode:指示是通过缓冲处理还是流处理来发送消息
默认情况下,HTTP、TCP/IP 和命名管道传输协议使用缓冲消息传输。
XmlDictionaryReaderQuotas.MaxStringContentLength:读取器返回最大字符串长度,默认为8192
1 文件编码引起的错误
问题描述:
客户端向服务端传输数据时,假如是一个文本文件,接口参数是Stream,那么服务端使用StreamReader或StreamWrite时,不指定编码,服务运行在win7 及更新的操作系统上,默认的编码方式为Unicode,如果文本文件含义中文,那么服务端接收的内容出现乱码。而且服务端接收到的文件大小大于客户端传输的文件大小。
服务端配置
绑定配置如下,其他不变
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService" maxBufferSize="170000"/>
</basicHttpBinding>
</bindings>
启动服务,报错,可见单独配置maxBufferSize是不行的。
修改配置如下,可正常运行:
服务端
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService" maxBufferSize="170000" maxReceivedMessageSize="170000"/>
</basicHttpBinding>
</bindings>
客户端配置
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService" sendTimeout="00:00:10" />
</basicHttpBinding>
</bindings>
调用接口
try
{
ServiceProxy proxy = new ServiceProxy();
//string s = proxy.GetData(1);
//Console.WriteLine(s);
Request r = new Request
{
Content = new FileStream("D:\\CSBMTEMP.txt", FileMode.Open),
FileName = "CSBMTEMP"
};
proxy.Upload(r);
Console.Read();
}
//catch (CommunicationException ex)
//{ //}
catch (Exception ex)
{ }
异常信息:
首先检查文件的大小,发现文件大小没有超过配置的最大值
将服务端配置按如下修改,也就是增大maxBufferSize和maxReceivedMessageSize的值。
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService" maxBufferSize="1700000" maxReceivedMessageSize="1700000">
</binding>
</basicHttpBinding>
</bindings>
运行客户端程序,发现服务端接收到文件为338K,远大于客户端上传文件大小,打开文件,看到乱码。
解决方法
将服务端Upload方法修改一下:
StreamReader sr = new StreamReader(request.Content,Encoding.Default);
StreamWriter sw = new StreamWriter("E:\\" + request.FileName + ".txt", false, Encoding.Default);
上面那两行代码添加编码方式为Encoding.Default,或者使用Encoding.GetEncoding("GB2312")这种编码方式。使用后者更好,因为明确地指出编码方式。
启动服务,运行客户端上传文件,成功且无乱码。文件大小也和客户端上传的相同。
2 maxBufferSize与maxReceivedMessageSize的设置
上面服务端maxBufferSize和maxReceivedMessageSize设置为1700000,远大于所传文件大小,那么将其改为167936会如何呢?
修改上面两个参数为167936,运行程序。结果报错:
那么,难道上传文件的数据会比这个大吗?是的,这只是消息的一部分内容。从客户端本地CLR类型来看,其传递的参数是一个对象:Request,它包含了两字段,Stream类型的Content和string类型的FileName。但这只是其中一部分原因。
经测试,若maxBufferSize和maxReceivedMessageSize设置为大于上传文件的二倍时,上传成功。由于默认采用缓冲处理模式(TransferMode .Buffered),缓冲处理是将消息全部缓存下来以后才对消息进行处理,猜想,缓存消息所需空间,加上处理消息也需要空间,那么两者的和就是二倍的传入消息大小。
若换成流传输模式(客户端不使用流模式,很奇怪的测试,不过依然通过)
服务端配置变为:
<basicHttpBinding>
<binding name="BasicHttpBinding_IService" transferMode="Streamed"/>
</basicHttpBinding>
客户端配置不变,即:
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService" sendTimeout="00:00:10" />
</basicHttpBinding>
</bindings>
客户端调用接口,服务端报错。
客户端配置不变,服务端配置变为:
<basicHttpBinding>
<binding name="BasicHttpBinding_IService" maxBufferSize="170000" maxReceivedMessageSize="170000" transferMode="Streamed">
</binding>
</basicHttpBinding>
客户端调用接口,正常将文件传输到服务器。
再次将服务端maxBufferSize和maxReceivedMessageSize修改为小于文件长度:150000,调用接口,可正常传输文件,但是只上传了文件的一半左右。继续将maxBufferSize和maxReceivedMessageSize调小为1000000,发现只上传了8K左右。结论是:当maxBufferSize和maxReceivedMessageSize设置小于消息大小的时候,程序正常运行,但是服务端无法接收客户端上传的全部数据。然而,如何准确地估计消息大小是个难题。最稳妥的办法是将上面那两个参数配置为消息体的最大值的二倍,另外从客户端设置上传消息的大小很多时候也是必要的。
将绑定换成netTcpBinding
服务端配置
<bindings>
<netTcpBinding>
<binding name="NetTcpBinding_IService" maxBufferSize="17936" maxReceivedMessageSize="17936"/>
</netTcpBinding>
</bindings>
显然maxBufferSize和maxReceivedMessageSize设置小于客户端发送的消息大小
采用默认的缓冲机制而并非流机制
客户端捕获异常:
从上面的异常中看不出是什么原因造成的,不过看下_remoteStackTraceString:
Server stack trace:
在 System.ServiceModel.Channels.StreamConnection.Read(Byte[] buffer, Int32 offset, Int32 size, TimeSpan timeout)
在 System.ServiceModel.Channels.SessionConnectionReader.Receive(TimeSpan timeout)
在 System.ServiceModel.Channels.SynchronizedMessageSource.Receive(TimeSpan timeout)
在 System.ServiceModel.Channels.TransportDuplexSessionChannel.Receive(TimeSpan timeout)
在 System.ServiceModel.Channels.TransportDuplexSessionChannel.TryReceive(TimeSpan timeout, Message& message)
在 System.ServiceModel.Dispatcher.DuplexChannelBinder.Request(Message message, TimeSpan timeout)
在 System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
在 System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
在 System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
从上面StreamConnection.Read,SessionConnectionReader.Receive大概猜测是maxBufferSize和maxReceivedMessageSize设置引起的,由于套接字连接终止,在服务端无法捕获这类异常。将参数调大就没有这个问题了,数据可正常上传。
那么采用netTcpBinding是比较安全的,因为一旦设置的过小,会抛异常而不会出现数据不完整(只上传一部分)的情况。
当数据传输过程中服务关闭或网络中断抛异常:
貌似与maxBufferSize和maxReceivedMessageSize这两个参数设置不正确所抛异常一样,而且_remoteStackTraceString也与其一样,真是让人迷惑啊!!!
绑定配置变换为:
<netTcpBinding>
<binding name="NetTcpBinding_IService" maxBufferSize="79360" maxReceivedMessageSize="79360" transferMode="Streamed"/>
</netTcpBinding>
客户端配置:
<netTcpBinding>
<binding name="NetTcpBinding_IService" sendTimeout="00:00:10"/>
</netTcpBinding>
客户端调用报错
修改客户端配置为:
<netTcpBinding>
<binding name="NetTcpBinding_IService" sendTimeout="00:00:10" transferMode="Streamed"/>
</netTcpBinding>
这次是由于maxBufferSize和maxReceivedMessageSize这两个参数设置小了的原因。
3 对于字符串传输的限制
服务端配置:
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService" maxBufferSize="220000" maxReceivedMessageSize="220000" transferMode="Streamed">
</binding>
</basicHttpBinding>
</bindings>
<services>
<service name="WCF_Find_Error_Lib.Service">
<endpoint address=""
binding="basicHttpBinding"
contract="WCF_Find_Error_Lib.IService"
bindingConfiguration="BasicHttpBinding_IService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<host>
<baseAddresses>
<add baseAddress="http://localhost/S" />
</baseAddresses>
</host>
</service>
</services>
客户端配置:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService" sendTimeout="00:00:10"/>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost/S" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IService" contract="IService"
name="BasicHttpBinding_IService" />
</client>
</system.serviceModel>
客户端调用:
ServiceProxy proxy = new ServiceProxy();
StreamReader sr = new StreamReader("D:\\CSBMTEMP.txt",Encoding.Default);
string str = sr.ReadToEnd();
sr.Close();
proxy.GetString(str);
运行客户端报错:
错误消息为:
格式化程序尝试对消息反序列化时引发异常: 对操作“GetString”的请求消息正文进行反序列化时出现错误。读取 XML 数据时,超出最大字符串内容长度配额 (8192)。
那么看一下传入的字符串大小为238367个字符,因此修改服务端配置文件,而保持客户端配置不变
<basicHttpBinding>
<binding name="BasicHttpBinding_IService" maxBufferSize="220000" maxReceivedMessageSize="220000" transferMode="Streamed">
<readerQuotas maxStringContentLength="240000"/>
</binding>
</basicHttpBinding>
运行客户端程序,依然报错信息如下:
此次是因为服务端maxBufferSize和maxReceivedMessageSize设置小了,将其设置为270000,客户端保持不变。
客户端抛异常:
服务端参数已经设置的比较大了,但是依然报错,发现客户端没有设置maxStringContentLength这个参数,更改客户端配置,服务端保持不变:
<binding name="BasicHttpBinding_IService" sendTimeout="00:00:10" transferMode="Streamed">
<readerQuotas maxStringContentLength="240000"/>
</binding>
运行客户端,依然报错
发现服务端maxReceivedMessageSize没有设置,更改客户端配置,服务端保持不变,至此信息被正确接收。
<basicHttpBinding>
<binding name="BasicHttpBinding_IService" sendTimeout="00:00:10" transferMode="Streamed" maxReceivedMessageSize="270000">
<readerQuotas maxStringContentLength="240000"/>
</binding>
</basicHttpBinding>
通过上述测试,发现:
1)当客户端配置不正确或服务端配置不正确时,异常可在客户端捕获,但捕获的异常信息相同,即客户端与服务端因同一类参数配置不正确所引发的异常信息一样,无法通过异常信息分辨出是由于客户端配置不正确还是由于服务端配置不正确引起的。
2)一般地,对于使用basicHttpBinding的服务,当由于配置不正确,抛出异常时,可以通过捕获的异常查看是哪个参数配置不正确,但是使用netTcpBinding的服务则无法通过客户端捕获的异常分辨是哪个参数配置的不正确。
3)客户端与服务端配置不一致时,可正常运行服务,但不一定得到正确的结果。某些资料建议将客户端和服务端配置设置为相同,不失为一种简单的办法,但忽略了配置参数的含义,建议深入理解各个参数的含义,合理配置。
4)多个参数配置不正确时,抛出的异常信息中会选则性地指出某个参数设置不正确,而不是将所有配置不正确的参数都指出来。
-----------------------------------------------------------------------------------------
转载与引用请注明出处。
时间仓促,水平有限,如有不当之处,欢迎指正。
学会WCF之试错法——数据传输的更多相关文章
- 学会WCF之试错法——客户端调用基础
1当客户端调用未返回结果时,服务不可用(网络连接中断,服务关闭,服务崩溃等) 客户端抛出异常 异常类型:CommunicationException InnerException: Message: ...
- 学会WCF之试错法——安全配置报错分析
安全配置报错分析 服务端配置 <system.serviceModel> <bindings> <wsHttpBinding> <binding name = ...
- 学会WCF之试错法——超时
服务契约 [ServiceContract] public interface IService { [OperationContract] string GetData(int value); [O ...
- 从Web Service和Remoting Service引出WCF服务
本篇先通过Web Service和Remoting Service创建服务,抛砖引玉,再体验WCF服务.首先一些基本面: 什么是WCF? Windows Communication Foundatio ...
- .net 学习路线感想
从上到大学到现在工作,已经有六年多了,发现学习编程到以开发为工作也是一个挺长的过程的. 大学中,从c语言到java.C#到其他各种语言的学习,还有其他知识的学习如:数据库(oracle.sql Ser ...
- .net 学习路线感想(转)
从上到大学到现在工作,已经有六年多了,发现学习编程到以开发为工作也是一个挺长的过程的. 大学中,从c语言到java.C#到其他各种语言的学习,还有其他知识的学习如:数据库(oracle.sql Ser ...
- wcf,socket,数据传输方式
WCF的最终目标是通过进程或不同的系统.通过本地网络或是通过Internet收发客户和服务之间的消息. WCF合并了Web服务..net Remoting.消息队列和Enterprise Servic ...
- WCF 项目应用连载[8] - 绑定、服务、行为 大数据传输与限流 - 下 (ServiceThrottlingAttribute)
因为ORM的原因,对Attribute编程有一种情节..所以这节的出现,完全是因为在WCF对自定义Attribute的一种应用. WCF 项目应用连载[7] - 绑定.服务.行为 大数据传输与限流 - ...
- 中小研发团队架构实践之生产环境诊断工具WinDbg 三分钟学会.NET微服务之Polly 使用.Net Core+IView+Vue集成上传图片功能 Fiddler原理~知多少? ABP框架(asp.net core 2.X+Vue)模板项目学习之路(一) C#程序中设置全局代理(Global Proxy) WCF 4.0 使用说明 如何在IIS上发布,并能正常访问
中小研发团队架构实践之生产环境诊断工具WinDbg 生产环境偶尔会出现一些异常问题,WinDbg或GDB是解决此类问题的利器.调试工具WinDbg如同医生的听诊器,是系统生病时做问题诊断的逆向分析工具 ...
随机推荐
- SqlServer和Oracle中一些常用的sql语句5 流程控制语句
--在sql语句中 begin...end 用来设定一个程序块 相关于c#中的{} declare @yz real,@w int --声明变量 set @w=120 --为变量赋值 if @w< ...
- Python开篇
一:Python的前世今生 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为 ...
- 如何开发webpack loader
关于webpack 作为近段时间风头正盛的打包工具,webpack基本占领了前端圈.相信你都不好意思说不知道webpack. 有兴趣的同学可以参考下我很早之前的webpack简介 . 确实webpac ...
- 慢SQL汇总
select count(*) as aggregate from `yqz_feed_praise` where `uid` = '580242' and `praise_uid` <> ...
- Linux命令-基本命令(1)
1. ll dfdfdfd 2. vi dfffd
- HTML5-前端开发很火且工资很高?
前言 晚上逛论坛看到一篇对从事HTML5前端开发的文章写的非常不错,和目前的市场形势差不多,然后我在其基础上给大家进行加工总结一下分享给大家.今天我们谈论的话题是<<为什么从事HTML5前 ...
- (MariaDB)MySQL数据类型详解和存储机制
html { font-family: sans-serif } body { margin: 0 } article,aside,details,figcaption,figure,footer,h ...
- 突发小事件,USB接口问题
昨天遇到的突发事件,突然USB接口全部瘫了,键盘鼠标全部不能用,换到别人电脑上可以,吓尿了,以为本子主板挂了,但是发现插U盘竟然可以识别而且可以打开,感觉可能是静电问题,果然,彻底关机,拔掉电池,然后 ...
- JAVA中java.util.Date、java.sql.Timestamp和String之间的互相转换
java.util.Date与的String互转 java.util.Date---->String /** * 将java.util.Date对象转化为String字符串 * @param d ...
- ASP 文件内部访问数据库的通常途径
创建至数据库的 ADO 连接(ADO connection) 打开数据库连接 创建 ADO 记录集(ADO recordset) 打开记录集(recordset) 从数据集中提取你所需要的数据 关闭数 ...