[WCF编程]10.操作:回调操作
一、回调操作概述
WCF支持服务将调用返回给它的客户端。在回调期间,许多方面都将颠倒过来:服务将成为客户端,客户端将编程服务。回调操作可以用在各种场景和应用程序中,但在涉及事件或者服务发生时间需要通知客户端时,显得特别有用。
回调操作通常被认为是双向操作。并非所有的绑定都支持回调操作,只有在具有了双向能力的绑定时,才支持回调操作。比如,HTTP协议本质上是与与连接无关的,所以他不能用于回调,所以,不能基于BasicHttpBingding绑定或WsHttpBingding绑定使用回调。为了让HTTP协议支持回调,WCF提供了WSDualHttpBingding绑定,它实际上设置了两个HTTP通道:一个用于从客户端到服务的调用,另一个则是服务到客户端的调用。WCF也为NetTcpBingding和NetNamePipeBingding绑定提供了对回调操作的支持。所以,TCP和IPC协议均支持双向通信。
双工回调并不是标准的操作,因为没有对于的行业标准来规定客户端如何传递客户端地址给服务,或者服务如何实现发布回调契约。双工回调会损害WCF的性能。很少用到WSDualHttpBingding,因为它无法穿越客户端和服务端的重重阻碍。这种连接线问题由Windows Azure AppFabric Service Bus解决了,在云上它支持双工对调,使用NetTcpRelayBingding绑定。
二、回调操作5步骤
1.创建标准契约和回调契约
/// <summary>
/// 回调契约
/// </summary>
public interface IMyContractCallback
{
[OperationContract]
void OnCallback(string name);
}[ServiceContract(CallbackContract </span>= <span style="color: #0000ff">typeof</span><span style="color: #000000">(IMyContractCallback))]
</span><span style="color: #0000ff">public</span> <span style="color: #0000ff">interface</span><span style="color: #000000"> IMyContract
{
[OperationContract]
</span><span style="color: #0000ff">void</span> DoSomething(<span style="color: #0000ff">string</span><span style="color: #000000"> name);
}</span></pre></div>
2.在服务端实现标准契约
[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Reentrant)]
public class MyContract : IMyContract
{
private IMyContractCallback callback;
public void DoSomething(string name)
{
callback = OperationContext.Current.GetCallbackChannel<IMyContractCallback>();
callback.OnCallback(name + "回来了");
}
}3.创建服务端承载
class Program
{
static void Main(string[] args)
{
ServiceHost host = new ServiceHost(typeof(WCF.CallbackOperation.Service.MyContract));
host.Open();
Console.WriteLine("服务启动成功......");
int i = 0;
foreach (ServiceEndpoint endpoint in host.Description.Endpoints)
{
i++;
Console.WriteLine("终结点序号:{0},终结点名称:{1},终结点地址:{2},终结点绑定:{3}{4}", i, endpoint.Name, endpoint.Address, endpoint.Binding, Environment.NewLine);
}Console.Read();
}
}</span></pre></div> App.config
<system.serviceModel>
<services>
<service name="WCF.CallbackOperation.Service.MyContract" behaviorConfiguration="CallbackTcpBehavior">
<endpoint name="tcp_IMyContract" address="MyContract" contract="WCF.CallbackOperation.Service.IMyContract" binding="netTcpBinding"></endpoint>
<endpoint address="MyContract/mex" binding="mexTcpBinding" contract="IMetadataExchange"></endpoint>
<host>
<baseAddresses>
<add baseAddress="net.tcp://127.0.0.1:9000"/>
</baseAddresses>
</host>
</service><span style="color: #0000ff"></</span><span style="color: #800000">services</span><span style="color: #0000ff">></span>
<span style="color: #0000ff"><</span><span style="color: #800000">behaviors</span><span style="color: #0000ff">></span>
<span style="color: #0000ff"><</span><span style="color: #800000">serviceBehaviors</span><span style="color: #0000ff">></span>
<span style="color: #0000ff"><</span><span style="color: #800000">behavior </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="CallbackTcpBehavior"</span><span style="color: #0000ff">></span>
<span style="color: #0000ff"><</span><span style="color: #800000">serviceMetadata </span><span style="color: #ff0000">httpGetEnabled</span><span style="color: #0000ff">="false"</span><span style="color: #ff0000"> httpsGetEnabled</span><span style="color: #0000ff">="false"</span><span style="color: #0000ff">/></span>
<span style="color: #0000ff"><</span><span style="color: #800000">serviceDebug </span><span style="color: #ff0000">includeExceptionDetailInFaults</span><span style="color: #0000ff">="true"</span><span style="color: #0000ff">/></span>
<span style="color: #0000ff"></</span><span style="color: #800000">behavior</span><span style="color: #0000ff">></span>
<span style="color: #0000ff"></</span><span style="color: #800000">serviceBehaviors</span><span style="color: #0000ff">></span>
<span style="color: #0000ff"></</span><span style="color: #800000">behaviors</span><span style="color: #0000ff">></span>
<span style="color: #0000ff"><</span><span style="color: #800000">diagnostics </span><span style="color: #ff0000">performanceCounters</span><span style="color: #0000ff">="All"</span><span style="color: #0000ff">/></span>
</system.serviceModel>
4.实现客户端代理
使用Visual Studio的SvcUtil工获取
5.在客户端实现并调用
class Program
{
static void Main(string[] args)
{
MyCallback callback = new MyCallback();
callback.CallService();
callback.Dispose();Console.Read();
} </span><span style="color: #0000ff">class</span><span style="color: #000000"> MyCallback : IMyContractCallback, IDisposable
{
</span><span style="color: #0000ff">private</span><span style="color: #000000"> MyContractClient m_Proxy; </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> CallService()
{
InstanceContext context </span>= <span style="color: #0000ff">new</span> InstanceContext(<span style="color: #0000ff">this</span><span style="color: #000000">);
m_Proxy </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> MyContractClient(context);
m_Proxy.DoSomething(</span><span style="color: #800000">"</span><span style="color: #800000">zxj</span><span style="color: #800000">"</span><span style="color: #000000">);
} </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> OnCallback(<span style="color: #0000ff">string</span><span style="color: #000000"> name)
{
Console.WriteLine(name);
} </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> Dispose()
{
m_Proxy.Close();
}
}
}</span></pre></div>
实例代码下载:下载
三、回调契约
回调操作是服务契约的一部分,它取决于服务契约对回调契约的定义。一个服务契约最多只能包含一个回调契约。一旦定义了回调契约,就需要客户端支持回调,并在每次调用中提供执行服务的回调终结点。若要定义回调契约,则可以使用ServiceContract特性提供的Type类型的属性CallbackContract。
public sealed class ServiceContractAttribute : Attribute
{
public Type CallbackContract { get; set; }
}在定义包含回调契约的服务契约时,需要为ServiceContract提供回调契约的类型,以及回调契约的定义,如下所示:
public interface ISomeCallbackContract
{
[OperationContract]
void OnCallback();
} [ServiceContract(CallbackContract=typeof(ISomeCallbackContract))]
public interface IMyContract
{
[OperationContract]
void DoSomething();
}注意,回调契约必须标记ServiceContract特性。因为只要类型定义为回调契约,就意味着它具有ServiceContract特性,并且在服务元数据中也将包含该特性。当然,我们仍然腰围所有的回调接口方法标记OperationContract特性。
当客户端导入的回调接口与原来的服务端定义的名称不同时,它会被修改为服务契约的接口名后将后缀Callback。例如,如果客户端导入上个例子的定义,则客户端会获得以下的定义:
/// <summary>
/// 回调契约
/// </summary>
public interface IMyContractCallback
{
[OperationContract]
void OnCallback(string name);
}[ServiceContract(CallbackContract </span>= <span style="color: #0000ff">typeof</span><span style="color: #000000">(IMyContractCallback))]
</span><span style="color: #0000ff">public</span> <span style="color: #0000ff">interface</span><span style="color: #000000"> IMyContract
{
[OperationContract]
</span><span style="color: #0000ff">void</span> DoSomething(<span style="color: #0000ff">string</span><span style="color: #000000"> name);
}</span></pre></div>
所以,建议在服务端使用命名规范,即在回调契约命名为服务契约接口后将后缀Callback。
四、客户端回调设置
客户端负责托管回调对象以及公开回调终结点。服务实例最内层的执行范围是实例上下文。InstanceContext类定义的构造函数能够将服务实例传递给宿主。
public sealed class InstanceContext : CommunicationObject, IExtensibleObject<InstanceContext>
{
// 实现服务实例的对象。
public InstanceContext(object implementation);
// 为实例上下文返回服务的实例。
public object GetServiceInstance();
......
}为了托管一个回调对象,客户端需要实例化回调对象,然后通过它创建一个上下文对象:
private MyContractClient m_Proxy; public void CallService()
{
InstanceContext context = new InstanceContext(this);
m_Proxy = new MyContractClient(context);
m_Proxy.DoSomething("zxj");
}值得一提的是,虽然回调方法是在客户端,但它们仍然属于WCF操作,因此他们都是一个操作调用上下文,通过OperationContext.Current访问。
双向代理
无论何时,只要服务终结点的契约定义了一个回调契约,客户端在与它进行交互时,都必须使用代理创建双向通信,并将回调终结点的引用传递给服务。因此没客户端使用的代理必须继承一个专门的代理类DuplexClientBase<T>,如下所示:
public abstract class DuplexClientBase<TChannel> : ClientBase<TChannel> where TChannel : class
{
protected DuplexClientBase(InstanceContext callbackInstance);
protected DuplexClientBase(object callbackInstance);
protected DuplexClientBase(InstanceContext callbackInstance, ServiceEndpoint endpoint);
protected DuplexClientBase(object callbackInstance, ServiceEndpoint endpoint);
protected DuplexClientBase(InstanceContext callbackInstance, Binding binding, EndpointAddress remoteAddress);
protected DuplexClientBase(object callbackInstance, Binding binding, EndpointAddress remoteAddress);
// 获取内部双工通道。
public IDuplexContextChannel InnerDuplexChannel { get; }
......
}客户端需要提供实例上下文绑定给托管宿主的DuplexClientBase<T>构造函数。代理会根据回调上下文构建终结点,从服务终结点配置信息里推断回调终结点的信息:回调终结点契约是通过服务契约回调类型定义的。回调终结点会使用与外发调用相同的绑定。对于地址,WCF会使用客户端机器名。只是简单地传递实例上下文给双向代理,并使用代理来调用服务暴露的终结点。为了简化这个过程,DuplexClientBase<T>提供了可以接受回调对象的构函数,并将其包装在上下文里。无论出于什么原因,当客户端需要访问上下文时,DuplexClientBase<T>都会另外提供IDuplexContextChannel类型的InnerDuplexChannel属性。它提供了通过InnerDuplexChannel属性访问上下文的方式。
使用Visual Studio能够为包含了回调契约的目标服务生成代理类,生成的类派生自DuplexClientBase<T>,如下所示:
public partial class MyContractClient : System.ServiceModel.DuplexClientBase<IMyContract>, IMyContract
{</span><span style="color: #0000ff">public</span><span style="color: #000000"> MyContractClient(System.ServiceModel.InstanceContext callbackInstance) :
</span><span style="color: #0000ff">base</span><span style="color: #000000">(callbackInstance)
{
} </span><span style="color: #0000ff">public</span> MyContractClient(System.ServiceModel.InstanceContext callbackInstance, <span style="color: #0000ff">string</span><span style="color: #000000"> endpointConfigurationName) :
</span><span style="color: #0000ff">base</span><span style="color: #000000">(callbackInstance, endpointConfigurationName)
{
} </span><span style="color: #0000ff">public</span> MyContractClient(System.ServiceModel.InstanceContext callbackInstance, <span style="color: #0000ff">string</span> endpointConfigurationName, <span style="color: #0000ff">string</span><span style="color: #000000"> remoteAddress) :
</span><span style="color: #0000ff">base</span><span style="color: #000000">(callbackInstance, endpointConfigurationName, remoteAddress)
{
} </span><span style="color: #0000ff">public</span> MyContractClient(System.ServiceModel.InstanceContext callbackInstance, <span style="color: #0000ff">string</span><span style="color: #000000"> endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
</span><span style="color: #0000ff">base</span><span style="color: #000000">(callbackInstance, endpointConfigurationName, remoteAddress)
{
} </span><span style="color: #0000ff">public</span><span style="color: #000000"> MyContractClient(System.ServiceModel.InstanceContext callbackInstance, System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
</span><span style="color: #0000ff">base</span><span style="color: #000000">(callbackInstance, binding, remoteAddress)
{
} </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> DoSomething()
{
</span><span style="color: #0000ff">base</span><span style="color: #000000">.Channel.DoSomething();
} </span><span style="color: #0000ff">public</span><span style="color: #000000"> System.Threading.Tasks.Task DoSomethingAsync()
{
</span><span style="color: #0000ff">return</span> <span style="color: #0000ff">base</span><span style="color: #000000">.Channel.DoSomethingAsync();
}
}
客户端可以使用派生的代理类创建回调实例,并将上下文作为它的宿主,创建代理、调用代理服务,这样就可以传递回调终结点的引用。
class MyCallback : IMyContractCallback, IDisposable
{
private MyContractClient m_Proxy;</span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> CallService()
{
InstanceContext context </span>= <span style="color: #0000ff">new</span> InstanceContext(<span style="color: #0000ff">this</span><span style="color: #000000">);
m_Proxy </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> MyContractClient(context);
m_Proxy.DoSomething(</span><span style="color: #800000">"</span><span style="color: #800000">zxj</span><span style="color: #800000">"</span><span style="color: #000000">);
} </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> OnCallback(<span style="color: #0000ff">string</span><span style="color: #000000"> name)
{
Console.WriteLine(name);
}
}
注意,只要客户端正在等待回调,就不能关闭代理。如果关闭回调终结点,当服务试图将调用返回时,就会导致服务端产生错误。
最常见的实现方式是由客户端自身实现回调契约,此时,客户端通常可以将代理定义为成员变量,并在释放客户端时关闭它,如下所示:
class MyCallback : IMyContractCallback,IDisposable
{
private MyContractClient m_Proxy;
public void CallService()
{
InstanceContext context = new InstanceContext(this);
m_Proxy = new MyContractClient(context);
m_Proxy.DoSomething();
}
public void OnCallback()
{
throw new NotImplementedException();
}
public void Dispose()
{
m_Proxy.Close();
}
}五、服务端的回调调用
随着服务端的每一次调用,客户端的回调终结点引用都会被传递到服务,组成传入消息的一部分。OperationContext类为服务提供了方便访问回调引用的途径,即调用泛型方法GetCallbackChannel<T>();
public sealed class OperationContext : IExtensibleObject<OperationContext>
{
// 获取调用当前操作的客户端实例的通道。
public T GetCallbackChannel<T>();
}服务如何处理回调引用及何时决定使用它,完全由服务选择,这一点毋庸置疑。服务能够从操作上下文中提取出回调引用,然后将它保存起来以备日后使用;或者可以在服务运行期间使用它,将调用返回客户端。如下所示:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class MyContract : IMyContract
{
private IMyContractCallback callback;
public void DoSomething(string name)
{
callback = OperationContext.Current.GetCallbackChannel<IMyContractCallback>();
callback.OnCallback(name + "回来了");
}
}回调重入
服务可能需要调用传入的回调引用。然而,这样的调用在默认情况下是禁止的,因为它受默认的服务并发管理的限制。在默认情况下,服务类被配置为单线程访问:服务实例上下文与锁关联,在任何时刻都只能有一个线程拥有锁,也只能有一个个线程能够访问上下文中的服务实例。在操作调用期间,在客户端发布的调用需要阻塞服务线程,并调用回调。问题是一旦回调返回它需要的重入的相同上下文及获取同一个锁的所有权,处理从相同通道的客户端返回的应答消息时就会导致死锁。注意,服务仍然可能调用到其它客户端的回调,或者调用其他服务,这属于正在调用的会导致死锁的客户端的回调。
如果单线程的服务实例试图调用返回给他的客户端,为了避免死锁,WCF会抛出InvilidOperationException异常。对于这种情况,有三种可能的解决方案:
第一种方案是配置服务,允许多线程访问。由于服务实例与锁无关,因此允许正在调用的客户端回调。但是这种情况也可能增加服务开发者的负担,因为它需要为服务提供同步。
第二种方案是将服务配置为重入。一旦配置为重入,服务实例就与锁管理,同时只允许单线程访问。如果服务正在回调它的客户端,WCF就会先释放锁。就目前而言,如果服务需要回调它的客户端,则可以使用ServiceBehavior特性的ConcurrencyMode属性,将服务的并发行为配置为多线程或者重入。
[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Reentrant)]
public class MyContract : IMyContract
{
private IMyContractCallback callback;
public void DoSomething(string name)
{
callback = OperationContext.Current.GetCallbackChannel<IMyContractCallback>();
callback.OnCallback(name + "回来了");
}
}第三种方案是将回调契约操作配置为单向操作,这样服务就能够安全地将调用返回给客户端。因为没有任何应答消息会竞用锁,即使并发模式设置为单线程,服务也能够支持回调。注意,实例代码中使用第二种方案,如使用第三种方案,请重新生成客户端代理类。
public interface IMyContractCallback
{
[OperationContract(IsOneWay = true)]
void OnCallback(string name);
} [ServiceContract(CallbackContract = typeof(IMyContractCallback))]
public interface IMyContract
{
[OperationContract]
void DoSomething(string name);
} public class MyContract : IMyContract
{
private IMyContractCallback callback;
public void DoSomething(string name)
{
callback = OperationContext.Current.GetCallbackChannel<IMyContractCallback>();
callback.OnCallback(name + "回来了");
}
}六、回调连接管理
如果客户端保持打开状态,则服务只能将调用返回给客户端。通常情况下,这建立在代理没有关闭的情况下。保证代理处于打开状态同样可以避免回调对象被垃圾回收期回收。如果服务维持了一个在对调终极点上的引用,而且客户端代理是关闭的,或者客户端应用程序已经推出,那么当服务调用回调时,就会从服务通道处获取一个ObjectDisposedException异常。因此,对于客户端而言,当它不再需要接收回调会客户端应用已经关闭,最好能够通知服务无。为此,可以在服务契约中显式添加一个Disconnect()方法。如果每个放哪广发调用都担忧回调引用,那么服务就能够在Disconnect()方法中将回调从内部存储结构中移除。
当然,也建议开发者同时在服务契约中也显式提供一个Connect()方法。定义Connect()方法,能够是客户端重复地连接或断开,同时还提供一个明确的时间点,以判断何时需要一个回调,因为回调只能够发生在调用Connect()方法之后。
[ServiceContract(CallbackContract = typeof(IMyContractCallback))]
public interface IMyContract
{
[OperationContract]
void DoSomething(string name);[OperationContract]
</span><span style="color: #0000ff">void</span><span style="color: #000000"> Connect(); [OperationContract]
</span><span style="color: #0000ff">void</span><span style="color: #000000"> DisConnect();
} [ServiceBehavior(ConcurrencyMode</span>=<span style="color: #000000">ConcurrencyMode.Reentrant)]
</span><span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span><span style="color: #000000"> MyContract : IMyContract
{
</span><span style="color: #0000ff">private</span><span style="color: #000000"> IMyContractCallback callback;
</span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> DoSomething(<span style="color: #0000ff">string</span><span style="color: #000000"> name)
{
callback </span>= OperationContext.Current.GetCallbackChannel<IMyContractCallback><span style="color: #000000">();
callback.OnCallback(name </span>+ <span style="color: #800000">"</span><span style="color: #800000">回来了</span><span style="color: #800000">"</span><span style="color: #000000">);
} </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> Connect()
{
</span><span style="color: #008000">//</span><span style="color: #008000">可以实现为回调列表,将回调引用添加到列表中</span>
}
</span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> DisConnect()
{
</span><span style="color: #008000">//</span><span style="color: #008000">可以实现为回调列表,将回调引用从列表中移除</span>
}
}连接管理与实例操作
单向服务
只有在操作调用回调引用对象自身或者将它存储在某些全局变量(如静态变量)时,单调服务才能够使用 回调引用。既然服务可能使用的存储在引用对象中的任何实例状态在操作返回时都会丢失,那么单调服务就必须使用某些静态变量用以存储回调引用。因而,单调服务特别需要类似于Disconnect()方法。如果没有该放哪广发,共享存储就会随着时间的推移,而出现大量冗余的回调引用。
单例服务
当然,单例服务也存在类似的情况。由与单例对象的生命周期不会结束,因而它会无限累计回调引用,随着时间的推移,回调的客户端不服存在,大多数回调引用也随之失效。定义DisConnect()方法可以保证单例服务只连接到对应的活动客户端。
会话服务
会话服务即使没有DisConnect()方法也能获得引用,只要它将回调引用保存在某个实例成员的变量中。原因在于当会话结束时,服务实例会被自动释放,整个会话不存在保持引用的危险。这就能够保证回调引用总是有效的。但是,如果会话服务为了让其它宿主段或跨会话访问而将它的回调引用保存在某个全局变量中,必须添加DisConnect()方法,已达到移除回调引用的目的,这就是因为在调用Dispose()期间,不能使用回调引用。
我们也可以为会话服务添加Connect()和DisConnect()方法,因为它允许客户端在会话期间决定何时启动或停止接受回调消息。
七、回调契约层级
设计回调契约时,需要注意设计上的约束。如果一个服务契约的基契约可回调接口,则服务契约定义的回调结构必须是它的契约定义的所有回调接口的子接口。如下,以下回调契约是无效的:
interface ICallbackContract1
{......} interface ICallbackContract2
{......} [ServiceContract(CallbackContract = typeof(ICallbackContract1))]
interface IMyBaseContract
{......} //无效
[ServiceContract(CallbackContract = typeof(ICallbackContract2))]
interface IMySubContract
{......}IMySubContract不能指定ICanllbackContract2为它的回调契约,因为ICallbackContract2不能ICallbackContract1的子类,而IMyBaseContract(IMySubContract的父接口)定义了ICallbackContract1作为它的回调接口。
满足约束的最直接方法实在回调契约层级反映服务契约的层次:
interface ICallbackContract1
{......} interface ICallbackContract2:ICallbackContract1
{......} [ServiceContract(CallbackContract = typeof(ICallbackContract1))]
interface IMyBaseContract
{......} [ServiceContract(CallbackContract = typeof(ICallbackContract2))]
interface IMySubContract
{......}[WCF编程]10.操作:回调操作的更多相关文章
- [WCF编程]10.操作:事件
一.事件概述 基础的WCF回调机制并不能阐明客户端与服务之间交互的本质.双向回调的规范使用可以通过事件来完成.客户端发生的相关事项都可以通过事件通知客户端或者多个客户端.事件可能源于直接的客户端调用, ...
- [WCF编程]10.操作:单向操作
一.单向操作概述 WCF提供了单向操作,一旦客户端调用,WCF会生成一个请求,但没有相关的应答信息返回给客户端.所以,单向操作是不能有返回值,服务抛出的任何异常都不会传递给客户端. 理想情况下,一旦客 ...
- [WCF编程]10.操作:请求/应答操作
一.调用操作概述 WCF除了支持经典的服务端-客户端的请求/应答操作外,还提供了对其他操作类型的内建支持,包括:即发即弃的单向调用:允许服务将调用返回给客户端的双向回调:允许客户端或服务器处理大量负荷 ...
- [WCF编程]10.操作:流操作
一.流操作概述 在默认情况下,当客户端调用服务时,服务只有在接收到完整的消息后才会被调用,同样,客户端只有在包含了调用结果的返回消息被完整接受时,才会解除对它的阻塞. 对于数据量小的消息,这种交换模式 ...
- .Net-WCF-图书:《WCF编程》
ylbtech-.Net-WCF-图书:<WCF编程> <WCF编程>是2008年1月机械工业出版社出版的图书,作者是Juval Lowy.Clemens Vasters. 1 ...
- WCF分布式开发步步为赢(10):请求应答(Request-Reply)、单向操作(One-Way)、回调操作(Call Back).
WCF除了支持经典的请求应答(Request-Reply)模式外,还提供了什么操作调用模式,他们有什么不同以及我们如何在开发中使用这些操作调用模式.今天本节文章里会详细介绍.WCF分布式开发步步为赢( ...
- 跟我一起学WCF(9)——WCF回调操作的实现
一.引言 在上一篇文章中介绍了WCF对Session的支持,在这篇文章中将详细介绍WCF支持的操作.在WCF中,除了支持经典的请求/应答模式外,还提供了对单向操作.双向回调操作模式的支持,此外还有流操 ...
- 重温WCF之数单向通讯、双向通讯、回调操作(五)
一.单向通讯单向操作不等同于异步操作,单向操作只是在发出调用的瞬间阻塞客户端,但如果发出多个单向调用,WCF会将请求调用放入到服务器端的队列中,并在某个时间进行执行.队列的存储个数有限,一旦发出的调用 ...
- 背水一战 Windows 10 (88) - 文件系统: 操作文件夹和文件
[源码下载] 背水一战 Windows 10 (88) - 文件系统: 操作文件夹和文件 作者:webabcd 介绍背水一战 Windows 10 之 文件系统 创建文件夹,重命名文件夹,删除文件夹, ...
随机推荐
- 免费图片存储和图话【提供demo下载】
我们不管是做博客系统还是其他网站,图片是免不了要使用到的.但是,我们都知道图片的访问是很耗资源的,同时也是很占磁盘空间的,且还特别占带宽. 所以,我们一般都会用到特定的图片服务器.不过,像我等屌丝平时 ...
- 当我谈 "加班有罪" 我在谈什么?
前言 PS. 本文只描述IT行业. 博客园果真人气比较高,我之前准备写个 "领域驱动系列",然后感觉大家不感兴趣,看来用的人不多,所以一直没动力续,但是昨天写了 [加班有罪] (h ...
- Unity3D游戏开发初探—4.开发一个“疯狂击箱子”游戏
一.预备知识—对象的”生“与”死“ (1)如何在游戏脚本程序中创建对象而不是一开始就创建好对象?->使用GameObject的静态方法:CreatePrimitive() 以上一篇的博文中的“指 ...
- RabbitMQ Exchange & Queue Design Trade-off
In previous post, I mentioned the discussion on StackOverflow regarding designing exchanges. Usually ...
- Java,extends,继承
1. 继承的概念: 继承在本职上是特殊--一般的关系.子类继承父类,表明子类是一种特殊的父类,并且具有父类所不具有的一些属性或方法. 2. 继承中的初始化顺序: 从类的结构上而言,其内部可以有如下四种 ...
- Java 对象引用方式 —— 强引用、软引用、弱引用和虚引用
Java中负责内存回收的是JVM.通过JVM回收内存,我们不需要像使用C语音开发那样操心内存的使用,但是正因为不用操心内存的时候,也会导致在内存回收方面存在不够灵活的问题.为了解决内存操作不灵活的问题 ...
- [译]AngularJS中DOM操作
再翻译一篇干货短文,原文:AngularJS jQuery 虽然Angularjs将我们从DOM的操作中解放出来了,但是很多时候我们还是会需要在controller/view加载之后执行一些DOM操作 ...
- Javascript刷题 》 查找数组元素位置
找出元素 item 在给定数组 arr 中的位置 输出描述: function indexOf(arr, item) { ..... } 如果数组中存在 item,则返回元素在数组中的位置,否则返回 ...
- This build of Sublime Text 2 has expired
使用一直很好的 sublime 2 最近一直提示: This build of Sublime Text 2 has expired, please update to a newer one fro ...
- Sql Server系列:索引基础
1 索引概念 索引用于快速查找在某个列中某个特定值的行,不使用索引,数据库必须从第1条记录开始读完整个表,知道找出需要的行.表越大,查询数据所花费的时间越多.如果表中查询的列有索引,数据库能快速到达一 ...