VB.net Wcf事件广播(订阅、发布)
二、实例分析 1、这是一个微软培训教程中用过的一个例子。我记得在学习Remoting时学过这个项目。很简单的一个例子: 服务器上维护一个JOB List,客户端用来提交、执行JOB,服务器将JOB状态和结果通知给每个客户端。这样就必须实现客户端订阅服务端事件。看起来是这个样子的。其实,非网络的事件,我们知道是比较容易实现的。也就是自定义事件的问题。一直以来,我总以为客户端订阅服务器事件也象在单机程序中那样。但由于两端不在一个程序域内,所以无法实现事件在两端的传播。我曾试图把委托或事件做为数据契约(DataContract)来处理,结果大家都知道。委托根本就无法传递和准确序列化,而事件根本就不支持数据契约(后来在书看到,事件必须添加FiledData特性才能支持契约,而委托虽然支持契约,却无法实现正确序列化)。不废话了。 2、原文章部分 略。(运行部分截图也见上文提到 的文章,不管是不是原创,毕竟对我有很多帮助,就不抢别人功劳了。呵呵。) 三、我的代码 由于这个例子相当简单,再加上本人也没什么写文章的天赋,所以,话就不多说了。直接上代码。。。。 1、使用事件进行广播 1)项目用到的JOB类:
- <DataContract()>
- Public Class job
- Private m_Name As String
- Private m_Status As String
- Private m_LastRunTime As DateTime
- Private m_LastRunResult As String
- Public Sub New()
- Me.Status = "Sleeping"
- Me.LastRunResult = ""
- End Sub
- <DataMember()>
- Public Property Name As String
- Get
- Return m_Name
- End Get
- Set(ByVal value As String)
- m_Name = value
- End Set
- End Property
- <DataMember()>
- Public Property Status As String
- Get
- Return m_Status
- End Get
- Set(ByVal value As String)
- m_Status = value
- End Set
- End Property
- <DataMember()>
- Public Property LastRunTime As DateTime
- Get
- Return m_LastRunTime
- End Get
- Set(ByVal value As DateTime)
- m_LastRunTime = value
- End Set
- End Property
- <DataMember()>
- Public Property LastRunResult As String
- Get
- Return m_LastRunResult
- End Get
- Set(ByVal value As String)
- m_LastRunResult = value
- End Set
- End Property
- End Class
- <ServiceContract()>
- Public Interface ICallback
- <OperationContract(IsOneWay:=True)>
- Sub StatusChanged(ByVal job As job)
- <OperationContract(IsOneWay:=True)>
- Sub OnAdd(ByVal newJob As job)
- End Interface
3)项目中用到的服务契约
- '<ServiceContract(SessionMode:=SessionMode.Required, CallbackContract:=GetType(ICallback))>
- '不使用事件,只使用委托时,是否可以不用会话
- '<ServiceContract(SessionMode:=SessionMode.Allowed, CallbackContract:=GetType(ICallback))>
- <ServiceContract(CallbackContract:=GetType(ICallback))>
- Public Interface IService
- '<OperationContract()>
- 'Function GetData(ByVal value As Integer) As String
- '<OperationContract()>
- 'Function GetDataUsingDataContract(ByVal composite As CompositeType) As CompositeType
- ' TODO: 在此添加您的服务操作
- 'IsInitiating 表示 是否可以在服务器上启动会话
- 'IsTerminating 指示服务操作在发送答复消息(如果存在)后,是否会导致服务器关闭会话。
- <OperationContract(IsOneWay:=True, IsInitiating:=True, IsTerminating:=False)>
- Sub Accept()
- <OperationContract(IsOneWay:=True, IsInitiating:=True, IsTerminating:=False)>
- Sub DoJob(ByVal jobName As String)
- <OperationContract(IsOneWay:=True, IsInitiating:=True, IsTerminating:=False)>
- Sub AddJob(ByVal newJob As job)
- <OperationContract(IsOneWay:=False, IsInitiating:=True, IsTerminating:=False)>
- Function GetJobs() As List(Of job)
- End Interface
- '<ServiceBehavior(InstanceContextMode:=InstanceContextMode.PerSession, ConcurrencyMode:=ConcurrencyMode.Multiple)>
- '不使用事件,只使用委托,是否可以不用会话
- '<ServiceBehavior(InstanceContextMode:=InstanceContextMode.Single, ConcurrencyMode:=ConcurrencyMode.Multiple)>
- <ServiceBehavior(InstanceContextMode:=InstanceContextMode.Single, ConcurrencyMode:=ConcurrencyMode.Multiple)>
- Public Class JobServerImplement
- Implements IService
- Public Shared m_jobs As List(Of job) '用来返回 job列表
- Public Shared jobs_Lock As Object '同步锁用
- '事件委托定义
- Public Delegate Sub JobServerEventHandler(ByVal sender As Object, ByVal e As JobCallbackEventArg)
- '定义共享的 事件 列表
- Public Shared onChangeEvents As List(Of JobServerEventHandler)
- Public Shared onAddEvents As List(Of JobServerEventHandler)
- '定义客户端 回调通道 列表
- Public Shared clientCallBackChannles As List(Of ICallback)
- 'job状态改变事件定义,事件必须定义为 共享
- 'Public Shared Event onJobStatusChanged As JobServerEventHandler
- Public Shared Custom Event onJobStatusChanged As JobServerEventHandler
- AddHandler(ByVal value As JobServerEventHandler)
- If jobs_Lock Is Nothing Then
- jobs_Lock = New Object()
- End If
- If onChangeEvents Is Nothing Then
- SyncLock jobs_Lock
- onChangeEvents = New List(Of JobServerEventHandler)
- End SyncLock
- End If
- SyncLock jobs_Lock
- onChangeEvents.Add(value)
- Console.WriteLine("AddHandler Done!")
- End SyncLock
- End AddHandler
- RemoveHandler(ByVal value As JobServerEventHandler)
- If onChangeEvents Is Nothing Then
- Return
- End If
- SyncLock jobs_Lock
- onChangeEvents.Remove(value)
- Console.WriteLine("RemoveHandler Done!")
- End SyncLock
- End RemoveHandler
- RaiseEvent(ByVal sender As Object, ByVal e As JobCallbackEventArg)
- For Each tmpH As JobServerEventHandler In onChangeEvents
- tmpH.BeginInvoke(sender, e, Nothing, Nothing) ' New System.AsyncCallback(AddressOf EndAsync), Nothing)
- Console.WriteLine("RaiseEvent")
- Next
- End RaiseEvent
- End Event
- 'Public Custom Event
- '内部事件传递 委托
- 'Private MyChangeHandler As JobServerEventHandler
- 'job添加事件定义,事件必须 共享
- 'Public Shared Event onAdd As JobServerEventHandler
- '内部事件传递委托
- 'Private MyAddHandler As JobServerEventHandler
- '回调 接口
- Dim callbackInterface As ICallback
- '每一次回调,分开后,如果 哪个客户端失败,不会影响其他存在客户端
- Private Sub CallBackOnce(ByVal theCallBack As ICallback, ByVal e As JobCallbackEventArg)
- Try
- If e.ChangeReason = "Add" Then
- Console.WriteLine("CallBackOnce reason: Add")
- theCallBack.OnAdd(e.Job)
- Else
- Console.WriteLine("CallBackOnce reason: Modi")
- theCallBack.StatusChanged(e.Job)
- End If
- Catch ex As Exception
- Console.WriteLine("CallBackOnce Got Error:" & ex.Message)
- End Try
- End Sub
- '不使用事件,直接使用委托来回调客户端
- Private Sub Server_Delegate_StatusChanged(ByVal sender As Object, ByVal e As JobCallbackEventArg)
- Try
- If e Is Nothing Or e.Job Is Nothing Then
- Console.WriteLine("Server_Delegate_StatusChanged e Or e.Job is nothing.")
- Exit Sub
- Else
- Console.WriteLine("Server_Delegate_StatusChanged Begin!")
- '
- 'Dim callBackOnce As ICallback
- For Each callOnce As ICallback In clientCallBackChannles
- CallBackOnce(callOnce, e)
- 'If e.ChangeReason = "Add" Then
- ' Console.WriteLine("Server_Delegate_StatusChanged reason: Add")
- ' callOnce.OnAdd(e.Job)
- 'Else
- ' Console.WriteLine("Server_Delegate_StatusChanged reason: Modi")
- ' callOnce.StatusChanged(e.Job)
- 'End If
- Next
- Console.WriteLine("Server_Delegate_StatusChanged Done!")
- End If
- Catch ex As Exception
- Console.WriteLine("Server_OnStatusChange Errors:" & ex.Message)
- End Try
- End Sub
- '服务器端状态改变 ,则回调客户端 方法
- Private Sub Server_OnStatusChanged(ByVal sender As Object, ByVal e As JobCallbackEventArg)
- Try
- If e Is Nothing Or e.Job Is Nothing Then
- Console.WriteLine("Server_OnStatusChange e Or e.Job is nothing.")
- Exit Sub
- Else
- Console.WriteLine("Server_OnStatusChange Begin!")
- '回调
- If e.ChangeReason = "Add" Then
- Console.WriteLine("server_onStatusChanged reason: Add")
- 'callbackInterface.StatusChanged(e.Job)
- callbackInterface.OnAdd(e.Job)
- Else
- Console.WriteLine("server_onStatusChanged reason: Modi")
- callbackInterface.StatusChanged(e.Job)
- End If
- Console.WriteLine("Server_OnStatusChange Done!")
- End If
- Catch ex As Exception
- Console.WriteLine("Server_OnStatusChange Errors:" & ex.Message)
- End Try
- End Sub
- '异步调用结束 处理
- Private Sub EndAsync(ByVal ar As IAsyncResult)
- '定义 处理 委托
- Dim dEndHandler As JobServerEventHandler = Nothing
- Console.WriteLine("EndAsync Begin!")
- Try
- '取得 异步 参数 返回的 IAsyncResult,并转换为 远程 类型
- '定义远程异步处理参数, 对委托的异步操作结果
- Dim arRemote As System.Runtime.Remoting.Messaging.AsyncResult = CType(ar, System.Runtime.Remoting.Messaging.AsyncResult)
- '取得 返回 的异步 委托,并转换为 本地的事件委托
- dEndHandler = CType(arRemote.AsyncDelegate, JobServerEventHandler)
- '进行 异步 调用结束 处理
- dEndHandler.EndInvoke(ar)
- Console.WriteLine("EndAsync End!")
- Catch ex As Exception
- '发生错误 ,从事件委托 列表中 去除该 委托
- RemoveHandler onJobStatusChanged, dEndHandler
- 'RemoveHandler onAdd, dEndHandler
- Console.WriteLine("EndAsync Errors:" & ex.Message)
- End Try
- End Sub
- '广播事件
- Private Sub BroadcastEvent(ByVal e As JobCallbackEventArg) ', ByVal tmpHandler As JobServerEventHandler)
- '判断是否有订阅事件
- 'C#中 可以使用 null判断,但VB中不可以,但却可以直接 调用,不会出现异常
- '
- Console.WriteLine("BroadcastEvent Begin!")
- Try
- 'For Each tmpH As JobServerEventHandler In onChangeEvents '.GetInvocationList() 'onJobStatusChangedEvent.GetInvocationList()
- ' '异步调用
- ' tmpH.BeginInvoke(Me, e, New System.AsyncCallback(AddressOf EndAsync), Nothing)
- 'Next
- '保存了客户端通道列表后,不需要 委托列表直接在通道列表中进行回调即可
- Server_Delegate_StatusChanged(Me, e)
- Console.WriteLine("BroadcastEvent End!")
- Catch ex As Exception
- Console.WriteLine("BroadcastEvent Errors:" & ex.Message)
- End Try
- End Sub
- Public Sub Accept() Implements IService.Accept
- Console.WriteLine("Accept Begin!")
- Console.WriteLine("启动服务实例")
- Try
- 'callBack = OperationContext.Current.GetCallbackChannel<Core.ICallback>()
- '取得调用当前操作的客户端实例的通道
- callbackInterface = OperationContext.Current.GetCallbackChannel(Of ICallback)()
- '添加 服务器 端委托 链表
- 'MyChangeHandler = New JobServerEventHandler(AddressOf Me.Server_OnStatusChanged)
- 'AddHandler onJobStatusChanged, MyChangeHandler
- 'AddHandler onJobStatusChanged, AddressOf Me.Server_OnStatusChanged
- 'MyAddHandler = New JobServerEventHandler(AddressOf Me.Server_OnAdd)
- 'AddHandler onAdd, MyAddHandler
- '如果不用事件,直接使用委托
- AddDelegate(AddressOf Me.Server_Delegate_StatusChanged)
- '保存 回调通道列表
- AddCallBack(callbackInterface)
- Catch ex As Exception
- Console.WriteLine("Accept Errors: " & ex.Message)
- End Try
- Console.WriteLine("Accept End!")
- End Sub
- Private Sub AddCallBack(ByVal chl As ICallback)
- Console.WriteLine("AddCallBack Begin!")
- Try
- If jobs_Lock Is Nothing Then
- jobs_Lock = New Object()
- End If
- If clientCallBackChannles Is Nothing Then
- SyncLock jobs_Lock
- clientCallBackChannles = New List(Of ICallback)
- End SyncLock
- End If
- SyncLock jobs_Lock
- clientCallBackChannles.Add(chl)
- Console.WriteLine("AddCallBack Done!")
- End SyncLock
- Catch ex As Exception
- Console.WriteLine("AddCallBack Got Error:" & ex.Message)
- End Try
- End Sub
- Private Sub AddDelegate(ByVal theDelegate As JobServerEventHandler)
- Console.WriteLine("AddDelegate Begin!")
- Try
- If jobs_Lock Is Nothing Then
- jobs_Lock = New Object()
- End If
- If onChangeEvents Is Nothing Then
- SyncLock jobs_Lock
- onChangeEvents = New List(Of JobServerEventHandler)
- End SyncLock
- End If
- SyncLock jobs_Lock
- onChangeEvents.Add(theDelegate)
- Console.WriteLine("AddDelegate Done!")
- End SyncLock
- Catch ex As Exception
- Console.WriteLine("AddDelegate Got Error:" & ex.Message)
- End Try
- End Sub
- Public Sub AddJob(ByVal newJob As job) Implements IService.AddJob
- Console.WriteLine("AddJob")
- Try
- If jobs_Lock Is Nothing Then
- jobs_Lock = New Object()
- End If
- If m_jobs Is Nothing Then
- SyncLock jobs_Lock
- m_jobs = New List(Of job)
- End SyncLock
- End If
- For Each jobItem As job In m_jobs
- If newJob.Name = jobItem.Name Then
- Console.WriteLine("Job 已存在。")
- Return
- End If
- Next
- '添加
- newJob.Status = "Sleeping"
- newJob.LastRunResult = "Created"
- SyncLock jobs_Lock
- m_jobs.Add(newJob)
- End SyncLock
- Console.WriteLine("AddJob 添加完毕。开始广播事件...")
- '引发事件
- Dim e As JobCallbackEventArg = New JobCallbackEventArg()
- e.Job = newJob
- e.ChangeReason = "Add"
- '广播事件
- BroadcastEvent(e)
- 'BroadcastEvent(e, onAdd)
- 'RaiseEvent onAdd(Me, e)
- 'RaiseEvent onJobStatusChanged(Me, e)
- Console.WriteLine("AddJob 广播事件完毕。")
- Catch ex As Exception
- Console.WriteLine("AddJob Errors: " & ex.Message)
- End Try
- Console.WriteLine("AddJob End!")
- End Sub
- '内部方法,用来取得 指定名称的 job
- Private Function GetJobByname(ByVal theJobName As String) As job
- For Each jobItem As job In m_jobs
- If jobItem.Name = theJobName Then
- Return jobItem
- End If
- Next
- Return Nothing
- End Function
- Public Sub DoJob(ByVal jobName As String) Implements IService.DoJob
- Dim theJob As job = GetJobByname(jobName)
- Try
- Dim e As JobCallbackEventArg = New JobCallbackEventArg()
- If theJob.Status = "Finished" Then
- Return
- End If
- theJob.Status = "Running"
- theJob.LastRunResult = "Working"
- theJob.LastRunTime = DateTime.Now
- e.Job = theJob
- '广播事件
- BroadcastEvent(e)
- '
- Console.WriteLine("任务" + theJob.Name + "正在执行")
- '耗时
- System.Threading.Thread.Sleep(1000 * 10)
- e = New JobCallbackEventArg()
- theJob.Status = "Finished"
- theJob.LastRunTime = DateTime.Now
- theJob.LastRunResult = "Done!"
- e.Job = theJob
- '广播事件
- BroadcastEvent(e)
- Catch ex As Exception
- '将 job状态修改 为
- theJob.Status = "Sleeping"
- theJob.LastRunResult = "Fail"
- End Try
- End Sub
- Public Function GetJobs() As System.Collections.Generic.List(Of job) Implements IService.GetJobs
- 'System.Threading.Thread.Sleep(1000 * 5) '为何要如此设置?耗时吗?还是为了 错开 同步 问题
- Console.WriteLine("GetJobs")
- Return m_jobs
- End Function
- End Class
- '回调用 参数 类别
- Public Class JobCallbackEventArg
- Inherits EventArgs
- Private m_CallbackJob As job
- Private m_ChangeReason As String
- Public Property Job As job
- Get
- Return m_CallbackJob
- End Get
- Set(ByVal value As job)
- m_CallbackJob = value
- End Set
- End Property
- Public Property ChangeReason As String
- Get
- Return m_ChangeReason
- End Get
- Set(ByVal value As String)
- m_ChangeReason = value
- End Set
- End Property
- End Class
- Imports System.Runtime.Serialization
- Imports System.ServiceModel
- Module Module1
- Sub Main()
- Using host As ServiceHost = New ServiceHost(GetType(WcfJobService.JobServerImplement))
- host.Open()
- Console.WriteLine("Service Started!")
- Console.WriteLine("Press any key to Exit!")
- Console.ReadKey()
- host.Close()
- End Using
- End Sub
- End Module
- <?xml version="1.0" encoding="utf-8" ?>
- <configuration>
- <system.diagnostics>
- <sources>
- <!-- 本节定义 My.Application.Log 的登录配置-->
- <source name="DefaultSource" switchName="DefaultSwitch">
- <listeners>
- <add name="FileLog"/>
- <!-- 取消注释以下一节可写入应用程序事件日志-->
- <!--<add name="EventLog"/>-->
- </listeners>
- </source>
- </sources>
- <switches>
- <add name="DefaultSwitch" value="Information" />
- </switches>
- <sharedListeners>
- <add name="FileLog"
- type="Microsoft.VisualBasic.Logging.FileLogTraceListener, Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"
- initializeData="FileLogWriter"/>
- <!-- 取消注释以下一节并用应用程序名替换 APPLICATION_NAME 可写入应用程序事件日志-->
- <!--<add name="EventLog" type="System.Diagnostics.EventLogTraceListener" initializeData="APPLICATION_NAME"/> -->
- </sharedListeners>
- </system.diagnostics>
- <system.serviceModel>
- <services>
- <service name="WcfJobService.JobServerImplement" behaviorConfiguration ="MEX">
- <host>
- <baseAddresses>
- <!--<add baseAddress ="net.tcp://localhost:8322/"/>-->
- <add baseAddress ="http://localhost:8300/"/>
- </baseAddresses>
- </host>
- <!--<endpoint address="Event" binding="netTcpBinding"
- bindingConfiguration="DuplexBinding" contract="WcfJobService.IService" />-->
- <endpoint address="Event" binding="wsDualHttpBinding"
- bindingConfiguration="DuplexWsHttpBinding" contract="WcfJobService.IService" />
- <!--<endpoint address ="MEX" binding ="mexTcpBinding" contract ="IMetadataExchange" />-->
- <endpoint address ="http://localhost:8080/MEX" binding ="mexHttpBinding" contract ="IMetadataExchange" />
- </service>
- </services>
- <bindings>
- <wsDualHttpBinding>
- <binding name="DuplexWsHttpBinding" sendTimeout ="00:00:05"></binding>
- </wsDualHttpBinding>
- <netTcpBinding>
- <binding name ="DuplexBinding" sendTimeout ="00:00:03" ></binding>
- </netTcpBinding>
- <wsHttpBinding>
- <binding name ="wsHttpBindingCallBack" sendTimeout ="00:00:05"></binding>
- </wsHttpBinding>
- </bindings>
- <behaviors >
- <serviceBehaviors >
- <behavior name="MEX">
- <serviceMetadata />
- </behavior>
- </serviceBehaviors>
- </behaviors>
- </system.serviceModel>
- </configuration>
6)客户端
配置文件app.config
- <?xml version="1.0" encoding="utf-8" ?>
- <configuration>
- <startup>
- <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0,Profile=Client" />
- </startup>
- <system.serviceModel>
- <bindings>
- <netTcpBinding>
- <binding name="NetTcpBinding_IService" closeTimeout="00:01:00"
- openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
- transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions"
- hostNameComparisonMode="StrongWildcard" listenBacklog="10"
- maxBufferPoolSize="524288" maxBufferSize="65536" maxConnections="10"
- maxReceivedMessageSize="65536">
- <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
- maxBytesPerRead="4096" maxNameTableCharCount="16384" />
- <reliableSession ordered="true" inactivityTimeout="00:10:00"
- enabled="false" />
- <security mode="Transport">
- <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
- <message clientCredentialType="Windows" />
- </security>
- </binding>
- </netTcpBinding>
- <wsDualHttpBinding>
- <binding name="wsDualHttpBinding_IService" closeTimeout="00:01:00"
- openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
- transactionFlow="false" hostNameComparisonMode="StrongWildcard"
- maxBufferPoolSize="524288" maxReceivedMessageSize="65536">
- <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
- maxBytesPerRead="4096" maxNameTableCharCount="16384" />
- </binding>
- </wsDualHttpBinding>
- </bindings>
- <client>
- <!--<endpoint address="net.tcp://localhost:8322/Event" binding="netTcpBinding"
- bindingConfiguration="NetTcpBinding_IService" contract="JobServiceClient.IService"
- name="NetTcpBinding_IService">-->
- <endpoint address="http://localhost:8300/Event" binding="wsDualHttpBinding"
- bindingConfiguration="wsDualHttpBinding_IService" contract="JobServiceClient.IService"
- name="wsDualHttpBinding_IService">
- <!--<identity>
- <userPrincipalName value="COMPUTER\Administrator" />
- </identity>-->
- </endpoint>
- </client>
- </system.serviceModel>
- </configuration>
客户端代码(Form) 说明:由于使用了Form作为客户端,就涉及到一个工作线程的问题,因为多线程中使用工作线程是最简单的,也是最便捷的。
- Imports System.Runtime.Serialization
- Imports System.ServiceModel
- Public Class Form1
- Implements JobServiceClient.IServiceCallback
- Private client As JobServiceClient.ServiceClient
- Private jobList As List(Of JobServiceClient.job) = New List(Of JobServiceClient.job)
- Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
- Dim jobName As String = InputBox("输入新 JOB 名称!")
- If jobName = "" Then
- MessageBox.Show("JOB 名称必须输入!")
- Exit Sub
- End If
- Dim newJob As JobServiceClient.job = New JobServiceClient.job()
- newJob.Name = jobName
- newJob.LastRunResult = "Sleeping"
- newJob.LastRunTime = DateTime.Now
- newJob.Status = "Sleeping"
- client.AddJob(newJob)
- End Sub
- Public Sub OnAdd(ByVal newJob As JobServiceClient.job) Implements JobServiceClient.IServiceCallback.OnAdd
- Dim tmpitem As ListViewItem = CreateViewListItem(newJob)
- Me.ListView1.Items.Add(tmpitem)
- End Sub
- Public Sub StatusChanged(ByVal job As JobServiceClient.job) Implements JobServiceClient.IServiceCallback.StatusChanged
- '遍历 列表 项
- For Each tmpItem As ListViewItem In Me.ListView1.Items
- If tmpItem.Text = job.Name Then
- '修改 JOB 状态
- tmpItem.SubItems(1).Text = job.Status
- tmpItem.SubItems(2).Text = job.LastRunTime.ToString()
- tmpItem.SubItems(3).Text = job.LastRunResult
- '判断 JOB状态
- If job.LastRunResult = "Fail" Then
- tmpItem.ImageIndex = 2
- ElseIf job.Status = "Running" Then ' job.LastRunResult = "Done" Then
- tmpItem.ImageIndex = 0
- ElseIf job.Status = "Sleeping" Then
- tmpItem.ImageIndex = 1
- Else
- tmpItem.ImageIndex = 1 'other
- End If
- End If
- Next
- End Sub
- Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
- '初始化 远程
- Try
- '取得自身 上下文实例
- Dim instanceMe As InstanceContext = New InstanceContext(Me)
- client = New JobServiceClient.ServiceClient(instanceMe)
- '订阅事务
- client.Accept()
- '加载Job列表
- LoadJobs()
- Catch ex As Exception
- MessageBox.Show(ex.Message)
- End Try
- End Sub
- Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
- '后台进程 工作
- '取得 JOB 列表
- Try
- jobList = client.GetJobs()
- If jobList Is Nothing Then
- Exit Sub
- End If
- '判断 当前线程工作状态
- If Me.ListView1.InvokeRequired Then
- '异步
- ListView1.Invoke(New MethodInvoker(AddressOf Me.UpdateViewList))
- Else
- Me.UpdateViewList()
- End If
- Catch ex As Exception
- MessageBox.Show(ex.Message)
- End Try
- End Sub
- Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
- '后台进程 工作完成
- Me.TextBox1.Text = "获取列表完毕!"
- End Sub
- Private Sub LoadJobs()
- '载入列表
- '启动后台线程
- Me.TextBox1.Text = "正在获取JOB列表"
- Me.BackgroundWorker1.RunWorkerAsync()
- End Sub
- Private Sub UpdateViewList()
- '重新载入列表
- Me.ListView1.Items.Clear()
- If jobList.Count < 1 Then
- Me.TextBox1.Text = "JOB列表为空。请先添加JOB。"
- Exit Sub
- End If
- For Each tmpJob As JobServiceClient.job In jobList
- Dim tmpItem As ListViewItem = CreateViewListItem(tmpJob)
- Me.ListView1.Items.Add(tmpItem)
- Next
- End Sub
- Private Function CreateViewListItem(ByVal theJob As JobServiceClient.job) As ListViewItem
- '通过 job 创建一个新的 列表项
- Dim itemStr() As String = {theJob.Name, theJob.Status, theJob.LastRunTime.ToString(), theJob.LastRunResult}
- Dim item As ListViewItem = New ListViewItem(itemStr)
- If theJob.LastRunResult = "Fail" Then '"Sleeping" Then
- item.ImageIndex = 2 'Sleeping
- ElseIf theJob.Status = "Running" Then
- item.ImageIndex = 0 'Done
- 'ElseIf theJob.Status = "Sleeping" Then
- ' item.ImageIndex = 3 'Delete
- Else
- item.ImageIndex = 1 'Fail
- End If
- item.Tag = theJob
- Return item
- End Function
- Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
- 'LoadJobs()
- 'Do Job
- '取得列表中被选择的行
- 'If Me.ListView1.SelectedIndices
- For Each iIndex As Integer In Me.ListView1.SelectedIndices
- client.DoJob(Me.ListView1.Items(iIndex).Text)
- 'MessageBox.Show(Me.ListView1.Items(iIndex).Text)
- Next
- End Sub
- Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
- 'delete job
- End Sub
- Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
- 'Exit
- End
- End Sub
- End Class
QQ不让传附件,要钱的,马化腾不当官真是可惜。全部代码可到以下地址下载:http://url.cn/GLTdxm
四、总结 要想在服务器端进行事件广播,其实最关键的是,将事件列表保存起来,然后在事件发生时,遍历就好了。然后去通知每一个客户端。在网上的文章 中之所以要用到会话,而且要保持会话状态 就是因只有这样才能在事件异步调用过程中找到自身的客户端回调通道。 而经过验证得之,实际上,只要我们把客户端的回调通道保存在一个共享数组中,这样,我们只要在发生事件时遍历这个数组就可以通知到每个订阅者。所以,我们只需要项目中服务和客户端可以进行回调就可以,这样我们可以使用wsDualHttpBinding进行设计 。而且也可以不用委托或者事件来进行。我们只要把事件状态回调就可以了。而这个回调本身是单向的。也不用异步处理。只要处理好服务器端的并发就可以了。 所以就有了下面的结构:
服务器端进行远程订阅:将订阅的事件和客户端回调通道保存到列表中。 服务器端发生要通知的事件:遍历两个数组进行回调通知。
当然,这个还不很完善,比如,在客户端关闭后,服务器端在回调时会发生异常。如果有大量的客户端关闭或网络异常,会导致服务器端阻滞,当然可以使用多线程 来解决这个问题。
下面是不用事件的服务契约和服务实现(部分代码,完整代码见http://url.cn/GLTdxm,这里的代码就是没有使用委托,也没有使用事件进行的广播。)
(图片丢失)
代码和附件说明:所见的都是最后版本,也就是没有用会话,没有用事件,也没有用委托来实现事件广播的。
注:代码部分因为是360网盘,不知为何已不能使用了。有空传一份到这里。
VB.net Wcf事件广播(订阅、发布)的更多相关文章
- 领域驱动模型DDD(二)——领域事件的订阅/发布实践
前言 凭良心来说,<微服务架构设计模式>此书什么都好,就是选用的业务过于庞大而导致代码连贯性太差,我作为读者来说对于其中采用的自研框架看起来味同嚼蜡,需要花费的学习成本实在是过于庞大,不仅 ...
- WCF Publisher/Subscriber 订阅-发布模式
本博后续将陆续整理这些年做的一些预研demo,及一些前沿技术的研究,与大家共研技术,共同进步. 关于发布订阅有很多种实现方式,下面主要介绍WCF中的发布订阅,主要参考书籍<Programming ...
- c# 事件的订阅发布Demo
delegate void del(); class MyClass1 { public event del eventcount;//创建事件并发布 public void Count() { ; ...
- AngularJS 事件广播与接收 $broadcast,$emit,$on 作用域间通信 封装factory服务 发布订阅
不同作用域之间通过组合使用$broadcast,$emit,$on的事件广播机制来进行通信. 一.说明 1.广播 $broadcast 说明:将事件从父级作用域传播至本作用域及子级作用域. 格式:$b ...
- 【JavaScript】让事件支持先发布后订阅
之前写过一个的事件管理器,就是普通的先订阅后发布模式.但实际场景中我们需要做到后订阅的也能收到发布的消息.比如我们关注微信公众号,还是能看到历史消息的.类似于qq离线消息,我先发给你,你登录了就能收到 ...
- 【C#】Event事件的订阅和发布
学习笔记:学习了委托和事件的用法.事件是一种特殊的委托,记录下事件从订阅到发布的流程. 学习资料源于视频:http://www.maiziedu.com/course/510-6891/ 新建一个De ...
- C#中的事件的订阅与发布
认识发布者/订阅者模式 发布者定义一系列事件,并提供一个注册方法: 订阅者向发布者注册自己的事件处理逻辑,供一个可被回调的方法,也就是事件处理程序:当发布者的事件被触发的时候,订阅者将通过回调函数得到 ...
- Publisher/Subscriber 订阅-发布模式
Publisher/Subscriber 订阅-发布模式 本博后续将陆续整理这些年做的一些预研demo,及一些前沿技术的研究,与大家共研技术,共同进步. 关于发布订阅有很多种实现方式,下面主要介绍WC ...
- 基于Http协议订阅发布系统设计
基于Http协议订阅发布系统设计 --物联网系统架构设计 1,订阅发布(subscriber-publisher) 订阅发布模式最典型的应用场景就是消息系统的设计.在消息系统的架构中 ...
随机推荐
- 解决:eclipse删除工程会弹出一个对话框提示“[project_name]”contains resources that are not in sync with"[workspace_name...\xx\..xx\..\xx]"
提示“[project_name]”contains resources that are not in sync with"[workspace_name...\xx\..xx\..\xx ...
- 【原】Github系列之二:开源 一行代码实现多形式多动画的推送小红点WZLBadge(iOS)
更新日志 V1.2 2015.09.25 1.UITabBarItem badge is supproted; 2.Enable change badge properties when badge ...
- Django model.py表单的默认值 默认允许为空
Field.null 默认是null=False,这时不能传入NULL型数据,但是可以是空字符. 如果BooleanField中想要null数据类型,可以选择NullBooleanField Fi ...
- php设计模式 装饰器模式
装饰器模式,可以动态地添加修改类的功能. 一个类提供了一项功能,如果要修改并添加额外的功能,传统的编程模式需要写一个子类继承它,并重新实现类的方法.使用装饰器模式,仅需要在运行时添加一个装饰器对象即可 ...
- Java javassist动态代理
package org.windwant.spring.core.proxy; import javassist.ClassPool; import javassist.CtClass; import ...
- 封装RabbitMQ.NET Library 的一点经验总结
这篇文章内容会很短,主要是想给大家分享下我最近在做一个简单的rabbitmq客户端类库的封装的经验总结,说是简单其实一点都不简单.为了节省时间我主要按照Library的执行顺序来介绍,在你看来这里仅仅 ...
- SQL Server自动化运维系列——批量执行SQL脚本(Power Shell)
需求描述 一般在生产环境中,在投产的情况下,需要批量的来执行SQL脚本文件,来完成整个投产,如果投产文件比较多的情况下,无疑这是一个比较痛苦的过程,所以本篇通过PowerShell脚本来批量完成. 监 ...
- SQL Server 2008 R2——使用数字辅助表(master..spt_values)实现用计数字段对记录进行重复显示
=================================版权声明================================= 版权声明:原创文章 谢绝转载 请通过右侧公告中的“联系邮 ...
- 解惑spring嵌套事物
工作中一直对spring中的事物管理都是最简单的配置 但是spring中的事物传播性配置 还有很多种,有时候经常疑惑service调用service的问题,今天的论坛上看到一篇写的非常详细的文字.记录 ...
- SSL handshake alert: unrecognized_name error since upgrade to Java 1.7
今天将jdk从1.6升级到1.7,但是HttpUrlConnection连接https出现问题了. javax.net.ssl.SSLProtocolException: handshake aler ...