none
急求双工回调的问题?? RRS feed

  • 问题

  • 工作流程:
    1.客户端发起gettask请求。
    2.服务端从数据库中得到任务后调用回调sendtask把任务送到客户端。
    问题:
    正常情况下该流程没有问题,但如果我把服务端关掉后再打开(网线拔了再插上也一样),第1步调用没问题,第2步总是报如下错“http://schemas.microsoft.com/2005/12/ServiceModel/Addressing/Anonymous 的请求操作在配置的超时(00:02:00)内未收到回复。分配给该操作的时间可能是更长超时的一部分。这可能由于服务仍在处理操作或服务无法发送回复消息。请考虑增加操作超时(将通道/代理转换为 IContextChannel 并设置 OperationTimeout 属性)并确保服务能够连接到客户端。”---感觉回调被阻塞一样。

    服务端相关代码:
        [ServiceContract(CallbackContract = typeof(ITaskCallback), SessionMode = SessionMode.Required)]
        public interface ITasksService
        {

            [OperationContract(IsOneWay = true)]
            void getTask(Task task);
     
        }
        //回调
        public interface ITaskCallback
        {
            [OperationContract]
            bool SendTask(Task task);
        }

       [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
        // NOTE: If you change the class name "TasksService" here, you must also update the reference to "TasksService" in App.config.
        public class TasksService : ITasksService
        {
            private string IP = "";
            public void getTask(Task task)
            {
                Business.addLog(">>getTask", task.OperID, 2);

                Business bs = new Business();
                task.TaskXml = "";
                string taskXML = bs.getTaskXML(ref task);
                if (taskXML.Length > 0)
                {
                    bs.saveToRECOperWorkInfo(task.OperID, task.ID, task.SkillID, task.BatchID);
                }
                Business.addLog("<<getTask", task.OperID, 2);

                try
                {
                    if (callback.SendTask(task))
                        Business.addLog("发送任务成功" + task.ID, task.OperID, 1);
                    else
                    {
                        if (taskXML.Length > 0)
                            workMoveBack(task.OperID, task.WorkID, task.BatchID, 'M');
                    }
                }
                catch (System.Exception e)
                {
                    Business.addLog("发送任务失败:" + task.ID + e.Message, task.OperID, 1);
                    if (taskXML.Length > 0)
                        workMoveBack(task.OperID, task.WorkID, task.BatchID, 'M');
                }
            }


            #region WCF回调接口:
            ITaskCallback callback
            {
                get { return OperationContext.Current.GetCallbackChannel<ITaskCallback>(); }
            }
            #endregion
        }

    客户端回调代码:

        public class CallbackHandler : ITasksServiceCallback
        {
            public bool SendTask(InputTest.Test.Task task)
            {
                try
                {

                    if (Form1.nBaseID != task.BaseID)
                    {
                        Business.addLog("BaseID不相等:" + task.ID + "  Form1.nBaseID=" + Form1.nBaseID.ToString() + " task.BaseID=" + task.BaseID.ToString(),1,0);
                        return false;
                    }
                    else
                    {
                        Form1.m_NewTask = task;
                        Business.addLog("引擎送来任务:" + task.ID + "  nBaseID=" + task.BaseID.ToString(), 1, 0);
                        Form1.getTaskEvent.Set();
                        return true;
                    }
     
                }
                catch (Exception e)
                {
                    Business.addLog("回调异常:" + e.Message, 1, 0);
                    return false;
                }

             
            }
        } 







    2009年9月14日 1:34

答案

  • 你好,
    请加上:

    [

    CallbackBehavior(UseSynchronizationContext = false)]

     

    public class CallbackHandler : ServiceReference.ITasksServiceCallback


    Please remember to mark the replies as answers if they help and unmark them if they provide no help. Welcome to the All-In-One Code Framework http://cfx.codeplex.com/! If you have any feedback, please tell us.
    • 已标记为答案 chenzhit 2009年9月24日 7:55
    2009年9月24日 3:45
    版主

全部回复

  • 神啊,怎么搞的啊!!!
    2009年9月14日 6:35
  • 你可以看看之前的一个讨论,http://social.msdn.microsoft.com/Forums/en-US/wcfzhchs/thread/a686462f-a342-4999-a881-31d3db2ad7f2
    当时讨论到回调的问题。
    有可能出现死锁。然后超时异常。



    另外你可以试验把这个00:02:00,修改为00:10:00,看看,我怀疑你是超时时间设置2分钟,太短了。
    你的拔插网线很可能花费了2分多钟。


    Frank Xu Lei--谦卑若愚,好学若饥
    专注于.NET平台下分布式应用系统开发和企业应用系统集成
    Focus on Distributed Applications Development and EAI based on .NET
    欢迎访问老徐的中文技术博客:Welcome to My Chinese Technical Blog
    欢迎访问微软WCF中文技术论坛:Welcome to Microsoft Chinese WCF Forum
    欢迎访问微软WCF英文技术论坛:Welcome to Microsoft English WCF Forum
    2009年9月14日 10:06
    版主
  • 由于业务和其他方面的考虑所以超时时间不能设置得太长,如果等待的时间太长任务会卡在系统中无法处理而超时。
    网上很多关于解决阻塞问题的方法都试过,貌似没用。刚看到一个资料好像有点像,就是错误契约。
    http://book.csdn.net/bookfiles/628/10062820200.shtml
    “如果客户端回调抛出的异常属于回调错误契约列出的异常,或者回调抛出一个FaultException异常,那么异常并不会导致回调通道发生错误,我们能够捕获异常,继续使用回调通道。然而,如果其中一个异常不属于错误契约的一部分,那么在抛出该异常之后,服务调用应会避免使用回调通道。”
    我现在怀疑是不是因为断网或服务端关闭过导致了一个不属于错误契约内的异常,在再次开启服务端捕获该异常,但它却导致通道出现错误,因此服务无法重用它,所以后续的回调都不可用。
    2009年9月15日 1:48
  • 你好,

    请问你是在什么时候关闭服务器端程序的?是在 1.客户端发起gettask请求 这之后 2.服务端从数据库中得到任务后调用回调sendtask把任务送到客户端 这之前吗? 异常是在服务器端被抛出还是在客户端抛出? 如下设置是否产生一样的结果?

    [OperationContract(IsOneWay=true)]
    bool SendTask(Task task);

    Please remember to mark the replies as answers if they help and unmark them if they provide no help. Welcome to the All-In-One Code Framework http://cfx.codeplex.com/! If you have any feedback, please tell us.
    2009年9月15日 3:15
    版主
  • 是的!

    2009年9月15日 6:20
  • 我现在的操作需要SendTask(Task task)返回bool值,所以也不能设置成[OperationContract(IsOneWay=true)]。
    2009年9月15日 6:29
  • 我在网上下了一个这方面的Demo代码,他们说这个问题解决了,但我在代码中将IsOneWay设置成true后问题好像还是在,任然没解决!
    2009年9月15日 6:32
  • 你好,
    能够上传一个重现问题的项目到http://skydrive.live.com/吗?请在这里帖下下载链接.我测试一下看看.
    Please remember to mark the replies as answers if they help and unmark them if they provide no help. Welcome to the All-In-One Code Framework http://cfx.codeplex.com/! If you have any feedback, please tell us.
    2009年9月16日 2:41
    版主
  • 那谢谢了!
    http://cid-45cad4abc91d67f0.skydrive.live.com/self.aspx/.Public/Artech.ThreadAffinity.rar
    这个是我在网上下的,它的现象和我的有点像,在客户端的窗体上“+”两边编辑框随便填个数,然后点“=”按钮即可。在接口连续调用了几次之后就一直连续超时。那麻烦帮我看看是哪出了问题?

    2009年9月18日 9:34
  • 你好,

    服务器端的maxConcurrentSessions默认值为10,只允许同时有10个会话.所以在回调完成后应该关闭会话.可以用下面的方法设置:



    public
    interface ICalculateCallback

    {

    [

    OperationContract(IsOneWay = true,IsTerminating = true)]

     

    void DisplayResult(double result);

    }

    [

    ServiceContract(CallbackContract = typeof(ICalculateCallback), SessionMode = SessionMode.Required)]

     

    public interface ICalculate

    {

    [

    OperationContract(IsOneWay=true)]

     

    void Add(double op1, double op2);

    }


    Please remember to mark the replies as answers if they help and unmark them if they provide no help. Welcome to the All-In-One Code Framework http://cfx.codeplex.com/! If you have any feedback, please tell us.
    2009年9月21日 11:22
    版主
  • 谢谢楼上版主的答复,我试过了,可能我的问题和我网上下的那个不是同一个问题,那个Demo通过你的方法问题是解决了。但我的可能不是这个问题,因为我的maxConcurrentSessions设置是1000。我的工程太大了,而且涉及很多模块,所以不好完整的贴上了,我再看看能不能精简些做个能重现问题的Demo,到时候能麻烦你再帮我看下吗?真的十分感谢!

    2009年9月22日 2:15
  • 好的,没有问题.
    Please remember to mark the replies as answers if they help and unmark them if they provide no help. Welcome to the All-In-One Code Framework http://cfx.codeplex.com/! If you have any feedback, please tell us.
    2009年9月22日 2:26
    版主

  • http://cid-45cad4abc91d67f0.skydrive.live.com/self.aspx/.Public/%e5%8f%8c%e5%b7%a5%e5%9b%9e%e8%b0%83%e9%97%ae%e9%a2%98Demo%e7%a8%8b%e5%ba%8f.rar
    我把我的程序改了一下,上面是下载连接,客户端程序可以随便用一个例如“5001”密码“000”的用户名和密码登陆,在Form1里有一个取任务线程getTaskThread(),在这个线程里调用取任务接口。当服务端开启,客户端程序登录,线程启动会取到一个模拟任务,在客户端界面上会显示取到的任务,按F1能提交任务,任务提交后会接着取下一任务。这样正常操作没有问题,然后在服务端的getTask接口实现函数的callback.SendTask(task)处定个调试断点,当程序走到此断点处时将服务程序关闭,等待1分钟左右重新将服务端开启,此时服务端回调接口会一直报超时异常。麻烦你再帮我看看,谢谢!有什么问题可以发邮件www.chenz@Sunyard.com和我核实。
    2009年9月22日 9:04
  • 上面上传的服务端有点问题,启动不了。需在 program.cs中的mian函数中将 if (UpdateMonitorThread.ToLower() == "true")中的代码全半注销掉即可。
               
    2009年9月22日 9:37
  • 你好,

    你的代码比较复杂,能否传一个简单的能够重现问题的项目?
    Please remember to mark the replies as answers if they help and unmark them if they provide no help. Welcome to the All-In-One Code Framework http://cfx.codeplex.com/! If you have any feedback, please tell us.
    2009年9月22日 10:21
    版主
  • 那我再删删。
    2009年9月22日 10:35
  • http://cid-45cad4abc91d67f0.skydrive.live.com/self.aspx/.Public/Demo.rar
    我稍微将代码整理了一下,我的客户端是做了一个解析XML以及根据它创建控件并显示的操作,这部分操作如果去掉好象就无法重现问题了,客户端中一些无关的代码我都收起来了,你只用关注Form1.cs中的GetTaskThread()和CallbakHander,具体说明我在代码里注释了。服务端能去的我都去掉了。我自己之前也自己另外写了一个简单的Demo程序,可就是怎么也试不出来,所以也只能将我原先的项目程序去掉一些东西后试了。很是郁闷啊!真的要谢谢你了。
    2009年9月22日 12:08
  • 哪个有时间帮我看看啊,谢了!
    2009年9月23日 3:49
  • 我一步一步跟,当我把我代码中动态创建桌面控件(Label和TextBox)的那几行代码注释掉问题就没有了,加上就又有了。应该是阻塞问题了,请问这种问题怎么解决?
    2009年9月23日 7:12
  • http://cid-45cad4abc91d67f0.skydrive.live.com/self.aspx/.Public/Demo.rar
    我根据跟踪到得问题重新写了一个Demo,客户端只用点击“确定”即可开启取任务线程,待正常取了几个任务后,关闭服务端程序。过20秒左右再开启服务端就无法正常通信,报超时异常。麻烦各位版主帮我看看。谢了!
    2009年9月23日 7:34
  • 在上面的Demo里我把创建控件的操作另起一个线程去做也不会阻塞,但我的项目如果将创建控件这部分不太方便放到别的线程做,所以想问下各位是否有其他解决方法?
    2009年9月23日 7:53
  • 你好,
    请加上:

    [

    CallbackBehavior(UseSynchronizationContext = false)]

     

    public class CallbackHandler : ServiceReference.ITasksServiceCallback


    Please remember to mark the replies as answers if they help and unmark them if they provide no help. Welcome to the All-In-One Code Framework http://cfx.codeplex.com/! If you have any feedback, please tell us.
    • 已标记为答案 chenzhit 2009年9月24日 7:55
    2009年9月24日 3:45
    版主
  • 谢谢Allen Chen - MSFT,真是这个问题。我很晕啊,我这个方法以前早就试过了,不知道问什么当时试过了感觉没用。我同事当时也试过,当时他在回调中加了SynchronizationContext.Post()这个方法,貌似没用,所以也没再试。以前一直对WCF不是很熟悉,通过这次的问题也学习了不少东西,感谢各位的解答。
    2009年9月24日 8:03