勇哥:
这个例子在一个服务进程内同时提供了两个服务client, mess。
一个用于api调用,一个用于发布订阅。
下面是工程列表:
下面是host宿主的app.config
两个服务的绑定一个是用管道,一个是tcp
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /> </startup> <system.serviceModel> <behaviors> <serviceBehaviors> <behavior name="SampleMethodbehavior"> <serviceDebug httpHelpPageEnabled="false"/> <serviceMetadata httpGetEnabled="false"/> <serviceTimeouts transactionTimeout="00:10:00"/> <serviceThrottling maxConcurrentCalls="1000" maxConcurrentInstances="1000" maxConcurrentSessions="1000"/> </behavior> <behavior name="MessageFunbehavior"> <serviceDebug httpHelpPageEnabled="false"/> <serviceMetadata httpGetEnabled="false"/> <serviceTimeouts transactionTimeout="00:10:00"/> <serviceThrottling maxConcurrentCalls="1000" maxConcurrentInstances="1000" maxConcurrentSessions="1000"/> </behavior> </serviceBehaviors> </behaviors> <bindings> <netTcpBinding> <binding name="tcpbinding2"> <security mode="None"> <transport clientCredentialType="None" protectionLevel="None"/> </security> </binding> </netTcpBinding> </bindings> <services> <!--<services> <service name="HardwareLayerService.DMCAPI.MotionCardAPI" behaviorConfiguration="MotionCardAPI"> <host> <baseAddresses> <add baseAddress="net.pipe://localhost/WCFService"/> </baseAddresses> </host> <endpoint address="" binding="netNamedPipeBinding" bindingConfiguration="" contract="HardwareLayerService.Interface.IMotionCardAPI"/> <endpoint address="mex" binding="mexNamedPipeBinding" contract="IMetadataExchange"/> </service> </services>--> <service name="ClassLibrary1.SampleMethod" behaviorConfiguration="SampleMethodbehavior"> <host> <baseAddresses> <add baseAddress="net.pipe://localhost/SampleMethod"/> </baseAddresses> </host> <endpoint address="" binding="netNamedPipeBinding" bindingConfiguration="" contract="ClassLibrary1.ISampleMethod"/> <endpoint address="mex" binding="mexNamedPipeBinding" contract="IMetadataExchange"/> </service> <service name="ClassLibrary2.MessageFun" behaviorConfiguration="MessageFunbehavior"> <host> <baseAddresses> <add baseAddress="net.tcp://localhost:11113/MessageFun"/> </baseAddresses> </host> <endpoint address="" binding="netTcpBinding" bindingConfiguration="tcpbinding2" contract="ClassLibrary2.IMessageFun"/> <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/> </service> </services> </system.serviceModel> </configuration>
宿主host是个winform程序
NotifyAll 用于服务端发送消息
Refresh 用于刷新客户端的连接
窗体的源码:
using ClassLibrary1; using ClassLibrary2; 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 WindowsFormsApplication1 { public partial class Form1 : Form { private List<ServiceHost> hosts = new List<ServiceHost>(); private SampleMethod pubsrv = new SampleMethod(); private MessageFun mess = new MessageFun(); public Form1() { InitializeComponent(); } private void btn_NotifyAll_Click(object sender, EventArgs e) { for (int i = 0; i < mess.list_SubscrberId.Count; i++) { try { mess.list_Callback[i].Notify(mess.list_SubscrberId[i].ToString()); } catch (Exception ex) { MessageBox.Show(ex.Data.ToString() + "\n无法连接客户:\n" + mess.list_SubscrberId[i].ToString()); } } } private void btn_Refresh_Click(object sender, EventArgs e) { listBox_SubsciberID.Items.Clear(); for (int i = 0; i < mess.list_SubscrberId.Count; i++) listBox_SubsciberID.Items.Add(mess.list_SubscrberId[i].ToString()); } private void Form1_Load(object sender, EventArgs e) { //pubsrv); //host = new ServiceHost(pubsrv); // typeof(SampleMethod)); //host.Open(); //(注1) hosts.Add(new ServiceHost(typeof(SampleMethod))); hosts.Add(new ServiceHost(mess)); foreach (var host in hosts) { host.Opening += (s, e1) => { Console.WriteLine($"服务[{host.GetType().Name}]已经打开了"); }; host.Open(); } } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { foreach (var m in hosts) { m.Close(); } } } }
(注1)
注意下面的添加服务的方式,影响是否能并发.
typeof和直接使用服务的实例是不一样的。如果想并发必须使用typeof的写法。
ServiceHost(typeof(SampleMethod)) ServiceHost(mess)
下面是Service的源码。
Service是服务与契约。即接口与它的实现类。
IMessageFun.cs
这个是发布订阅功能的契约。
using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.Text; using System.Threading.Tasks; namespace ClassLibrary2 { //SessionMode = SessionMode.Required, [ServiceContract( CallbackContract = typeof(ICallbackEvents))] public interface IMessageFun { [OperationContract(IsOneWay = true)] void Subscribe(Guid id); [OperationContract(IsOneWay = true)] void UnSubscribe(Guid id); } public interface ICallbackEvents { [OperationContract(IsOneWay = true)] void Notify(string msg); } }
MessageFun.cs
Subscribe是订阅
UnSubscribe是取消订阅
using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.Text; using System.Threading.Tasks; namespace ClassLibrary2 { [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)] public class MessageFun : IMessageFun { public List<Guid> list_SubscrberId = new List<Guid>(); public List<ICallbackEvents> list_Callback = new List<ICallbackEvents>(); public void Subscribe(Guid id) { if (list_SubscrberId.IndexOf(id) < 0)//如果不存在,则添加 { list_SubscrberId.Add(id); ICallbackEvents callback = OperationContext.Current.GetCallbackChannel<ICallbackEvents>(); list_Callback.Add(callback); } } public void UnSubscribe(Guid id) { list_Callback.RemoveAt(list_SubscrberId.IndexOf(id)); list_SubscrberId.Remove(id); } } }
ISampleMethod.cs
using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.Text; using System.Threading.Tasks; namespace ClassLibrary1 { [ServiceContract(SessionMode = SessionMode.Allowed)] public interface ISampleMethod { [OperationContract] string MethodOne(string msg); [OperationContract] string MethodTwo(string msg); } }
SampleMethod.cs
using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ClassLibrary1 { [ServiceBehavior(UseSynchronizationContext = false, InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)] //[ServiceBehavior(UseSynchronizationContext = false)] public class SampleMethod : ISampleMethod { public string MethodOne(string msg) { OperationContext operationContext = OperationContext.Current; InstanceContext instanceContext = operationContext.InstanceContext; string info = "InstanceContext HashCode: " + instanceContext.GetHashCode().ToString(); System.Threading.Thread.Sleep(5000); return msg + "\n->" + info + "\n->ManagedThreadId:" + Thread.CurrentThread.ManagedThreadId.ToString() + " " + System.DateTime.Now.ToString(); } public string MethodTwo(string msg) { OperationContext operationContext = OperationContext.Current; InstanceContext instanceContext = operationContext.InstanceContext; string info = "InstanceContext HashCode: " + instanceContext.GetHashCode().ToString(); //System.Threading.Thread.Sleep(2000); return msg + "\n->" + info + "\n->ManagedThreadId:" + Thread.CurrentThread.ManagedThreadId.ToString() + " " + System.DateTime.Now.ToString(); } } }
下面是客户端的ui
要使用发布订阅,先点击“订阅”,然后去服务端点击Refresh,再点击 NotifyAll 。
另外几个按钮测试并发。
窗口源码:
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; using WindowsFormsApp1.client; using WindowsFormsApp1.mess; namespace WindowsFormsApp1 { public partial class Form1 : Form { private Guid Myid; private Guid Myid2; private client.SampleMethodClient myclinet1=null; private client.SampleMethodClient myclinet2=null; private mess.MessageFunClient messClient; public Form1() { InitializeComponent(); Myid = System.Guid.NewGuid(); Myid2= Guid.NewGuid(); CallbackEvents callback = new CallbackEvents(); InstanceContext context = new InstanceContext(callback); messClient = new mess.MessageFunClient(context); myclinet1 = new client.SampleMethodClient(); myclinet2 = new client.SampleMethodClient(); } private void button1_Click(object sender, EventArgs e) { Task.Factory.StartNew(() => { myclinet1.MethodOneAsync(new client.MethodOneRequest("c1_1")); myclinet1.MethodOneCompleted -= Myclinet1_MethodOneCompleted; myclinet1.MethodOneCompleted += Myclinet1_MethodOneCompleted; myclinet1.MethodTwoAsync(new client.MethodTwoRequest("c1_2")); myclinet1.MethodTwoCompleted -= Myclinet1_MethodTwoCompleted; myclinet1.MethodTwoCompleted += Myclinet1_MethodTwoCompleted; myclinet2.MethodOneAsync(new client.MethodOneRequest("c2_1")); myclinet2.MethodOneCompleted -= Myclinet2_MethodOneCompleted; myclinet2.MethodOneCompleted += Myclinet2_MethodOneCompleted; myclinet2.MethodTwoAsync(new client.MethodTwoRequest("c2_2")); myclinet2.MethodTwoCompleted -= Myclinet2_MethodTwoCompleted; myclinet2.MethodTwoCompleted += Myclinet2_MethodTwoCompleted; }); } private void Myclinet2_MethodTwoCompleted(object sender, client.MethodTwoCompletedEventArgs e) { OutMsg(e.Result.MethodTwoResult.ToString()); } private void Myclinet2_MethodOneCompleted(object sender, client.MethodOneCompletedEventArgs e) { OutMsg(e.Result.MethodOneResult.ToString()); } private void Myclinet1_MethodTwoCompleted(object sender, client.MethodTwoCompletedEventArgs e) { OutMsg(e.Result.MethodTwoResult.ToString()); } private void Myclinet1_MethodOneCompleted(object sender, client.MethodOneCompletedEventArgs e) { OutMsg(e.Result.MethodOneResult.ToString()); } public void OutMsg(string str) { if (RtbMsg.IsDisposed) return; if (!RtbMsg.IsHandleCreated) return; if(RtbMsg.InvokeRequired) { Action<string> act = OutMsg; RtbMsg.Invoke(act, new object[] { str }); } else { RtbMsg.AppendText(str+"\n"); } } private void button2_Click(object sender, EventArgs e) { myclinet1.MethodOneAsync(new client.MethodOneRequest("c1_1")); myclinet1.MethodOneCompleted -= Myclinet1_MethodOneCompleted; myclinet1.MethodOneCompleted += Myclinet1_MethodOneCompleted; } private void button3_Click(object sender, EventArgs e) { myclinet1.MethodTwoAsync(new client.MethodTwoRequest("c1_2")); myclinet1.MethodTwoCompleted -= Myclinet1_MethodTwoCompleted; myclinet1.MethodTwoCompleted += Myclinet1_MethodTwoCompleted; } private void Form1_Load(object sender, EventArgs e) { } private void button4_Click(object sender, EventArgs e) { if (myclinet1 == null) { CallbackEvents callback = new CallbackEvents(); InstanceContext context = new InstanceContext(callback); messClient = new mess.MessageFunClient(context); } messClient.Subscribe(new Subscribe() { id = Myid }); } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { try { myclinet1.Close(); myclinet2.Close(); messClient.Close(); } catch { myclinet1.Abort(); myclinet2.Abort(); messClient.Abort(); } } } class CallbackEvents :mess.IMessageFunCallback { public IAsyncResult BeginNotify(Notify request, AsyncCallback callback, object asyncState) { throw new NotImplementedException(); } public void EndNotify(IAsyncResult result) { throw new NotImplementedException(); } public void Notify() { MessageBox.Show("Subscibe Ok"); } public void Notify(Notify request) { MessageBox.Show($"Subscibe Ok2 [{request.msg}]"); } } }

