WCF 订阅(Pub/Sub)服务,实现服务端调用客户端

本文档展示了如何使用WCF(Windows Communication Foundation)创建一个服务,该服务支持订阅和回调功能。服务端通过ServiceBehavior设置为可重入和单例模式,允许多个客户端订阅并接收推送消息。客户端通过DuplexChannelFactory创建通道,订阅服务并接收回调。源代码可在GitHub上找到。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值