勇哥注:
并非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的分享


少有人走的路


















