共同探讨QTP相关问题

WCF CallBack

上一篇 / 下一篇  2008-04-07 20:31:20 / 个人分类:C#

Callback 机制又被称之为 "duplex call",说白了就是在原有基础上,为客户端也添加一个服务端点,让服务器能调用这个客户端 "服务",从而实现所谓的回调机制。也正因为如此,通讯 binding 就必须支持双向通讯能力(bidirectional-capable),通常我们会选择 WSDualHttpBinding、NetTcpBinding 以及 NetNamedPipeBinding。51Testing软件测试网y:^4g*{Z nL
51Testing软件测试网F,}XW V5v0tV-Ic
接下来,我们回忆一下 Callback 的开发步骤。
"P\0_ vqE,CC!S])k0
Um'b:CU01. 创建服务契约。
M$^J d/z#s9K.V0
[ServiceContract(SessionMode=SessionMode.Required)]51Testing软件测试网l!Kks}K'z
public interface IService51Testing软件测试网Bze-o3j9wV0p
{
D.A)V#y_!h}b0  [OperationContract]51Testing软件测试网6u!Ji1a!]2fRM,Kh2X
  void Test();
*y9R+V5^}r0}

/zaH&[#@3Lz9dI9h R9B02. 定义回调接口。51Testing软件测试网(hd:MX.Q
51Testing软件测试网*q ]E6C!}x
由于回调接口的实现类型在客户端执行,因此无需添加 ServiceContract。
Ly:n^Mf6`5LC0
public interface ICallBack51Testing软件测试网{Ab3OR6Nn
{
\/A~'}/W`/[-^J4?0  [OperationContract]
bCv O u$tX3I0  void Execute(DateTime d);51Testing软件测试网PSO,LhZPm.Q
}
51Testing软件测试网)OB ?]0oYC1w#XJ2L4jU,p
3. 实现服务契约。
9i RH:E0U0
5ce B }#E Oo5m3vM0我们可以使用 "OperationContext.Current.GetCallbackChannel<T>()" 来回调客户端 "Callback"。
8}lOnu2Z/h8z A0
public class MyService : IService, IDisposable
@(~da1_xy0{51Testing软件测试网] FTkF#Xh f1x6c
  public void Test()51Testing软件测试网8[1n0~:WU
  {51Testing软件测试网nSJ/TLfg I7y"F
    Console.WriteLine("Service Invoke CallBack...");51Testing软件测试网fQtRFQs6C
    OperationContext.Current.GetCallbackChannel<ICallBack>().Execute(DateTime.Now);
icVb o~E K'm{0  }51Testing软件测试网8g1h2m z9wH&_O
51Testing软件测试网W;psA)q3g D!U k`m
  public void Dispose()51Testing软件测试网5J/o$GB~Q4o
  {51Testing软件测试网,]1aqj'Vy
    Console.WriteLine("Dispose:{0}", DateTime.Now);
Y3sMs|4[7kY5i0  }
s0B3K*Z'N8MJ0}

MmGw\Z['N)EW/Kd04. 创建 ServiceHost。51Testing软件测试网 q'z7_`%j9c8R
51Testing软件测试网Zdk3DX$DEP1l
注意发布 Metadata。51Testing软件测试网c y0g:Yhn)M{
ServiceHost host = new ServiceHost(typeof(MyService), new Uri("http://localhost:8080/myservice"));
\ E7`0Ju h!w0host.AddServiceEndpoint(typeof(IService), new WSDualHttpBinding(), "");
J2x"x V;~i"bW"bz051Testing软件测试网LQ&e,R&Ov Wd6}_
ServiceMetadatabehavīor metadata = new ServiceMetadatabehavīor();
T"Lf3vt |0metadata.HttpGetEnabled = true;
pYct2@0host.Descrīption.behavīors.Add(metadata);
}B$O:[.GJRV051Testing软件测试网+O`Ah{;c8pj L
host.Open();

_&u,ZJC?,s.f05. 使用 Svcutil.exe 或 VS2005 创建客户端代理。
2xg)QG2O^e+H0
//------------------------------------------------------------------------------51Testing软件测试网S}J3pG:crR
// <auto-generated>51Testing软件测试网HH&G A%~:J S8s
//   此代码由工具生成。51Testing软件测试网3YZ:?1i5w"D+vTnf5a
//   运行库版本:2.0.50727.4251Testing软件测试网'JA}%UX
//51Testing软件测试网5z&T/U/s&lC
//   对此文件的更改可能会导致不正确的行为,并且如果
p]0|Z-N&T:I2Bm~0//   重新生成代码,这些更改将会丢失。51Testing软件测试网s Y/}(a;R/TK#b
// </auto-generated>
-f&G\%Az^7bqR-x-D0//------------------------------------------------------------------------------51Testing软件测试网cs-njz
51Testing软件测试网jMDTM Bs#EO"i
namespace ConsoleApplication1.localhost51Testing软件测试网(aeb%F5|'U(o6k:M ?8L,E
{51Testing软件测试网X;UCuKI au
  [GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
B"u aV3muE0  [ServiceContractAttribute(ConfigurationName = "ConsoleApplication1.localhost.IService", CallbackContract = typeof(IServiceCallback), SessionMode = SessionMode.Required)]51Testing软件测试网0V L$L i aA1a C
  public interface IService51Testing软件测试网,OenVoy_h lM3c
  {
7M)A6VZy}A0    [OperationContractAttribute(Action = "http://.../IService/Test", ReplyAction = "http://.../IService/TestResponse")]51Testing软件测试网 b:{+j.@`]:qU*`
    void Test();51Testing软件测试网|3Iub/le r
  }
S%w@$bT f}0
`w0DhT0  [GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
u9\ze X"U-u0u}8P0  public interface IServiceCallback
&PzZ9x7Dp7j6`H0  {
_-d ~$^1R/M0    [OperationContractAttribute(Action = "http://.../IService/Execute", ReplyAction = "http://.../IService/ExecuteResponse")]51Testing软件测试网Nw4b+d.ub9eA
    void Execute(System.DateTime d);
B|;D,V|!~)P o0f}0  }51Testing软件测试网 B.h3QU!jW
51Testing软件测试网,wUk j+v Qb9x
  [GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]51Testing软件测试网%bB,tL/rjE$m
  public interface IServiceChannel : IService, IClientChannel51Testing软件测试网"w"OR8|cJ \`!Q'I
  {51Testing软件测试网#Cv;z:P.\@ u
  }
.b }V#pB4f\C {,U0
`WRS2{0  [DebuggerStepThroughAttribute()]51Testing软件测试网w~+R |M0R1TJ
  [GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
(K e2F,w{%] G"u0  public partial class ServiceClient : DuplexClientBase<IService>, IService51Testing软件测试网Mg!ew9@
  {
@+k7m4]:W-pw)ZG0    public ServiceClient(System.ServiceModel.InstanceContext callbackInstance)51Testing软件测试网,v#g,I5|A,JRn7^
      : base(callbackInstance)
}+c)FKryT0    {51Testing软件测试网#]ew4us
    }51Testing软件测试网3NF$v K)M kbG

*O4OXM:Y0    public void Test()51Testing软件测试网'@ Sd`^
    {
3`/M^#r.w u/Sp,QV+w0      base.Channel.Test();
^F\El{ v u1Y8q0    }
{[ C_7B _%e"A I0  }
,]*W[A8e!D;Uy:S0}
51Testing软件测试网E]*o\Pt
6. 创建客户端回调接口的实现类型。51Testing软件测试网U5lwkyW:^t7z
class CallBack : IServiceCallback
t{2Q6Mu0{51Testing软件测试网 zK|0`l SW(D lL
  public void Execute(DateTime d)
l2OX3D ?l"C ]|0  {
1q\3IrHJeh&G0    Console.WriteLine("Client:{0}", d);
m6O@ E Fm:D mB0  }51Testing软件测试网tW;xWM&?
}

3@ P \C*ka07. 为客户端自动生成的配置文件添加 clientBaseAddress。51Testing软件测试网 w/nP|Y3C @

Q&L(B ]3S3X]z)s KA0服务器通过这个地址调用客户端回调服务。在同一台机器上调试,注意使用一个不同的端口。51Testing软件测试网$q5Y`%{5G[)G.S
<?xml version="1.0" encoding="utf-8" ?>
%\ Mc9B$U#@H:b$w0a9S2]0<configuration>51Testing软件测试网 cAn T#y;Q7V&\
  <system.serviceModel>51Testing软件测试网Q+`#CT1gEsxU D
    <bindings>51Testing软件测试网1[8nXCn%A
      <wsDualHttpBinding>51Testing软件测试网0`~_Hh&i(` Z
        <binding name="WSDualHttpBinding_IService" clientBaseAddress="http://localhost:8081/" >51Testing软件测试网M Z^`#zi5@N"Q
        </binding>51Testing软件测试网P|O)q2R
      </wsDualHttpBinding>
D#?zy\0    </bindings>51Testing软件测试网)WC;l5e:l%P|7Q(q
    <client>51Testing软件测试网4qE` ij2b `
      <endpoint address="http://localhost:8080/myservice" binding="wsDualHttpBinding"
t6Q-SJs$G_*iu0        bindingConfiguration="WSDualHttpBinding_IService" contract="ConsoleApplication1.localhost.IService"
#} @#S&p#]]0        name="WSDualHttpBinding_IService">51Testing软件测试网I(^CS._~7] n
        <identity>
%y(e"OHo h}b0          <userPrincipalName value="HZH\Administrator" />
-X&Y$uf[)Z%w&jpv0        </identity>
)E&`@LmH&e0      </endpoint>
@[w Xt | vXh0    </client>
nMx|)Gga,OD0  </system.serviceModel>
|6~ z+YpJ)?OX ~0</configuration>

&E&xe_S"k08. 创建客户端调用代码。
&z{ V;g6Zab,n[ Z1K8t:S0
InstanceContext instance = new InstanceContext(new CallBack());51Testing软件测试网X;}zPT U
using (ServiceClient client = new ServiceClient(instance))51Testing软件测试网bcS;cP _
{
hC5o+G|0  client.Test();51Testing软件测试网SH I H#NiKNU
}

Gs'ku$a\ g3B9A7i0运行,看看结果。你看到了什么?异常!!!51Testing软件测试网idnG,dnY,T"LO
用户代码未处理 System.InvalidOperationException
6I DC-Nx$M4s%]!H3l ~0  Message="This operation would deadlock because the reply cannot be received until the current Message completes processing. If you want to allow out-of-order message processing, specify ConcurrencyMode of Reentrant or Multiple on ServicebehavīorAttribute."
SWC4O?`0  Source="mscorlib"
+h6L'[$u v/e!~.C_0  StackTrace:
`*v5@!f.Z l0    Server stack trace: 51Testing软件测试网8w['v!h+US S\
      在 System.ServiceModel.Channels.ServiceChannel.PrepareCall(ProxyOperationRuntime operation, Boolean oneway, ProxyRpc& rpc)

j{tp9y@$A0死锁(deadlock)?没错,你找到了让本文继续下去的理由。51Testing软件测试网~VKu$I\Mh

1UgeI%xN2Ta8G.f(s}0在缺省情况下,服务实例是 single-threaded (ConcurrencyMode=ConcurrencyMode.Single) 的。当服务实例调用客户端回调服务时,方法被阻塞,直到客户端回调服务消息返回(reply message)。而问题是,Single模式不允许重入调用(reentrant calls),即便系统获得返回消息,也会因为无法重入调用方法解除阻塞而造成死锁。了解了这些背景,要解决这个死锁也很简单。51Testing软件测试网\_/A:YO4f"? Im&w
51Testing软件测试网e"l#Nj2^ ksp
方法1: 将回调接口方法设置为 "IsOneWay=true",这样就无需处理客户端返回消息。51Testing软件测试网,N;`oY2RZ~9u*V

8ndq9_6cTh]$Q0为回调接口方法添加 IsOneWay。
public interface ICallBack51Testing软件测试网 ya6nB#HY}$I.m%A
{51Testing软件测试网6h-|VAHig#b
  [OperationContract(IsOneWay=true)]51Testing软件测试网X7s2o ~5VR@
  void Execute(DateTime d);51Testing软件测试网lY#q Yy/?
}
51Testing软件测试网@Z,A fZc!w
修改客户端代理文件,添加 IsOneWay,删除 ReplyAction。
[GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]51Testing软件测试网V}1bFR:k QdY|}
public interface IServiceCallback51Testing软件测试网U-h0q c8h AI&|
{51Testing软件测试网@}R ZyY}t
  [OperationContractAttribute(IsOneWay = true, Action = "http://.../IService/Execute")]51Testing软件测试网Py6|%P1B'aliF
  void Execute(System.DateTime d);51Testing软件测试网Rp5P8f i(?
}

*k~ f8jra0方法2: 修改服务契约的并发模式,改为 "ConcurrencyMode.Reentrant" 或 "ConcurrencyMode.Multiple"。51Testing软件测试网zs7x5`)W@X*H)q&b

#L0nQ8~u*Il r0和方法1不同,本方法无需对客户端做出修改。
pI JR+A&Eg0
[Servicebehavīor(ConcurrencyMode=ConcurrencyMode.Reentrant)]51Testing软件测试网A%qwR}"X#C0_
public class MyService : IService, IDisposable
.G u'xRWZ0{51Testing软件测试网P2_ ov r UB6_S#{ O
  public void Test()
vr H P JC/]0  {
6L i1k$G9[%Q5Gg,v0    Console.WriteLine("Service Invoke CallBack...");51Testing软件测试网E9PV?U'e
    OperationContext.Current.GetCallbackChannel<ICallBack>().Execute(DateTime.Now);
`S$j[?|bk4i0  }
ZS+fdq!i051Testing软件测试网 ir9tmv2D%j
  public void Dispose()51Testing软件测试网.P AZ;S U
  {51Testing软件测试网{3R%jzg2C;zu3C
    Console.WriteLine("Dispose:{0}", DateTime.Now);51Testing软件测试网0~GRQ"Y0i:`P
  }
9RJL?a Y(i9A0}
51Testing软件测试网/c!oBt{/F
无论使用哪种方法,修改后再次运行,我们得到了应有的结果。51Testing软件测试网^t XOy%i*y

i0^ vy c5o9`0回调机制可以用来实现类似事件通知等功能,但对于多数情况下,并不推荐使用。服务器要连接客户端实现回调,可能面临很多问题,诸如内部网络地址、防火墙等等,而且回调机制也让服务实例的生存期管理变得更复杂。

TAG:

 

评分:0

我来说两句

Open Toolbar