在本次实验中你将会看到几种不同的operation方式:request/reply、one-way和duplex callbacks。打开Operation文件夹下的Operation.sln解决方案。程序是一个交通灯的管理系统。
解决方案包含了三个项目:
TrafficLightClient项目包含了一个winform程序,用来表示单个交通灯:
TrafficController项目包含了一个winform程序,用来设置当前交通灯的状态:
你可以通过RadioButton来设置交通灯的状态,并通过点击Timer button来启动一个Timer来自动改变所有交通灯的状态。
最后,TraficLightService项目包含了一个服务,用来接受交通灯的新状态,并把这个状态通知所有正在运行的交通灯。
这个程序的架构如图所示:
交通灯连接到TrafficLightService来订阅交通灯状态。TrafficControllerForm会调用TrafficLightService并通知它新的交通灯的状态。TrafficLightService再把这个状态通知到所有的订阅者。
编译并运行程序,现在还没有很多事情会发生。目前唯一的contract是ITrafficLightStatus,TrafficControllerForm用它来设置状态:
{
Red,
Yellow,
Green
}
[ServiceContract]
interface ITrafficLightStatus
{
[OperationContract]
void SetState(LightColor color);
[OperationContract]
LightColor GetState();
}
开发Service
TrafficLightService实现了ITrafficLightStatus接口:
class TrafficLightService : ITrafficLightStatus
{
LightColor m_Color = LightColor.Red;
public void SetState(LightColor color)
{
m_Color = color;
}
public LightColor GetState()
{
return m_Color;
}
}
TrafficLightService是一个单实例,也就是所有的client端都可以访问到它的状态。我们还需要添加一些功能让client端能够连接到service,并且让service回调它们通知状态。
在TrafficLightService.cs文件中加入ITrafficLightManager和ITrafficLightManagerCallback接口:
interface ITrafficLightManager
{
[OperationContract]
LightColor Connect();
[OperationContract]
void Disconnect();
}
interface ITrafficLightManagerCallback
{
[OperationContract(IsOneWay=true)]
void OnStateChanged(LightColor newColor);
}
ITrafficLightManager只有一个任务,就是让client连接或者断开服务。并且告诉了WCF回调的接口。
TrafficLightService将会保存回调的引用,这样当交通灯状态改变时,它将会通知所有的client端这个新的状态。添加如下代码:
class TrafficLightService : ITrafficLightStatus, ITrafficLightManager
{
LightColor m_Color = LightColor.Red;
List<ITrafficLightManagerCallback> m_Callback = new List<ITrafficLightManagerCallback>();
public void SetState(LightColor color)
{
m_Color = color;
m_Callback.ForEach(delegate(ITrafficLightManagerCallback callback)
{
callback.OnStateChanged(color);
});
}
……
public LightColor Connect()
{
ITrafficLightManagerCallback callback = OperationContext.Current.GetCallbackChannel<ITrafficLightManagerCallback>();
m_Callback.Add(callback);
return m_Color;
}
public void Disconnect()
{
ITrafficLightManagerCallback callback = OperationContext.Current.GetCallbackChannel<ITrafficLightManagerCallback>();
m_Callback.Remove(callback);
}
}
在app.config中为ITrafficLightManager添加endpoint:
<service name = "TrafficLightService">
<endpoint
address = "net.tcp://localhost:8001/TrafficLightService"
binding = "netTcpBinding"
contract = "ITrafficLightStatus"
/>
<endpoint
address="net.tcp://localhost:8002/TrafficLightService"
binding="netTcpBinding"
contract="ITrafficLightManager"
/>
</service>
</services>
编译程序,确保服务器端都正确。
开发client
在Proxy.cs文件中添加如下的定义:
interface ITrafficLightManager
{
[OperationContract]
LightColor Connect();
[OperationContract]
void Disconnect();
}
interface ITrafficLightManagerCallback
{
[OperationContract(IsOneWay=true)]
void OnStateChanged(LightColor newColor);
}
class TrafficLightManagerClient : DuplexClientBase<ITrafficLightManager>, ITrafficLightManager
{
public TrafficLightManagerClient(InstanceContext inputInstance)
: base(inputInstance)
{ }
public LightColor Connect()
{
return Channel.Connect();
}
public void Disconnect()
{
Channel.Disconnect();
}
}
注意到TrafficLightManagerClient是从DuplexClientBase<T>这个基类继承而来,主要的目的就是为了回调。
修改TrafficLightClient项目的app.config文件,加入endpoint:
<endpoint
address="net.tcp://localhost:8002/TrafficLightService"
binding="netTcpBinding"
contract="ITrafficLightManager" />
</client>
打开TrafficLight.cs文件,加入ITrafficLightManager的实现,添加一个代理类的实例;向服务端出入一个引用。
{
TrafficLightManagerClient m_TrafficManager;
public TrafficLight()
{
InitializeComponent();
InstanceContext instance = new InstanceContext(this);
m_TrafficManager = new TrafficLightManagerClient(instance);
m_TrafficLightControl.State = m_TrafficManager.Connect();
}
void OnClosing(object sender, FormClosingEventArgs e)
{
m_TrafficManager.Disconnect();
m_TrafficManager.Close();
}
public void OnStateChanged(LightColor newColor)
{
Thread.Sleep(300);
m_TrafficLightControl.State = newColor;
}
}
运行程序
解决方案已经配置为了多项目启动,直接按F5编译并运行程序。为了观察多个client端的运行情况,可以多启动几个client端:
我们可以观察到在OnStateChanged方法中会有延迟,几个client端不是同时在改变状态。解决这个问题我们可以将这个通知client的方法配置为one-way。在TrafficLightService.cs文件中将OnStateChanged方法配置为one-way:
{
[OperationContract(IsOneWay=true)]
void OnStateChanged(LightColor newColor);
}
这下再运行一下程序,所有client端就会一起改变状态了。