1 服务端服务契约
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace WcfService
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
[ServiceContract(CallbackContract =typeof(IServiceCall))]
public interface IService
{
[OperationContract(IsOneWay = true)]
void Subscribe(string guid);
// TODO: Add your service operations here
[OperationContract(IsOneWay = true)]
void sendvalue();
// TODO: Add your service operations here
}
public interface IServiceCall
{
[OperationContract(IsOneWay = true)]
void sendout(string value);
}
}
2 服务端服务实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace WcfService
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together.
// NOTE: In order to launch WCF Test Client for testing this service, please select Service1.svc or Service1.svc.cs at the Solution Explorer and start debugging.
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, InstanceContextMode = InstanceContextMode.Single)]
public class Service1 : IService
{
public List<string> clients = new List<string>();
public Dictionary<string,IServiceCall> callBacks = new Dictionary<string, IServiceCall>();
public void Subscribe(string value)
{
if (clients.IndexOf(value) < 0)
{
this.clients.Add(value);
callBacks.Add(value, OperationContext.Current.GetCallbackChannel<IServiceCall>());
}
else
{
//证明客户端重启了,更新下实例
callBacks[value] = OperationContext.Current.GetCallbackChannel<IServiceCall>();
}
}
public void sendvalue()
{
foreach (var item in callBacks)
{
item.Value.sendout(item.Key);
}
}
}
}
需要强调的是,我们在这把ServiceBehavior设为可重入的来实现回调功能。将其实例模式设置为单例模式,以让多个订阅者公用一个发布者。
3 服务端config
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.6.1" />
<httpRuntime targetFramework="4.6.1"/>
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="Service">
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="Service" name="WcfService.Service1">
<endpoint address="" binding="wsDualHttpBinding" contract="WcfService.IService">
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<!--
To browse web app root directory during debugging, set the value below to true.
Set to false before deployment to avoid disclosing web app folder information.
-->
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>
4 客户端服务
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
namespace client
{
[ServiceContract(CallbackContract = typeof(IServiceCall))]
public interface IService
{
[OperationContract(IsOneWay =true)]
void Subscribe(string guid);
// TODO: Add your service operations here
[OperationContract(IsOneWay =true)]
void sendvalue();
//[OperationContract]
//CompositeType GetDataUsingDataContract(CompositeType composite);
}
}
5 客户端callback
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
namespace client
{
public interface IServiceCall
{
[OperationContract(IsOneWay = true)]
void sendout(string value);
}
}
6 callback实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace client
{
public class Class1:IServiceCall
{
public void sendout(string value)
{
Console.WriteLine(value);
}
}
}
7 客户端调用服务端
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
namespace client
{
class Program
{
static void Main(string[] args)
{
Uri uri = new Uri("http://127.0.0.1:8088/Service.svc");
EndpointAddress eaddr = new EndpointAddress(uri);
WSDualHttpBinding wSDualHttpBinding = new WSDualHttpBinding();
//BasicHttpBinding basicHttpBinding = new BasicHttpBinding();
DuplexChannelFactory<IService> channelFactory = new DuplexChannelFactory<IService>(new InstanceContext(new Class1()), wSDualHttpBinding);
IService channel = channelFactory.CreateChannel(eaddr, uri);
//IService itest = DuplexChannelFactory<IService>.CreateChannel(wSDualHttpBinding, eaddr);
channel.Subscribe("123456");
channel.sendvalue();
Console.ReadKey();
}
}
}
需要说明的是可以有多个客户端订阅服务端,其中一个客户端用来调用channel.sendvalue(),实现服务端主动调用客户端
源码
https://github.com/xdqt/WCF-subscription.git