wcf的双工通讯的变种:发布订阅

勇哥注:

并非wcf有一种专门的发布/订阅方式,它只是双工方式的一种变形。

其实就是用一点编程技巧在双工方式上面改进而来的。


如下图所示:

上面是客户端,下面窗口是服务端。

首先你在客户端点击两次“订阅”,服务端点“刷新”就可以看到最新的订阅客户列表。

然后点“发消息”,客户端就能收到结果。

客户端收到的结果是两个订阅者的信息。


image.png


image.png


服务端

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 构造函数。”

image.png


这个错误的原因是你构造ServieHost的时候用的是实例:

host = new ServiceHost(pub);

而不是类型:

host=new ServiceHost(typeof(PublishService));


要解决这个问题需要在PublishService上加上服务行为的定义,如下:

image.png



本文代码下载:


链接:https://pan.baidu.com/s/1HZujqpqOTVLZsCSealXlkA 

提取码:xohf 

--来自百度网盘超级会员V6的分享


本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:
本帖最后由 勇哥,很想停止 于 2024-07-04 21:55:37 编辑

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2025年4月    »
123456
78910111213
14151617181920
21222324252627
282930
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864