勇哥:
这个例子在一个服务进程内同时提供了两个服务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}]");
}
}
}

