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>
效果
-
服务端
-
客户端