勇哥注:
并非wcf有一种专门的发布/订阅方式,它只是双工方式的一种变形。
其实就是用一点编程技巧在双工方式上面改进而来的。
如下图所示:
上面是客户端,下面窗口是服务端。
首先你在客户端点击两次“订阅”,服务端点“刷新”就可以看到最新的订阅客户列表。
然后点“发消息”,客户端就能收到结果。
客户端收到的结果是两个订阅者的信息。
服务端
winform代码:
using System; using System.CodeDom; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.ServiceModel; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace WcfService { public partial class Form1 : Form { PublishService pub = new PublishService(); ServiceHost host; public Form1() { InitializeComponent(); host = new ServiceHost(pub); //host=new ServiceHost(typeof(PublishService)); } private void Form1_Load(object sender, EventArgs e) { host.Opened += delegate { label1.Text = "服务启动成功"; }; host.Open(); } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { try { host.Close(); } catch { host.Abort(); } } private void button1_Click(object sender, EventArgs e) { //刷新 listBox1.Items.Clear(); foreach(var m in pub.UserList) { listBox1.Items.Add(m.ToString()); } } private void button2_Click(object sender, EventArgs e) { //发消息 for(int i=0;i< pub.CallbackList.Count;i++) { try { pub.CallbackList[i].Nodify($"{pub.UserList[i].ToString()},{DateTime.Now.ToString()}"); } catch(Exception ex) { MessageBox.Show($"{ex.Data},无法连接"); } } } } }
服务与契约:
using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.Text; using System.Threading.Tasks; namespace WcfService { [ServiceContract(CallbackContract =typeof(ICallbackEvents))] public interface IPublishService { [OperationContract(IsOneWay = true)] void Subscribe(Guid id); [OperationContract(IsOneWay =true)] void Unsubscribe(Guid id); } public interface ICallbackEvents { [OperationContract(IsOneWay =true)] void Nodify(string msg); } [ServiceBehavior( ConcurrencyMode= ConcurrencyMode.Reentrant, InstanceContextMode = InstanceContextMode.Single)] public class PublishService : IPublishService { public List<Guid> UserList = new List<Guid>(); public List<ICallbackEvents> CallbackList = new List<ICallbackEvents>(); public void Subscribe(Guid id) { int idx = UserList.FindIndex(s => s == id); if (idx < 0) { UserList.Add(id); ICallbackEvents call= OperationContext.Current.GetCallbackChannel<ICallbackEvents>(); CallbackList.Add(call); } } public void Unsubscribe(Guid id) { UserList.Remove(id); CallbackList.RemoveAt(UserList.IndexOf(id)); } } public class CallbackEvents : ICallbackEvents { public Guid Id { get; set; } public void Nodify(string msg) { } } }
客户端
winform代码:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.ServiceModel; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace Client { public partial class Form1 : Form { CallbackFun call = new CallbackFun(); back.PublishServiceClient client; Guid myGuid; public Form1() { InitializeComponent(); call.act =OutMsg; myGuid = System.Guid.NewGuid(); InstanceContext instance = new InstanceContext(call); client = new back.PublishServiceClient(instance, "myback"); } private void button1_Click(object sender, EventArgs e) { //订阅 call = new CallbackFun() { act= OutMsg }; InstanceContext instance = new InstanceContext(call); client = new back.PublishServiceClient(instance); myGuid = System.Guid.NewGuid(); client.Subscribe(myGuid); } private void button2_Click(object sender, EventArgs e) { //取消订阅 client.Unsubscribe(myGuid); client.Close(); } private void OutMsg(string msg) { SetRtbMsg($"{msg}\r\n"); } private void SetRtbMsg(string msg) { if(this.richTextBox1.InvokeRequired) { Action<string> act = SetRtbMsg; this.richTextBox1.BeginInvoke(act, new object[] { msg }); } else { this.richTextBox1.AppendText(msg); } } } public class CallbackFun : back.IPublishServiceCallback { public Action<string> act { get; set; } public void Nodify(string msg) { act(msg); } } }
服务端的winform代码中
public Form1() { InitializeComponent(); host = new ServiceHost(pub); //host=new ServiceHost(typeof(PublishService)); }
会报一个错误:
System.InvalidOperationException:“要使用采用服务实例的其中一个 ServiceHost 构造函数, 服务的 InstanceContextMode 必须设置为 InstanceContextMode.Single。 可以通过 ServiceBehaviorAttribute 进行此配置。 否则,请考虑使用采用 Type 参数的 ServiceHost 构造函数。”
这个错误的原因是你构造ServieHost的时候用的是实例:
host = new ServiceHost(pub);
而不是类型:
host=new ServiceHost(typeof(PublishService));
要解决这个问题需要在PublishService上加上服务行为的定义,如下:
本文代码下载:
链接:https://pan.baidu.com/s/1HZujqpqOTVLZsCSealXlkA
提取码:xohf
--来自百度网盘超级会员V6的分享

