杭州网站建设哪家好,郑州做网站那,西宁网站建设价格,不会代码可以做网站吗随笔- 197 文章- 0 评论- 3407 十五天精通WCF——第一天 三种Binding让你KO80%的业务 转眼wcf技术已经出现很多年了#xff0c;也在.net界混的风生水起#xff0c;同时.net也是一个高度封装的框架#xff0c;作为在wcf食物链最顶端的我们所能做的任务已经简单的不能再简单… 随笔- 197 文章- 0 评论- 3407 十五天精通WCF——第一天 三种Binding让你KO80%的业务 转眼wcf技术已经出现很多年了也在.net界混的风生水起同时.net也是一个高度封装的框架作为在wcf食物链最顶端的我们所能做的任务已经简单的不能再简单了 再简单的话马路上的大妈也能写wcf了好了wcf最基本的概念我们放在后面慢慢分析下面我们来看看神奇的3个binding如何KO我们实际场景中的80%的业务场景。 一basicHttpBinding 作为入门第一篇也就不深入谈谈basic中的信道栈中那些啥东西了你只需要知道有ABC三个要素注意不是姨妈巾哦如果需要详细了解可以观赏我以前的系列。在 这里我就不多说了太简单的东西没意思先看个例子简单感受了你只需知道的是basic走的是http协议就好了传输消息为soap。 1. 契约 1 using System.Runtime.Serialization;2 using System.ServiceModel;3 4 namespace MyService5 {6 [ServiceContract]7 public interface IHomeService8 {9 [OperationContract]
10 int GetLength(string name);
11 }
12 } 2. 实现类 1 using System;2 using System.Messaging;3 using System.Threading;4 5 namespace MyService6 {7 public class HomeService : IHomeService8 {9 public int GetLength(string name)
10 {
11 return name.Length;
12 }
13 }
14 } 3. 服务启动 1 using System;2 using System.ServiceModel;3 4 namespace MyService5 {6 class Program7 {8 static void Main(string[] args)9 {
10 using (ServiceHost host new ServiceHost(typeof(HomeService)))
11 {
12 try
13 {
14 host.Open();
15
16 Console.WriteLine(服务开启);
17
18 Console.Read();
19 }
20 catch (Exception e)
21 {
22 Console.WriteLine(e.Message);
23 }
24 }
25 }
26 }
27 } 4. 配置config文件 ?xml version1.0 encodingutf-8 ?
configurationsystem.serviceModelbindingsnetTcpBindingbinding nameIHomeServiceBinding //netTcpBinding/bindingsbehaviorsserviceBehaviorsbehavior nameserviceMetadata httpGetEnabledtrue /serviceDebug includeExceptionDetailInFaultstrue //behavior/serviceBehaviors/behaviorsservicesservice nameMyService.HomeServiceendpoint addresshttp://127.0.0.1:1920/HomeService bindingbasicHttpBinding contractMyService.IHomeServiceidentitydns valuelocalhost //identity/endpointendpoint addressmex bindingmexHttpBinding contractIMetadataExchange /hostbaseAddressesadd baseAddresshttp://127.0.0.1:1920//baseAddresses/host/service/services/system.serviceModel
/configuration 5. 然后通过 servicehost 启动服务端 using System;
using System.ServiceModel;namespace MyService
{class Program{static void Main(string[] args){using (ServiceHost host new ServiceHost(typeof(HomeService))){try{host.Open();Console.WriteLine(服务开启);Console.Read();}catch (Exception e){Console.WriteLine(e.Message);}}}}
} 好了到现在为止服务端全部开启完毕接下来我们通过“添加服务引用”来添加对客户端的引用 1 using System;2 3 namespace ConsoleApplication14 {5 class Program6 {7 static void Main(string[] args)8 {9 HomeServiceReference.HomeServiceClient client new HomeServiceReference.HomeServiceClient();
10
11 var s client.GetLength(12345);
12
13 Console.WriteLine(长度为:{0}, s);
14
15 Console.Read();
16 }
17 }
18 } 麻蛋就这么简单是的就这样简单的五步基于http的通信就这样被不小心的完成了真不好意思。 二netTcpBinding 有了basic的代码现在我们要改成tcp通信这会通信走的是字节流很简单改一下服务端的config文件就好了大家也知道这种性能要比basic好。 ?xml version1.0 encodingutf-8 ?
configurationsystem.serviceModelbehaviorsserviceBehaviorsbehavior namemxbehaviorserviceMetadata httpGetEnabledtrue /serviceDebug includeExceptionDetailInFaultstrue //behavior/serviceBehaviors/behaviorsservicesservice nameMyService.HomeService behaviorConfigurationmxbehaviorendpoint addressnet.tcp://localhost:19200/HomeService bindingnetTcpBinding contractMyService.IHomeServiceidentitydns valuelocalhost //identity/endpointendpoint addressmex bindingmexHttpBinding contractIMetadataExchange/hostbaseAddressesadd baseAddresshttp://localhost:1920/HomeService//baseAddresses/host/service/services/system.serviceModel
/configuration 三netMsmqBinding msmq这个玩意我想大家都清楚一个物理上的文件好处呢你也明白就是client和service的所有通信都要经过它的手这样任何一方出了问题只要 它在就没问题了。同样我们把tcp改成msmq也是非常简单的不过要注意msmqbinding中是不可以让契约方法有返回值的。所以我们加上isoneway就好了。 using System.Runtime.Serialization;
using System.ServiceModel;namespace MyService
{[ServiceContract]public interface IHomeService{[OperationContract(IsOneWay true)]void GetLength(string name);}
} 然后我在mmc上新建一个消息队列如下 然后我们再改动以下配置文件 ?xml version1.0 encodingutf-8 ?
configurationsystem.serviceModelbehaviorsserviceBehaviorsbehavior namemxbehaviorserviceMetadata httpGetEnabledtrue /serviceDebug includeExceptionDetailInFaultstrue //behavior/serviceBehaviors/behaviorsbindingsnetMsmqBindingbinding namemsmqbindingsecurity modeNone//binding/netMsmqBinding/bindingsservicesservice nameMyService.HomeService behaviorConfigurationmxbehaviorendpoint addressnet.msmq://localhost/private/homequeue bindingnetMsmqBindingcontractMyService.IHomeService bindingConfigurationmsmqbindingidentitydns valuelocalhost //identity/endpointendpoint addressmex bindingmexHttpBinding contractIMetadataExchange /hostbaseAddressesadd baseAddresshttp://localhost:19200/HomeService//baseAddresses/host/service/services/system.serviceModel
/configuration 纵观上面的三种binding配置起来何其简单底层的各种通讯协议貌似对我来说都是透明的其实呢wcf在底层做了何其多的事情而我却没有挖掘。。。 这对码农里说也是一种悲哀啊。。。出了问题就只能祷告上天。。。下一篇我会开始深入剖析。 随笔- 197 文章- 0 评论- 3407 十五天精通WCF——第二天 告别烦恼的config配置 经常搞wcf的基友们肯定会知道当你的应用程序有很多的“服务引用”的时候是不是有一种疯狂的感觉。。。从一个环境迁移到另外一个环境你需要改变的 endpoint会超级tmd的多简直就是搞死了人。。。好了这篇我们来看看如何最小化配置。 一精简service的config配置 就像上一篇的代码一样我的service端的config配置如下 1 ?xml version1.0 encodingutf-8 ?2 configuration3 system.servicemodel4 behaviors5 servicebehaviors6 behavior namemxbehavior7 servicemetadata httpgetenabledtrue /8 servicedebug includeexceptiondetailinfaultstrue /9 /behavior
10 /servicebehaviors
11 /behaviors
12 services
13 service namemyservice.homeservice behaviorconfigurationmxbehavior
14 endpoint addressnet.tcp://localhost:1920/homeservice bindingnettcpbinding contractmyservice.ihomeservice
15 identity
16 dns valuelocalhost /
17 /identity
18 /endpoint
19 endpoint addressmex bindingmexhttpbinding contractimetadataexchange /
20 host
21 baseaddresses
22 add baseaddresshttp://localhost:19200/homeservice/
23 /baseaddresses
24 /host
25 /service
26 /services
27 /system.servicemodel
28 /configuration 通过上面的代码你应该知道在system.servicemodel下的所有节点都是wcf专属的节点所有的节点数据都会被开启servicehost这个监听器时捕获到下面我可以 通过servicehost这个监听器的源码下面找找相关的读取config节点的代码。 通过上面的截图你是不是有一种感觉就是service的底层也是通过代码动态的读取config下面的节点来获取数据那就意味着我可以直接将代码写入到code中 对吧这样我就可以把我认为该配置的东西配置起来不该配置的东西全部放到代码里面去这样我的灵活性是不是非常的强大。。。。爽吧说干就干。。。 1 class Program12 {3 static void Main(string[] args)4 {5 ServiceHost host new ServiceHost(typeof(HomeService), new Uri(http://localhost:19200/HomeService));6 7 host.AddServiceEndpoint(typeof(IHomeService), new NetTcpBinding(), net.tcp://localhost:1920/HomeService);8 9 //公布元数据
10 host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled true });
11 host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), mex);
12
13 host.Open();
14
15 Console.WriteLine(服务已经开启。。。);
16
17 Console.Read();
18 }
19 } 有人就要说了地址的话肯定不能是写死的必须变活简单啊我就仅仅把ip地址配置到config里面去不就完事了对不对。 configurationappSettingsadd key baseurl valuehttp://localhost:19200/HomeService/add key endpoindurl valuenet.tcp://localhost:1920/HomeService//appSettings 1 class Program12 {3 static void Main(string[] args)4 {5 ServiceHost host new ServiceHost(typeof(HomeService), new Uri(ConfigurationManager.AppSettings[baseurl]));6 7 host.AddServiceEndpoint(typeof(IHomeService), new NetTcpBinding(), ConfigurationManager.AppSettings[endpoindurl]);8 9 //公布元数据
10 host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled true });
11 host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), mex);
12
13 host.Open();
14
15 Console.WriteLine(服务已经开启。。。);
16
17 Console.Read();
18 }
19 } 现在看的话是不是清楚多了如果你觉得我的代码比较累赘你可以封装成一个方法然后就可以动态的配置nettcpbasicws*等等对吧。。。好了说完服 务端接下来我们看看client端如何避免。 二精简client的config配置 就像上一节那样如果我用“服务引用”的话vs会偷偷的用svcutil.exe来给我们生成一个proxy类和一个config文件proxy类也就是你看到的xxxclient。。。 可恶的是config里面会给我生成一些乱七八糟的东西如下图 1 ?xml version1.0 encodingutf-8 ?2 configuration3 system.serviceModel4 bindings5 netTcpBinding6 binding nameNetTcpBinding_IHomeService /7 /netTcpBinding8 /bindings9 client
10 endpoint addressnet.tcp://localhost:1920/HomeService bindingnetTcpBinding
11 bindingConfigurationNetTcpBinding_IHomeService contractHomeServiceReference.IHomeService
12 nameNetTcpBinding_IHomeService
13 identity
14 dns valuelocalhost /
15 /identity
16 /endpoint
17 /client
18 /system.serviceModel
19 /configuration 同服务器端一样如果我用code做掉是不是非常的爽呢那可不可以做掉呢 我们还得看一下proxy的源码首先你会看到其实所谓的proxy只是一个继承 自clientbase的一个类如下图。 上面的两幅图你会发现最后的proxy类是通过ChannelFactoryTChannel类来完成助攻的那话说回来了既然底层用了ChannelFactoryTChannel 那何不我在代码里面就用ChannelFactoryTChannel不是更好吗这样config也省了对吧说干就干啦。。。 1 static void Main(string[] args)
2 {
3 ChannelFactoryIHomeService factory new ChannelFactoryIHomeService(new NetTcpBinding(), net.tcp://localhost:1920/homeservice);
4
5 var channel factory.CreateChannel();
6
7 var result channel.GetLength(12345);
8 } 好了代码就这么简单现在是不是感觉自己萌萌大啦~~~ 十五天精通WCF——第三天 client如何知道server提供的功能清单 通常我们去大保健的时候都会找姑娘问一下这里能提供什么服务什么价格这时候可能姑娘会跟你口述一些服务或者提供一份服务清单这样的话大 家就可以做到童嫂无欺这样一份活生生的例子在wcf中同样是一个道理只有client了解service能提供哪些功能client才可以根据server提供的功能进行 消费那问题来了service怎么把功能提供给client进行选择呢这个就是我这一篇要聊的wsdlweb service description language。。。 一wsdl 现在你已经知道了wsdl就是server提供给client的清单那下面问题就来了。server是如何提供的呢你要是比较仔细的话可能会知道我在上一 篇提到的一个endpoint如下截图。 在上面这幅图中你可以看到Homeservice提供了两个端点一个是“服务端点“一个是“元数据端点”。并且你也看到了元数据的端点地址是 http://192.168.16.16:19200/mex当client通过svcutil访问这个地址的时候就拿到了server能提供的功能清单然后client就可以根据这些功能生成一 个代理文件然后的然后就是你懂得各种啪啪啪XXXClient。 二眼见为实 1.见证wsdl 要想看见wsdl你只需要通过http://localhost:19200打开服务地址、如下图 然后点击http://localhost:19200/?singleWsdl 现在你看到的就是server功能清单太tmd的重量级了已经完完全全果体在世人前了下一小节我们再详细的分析下。 2. 见证client端的XXXclient 刚才我也说了当你用vs做“服务引用”的时候svcutil会根据http://localhost:19200/mex的地址来查看wsdl然后生成代理下面我们具体来看一下。 点击确定之后我们就可以看到在 Service References 文件夹下面生成了一个Reference.cs 文件。 然后我们打开Reference.cs就可以看到一个继承于ClientBase的HomeServiceClient。 三详细分析wsdl文件 学wcf你一定要像svcutil一样能够看得懂wsdl。 1. 首先看下server提供了一个Update操作参数是一个id一个Student这个自定义的复杂类型同时返回也是Student这个 复杂类型。 1 namespace MyService
2 {
3 [ServiceContract]
4 public interface IHomeService
5 {
6 [OperationContract]
7 Student Update(int id, Student stu);
8 }
9 } 2. wsdl这个xml文件刚才你也看到了下面我们一个个节点看看 1 portType 和 operation节点 当你看到下面的截图后我想你也能猜的出来portType就是契约IHomeServiceoperation就是契约方法Update不过有点意思的是在operation 下面你看到了一个input一个output这个就是所谓的 ”输入消息“”输出消息”那是什么意思呢 也就是说client到server的消息叫做“输入消息”server到 client端叫做“输出消息”到这里你应该似乎明白了我C#中的Update方法是有入参和出参的然而这映射到wsdl中就是两条消息input和output这个也就是经典 的“请求-响应“模式。 好了继续往下看在wsdl:input和wsdl:output中分别有一个Action属性这个非常有意思wcf的底层就是通过这个地址来找到对应的方法比如我们看到的代理 类中的Update方法上面就有这么一段。 2 message 和 types节点 继续往下看的话你会发现input和output中还有一个message属性对应的为IHomeService_Update_InputMessage和IHomeService_Update_OutputMessage 这个正好是message节点的引用如下图 从这个图中你可以看到input和output下面都有一个wsdl:part节点这个就是表明input和output中需要携带的参数比如elementtns:Update就引用了 element中NameUpdate的节点如下图 好了最后我再截一张图可以看到传输协议为soap服务地址等等。。。然后就没什么好说的了。 十五天精通WCF——第四天 你一定要明白的通信单元Message 转眼你已经学了三天的wcf了是不是很好奇wcf在传输层上面到底传递的是个什么鸟毛东西呢应该有人知道是soap那soap这叼毛长得是什么 样呢这一篇我们来揭开答案。。。 一soap到底长成什么样子 为了能看清soap长的啥样我可以用强大的Fiddler来监视一下突然好激动啊 1.Server 1 static void Main(string[] args)2 {3 ServiceHost host new ServiceHost(typeof(HomeService), new Uri(http://192.168.1.105:19200));4 5 host.AddServiceEndpoint(typeof(IHomeService), new BasicHttpBinding(), HomeServie);6 7 //公布元数据8 host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled true });9
10 host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), mex);
11
12 host.Open();
13
14 Console.WriteLine(服务已经开启。。。);
15
16 Console.Read();
17 } 2.Client 1 ChannelFactoryIHomeService factory new ChannelFactoryIHomeService(new BasicHttpBinding(), new EndpointAddress(http://192.168.1.105:19200/HomeServie));
2
3 var client factory.CreateChannel();
4
5 client.Update(王八蛋); 现在我想你大概看清楚了这玩意是个么样子一个建立在xml上面的一种消息格式根元素是envelope我知道这叼毛翻译过来就是“信封”所以就有了”封头“ 和”封体”就是s:Header 和 s:Body从这个soap中你可以看到它忽略了header然后我们继续往下看还记得Update的意思吗如果你读懂了上一篇 你应该知道这是一个Action也就是所谓的input消息。与之对应的就是UpdateResponse这个output消息对吧还记得xmlnshttp://tempuri.org/吗 它就是IHomeService的默认命名空间对吧。。。 下一个我们关注的是Update这个Action中的str这个你也看得到这个就是上图中Update方法中的str参数最后我们来看一下UpdateResponse中 的UpdateResult xmlns:ahttp://schemas.datacontract.org/2004/07/MyService不知道你是否还记得它就是WSDL中关于Student的XSD结 构看下图 好了wcf中的soap结构我们也大概了解了一下不知道有没有引发你对soap更深入的思考呢 二对soap的更深入思考 通过fiddler观察你应该也明白了不管是客户端还是服务端wcf的高层封装都是仅仅拿出了Envelope中的body节点而其他节点对我们来说好像并 没有什么卵用比如我说的Header节点这么说来Header是不是有点浪费呢那下面有一个问题来了wcf在底层用什么来构造消息的呢下面 我们大概找下client端的源码。。。 通过上面的图你现在应该也知道了在.net中其实tmd的就是message构造的所以我想告诉你的是既然wcf在底层也是用message来构造的何不我自己 就来构造message消息呢岂不美哉这样我就可以随意操作message对吧。。。不然wcf这个高层封装的叼毛对我来说就是一种束缚。。。因 为我已经知道了service公布的wsdl所以我可以轻松构造message。。。 三用message来调用Server端 废话不多说构造message你一定要知道下图中的三点input这个Action契约方式 和 服务地址。 好了下面我先来构造数据契约指定服务契约的命名空间 和 Action在Soap中的名称 1 [DataContract(Namespace http://tempuri.org/, Name Update)]
2 class Test
3 {
4 [DataMember]
5 public string str { get; set; }
6 } 然后我把这个数据契约塞到envelope中的body中如下 1 BasicHttpBinding bingding new BasicHttpBinding();2 3 BindingParameterCollection param new BindingParameterCollection();4 5 var u new Test() { str 王八蛋 };6 7 Message request Message.CreateMessage(MessageVersion.Soap11, http://tempuri.org/IHomeService/Update, u);8 9 IChannelFactoryIRequestChannel factory bingding.BuildChannelFactoryIRequestChannel(param);
10
11 factory.Open();
12
13 IRequestChannel channel factory.CreateChannel(new EndpointAddress(http://192.168.1.105:19200/HomeServie));
14
15 channel.Open();
16
17 var result channel.Request(request);
18
19 channel.Close();
20
21 factory.Close(); 接下来我们跑起来看一下效果咋样。。。 看没看到这个就是我手工构造的Message是不是太帅了。。。哈哈太帅的应该在后面刚才也说了既然大家玩的都是Message而你这个几把wcf却仅仅把 我的message.body拿出来了那干脆我直接在契约方法中加message岂不是更好么自由操作Message还有个什么好处呢当然啦我可以在Message的 Header中加一些参数tokenclient的ip地址client的身份client的时间等等这些统计信息对吧。。。这样才是最帅的好了说干就干我们修改下server端的 契约方法只用来接受Message。 server端 1 public class HomeService : IHomeService2 {3 public Message Update(Message message)4 {5 var header message.Headers;6 7 var ip header.GetHeaderstring(ip, string.Empty);8 9 var currentTime header.GetHeaderstring(currenttime, string.Empty);
10
11 //这个就是牛逼的 统计信息。。。
12 Console.WriteLine(客户端的IP ip 当前时间 currentTime);
13
14 return Message.CreateMessage(message.Version, message.Headers.Action Response, 等我吃完肯德基再打死你这个傻逼);
15 }
16 } client端 1 namespace ConsoleApplication12 {3 class Program4 {5 static void Main(string[] args)6 {7 BasicHttpBinding bingding new BasicHttpBinding();8 9 BindingParameterCollection param new BindingParameterCollection();
10
11 var u new Test() { str 王八蛋 };
12
13 Message request Message.CreateMessage(MessageVersion.Soap11, http://tempuri.org/IHomeService/Update, u);
14
15 //在header中追加ip信息
16 request.Headers.Add(MessageHeader.CreateHeader(ip, string.Empty, Dns.GetHostByName(Dns.GetHostName()).AddressList[0].ToString()));
17 request.Headers.Add(MessageHeader.CreateHeader(currenttime, string.Empty, DateTime.Now));
18
19 IChannelFactoryIRequestChannel factory bingding.BuildChannelFactoryIRequestChannel(param);
20
21 factory.Open();
22
23 IRequestChannel channel factory.CreateChannel(new EndpointAddress(http://192.168.1.105:19200/HomeServie));
24
25 channel.Open();
26
27 var result channel.Request(request);
28
29 channel.Close();
30
31 factory.Close();
32 }
33 }
34
35 [DataContract(Namespace http://tempuri.org/, Name Update)]
36 class Test
37 {
38 [DataMember]
39 public string str { get; set; }
40 }
41 } 然后我们用Fiddler监视一下结果 现在一切都如我所愿好了我想你也大概明白了这个神奇的message也不要忘了它就是wcf的基本通信单元我要去吃肯德基了。。。。。。 十五天精通WCF——第五天 你需要了解的三个小技巧 一: 服务是端点的集合 当你在开发wcf的时候你或许已经注意到了一个service可以公布多个endpoint确实是这样在wcf中有一句很经典的话叫做“服务是端点的集合就 比如说一个普普通通的服务它就公布了一个服务端点一个元数据端点对吧。。。 仔细一想这个问题就好玩了既然一个service可以公布多个endpoint而且我还知道wcf中有很多的binding这些binding对应着很多的传输方式那是不是 说我一个service可以用多种协议方法对外公布比如说同时以nettcpbasicmsmqbindingudp等方式公布对吧那这样的话是不是超级好玩如果对方 是非.net程序那就可以调用我的basic如果对方是.net程序那是不是可以调用我的nettcp对不对。。。当然啦wcf无所不能这是一个史上无比强大的牛 逼框架牛逼的要死已经逼得程序员只需随便改几个配置就能达到完全不一样的效果。。。下面我同时用nettcp和basic的方式来同时公布服务好了现在我 们就来见证奇迹吧。。。 Service 1 using System;2 using System.Runtime.Serialization;3 using System.ServiceModel;4 using System.ServiceModel.Channels;5 using System.Threading;6 7 namespace MyService8 {9 public class HomeService : IHomeService
10 {
11 public Student Update(Student message)
12 {
13 return new Student() { Name 一线码农 };
14 }
15 }
16
17 [DataContract]
18 public class Student
19 {
20 [DataMember]
21 public string Name { get; set; }
22
23 [DataMember]
24 public int Age { get; set; }
25 }
26 } Host 1 class Program12 {3 static void Main(string[] args)4 {5 ServiceHost host new ServiceHost(typeof(HomeService), new Uri(http://192.168.1.105:1920));6 7 host.AddServiceEndpoint(typeof(IHomeService), new BasicHttpBinding(), HomeServie);8 9 host.AddServiceEndpoint(typeof(IHomeService), new NetTcpBinding(), net.tcp://192.168.1.105:1921/HomeServieTcp);
10
11 host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled true });
12
13 host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), mex);
14
15 host.Open();
16
17 Console.Read();
18 }
19 } Client端 1 class Program2 {3 static void Main(string[] args)4 {5 //basic 方式6 ChannelFactoryIHomeService factory new ChannelFactoryIHomeService(new BasicHttpBinding(),7 new EndpointAddress(http://192.168.1.105:1920/HomeServie));8 9 var client factory.CreateChannel();
10
11 var result client.Update(new Student() { });
12
13
14 //nettcp方式
15 factory new ChannelFactoryIHomeService(new NetTcpBinding(),
16 new EndpointAddress(net.tcp://192.168.1.105:1921/HomeServieTcp));
17
18 client factory.CreateChannel();
19
20 result client.Update(new Student() { });
21 }
22 } 通过上面的代码是不是已经发现我在client端既可以用basic的方式调用又可以用nettcp的方式调用这个技巧是不是感觉wcf无比强大呢 二Host寄宿多个Service 我们知道wcf的寄宿方式有很多种有iis有windowservice还有简单方便的console方式而默认情况下我们最通常的方法都是一个service一个寄宿 而其实呢 其实一个寄宿host可以承载多个service看起来是不是很好玩如果说你有10个servcie现在你只需要用一个console host就能寄宿起来废 话不多说我演示一下给你看就好了。 Service 1 namespace MyService2 {3 [ServiceContract]4 public interface IHomeService5 {6 [OperationContract]7 Student Update(Student message);8 }9
10 [ServiceContract]
11 public interface IFlyService
12 {
13 [OperationContract]
14 Student Fly(Student stu);
15 }
16 } Host 1 class Program12 {3 static void Main(string[] args)4 {5 //第一个 这是Home服务6 ServiceHost host new ServiceHost(typeof(HomeService), new Uri(http://192.168.1.105:1920));7 host.AddServiceEndpoint(typeof(IHomeService), new BasicHttpBinding(), HomeServie);8 host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled true });9 host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), mex);
10 host.Open();
11
12 Console.WriteLine(Home服务开启。。。。);
13
14 //第一个 这是Fly服务
15 var host2 new ServiceHost(typeof(FlyService), new Uri(http://192.168.1.105:1930));
16 host2.AddServiceEndpoint(typeof(IFlyService), new BasicHttpBinding(), FlyServie);
17 host2.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled true });
18 host2.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), mex);
19 host2.Open();
20
21 Console.WriteLine(Fly服务开启。。。。);
22
23 Console.Read();
24 }
25 } 有没有看到现在两个服务都开启了这种方式看起来是不是很爽呀否则的话你需要开启两个Host这样的话我的手续就精简了。。。对吧。。 三 Tcp中的端口共享 这玩意听起来大家都懂端口共享嘛不就是两个程序共享一个端口对吧在通常情况下我们肯定会认为这无法做到其实呢在Wcf中我们还是可以玩 的也就是一个PortSharingEnabled的事如果说端口可以共享的话那我们的service是不是就可以少开辟几个端口呢同样这也方便我们进行service的管 理下面我给大家继续演示一下。。。很好玩的么么哒 可以看到我的两个host都是用1920的端口并且现在我真的开启起来啦。。。。好了三种技巧都说到了我想你在现实的wcf开发中或多或少的都能接 触的到希望对你有用~~~~ 随笔- 197 文章- 0 评论- 3407 十五天精通WCF——第六天 你必须要了解的3种通信模式 wcf已经说到第六天了居然还没有说到这玩意有几种通信模式惭愧惭愧不过很简单啦单向请求-响应双工模式其中的第二种“请求-响应“ 模式这个大家不用动脑子都清楚这一篇我大概来分析下。 一“请求-响应“模式 如果你看了我上一篇的博文你应该非常清楚这种类似“本地调用”的方式wcf同样也分为“同步”和“异步”两种不过不管是异步还是同步最终都逃 不过是“请求-响应”这个事实对吧。 1 同步方式 这种方式我想没什么好说的前面几篇我已经说的非常清楚了具体使用方法可以参考我的前面几篇文章。。。谢啦~~~~ 2 异步方式 通常我们都有这样的一个思维遇到耗时的东西第一反应就想到了多线程毕竟多线程也是一种负载均衡在wcf这种”请求-响应“模式同样也支持异 步很神奇吧而且神奇到可以在“服务引用“界面上做到一键生成什么你不信不信你看。。。 然后我非常好奇的看下XXXClient给我们生成的是个什么代码。。。 通过client端的proxy代码你可以清楚的看到这鸡巴WCF真的不容易给我们生成了两种“异步模式”第一种是最古老的beginXXXendXXX模式 还有一种是被Jeffrey Richter 严重鄙视的“事件异步模式”。。。没什么好说的截图一下给大家看看。 二“单向“模式 很多时候我们或许都有这样的需求比如说订单提交成功的时候我需要给客户发送邮件但是你想想我发送邮件这个任务只是我订单流程的 一个“额外任务“也就是说它的失败不应该会阻止我的订单流程并且它的逻辑时间不应该会阻碍我的下单总时间对吧。。。这样的话我的订单时 间才会最小化为了达到不影响下单总时间的效果我的想法就是client端直接把消息丢给信道就好了然后不管server端有没有真的接收到处理的 慢不慢过的好不好等等非常开心的是这些对wcf来说真的是小菜一碟只需要一个轻轻松松的”IsOneWaytrue“属性就可以了。。。牛逼的要 死。。。还有就是因为是单向的所以契约方法就没有存在返回值的必要了我说的对吧。。。嘿嘿~~~ 1 [ServiceContract]
2 public interface IHomeService
3 {
4 [OperationContract(IsOneWay true)]
5 void Update(Student message);
6 } 1 namespace MyService2 {3 public class HomeService : IHomeService4 {5 public void Update(Student message)6 {7 Console.WriteLine(message.Name);8 }9 }
10
11 [DataContract]
12 public class Student
13 {
14 [DataMember]
15 public string Name { get; set; }
16
17 [DataMember]
18 public int Age { get; set; }
19 }
20 } 为了验证是否真的是单向通讯我可以用二种方法验证下。 1. wsdl中是否有output这个message 通过下面的图我想你看的很清楚了你再也没有找到我们熟悉的“output”这个message这就说明貌似真的是单向的了因为wsdl就是web服务的清单。 2. 使用fillder监视一下请求消息 1 class Program
2 {
3 static void Main(string[] args)
4 {
5 HomeServiceClient client new HomeServiceClient();
6
7 client.Update(new Student() { Name hxc });
8 }
9 } 正如我在图中说的那样非常奇怪我的IsOneWay模式竟然在http模式下行不通但是你要记住http模式天生就是“请求-响应”模式它完全做不了 单向模式说明白一点就是“wcf发现你的bingding不支持单向“的时候它并不会报错还是用自己天生的”请求-相应“模式来模拟”单向通信“这就是你 看到的非常奇怪的Http 202这个http状态码很多人包括我都不知道http202 是几个意思没关系我们百科一下就好了。。。下面框框的里面的字 已经说的非常清楚了感谢感谢。。。 三“双向“ 模式 这个通讯其实没什么好讲的也只有tcp模式才会天生支持而http模式天生就不支持就像上面一样如果非要用http来支持“双向通讯“那又是在 坑wcf他爹这样就会逼着他爹在底层再建立一个“请求-响应“模式来支持所谓的”双向通讯“而且”双向通讯“这个玩意还不如用两个单向的”请求-响应”模 式或者两个“单向模式”来支持而且两个”请求-响应“模式比”双向通讯“有更大的灵活性反正我是对它不感冒了解一下即可如果大家比较感兴趣可以 在wcf官网上看一下https://msdn.microsoft.com/zh-cn/library/ms735119.aspx。 好了就说到这里洗洗睡了晚安~~~~ 随笔- 197 文章- 0 评论- 3407 十五天精通WCF——第七天 Close和Abort到底该怎么用才对得起观众 一文起缘由 写这一篇的目的源自于最近看同事在写wcf的时候用特别感觉繁琐而且云里雾里的嵌套try catch来防止client抛出异常特别感觉奇怪就比如下面的代码。 1 public void StartNormalMarketing(int shopId, Listint marketingIdList)2 {3 4 using (SendEventMarketingService.DistributeServiceClient client new SendEventMarketingService.DistributeServiceClient())5 {6 try7 {8 9 client.StartByMarketingIDList(shopId, marketingIdList, SendEventMarketingService.MarketingType.NormalMarketing);
10
11 }
12 catch (Exception ex)
13 {
14 LogHelper.WriteLog(常规营销活动开启服务, ex);
15 }
16 finally
17 {
18 try
19 {
20 client.Close();
21 }
22 catch (Exception)
23 {
24 client.Abort();
25 }
26 }
27 }
28 } 看完上面的代码不知道你是否有什么感想而且我还问了同事为什么try catch要写成这样同事说是根据什么书上来的什么最佳实践这话一说我也不敢轻易 怀疑了只能翻翻源代码看看这话是否有道理首先我来说说对这段代码的第一感觉。。。 1. 代码特别繁琐 我们写代码特别不喜欢繁琐上面的代码就是一例你try catch就try catch还在finally中嵌套一个try catch真的有点感觉像吃了两只癞蛤蟆一样。。。 2. 混淆close和abort的用法 这种代码给人的感觉就是为什么不精简一下呢比如下面这样起码还可以少写一对try catch对吧。 1 public void StartNormalMarketing(int shopId, Listint marketingIdList)2 {3 4 using (SendEventMarketingService.DistributeServiceClient client new SendEventMarketingService.DistributeServiceClient())5 {6 try7 {8 9 client.StartByMarketingIDList(shopId, marketingIdList, SendEventMarketingService.MarketingType.NormalMarketing);
10
11 client.Close();
12 }
13 catch (Exception ex)
14 {
15 LogHelper.WriteLog(常规营销活动开启服务, ex);
16
17 client.Abort();
18 }
19 }
20 } 而且乍一看这段代码和文中开头那一段代码貌似实现一样但是某些人的“最佳实践”却不是这样所以确实会导致我这样的后来人犯迷糊对吧。。。反正我就是头晕 简直就是弄糊涂到什么时候该用close什么时候该用abort。。。 二探索原理 为了弄明白到底可不可以用一个try catch来替代之下面我们一起研究一下。 1. 从代码注释角度甄别 从类库的注释中可以比较有意思的看出abort方法仅仅比close多一个“立即”再无其他有意思不过这对我来说并没有什么卵用因为这个注释太 笼统了为了让自己更加彻底的明白只能来翻看下close和abort的源代码。 2. 从源码角度甄别 为了方便让ILSpy调试Client代码现在我决定用ChannelFactory来代替如下图 1 namespace ConsoleApplication12 {3 class Program4 {5 static void Main(string[] args)6 {7 ChannelFactoryIHomeService factory new ChannelFactoryIHomeService();8 9 try
10 {
11 var channel factory.CreateChannel();
12
13 factory.Close();
14 }
15 catch (Exception ex)
16 {
17 factory.Abort();
18 }
19 }
20 }
21 } 为了让大家更好的理解我把close方法的源码提供如下 1 // System.ServiceModel.Channels.CommunicationObject2 [__DynamicallyInvokable]3 public void Close(TimeSpan timeout)4 {5 if (timeout TimeSpan.Zero)6 {7 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(timeout, SR.GetString(SFxTimeoutOutOfRange0)));8 }9 using ((DiagnosticUtility.ShouldUseActivity this.TraceOpenAndClose) ? this.CreateCloseActivity() : null)
10 {
11 CommunicationState communicationState;
12 lock (this.ThisLock)
13 {
14 communicationState this.state;
15 if (communicationState ! CommunicationState.Closed)
16 {
17 this.state CommunicationState.Closing;
18 }
19 this.closeCalled true;
20 }
21 switch (communicationState)
22 {
23 case CommunicationState.Created:
24 case CommunicationState.Opening:
25 case CommunicationState.Faulted:
26 this.Abort();
27 if (communicationState CommunicationState.Faulted)
28 {
29 throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this);
30 }
31 goto IL_174;
32 case CommunicationState.Opened:
33 {
34 bool flag2 true;
35 try
36 {
37 TimeoutHelper timeoutHelper new TimeoutHelper(timeout);
38 this.OnClosing();
39 if (!this.onClosingCalled)
40 {
41 throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException(OnClosing), Guid.Empty, this);
42 }
43 this.OnClose(timeoutHelper.RemainingTime());
44 this.OnClosed();
45 if (!this.onClosedCalled)
46 {
47 throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException(OnClosed), Guid.Empty, this);
48 }
49 flag2 false;
50 goto IL_174;
51 }
52 finally
53 {
54 if (flag2)
55 {
56 if (DiagnosticUtility.ShouldTraceWarning)
57 {
58 TraceUtility.TraceEvent(TraceEventType.Warning, 524292, SR.GetString(TraceCodeCommunicationObjectCloseFailed, new object[]
59 {
60 this.GetCommunicationObjectType().ToString()
61 }), this);
62 }
63 this.Abort();
64 }
65 }
66 break;
67 }
68 case CommunicationState.Closing:
69 case CommunicationState.Closed:
70 goto IL_174;
71 }
72 throw Fx.AssertAndThrow(CommunicationObject.BeginClose: Unknown CommunicationState);
73 IL_174:;
74 }
75 } 然后我提供一下Abort代码 1 // System.ServiceModel.Channels.CommunicationObject2 [__DynamicallyInvokable]3 public void Abort()4 {5 lock (this.ThisLock)6 {7 if (this.aborted || this.state CommunicationState.Closed)8 {9 return;
10 }
11 this.aborted true;
12 this.state CommunicationState.Closing;
13 }
14 if (DiagnosticUtility.ShouldTraceInformation)
15 {
16 TraceUtility.TraceEvent(TraceEventType.Information, 524290, SR.GetString(TraceCodeCommunicationObjectAborted, new object[]
17 {
18 TraceUtility.CreateSourceString(this)
19 }), this);
20 }
21 bool flag2 true;
22 try
23 {
24 this.OnClosing();
25 if (!this.onClosingCalled)
26 {
27 throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException(OnClosing), Guid.Empty, this);
28 }
29 this.OnAbort();
30 this.OnClosed();
31 if (!this.onClosedCalled)
32 {
33 throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException(OnClosed), Guid.Empty, this);
34 }
35 flag2 false;
36 }
37 finally
38 {
39 if (flag2 DiagnosticUtility.ShouldTraceWarning)
40 {
41 TraceUtility.TraceEvent(TraceEventType.Warning, 524291, SR.GetString(TraceCodeCommunicationObjectAbortFailed, new object[]
42 {
43 this.GetCommunicationObjectType().ToString()
44 }), this);
45 }
46 }
47 } 仔细观察完这两个方法你会发现什么呢至少我可以提出下面四个问题 1Abort是Close的子集吗 是的因为如果你看懂了Close你会发现Close只针对Faulted 和Opened做了判断而其中在Faulted的枚举下会调用原生的Abort方法。。。如下图 2我能监视Client的各种状态吗比如CreatedOpeningFaultClosed等等。。。 当然可以了wcf的信道老祖宗就是ICommunicationObject而它就有5种监听事件这些就可以随时监听懂伐 1 static void Main(string[] args)2 {3 ChannelFactoryIHomeService factory new ChannelFactoryIHomeService(new BasicHttpBinding(), new EndpointAddress(http://localhost:1920/HomeServie));4 5 try6 {7 factory.Opened (o, e) 8 {9 Console.WriteLine(Opened);
10 };
11
12 factory.Closing (o, e)
13 {
14 Console.WriteLine(Closing);
15 };
16
17 factory.Closed (o, e)
18 {
19 Console.WriteLine(Closed);
20 };
21
22 var channel factory.CreateChannel();
23
24 var result channel.Update(new Student() { });
25
26 factory.Close();
27 }
28 catch (Exception ex)
29 {
30 factory.Abort();
31 }
32 } 3Abort会抛出异常吗 从这个截图中可以看到非常有意思的一段那就是居然abort活生生的把异常给吞了。。。骨头都不给吐出来。。。真tmd的神奇到家了想想也有道理因为只有 这样我们上层的代码在catch中才不会二次抛出“未处理异常”了对吧再转念看一下Close方法。 从上面图中可以看到Close在遇到Faulted之后调用Abort方法如果说Abort方法调用失败Close方法会再次判断状态如果还是Faulted的话就会向上抛出 异常。。。这就是为什么Abort不会抛异常Close会的原因所以Close千万不要放在Catch块中。 4. Abort代码大概都干了些什么 这个问题问的好要能完美解决的话我们看下代码如下图从图中可以看到Abort的大目的就是用来关闭信道具体会经过closeingabort和closed这 三个方法同时这三个事件也会被老祖宗ICommunicationObject监听的到。 好了最后我们关注的一个问题在于下面这条语句是否应该放在Try块中 1 ChannelFactoryIHomeService factory new ChannelFactoryIHomeService(new BasicHttpBinding(), new EndpointAddress(http://localhost:1920/HomeServie)); 很简单我们简要的看一下代码看里面是否会有“异常”抛出即可。。。。 可以看到在new的过程中可能或许会有异常的产生所以最好把try catch改成下面这样。。。 1 class Program2 {3 static void Main(string[] args)4 {5 ChannelFactoryIHomeService factory null;6 try7 {8 factory new ChannelFactoryIHomeService(new BasicHttpBinding(), new EndpointAddress(http://localhost:1920/HomeServie));9
10 var channel factory.CreateChannel();
11
12 var result channel.Update(new Student() { });
13
14 factory.Close();
15
16 throw new Exception();
17 }
18 catch (Exception ex)
19 {
20 if (factory ! null)
21 factory.Abort();
22 }
23 }
24 } 好了综合我上面所说的一切我个人觉得最好的方式应该是上面这样夜深了睡觉了晚安。 随笔- 197 文章- 0 评论- 3407 十五天精通WCF——第八天 对“绑定”的最后一点理解 转眼已经中断10几天没有写博客了也不是工作太忙正好碰到了端午节然后最近看天津台的爱情保卫战入迷了。。。太好看了一直都是回味无穷。。。而且 涂磊老师话说的真是tmd的经典然后就这样耽搁了好了话不多说这篇我们看看binding中最后一点需要知道的东西。 一信道栈 我在之前的文章中多次提到信道栈不知道大家对它的概念是否有了解其实想想也还是蛮简单的既然是栈那么这个栈肯定就不止一个元素了对吧第二个 的话既然是栈那么肯定就遵循FILO的原则可能你会说这个还是蛮抽象的能给个具体的例子么恭喜你wcf中还真有一个方法CreateBindingElements 下面我们具体看看。。。 1. 简单看看各种binding的栈中都有些什么 看到上面的监控窗口是不是有点意思在BasicHttpBinding的信道栈中有两个元素分别是HttpTransportBindingElement和TextMessageEncodingBindingEl ement通过名字也能很容易的判断出来一个是“http传输协议”一个是“文本消息编码协议”然后再看看复杂一点的WSHttpBinding你会发现他不光有Basic 的所有东西还包括SymmetricSecurityBindingElement(安全协议 和 TransactionFlowBindingElement事务流现在你心中是不是有底了起码我知道各 种Binding里面都有些啥为了更好的理解我来画一张简图。 上面这个图大概也就表达了我的意思当我们Client在走WSHttpBinding这个协议的时候Client端的InputMessage会先走 TransactionFlowSymmetricSec urityTextMessageEncoding最后走HttpTransport然后Service端就按照客户端进行“反向处理”通过一阵禁脔之后我们就拿到了安全的OutputMessage。 二BindingElement的跨绑定性 你要是很仔细的话你肯定会发现其实Binding就是一个预先默认配置好的信道栈对不对你也看到了每一种Binding都有属于自己的BindingElements 恰恰这些Elements是可以跨Binding的也就是说我可以自由组合Elements这样是不是可以给我们这些寒酸的码农最大的灵活性对吧举个简单的例子 BasicHttpBinding有两个绑定元素其中对soap消息进行的是TextMessageEncoding编码对吧而netTcpBinding对soap进行的BinaryMessageEncoding 然后你也应该知道了我想做一个自定义的Binding其中消息编码是BinaryMessage传输协议是HttpTransport那怎么做呢 Host文件 1 class Program12 {3 static void Main(string[] args)4 {5 ServiceHost host new ServiceHost(typeof(HomeService), new Uri(http://192.168.1.105:1920));6 7 var customBinding new CustomBinding();8 9 customBinding.Elements.Add(new BinaryMessageEncodingBindingElement());
10 customBinding.Elements.Add(new HttpTransportBindingElement());
11
12 host.AddServiceEndpoint(typeof(IHomeService), customBinding, HomeServie);
13
14 host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled true });
15
16 host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), mex);
17
18 host.Open();
19
20 Console.WriteLine(服务已经开启);
21
22 Console.Read();
23 }
24 } Client调用 1 static void Main(string[] args)2 {3 ServiceReference1.HomeServiceClient client new ServiceReference1.HomeServiceClient();4 5 var result client.Update(你好);6 7 Console.WriteLine(server value: result);8 9 Console.Read();
10 } 最后我们用Fiddler监视一下最后我们看看都是些乱码。 这篇就说到这里了希望对你有帮助下一篇我们看看WCF中的Behavior很好玩的哦~~~ 随笔- 197 文章- 0 评论- 3407 十五天精通WCF——第九天 高级玩法之自定义Behavior 终于我又看完了二期爱情保卫战太酸爽了推荐链接http://www.iqiyi.com/a_19rrgublqh.html?vfm2008_aldbd不多说谁看谁入迷下面言归正传 看看这个很有意思的Behavior。 一 Behavior这个泼妇的厉害 在前面的文章中我也清楚的说明了整个wcf通信流而Behavior这个泼妇可以在wcf通信流中的任何地方插上一脚蛮狠无比利用的好让你上天堂利用的不 好让你下地狱。。。下面让你看看behavior到底有哪些可以注入的点先画个简图 上面的图大概就是wcf的通信简图所有蓝色字体都是Behavior注入的点其中Client和Service端都可以注入如果按照功能分的话又可以分为“操作级别”和 ”端点级别“下面我来简要的分解下。 二端点级别Behavior 从图中你也可以看到消息检查器是放在Channel这个级别的也就是说它可以监视Client和Server的入站请求也就是说所有的请求都需要通过它转发如果 这样的话那我是不是可以在这个注入点上自由的修改变更拦截入站和出站请求而且利用这个特性我还可以做很多的事情比如日志记录记录统计等等下 面我们来看看这个怎么使用 只需要extends IEndpointBehavior 和 IDispatchMessageInspector然后加入EndpointBehaviors即可。。。 1. IDispatchMessageInspector 1 public class MyDispatchMessageInspector : IDispatchMessageInspector2 {3 public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)4 {5 Console.WriteLine(request.ToString());6 return request;7 }8 9 public void BeforeSendReply(ref Message reply, object correlationState)
10 {
11 Console.WriteLine(reply.ToString());
12 }
13 } 2. IEndpointBehavior 1 public class MyEndpointBehavior : IEndpointBehavior2 {3 public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)4 {5 }6 7 public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)8 {9 }
10
11 public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
12 {
13 endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MyDispatchMessageInspector());
14 }
15
16 public void Validate(ServiceEndpoint endpoint)
17 {
18 }
19 } 3. 将MyEndpointBehavior加入到Host中 1 static void Main(string[] args)2 {3 ServiceHost host new ServiceHost(typeof(HomeService), new Uri(http://127.0.0.1:1920));4 5 host.AddServiceEndpoint(typeof(IHomeService), new BasicHttpBinding(), HomeServie);6 7 host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled true });8 9 host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), mex);
10
11 host.Description.Endpoints[0].EndpointBehaviors.Add(new MyEndpointBehavior());
12
13 host.Open();
14
15 Console.WriteLine(服务已经开启);
16
17 Console.Read();
18 } 4. 最后我们看一下服务方法 1 public class HomeService : IHomeService
2 {
3 public string Update(string message)
4 {
5 Console.WriteLine(我在Action方法 message);
6
7 return my reply!!!;
8 }
9 } 下面看看效果。。。在效果图中你应该看到了。在我的Action中的方法前后各有一段“入站消息”和“出站消息”是不是很爽 三操作级别Behavior 从文章开头的简图中你应该看到了Operation级别的Behavior比较多有“操作启动器IOperationInvoker),参数检查(IParameterInspector)“ “消息格式化器IDispatchMessageFormatter”等等。。。 为什么说等等这个词很简单啊其实还有很多系统内置的既然是Operation那就必 然是针对方法的还记得OperationContract是怎么套在方法上的吗 是特性对吧同样的道理OperationBehavior也是一样那怎么用呢 同样也是很简单的继承几个接口即可。。。 1 IParameterInspector 的玩法 其实没什么好说的既然是属于Operation下面的Behavior那都是通过特性注入的而这个IParameterInspector可以做到类似Mvc的Model验证下面 我做个简单的Action参数长度验证长度不超过8个字符。 1. IParameterInspector 1 public class MyIParameterInspector : IParameterInspector2 {3 public int MaxLength { get; set; }4 5 public MyIParameterInspector(int MaxLength)6 {7 this.MaxLength MaxLength;8 }9
10 /// summary
11 /// 出站的操作
12 /// /summary
13 /// param nameoperationName/param
14 /// param nameoutputs/param
15 /// param namereturnValue/param
16 /// param namecorrelationState/param
17 public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
18 {
19
20 }
21
22 /// summary
23 /// 入站的参数
24 /// /summary
25 /// param nameoperationName/param
26 /// param nameinputs/param
27 /// returns/returns
28 public object BeforeCall(string operationName, object[] inputs)
29 {
30 foreach (var item in inputs)
31 {
32 if (Convert.ToString(item).Length MaxLength)
33 {
34 throw new Exception(码单长度不能超过 MaxLength 个长度);
35 }
36 }
37
38 return null;
39 }
40 } 2. IOperationBehavior 1 public class MyOperationBehavior : Attribute, IOperationBehavior2 {3 public int MaxLength { get; set; }4 5 public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)6 {7 8 }9
10 public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
11 {
12
13 }
14
15 public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
16 {
17 dispatchOperation.ParameterInspectors.Add(new MyIParameterInspector(MaxLength));
18 }
19
20 public void Validate(OperationDescription operationDescription)
21 {
22
23 }
24 } 3. 在Action在加上MyOperationBehavior 这个 Attribute 1 public class HomeService : IHomeService2 {3 [MyOperationBehavior(MaxLength 5)]4 public string Update(string message)5 {6 Console.WriteLine(我在Action方法 message);7 8 return my reply!!!;9 }
10 } 4. 然后我在客户端故意输入大于5的字符看看效果怎么样 1 public class Program12 {3 static void Main(string[] args)4 {5 HomeServiceClient client new HomeServiceClient();6 7 client.Update(我故意输入了很多的字符哈哈。。。。。);8 9 Console.Read();
10 }
11 } 5. 最后看看效果图可以看到最终的入站消息会抛出一个异常。。。 2 MessageFormatter,IOperationInvoker 的玩法 剩下的这两个玩法都差不多你只需要extends一下然后加入到OperationBehavior即可有了上面的思想我想下面这些使用起来都不是问题吧。。。 随笔- 197 文章- 0 评论- 3407 十五天精通WCF——第十天 学会用SvcConfigEditor来简化配置 我们在玩wcf项目的时候都是自己手工编写system.serviceModel下面的配置虽然在webconfig中做wcf的服务配置的时候vs提供大多 数的代码提示但对于不太熟悉服务配置的小鸟们来说有些困难而且一些服务配置也容易遗漏大多情况下我们都是copy一份服务配置然 后在服务配置上面修修改改对吧。。。其实呢.net给我们提供了一个强大的scvconfigeditor这个工具化的软件来帮助我们生成wcf的配置是 不是很神奇 一工具在何处 当然在无比牛逼的Microsoft SDK下面啦在C:\Program Files (x86)\Microsoft SDKs\Windows下面你会找到很多的版本如下图 对吧你已经看到了很多的版本当然啦我肯定要找最新的啦一禁脔我进去了v8.0A如下图 C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools 你应该也看到了各种牛逼的工具很眼馋吧不过这一篇我们还是看重SvcConfigEditor。 二 如何使用SvcConfigEditor 1. 双击打开选择“文件” “新建配置”。 2. 然后我们选择 “新建服务” “填写服务名” 3. 然后我们给service定义一个host 点击 主机 新建“ 填写基址。 4. 到这一步你是不是特别想看一看生成的config配置是咋样的好啊满足你的虚荣心我们只需要点 击保存“选择一个路径即可。。。 5. 好了你的虚荣心得到满足了下面我们来定义endpoint了其实也是非常非常简单的 点击”终结点 新建服务终结点然后我们就象征性的填写一些AddressContractBinding即可如下图 6. 上面我们就已经定义了一个basichttpbinding了下一步的话我们还记得要公布一个mexhttpbinding 这样我的svcutil才能服务引用对吧所以方法也是很简单继续“新建终结点”如下图 7. 最后我还记得mex需要有一个behavior让http的get可以访问有了这个神器同样简单我们可以 点击“高级” 服务行为 新建。 8. 最后我们保存来看一下生成的appconfig是啥样的 则么样我不需要写一个字的config配置就完成了基本的服务配置如果你还想玩高级的可以自己试着琢磨琢磨SvcConfigEditor。 好了差不多可以睡了下一篇我们来研究研究 SvcConfigEditor中的诊断工具很好玩的啦~~~~~ 随笔- 197 文章- 0 评论- 3407 十五天精通WCF——第十一天 如何对wcf进行全程监控 说点题外话我们在玩asp.net的时候都知道有一个叼毛玩意叫做“生命周期”我们可以用httpmodule在先于页面的page_load中 做一些拦截这样做的好处有很多比如记录日志参数过滤全局登录验证等等。。。在wcf里面的话也是有类似的功能第一种就是在 endpoint中加上runtime的behavior这样的话就可以先于“服务方法”做拦截第二种方法呢也就是我们这一篇所说的全程监控俗称 ”诊断功能”。 一诊断 我也说了“诊断”这是wcf的一个专业术语意思也就是监控wcf的所有动向如果往下说的话可以分为监控 wcf的message 和 wcf 本身的服务状态信息和端对端的流转消息。 1. 端对端的流转消息 在玩wcf之前不知道有多少人熟悉Diagnostics对的它就是.net自带的日志类当然在这个年代记录日志的组件有很多比如 log4netNlog等等。。。不过话说回来Diagnostics这个叼毛用起来还比较另类它由“跟踪源” 和 “监听器”组成。分别就是TraceSource 来指定跟踪源用TraceListener来指定跟踪源的监听器所以理所当然TraceSource的所有踪迹都会被TraceListener监听到下面我们 看看怎么玩。 ?xml version1.0 encodingutf-8?
configurationsystem.diagnosticssourcessource nameSystem.ServiceModel switchValueActivityTracinglistenersadd namemylisteners typeSystem.Diagnostics.XmlWriterTraceListener initializeDataE:\1.txt //listeners/source/sourcestrace autoflushtrue//system.diagnosticssystem.serviceModelbehaviorsserviceBehaviorsbehaviorserviceMetadata httpGetEnabledtrue /serviceDebug includeExceptionDetailInFaultsfalse //behavior/serviceBehaviors/behaviorsservicesservice nameMyService.HomeServiceendpoint addressHomeService bindingwsHttpBindingcontractMyService.IHomeServiceidentitydns valuelocalhost //identity/endpointendpoint addressmex bindingmexHttpBinding contractIMetadataExchange /hostbaseAddressesadd baseAddresshttp://192.168.1.107:1920 //baseAddresses/host/service/services/system.serviceModel/configuration 从上面的配置中可以看到你有没有发现我在配置system.diagnostics的时候和wcf一点关系都没有我并没有在system.ServiceModel 下对diagnostics有一丁点的配置对吧这说明什么说明“踪迹跟踪”功能和wcf一点关系都没有但却可以完整的记录wcf的踪迹信息然 后我稍微解释下listeners节点在这里我配置了一个XmlWriterTraceListener的监听器然后把输出文件的路径配置在initializeData属性下 其实都是diagnostics本身的知识范畴和wcf一点关系都没有好了下面我开启下程序看看到底都追踪到什么 有没有看到当我的服务启动之后追踪信息就全部来了。。。但是接下来有一个问题来了这个很杂乱的xml该怎么看才能最舒舒服服的 呢不用着急啦wcf同样给我们提供了一个叫做SvcTraceView的工具专门就是用来查找这个“踪迹信息”的工具的路径在 C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools 下面的事情就是打开它附加一下1.txt文件就好了如下图 从左边的“活动图”中大概可以看到HomeService这个服务启动到运行经历了一些什么样的悲惨故事。。。有兴趣的话大家可以自己动 手试试啦。 2. 监控input和ouput的message 如果要监控message的话我们需要再定义一个TraceSource 和 TraceListener即可不过这次监听的是System.ServiceModel. MessageLogging跟踪源然后在System.ServiceModel下面配置一下message的参数如下 1 ?xml version1.0 encodingutf-8?2 configuration3 4 system.diagnostics5 sources6 source nameSystem.ServiceModel switchValueActivityTracing7 listeners8 add namemylisteners typeSystem.Diagnostics.XmlWriterTraceListener initializeDataE:\1.txt /9 /listeners
10 /source
11 source nameSystem.ServiceModel.MessageLogging switchValueActivityTracing
12 listeners
13 add namemessagelogging typeSystem.Diagnostics.XmlWriterTraceListener initializeDataE:\2.txt/
14 /listeners
15 /source
16 /sources
17 trace autoflushtrue/
18 /system.diagnostics
19
20 system.serviceModel
21
22 diagnostics
23 messageLogging logEntireMessagetrue logMalformedMessagestrue logMessagesAtTransportLeveltrue /
24 /diagnostics
25
26 behaviors
27 serviceBehaviors
28 behavior
29 serviceMetadata httpGetEnabledtrue /
30 serviceDebug includeExceptionDetailInFaultsfalse /
31 /behavior
32 /serviceBehaviors
33 /behaviors
34
35 services
36 service nameMyService.HomeService
37 endpoint addressHomeService bindingbasicHttpBinding
38 contractMyService.IHomeService
39 identity
40 dns valuelocalhost /
41 /identity
42 /endpoint
43 endpoint addressmex bindingmexHttpBinding contractIMetadataExchange /
44 host
45 baseAddresses
46 add baseAddresshttp://192.168.1.107:1920 /
47 /baseAddresses
48 /host
49 /service
50 /services
51
52 /system.serviceModel
53
54 /configuration 这次我准备来跑一下客户端调用Server端的Update方法看看能抓到啥样的Messsage。 现在我迫不及待的想用SvcTraceView打开下2.txt看看都拿到了什么追踪信息。。。 好了这篇我也只是引路式的介绍下SvcTraceView具体更深入的玩法大家可以琢磨琢磨对了如果大家想对Source和Listener的 一些参数需要进一步了解可以参考下SvcConfigEditor比如下面这样一目了然你懂的。。。 十五天精通WCF——第十二天 说说wcf中的那几种序列化 我们都知道wcf是由信道栈组成的在我们传输的参数走到传输信道层之前先需要经过序列化的过程也就是将参数序列化为message这篇 我们就来说说这里的序列化蛮有意思的可能初学者也明白在wcf中默认的序列化是DataContractSerializer确实是这样不过wcf在信道中 其实不仅仅支持DataContractSerializer它还支持其他类型的序列化比如XmlSerializerNetDataContractSerializer以及DataContractJson Serializer下面我们一起来见证下。 1. XmlSerializer 要了解XmlSerializer我们先来简单看看NetDataContractSerializer在前面的文章中我也说过DataContract就是将我们的model序列化为 XSD第二点就是使用DataContract的原则就是你必须在Model上加DataContract而且在你要序列化的字段上加DataMember。这样才能够正确的序列 化为了演示我们先看看默认的序列化Model会变成啥样 1 [DataContract]2 public class Student3 {4 [DataMember]5 public int ID { get; set; }6 7 [DataMember]8 public string Name { get; set; }9
10 [DataMember]
11 public string SNS { get; set; }
12 } 但是在有些情况下你可能并不适合用DataContract比如Model是第三方提供的那么这个时候你的Model可能就不会有DataContract标记那这样的 话wcf就无法进行序列化那我如果非要保证wcf能正常跑起来的话还有其他好的办法吗当然了肯定有办法这就好比谈恋爱一样总不能 在一棵树上吊死吧没人谁离不开谁也不会谁离开了谁会死天涯何处无芳草男儿何患无妻对吧。Wcf中也一样既然DataContract用不了自 然会有替代它的人那这个人就是XmlSerializer使用起来也很简单就是在契约方法上面加上XmlSerializerFormat即可然后我们把Model的 DataContract全部去掉。 是不是很简单下面我们就要验证一下看看这个Format是否进入到了这个Operation的Behavior中 从上面的图中你也看到了 XmlSerializerFormat 已经被注入到Behavior中并且是由类XmlSerializerOperationBehavior代为处理。 接下来我们用fiddler监视一下看看Message中的Body是否真的按照XmlSerializer 序列化了。 有没有看到这次Message的Body已经和文章开头处的Message不一样了。 2. NetDataContract 这个玩意也没什么好说的光从表面上看它和DataContract唯一不同的地方就是多了一个Net所以你大概也能猜到这个功能大概和DataCont ract一样只不过比DataContract多了一个程序集保存那这句话是什么意思呢就是NetDataContract会把程序集的命名空间和类名都保存到XSD中 在反序列化的过程中必须要用同样的程序集才能解开其实不管我们是做SOA或者面向对象编程都讲究接口编程而NetDataContract给你的印象就是面 向对象编程当然这也有好处比如说如果把程序集带进去就好像秘钥一样必须有它才能解开对吧所以导致wcf项目组并不对NetDataContract感冒 所以在实际应用上也不建议使用。 3. DataContractJsonSerializer 看到上面这个带有Json的字样我想大家都知道这玩意是干什么的没错他就是将我们的Model序列化成Json这在wcf的rest编码使用的很广 如果大家有兴趣的话我在下一篇会详细描述这里我们先简单看一看。 好了这一篇就说这些了洗洗睡了。。。 随笔- 197 文章- 0 评论- 3407 十五天精通WCF——第十三天 用WCF来玩Rest 在我们玩wcf的时候都会潜意识的觉得wcf就是通过soap协议交换消息的并且可以在basictcpmsmq等等绑定中任意切换 牛逼的一塌糊涂但是呢如果说哪一天wcf不再使用soap协议而是采用json格式的字符串是不是有一点颠覆你对wcf的认识的 从传统意义上说wcf是非常重量级的很明白的一个例子就是太多太多的配置尤其是Behavior的配置而且behavior对wcf来说又是重 中之重它对wcf的扩展和性能又是最重要的可恨的是wcf在bindingbehaviorcontract之中的配置又是非常非常的保守可以说用 wcf来玩分布式这些默认配置是完全做不到的就比如说basicbinding的基类HttpBindingBase。 抱怨的话我也不说了可能微软也觉得这个问题是个不小的问题然后就有了轻量级的 asp.net web api你可以看到它和wcf比起来精 简多了也许让我们这些码农更加的专注于业务吧既然wcf带了这玩意我也得必须约谈一下。 一UriTemplate 要说rest还得先说UriTemplate因为wcf用UriTemplate来做rest中的uri模板匹配然后用WebInvoke这个OperationBehavior 插入到wcf的心脏中说的玄乎一点这个就有点像mvc中的路由匹配机制下面我举个例子 1. 用UriTemplate来告知可以监视的完整Url 从下面的图中可以看到三个元素服务地址模板入参这里面的”1“这三个元素组合在一起就构成了完整的remote url 然后这个完整的url就是我模板/User/{id}监视的对象。 2. 通过UriTemplate来解析url中的参数。 既然可以构建url那当然可以解析url啦对吧下面这张图可以很清晰的告知你当外来的urlhttp://127.0.1:1920/HomeService /User/1过来的时候应该被哪个uriTemplate所接收。 正是因为UriTemplate具有这样的url构建和解析能力所以wcf就把UriTemplate作为WebInvoke和WebGet这两个属性的参数来动态 解析外来的url然后根据这个url分配到具体的服务方法上下面我们具体看一看。 二WebGetWebInvoke的使用 刚才也说了WebGet和WebInvoke正是用了UriTemplate才具有了路由转向的功能还有就是默认返回的是xml这里就用json 值作为服务返回的格式 1 [ServiceContract]2 public interface IHomeService3 {4 [OperationContract]5 [WebGet(UriTemplate Get/{id}, RequestFormat WebMessageFormat.Json, ResponseFormat WebMessageFormat.Json)]6 Student Get(string id);7 8 [OperationContract]9 [WebInvoke(Method POST, UriTemplate Add, RequestFormat WebMessageFormat.Json,
10 ResponseFormat WebMessageFormat.Json)]
11 string Add(Student stu);
12 } 对了Rest推荐使用Http协议中的GetPostDeletePut来作为CURD的状态机制然后就是你如果看懂了UriTemplate那你现在应 该知道这个Template在监视什么类型的url。做完了上面的coding下面我们需要在webconfig中通过behavior来指定启动“web编程模型” 就比如下面这样。 1 ?xml version1.0 encodingutf-8?2 configuration3 4 system.diagnostics5 sources6 source nameSystem.ServiceModel switchValueActivityTracing7 listeners8 add namemylisteners typeSystem.Diagnostics.XmlWriterTraceListener initializeDataE:\1.txt /9 /listeners
10 /source
11 source nameSystem.ServiceModel.MessageLogging switchValueActivityTracing
12 listeners
13 add namemessagelogging typeSystem.Diagnostics.XmlWriterTraceListener initializeDataE:\2.txt/
14 /listeners
15 /source
16 /sources
17 trace autoflushtrue/
18 /system.diagnostics
19
20 system.serviceModel
21
22 diagnostics
23 messageLogging logEntireMessagetrue logMalformedMessagestrue logMessagesAtTransportLeveltrue /
24 /diagnostics
25
26 behaviors
27 serviceBehaviors
28 behavior
29 serviceMetadata httpGetEnabledtrue /
30 serviceDebug includeExceptionDetailInFaultstrue /
31 /behavior
32 /serviceBehaviors
33 endpointBehaviors
34 behavior namewebbehavior
35 webHttp /
36 /behavior
37 /endpointBehaviors
38 /behaviors
39
40 services
41 service nameMyService.HomeService
42 endpoint addressHomeService bindingwebHttpBinding behaviorConfigurationwebbehavior
43 contractMyService.IHomeService
44 identity
45 dns valuelocalhost /
46 /identity
47 /endpoint
48 endpoint addressmex bindingmexHttpBinding contractIMetadataExchange /
49 host
50 baseAddresses
51 add baseAddresshttp://127.0.0.1:1920 /
52 /baseAddresses
53 /host
54 /service
55 /services
56
57 /system.serviceModel
58
59 /configuration 其实呢也就是代码中的WebHttpBehavior类 好了我现在服务地址也出来了http://127.0.0.1:1920 然后服务方法的template也指定了。只要http.sys监控到了template 匹配的url服务方法就会被执行比如我现在在浏览器里面输入http://127.0.0.1:1920/HomeService/Get/1 来测试下Get操作。 可以看到get方法成功了也正确的匹配了我的服务方法Get。 1 public class HomeService : IHomeService2 {3 public Student Get(string id)4 {5 return new Student() { ID Convert.ToInt32(id), Name hxc, SNS 001 };6 }7 8 public string Add(Student stu)9 {
10 return hello;
11 }
12 } 然后我们看看Add方法我在HttpWebRequest中模拟测试如下。 View Code 好了大概就说这么多了如果说你不嫌麻烦你可以用WCF Rest还有就是不要忘了很多的默认配置如果你觉得太繁琐 可以用用asp.net web api。 随笔- 197 文章- 0 评论- 3407 十五天精通WCF——第十四天 一起聊聊FaultException 我们在玩web编程的时候可能你会不经意的见到一些http500的错误我想你应该不会陌生的原因你应该也知道服务器异常嘛 这时候clr会把这个未处理的异常抛给iis并且包装成http500的错误返回到客户端就比如下面这样。 从这张图中我故意输入了xss字符然后的然后web程序自爆异常其实我想表达的意思就是虽然说web程序抛异常了但不代表iis就 挂了所以iis还是需要给客户端做出反馈这就有了http header和body信息同样的道理wcf的服务器异常机制也是这样。。。service 抛出了异常不代表console就挂了console要做的事情就是把这个异常包装起来丢给调用方而wcf是怎么包装的呢就是用了这篇所 说的FaultException。。。 一FaultException 1. faultexception是干什么的 刚才我也说了这个异常就是wcf来包装远程错误的具体的类含义就是表示“SOAP错误“如果你够细心的话你还会发现到它有个属性 叫Serializable有了它这个叼毛就可以序列化到Soap消息中对伐 2. 如果挖出faultexception 挖出这个exception的方法有很多比如我来造一个“除以0”的异常如下所示 Service 1 public class HomeService : IHomeService2 {3 public Student Get(string id)4 {5 //这里必然会抛出异常。。。6 var result Convert.ToInt32(id) / Convert.ToInt32(0);7 8 return new Student() { ID Convert.ToInt32(id), Name hxc, SNS 001 };9 }
10 } Client 1 public class Program12 {3 static void Main(string[] args)4 {5 using (HomeServiceClient client new HomeServiceClient())6 {7 try8 {9 var result client.Get(1);
10 }
11 catch (Exception ex)
12 {
13
14 }
15 }
16 }
17 } 看到了没有虽然wcf的service已经抛出异常了但是还是被clr用Faultexception包装起来了正如你看到了s:Fault节点仔细往下看的话 你还会看到faultcode,faultstring,detail等等属性节点那下面有个问题就来了我们平时在Client端都习惯这么写。 1 using (HomeServiceClient client new HomeServiceClient())2 {3 try4 {5 var result client.Get(1);6 }7 catch (Exception ex)8 {9 client.Abort();
10 }
11 } 但是这么写有个什么问题呢就是不管客户端抛出什么异常我们都习惯用基类异常Exception捕获但是wcf有一点非常恶心的就是 它的异常信息非常的少第一眼根本看不出个一二三这是因为所有的异常你都用顶级的exception捕获自然你能知道的信息就非常少 这也很正常如果你想要更详细的信息你是不是应该在Client端写上更具体的异常捕获类呢就比如你现在已经知道的FaultException 是因为服务器的错误都是由它处理的。 如果现在你按照上图中所coding的那样你是不是对异常信息可以了解的更深起码你知道这个异常的抛出绝逼是因为通道是正常的只是 servcie抛出异常了而已。。。那你可能要问了我这话的言外之意就是还有其他异常类也会捕获wcf抛出的异常对的比如说你的信道出现 故障这时候会抛出一个“通信异常CommunicationException”。 三如何挖出“通信异常” 挖出这个异常也是很简单的现在我们需要使用”会话级别“的binding比如说nettcpbindingwshttpbinding这里的话我选择 后者因为是这样的第一次服务器抛异常以后客户端和服务器端通信信道就会关闭如果你在客户端不重新new一个client那么这时候你 第二次再使用client的话这个时候就会产生“信道故障“抛出CommunicationException而当你看到CommunicationException的时候 你可以非常有自信的说老子的wcf根本就没有连接到service而是在client端就被杀死了。。。下面我演示一下。 四自定义FaultException 现在你应该知道了只要是Servcie的Exception都会抛出 FaultException对吧而且你用Fiddler观察的话也看的出其中的faultcode 和faultstring貌似都不是很详细那我就有一个想法了既然wcf会自己给我包装个FaultException那何不我自己就在发生异常的时候自己包 装一个自定义的FaultException然后我可以包装一些我自己想要告诉客户端的信息这样的话是不是灵活性非常的大呢想法很不错wcf 也是恩准这么做的下面我把service的get方法更改如下在FaultException中自定义ReasonCodeAction等等自定义信息。 1 public class HomeService : IHomeService2 {3 public Student Get(string id)4 {5 try6 {7 //这里必然会抛出异常。。。8 var result Convert.ToInt32(id) / Convert.ToInt32(0);9
10 return new Student() { ID Convert.ToInt32(id), Name hxc, SNS 001 };
11 }
12 catch (Exception ex)
13 {
14 var reason new FaultReason(你这个战斗力只有五的渣渣。。。 这么简单的错误都出来了搞个鸡巴毛);
15
16 var code new FaultCode(500);
17
18 var faultException new FaultException(reason, code, 是Get这个王八蛋);
19
20 throw faultException;
21 }
22 }
23 } 好了大概就说这么多了我的目的也很简单在写wcf的client的时候尽量做到异常越具体越好这样方便我们尽可能快的排查问题因为 wcf的异常信息真的太tmd坑爹了减轻痛苦从小做起~~~ 随笔- 197 文章- 0 评论- 3407 十五天精通WCF——终结篇 那些你需要注意的坑 终于一路走来到了本系列的最后一篇了这一篇也没什么好说的整体知识框架已经在前面的系列文章中讲完了wcf的配置众多如果 不加一些指定配置你可能会遇到一些灾难性的后果快来一睹为快吧。 一 第一个大坑 【数据传输量】 我们使用wcf的目的就是用来进行分布式的数据交互既然是交互就一定要进行数据交换可能一些新人并没有注意到wcf在数据传输量上 面做了一个大小限制比如我现在要传输一个2m的txt给service会出现什么情况 1 static void Main(string[] args)2 {3 try4 {5 var txt File.ReadAllText(E:\\1.txt);6 7 HomeServiceClient client new HomeServiceClient();8 9 client.Get(txt);
10
11 int i 10;
12
13 }
14 catch (Exception ex)
15 {
16
17 throw;
18 }
19 } 可是的可是我们在玩aspnet的时候再大的传输量都见过但为什么这玩意就抛异常了呢下面一个问题就来了这个传输默认值到底 是多少 接下来我们就用ILSpy翻翻看。 可以看到这个叼毛玩意居然只有 64k。。。没错你看到的就是64k也就说明你的传输量不能大于64k否则请求就会在client端拒绝 知道了原因我们现在就可以这么修改config了。 bindingsnetTcpBindingbinding nameMySessionBinding maxReceivedMessageSize2147483647//netTcpBinding/bindings 有很多资料在配置这个坑的时候也会使用MaxBufferSize 和 MaxBufferPoolSize就是用来增加缓冲区和缓冲池的大小。 一 第二个大坑 【并发量太低】 说起这个大坑还得先从一段代码说起下面是一段对服务进行2w次并发调用然后我们看看效果。 public class Program1{static void Main(string[] args){try{for (int i 0; i 200000; i){try{Task.Factory.StartNew((obj) {try{HomeServiceClient client new HomeServiceClient();Console.WriteLine(第 {0} 个请求开始。。。, obj);client.Get(12312);Console.WriteLine(第 {0} 个请求结束。。。, obj);}catch (Exception ex){Console.WriteLine(ex.Message);}}, i);}catch (Exception ex){Console.WriteLine(ex.Message);}}Console.Read();}catch (Exception ex){throw;}}} 从上面你可以看到当并发数达到800左右的时候servcie端就开始拒绝client端过来的请求了并且之后的1min的时间里client端 开始出现超时异常这肯定不是我想看到的 那有人就要说了我的并发达到800多很正常啊如果提高这个并发呢其实在wcf里面 有一个叫做ServiceThrottlingElement绑定元素它就是用来控制服务端的并发数。 这三个属性的大概意思我想大家都看的明白不过有点奇怪的是这三个属性的默认值 和 ILSpy中看到的不一样。。。 也懒的研究源码了不管怎么样反正这三个属性值都是int类型的所以我将他们设置为int.maxValue就好了。 system.serviceModelbehaviors serviceBehaviors behavior namenettcpBehaviorserviceMetadata httpGetEnabledfalse /!--是否在错误中包含有关异常的详细信息--serviceDebug includeExceptionDetailInFaultsTrue /serviceThrottling maxConcurrentCalls2147483647 maxConcurrentInstances2147483647 maxConcurrentSessions2147483647 //behavior/serviceBehaviors/behaviorsbindingsnetTcpBindingbinding nameMySessionBinding //netTcpBinding/bindingsservicesservice behaviorConfigurationnettcpBehavior nameMyService.HomeServiceendpoint addressnet.tcp://127.0.0.1:19200/HomeService bindingnetTcpBindingbindingConfigurationMySessionBinding contractMyService.IHomeService /endpoint addressmex bindingmexHttpBinding contractIMetadataExchange /hostbaseAddressesadd baseAddresshttp://127.0.0.1:1920 //baseAddresses/host/service/services/system.serviceModel 然后我们再把程序跑起来看一看。。。 现在你可以发现并发早已突破800了不过你要记住如果并发数太多容易造成系统资源耗尽导致崩溃这时候负载均衡就来 了对吧wcf需要修改的配置还有很多正因为wcf框架庞大很多默认配置不符合生产需求所以大家在工作中需要注意这个系列 就到此打住了希望对你有帮助。 转载于:https://www.cnblogs.com/kingCpp/p/4705931.html