none
客户端调用服务端方法服务端进行回调会出错,请指教!? RRS feed

  • 问题

  • 做了一个类似聊天室的程序
    客户端调用服务端的Join方法,在服务端的Join方法中如果加入成功
    后,直接向每个客户端回调并在客户端的form中显示某某用户已经加入
    回调的时候也包含这个加入的客户!
    但是如果服务端回调的话,客户端会等待一段时间后抛出通道状态'出错'
    现在我给出代码!
    所有的Config配置正确!(部分配置是使用代码写的少数配置写入config)
    服务端契约!(部分代码)
    <ServiceContract(CallbackContract:=GetType(IServiceCallBack), SessionMode:=SessionMode.Required)> _
    Public Interface IService1
        <OperationContract()> _
        Sub SendToCods(ByVal sId As String, ByVal smessage As String)
        <OperationContract(IsOneWay:=False, IsTerminating:=False)> _
        Sub Join(ByVal MeInfo As ClientInfoBase)
    End Interface
    回调契约!
    Public Interface IServiceCallBack
        <OperationContract(IsOneWay:=True)> _
            Sub ReceiveMessage(ByVal sId As String, ByVal sMessage As String)
    End Interface
    服务端代码:
    Public Sub Join(ByVal MeInfo As ClientInfoBase) Implements WcfClientClass.IService1.Join
            Try
                Dim oCb As IServiceCallBack = OperationContext.Current.GetCallbackChannel(Of IServiceCallBack)()
                Dim propertys As MessageProperties = OperationContext.Current.IncomingMessageProperties
                Dim EndPoint As RemoteEndpointMessageProperty
                If UCase(propertys.Via.Scheme) = UCase("NET.PIPE") Then
                    EndPoint = New RemoteEndpointMessageProperty("LocalHost", 0)
                Else
                    EndPoint = propertys(RemoteEndpointMessageProperty.Name)
                End If
                SendToCods("【系统通知】", MeInfo.UserId + " " + MeInfo.UserName + "已经登陆成功!!【" + EndPoint.Address.ToString + " " + EndPoint.Port.ToString + " 】")
                RaiseEvent ItemAdded()
            Catch ex As Exception
                Throw New ApplicationException(ex.Message, ex)
            Finally
            End Try
    End Sub
    客户单调用:(客户端已经引用接口)
            If textUser.Text = "" Then
                GetMsg.ShowMsg("必须输入登陆名称!!")
                textUser.Focus()
                Exit Sub
            End If
            If testId.Text = "" Then
                GetMsg.ShowMsg("必须输入登陆ID!!")
                testId.Focus()
                Exit Sub
            End If
            Try
                meInfo.JoinMode = ClientInfoBase.ClientMode.Pos
                meInfo.UserId = testId.Text
                meInfo.UserName = textUser.Text
                context = New InstanceContext(Me)
                myCods = New WcfCodService.Service1Client(context)
                If myCods.Endpoint.Binding.GetType Is GetType(WSDualHttpBinding) Then
                    Dim ws As WSDualHttpBinding = myCods.Endpoint.Binding
                    ws.ClientBaseAddress = New Uri("http://192.168.1.188:8524/" + Guid.NewGuid().ToString())
                    ws.Security.Mode = WSDualHttpSecurityMode.None
                ElseIf myCods.Endpoint.Binding.GetType Is GetType(NetTcpBinding) Then
                    Dim ws As NetTcpBinding = myCods.Endpoint.Binding
                    ws.Security.Mode = SecurityMode.None
                    ' myCods.Endpoint.Address = New System.ServiceModel.EndpointAddress("net.tcp://192.168.1.188:8525/WcfCods")
                ElseIf myCods.Endpoint.Binding.GetType Is GetType(NetNamedPipeBinding) Then
                    Dim ws As NetNamedPipeBinding = myCods.Endpoint.Binding
                    ws.Security.Mode = NetNamedPipeSecurityMode.None
                End If
                myCods.Join(meInfo)
                SetEnable(True)
            Catch ex As Exception
                GetMsg.ShowErr(ex)
            End Try

    当客户端调用服务端时 Mycode.Join(meInfo) 服务端回调客户端
    Public Sub ReceiveMessage(ByVal sId As String, ByVal sMessage As String) Implements WcfCodService.IService1Callback.ReceiveMessage
            '显示在窗体上有XXX登陆进来
            ShowXx(sId, sMessage)
    End Sub
    可是在Mycode.Join(meInfo)总是过好久就出错!但客户端ReceiveMessage会成功!
    为什么,是不是服务端的           
    SendToCods("【系统通知】", MeInfo.UserId + " " + MeInfo.UserName + "已经登陆成功!!【" + EndPoint.Address.ToString + " " + EndPoint.Port.ToString + " 】")
    给这个正在登陆的客户端回调使用的通道出问题?

     

    2010年5月13日 2:41

答案

  • 貌似回调的问题很多,都是多个客户端的时候出现客户端卡死的现象。

    http://social.microsoft.com/Forums/zh-CN/wcfzhchs/thread/c930e56c-0c32-4bc4-8503-b4775c135184

    这里也有一个。UseSynchronizationContext=false,


    Frank Xu Lei--谦卑若愚,好学若饥
    专注于.NET平台下分布式应用系统开发和企业应用系统集成
    Focus on Distributed Applications Development and EAI based on .NET
    欢迎访问老徐的博客:Welcome to My Technical Blog
    欢迎访问老徐的网站:Welcome to My Website
    欢迎访问微软WCF中文技术论坛:Welcome to Microsoft Chinese WCF Forum
    欢迎访问微软WCF英文技术论坛:Welcome to Microsoft English WCF Forum
    • 已标记为答案 HaHaWcf 2010年5月19日 4:49
    2010年5月13日 15:29
    版主
  • IsInitiating = true 设置 join 方法;

    IsTerminating = true 设置 leave 方法;

    都设置为 IsOneWay.

    同时设置 PerSession (每会话一个实例)

     

    • 已标记为答案 HaHaWcf 2010年5月19日 4:49
    2010年5月16日 3:34
  • join 一次,同时该方法返回 ("【系统通知】", MeInfo.UserId + " " + MeInfo.UserName + "已经登陆成功!!【" + EndPoint.Address.ToString + " " + EndPoint.Port.ToString + " 】") 信息。

    并且你需要自己编码保存 IServiceCallback ,比如简单的使用

    private static readonly List<IServiceCallBack> clientCallbacks = new List<IServiceCallBack>();

     

    回调是由服务端发起,比如你的某个好友  A 上线了,即 A 调用了 Join 方法,通过代码逻辑从 clientCallbacks 中找出好友是 A 的所有 IServiceCallback ,

    clientCallbacks.FindAll(O=>O.IsFriendOf("A")).ForEach(O=>O.Hello("我上线了,快联系我吧!"));

    该方法在 Join 中实现,由于不会调用 A 的 IServiceCallback,故不会产生死锁。

    关于如何实现一个通用的事件通知机制,我可以推荐你阅读 CAL 的 EventAggregator 实现代码。

     

    在会话模式下,通道断开,服务器会抛出通讯异常,通过扩展ErroHandler,捕获异常并移除断开的IServiceCallback。通常,也可以使用 ((IChannel)IServiceCallback).State 来判断通道状态,这不够完备,还需要使用 Try catch 包裹回调操作。

    • 已标记为答案 HaHaWcf 2010年5月20日 2:36
    2010年5月19日 15:17

全部回复

  • 版主啊!帮看下啊,是不是客户端在调用服务端方法的同时不能在此方法上回调客户端??
    2010年5月13日 6:46
  • 版主!!!!!!!!!!
    2010年5月13日 9:18
  • 貌似回调的问题很多,都是多个客户端的时候出现客户端卡死的现象。

    http://social.microsoft.com/Forums/zh-CN/wcfzhchs/thread/c930e56c-0c32-4bc4-8503-b4775c135184

    这里也有一个。UseSynchronizationContext=false,


    Frank Xu Lei--谦卑若愚,好学若饥
    专注于.NET平台下分布式应用系统开发和企业应用系统集成
    Focus on Distributed Applications Development and EAI based on .NET
    欢迎访问老徐的博客:Welcome to My Technical Blog
    欢迎访问老徐的网站:Welcome to My Website
    欢迎访问微软WCF中文技术论坛:Welcome to Microsoft Chinese WCF Forum
    欢迎访问微软WCF英文技术论坛:Welcome to Microsoft English WCF Forum
    • 已标记为答案 HaHaWcf 2010年5月19日 4:49
    2010年5月13日 15:29
    版主
  • 我试了你说的方法!没用的 我的服务端有一个方法 Join() 在这个方法中有回调客户端的方法 如果客户端调用了这个服务端的Join() 经过调试服务端的代码可以执行完并也调用的回调方法,而客户端就会报错通道‘出错’状态! 是不是在客户端在同一个通道调用带有回调方法的服务端代码不行啊!!?????????
    2010年5月14日 2:51
  • 另外,不存在并发情况,因为我只用了一个客户端来测试就over
    2010年5月14日 2:56
  • 版主!在你的开发过程中有没有用到客户端调用服务端方法而这个服务端方法又带有回调客户端的过程!帮忙试试看看是不是会出错啊!
    2010年5月15日 4:17
  • IsInitiating = true 设置 join 方法;

    IsTerminating = true 设置 leave 方法;

    都设置为 IsOneWay.

    同时设置 PerSession (每会话一个实例)

     

    • 已标记为答案 HaHaWcf 2010年5月19日 4:49
    2010年5月16日 3:34
  • 这个问题已解决!

    其实客户端代码和服务端代码都没错,错就错在Wcf回调机制能产生循环等待,即:客户端调用服务代码,服务端同时回调客户端,而客户端的回调又必须依赖客户端的主调用的完成而完成,而客户端的主调用又必须依赖服务端的回调的完成而完成,所以就over了。因此提醒广大开发人员,避免此类问题的产生因此注意少用这种调用

    也可以将服务端设置为:<ServiceBehavior(ConcurrencyMode:=ConcurrencyMode.Reentrant, InstanceContextMode:=InstanceContextMode.Single)> _

    客户端:设置为:<CallbackBehaviorAttribute(IncludeExceptionDetailInFaults:=True, UseSynchronizationContext:=False, ValidateMustUnderstand:=True)> _

    这样就不会造成死锁现象,但是那个调用先执行,先执行的就会报错(带回调的)!依次类推..

     

    还有就是:WCF 使用的场景(需要开发的项目或要求)并不是万能的,原本想使用WCF做个既可以服务于数据库调用.又可以监控客户端的调用操作的项目(包括发送文字通知给各个客户,如果有可能还想传出语音过去),可是经过这么长时间的了解.其实WCF在作为服务方是非常优秀的但要是让服务端和客户端项目沟通并不是很好的方案.

    就光一个KeepAlives就很麻烦.如果非要用WCF做监控客户端的项目的话,那么WCF必须提供静态的数组保存每个客户端的上下文信息,还要监控客户掉线等工作.这里面还有WCF的内部机制什么超时.会话.啦!!所以如果哪位想使用WCF做类此OICQ等的聊天工具基本上需要放弃!因为WCF的诞生就是为了服务的而不是为了双向沟通的.即使他可以双向沟通你也必须按需要来进行,不能滥用!!

    于是:我决定各取所长,使用WCF的特长作为服务用供客户端调用,使用socket完成客户端监控和聊天功能!.

    其次就是WCF的报错!

    今天在调用服务端过程时又报状态‘出错’经过一上午的调试原来是传输数据太大.真是恐怖.如何截获更加明晰的错误描述也是一个课题!也希望论坛的高手们多发些经验

    以上说明这是在现有的水平上做的言论,如有错误请指正!!

    结贴啦

     

     

    2010年5月19日 4:49
  • join 一次,同时该方法返回 ("【系统通知】", MeInfo.UserId + " " + MeInfo.UserName + "已经登陆成功!!【" + EndPoint.Address.ToString + " " + EndPoint.Port.ToString + " 】") 信息。

    并且你需要自己编码保存 IServiceCallback ,比如简单的使用

    private static readonly List<IServiceCallBack> clientCallbacks = new List<IServiceCallBack>();

     

    回调是由服务端发起,比如你的某个好友  A 上线了,即 A 调用了 Join 方法,通过代码逻辑从 clientCallbacks 中找出好友是 A 的所有 IServiceCallback ,

    clientCallbacks.FindAll(O=>O.IsFriendOf("A")).ForEach(O=>O.Hello("我上线了,快联系我吧!"));

    该方法在 Join 中实现,由于不会调用 A 的 IServiceCallback,故不会产生死锁。

    关于如何实现一个通用的事件通知机制,我可以推荐你阅读 CAL 的 EventAggregator 实现代码。

     

    在会话模式下,通道断开,服务器会抛出通讯异常,通过扩展ErroHandler,捕获异常并移除断开的IServiceCallback。通常,也可以使用 ((IChannel)IServiceCallback).State 来判断通道状态,这不够完备,还需要使用 Try catch 包裹回调操作。

    • 已标记为答案 HaHaWcf 2010年5月20日 2:36
    2010年5月19日 15:17
  • 该方法在 Join 中实现,由于不会调用 A 的 IServiceCallback,故不会产生死锁。

    双手赞成!如果回调A自然死锁!

    回头有时间挑战下WCF实现QQ功能! 看看如果挂机1个月会不会很正常!

     

    2010年5月20日 2:34