WCF-双工通讯

WCF双工通讯只要是通过调用由客户端实现的接口ICallback, 来实现服务端调用该接口方法来实现回调,从而把相关数据或对象传递到客户端的一种实现方式。当然目前通过WebSocket 也可以很容易实现,这里暂不讨论其他方法,只对WCF方法做一个简单的记录,以备忘记。

说明

服务端主要通过OperationContext 操作上下文的当前对象的GetCallbackChannel() 方法来获取由客户端实现的回调接口实例,从而可以通过该实例来调用客户端方法。
如:IClientCallback callback=OperationContext.Current.GetCallbackChannel<IClientCallback>();
而客户端通过DuplexChannelFactory 类创建双通道工厂,通过该工厂类的实例创建通道实例,即服务实例,进而可以通过该实例调用服务端实现的方法。
如:

ClientCallback callback=new ClientCallback();
InstanceContext context=new InstanceContext(callback);
DuplexChannelFactory<IMyService> factory=new DuplexChannelFactory<IMyService>(context,new NetTcpBinding(),new EndPointAddress("net.tcp://localhost:10021/MyService"))

  • 服务端

服务端接口(契约)

using System.ServiceModel;

  [ServiceContract(CallbackContract =typeof(IClientCallback))]
  public interface IMyService
  {
      [OperationContract(IsOneWay = true)]
      void RegisterClient();
      [OperationContract]
      void SendMessage(string message);
  }

回调接口(该接口在服务端不实现,由客户端实现)

 [ServiceContract]
 public interface IClientCallback
 {
     [OperationContract(IsOneWay = true)]
     void NotifyClient(string message);
     [OperationContract(IsOneWay = true)]
     void ServerTimeUpdate(DateTime time);
 }

服务端实现接口类(即提供相关服务)

  //服务实现
  [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
  public class MyService : IMyService
  {
      private IClientCallback callback;
      private Timer timer;
      public MyService() { 
         //获取客户端回调通道
         callback=OperationContext.Current.GetCallbackChannel<IClientCallback>();
      }
      public void RegisterClient()
      {
          Console.WriteLine("客户端已连接");
          //设置定时器,定期向客户端发送服务器时间
          timer = new Timer(1000);
          timer.Elapsed += (s, e) => {
              try
              {
                  callback.ServerTimeUpdate(DateTime.Now);
              } catch(Exception ex) {
                 Console.WriteLine(ex.ToString());
              }
          };
          timer.Start();
      }

      public void SendMessage(string message)
      {
          Console.WriteLine($"收到客户端消息:{message}");
          //向客户端发送响应
          callback.ServerTimeUpdate(DateTime.Now);
      }
  }

服务端启动WCF方法

  internal class Program
  {
      static void Main(string[] args)
      {
          using (ServiceHost host = new ServiceHost(typeof(MyService)))
          {
              try {
                  host.Open();
                  Console.WriteLine("双工服务已启动!");
                  foreach (var ep in host.Description.Endpoints)
                  {
                      Console.WriteLine(ep.ListenUri);
                  }
              }
              catch(Exception ex)  {
                  Console.WriteLine(ex.Message);
              }
              Console.ReadKey();
          }
      }
  }

服务端配置文件

<system.serviceModel>
	<services>
		<service name="DuplexService.Services.MyService">
			<endpoint address="" binding="wsDualHttpBinding" contract="DuplexService.Services.IMyService" />
			<endpoint address="" binding ="netTcpBinding" contract="DuplexService.Services.IMyService" />
			<host>
				<baseAddresses>
					<add baseAddress="http://localhost:10020/MyService"/>
					<add baseAddress="net.tcp://localhost:10021/MyService" />
				</baseAddresses>
			</host>
		</service>
	</services>
	<behaviors>
		<serviceBehaviors>
			<behavior>
				<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
			</behavior>
		</serviceBehaviors>
	</behaviors>
</system.serviceModel>

客户端

客户端对回调接口的实现

 public class ClientCallback : IMyServiceCallback
 {
     /// <summary>
     /// 服务端通过回调接口来对客户端实例方法进行回调,给客户端传递消息
     /// </summary>
     /// <param name="message"></param>
     /// <exception cref="NotImplementedException"></exception>
     public void NotifyClient(string message)
     {
         Console.WriteLine($"收到服务端发送的消息:{message}");
     }

     /// <summary>
     /// 服务端通过回调接口来对客户端实例方法进行回调,给客户端传递时间
     /// </summary>
     /// <param name="time"></param>
     /// <exception cref="NotImplementedException"></exception>
     public void ServerTimeUpdate(DateTime time)
     {
         Console.WriteLine($"收到服务端回发的更新时间:{time.ToString("YYYY-MM-dd hh:mm:ss")}");
     }
 }

客户端实现双通道调用

 internal class Program
 {
     static void Main(string[] args)
     {
         //创建回调实例和实例上下文
         ClientCallback callback= new ClientCallback();
         InstanceContext context = new InstanceContext(callback);

         //创建双通道工厂(提供创建和管理不同类型的双工通道的方式,客户端使用这些通道在服务终结点发送和接收消息)
         DuplexChannelFactory<IMyService> fact = new DuplexChannelFactory<IMyService>(context,new NetTcpBinding(),
             new EndpointAddress("net.tcp://localhost:10021/MyService"));
         //创建到指定终结点指定类型的通道
         IMyService channel = fact.CreateChannel();
        
         try {
             //注册客户端
             channel.RegisterClient();
             Console.WriteLine("已连接到服务器,输入消息发送给服务器");
             string input;
             while ((input = Console.ReadLine()) != "exit")
             {
                 channel.SendMessage(input);
             }
             channel.SendMessage("客户端退出");
         }
         catch (Exception ex)
         {
             Console.WriteLine($"发生错误:{ex.Message}");
             //使通讯对象从其当前状态转换到关闭状态
             channel.Stop();
             fact.Close();
         }
     }
 }

客户端配置文件

 <system.serviceModel>
     <bindings>
         <netTcpBinding>
             <binding name="NetTcpBinding_IMyService">
                 <security>
                     <transport sslProtocols="None" />
                 </security>
             </binding>
         </netTcpBinding>
         <wsDualHttpBinding>
             <binding name="WSDualHttpBinding_IMyService" />
         </wsDualHttpBinding>
     </bindings>
     <client>
         <endpoint address="http://localhost:10020/MyService" binding="wsDualHttpBinding"
             bindingConfiguration="WSDualHttpBinding_IMyService" contract="ServiceReference1.IMyService"
             name="WSDualHttpBinding_IMyService">
             <identity>
                 <userPrincipalName value="DESKTOP-H27UFUR\Administrator" />
             </identity>
         </endpoint>
         <endpoint address="net.tcp://localhost:10021/MyService" binding="netTcpBinding"
             bindingConfiguration="NetTcpBinding_IMyService" contract="ServiceReference1.IMyService"
             name="NetTcpBinding_IMyService">
             <identity>
                 <userPrincipalName value="DESKTOP-H27UFUR\Administrator" />
             </identity>
         </endpoint>
     </client>
 </system.serviceModel>

效果

  • 服务端
    image

  • 客户端
    image

posted @ 2025-09-23 09:59  丹心石  阅读(7)  评论(0)    收藏  举报