none
WCF 通道持续时间? RRS feed

  • 问题

  • 您好!我有个WCF服务在运行和数据传输都正常。但如果有十分钟没有数据传输时就出现“通信对象System.ServiceModel.Channels.ServiceChannel 无法用于通信,因为其处于“出错”状态 ”的错误。貌似WCF关闭了通讯渠道。请问怎么解决?
    2009年6月10日 3:31

答案

  • 谢谢版主的解答,我找到了http://msdn.microsoft.com/zh-cn/library/system.servicemodel.reliablesession.inactivitytimeout.aspx
    2009年6月22日 1:49

全部回复

  •     Hi,
       你检查一下:
       1)channel 的timeout属性,设置一个值;
       2)判断以下proxy.state,如果是错误,重新open();
      看看有作用~~~
       
    Frank.Xu Lei--谦卑若愚,好学若饥
    专注于.NET平台下分布式应用系统开发和企业应用系统集成
    Focus on Distributed Applications Development and EAI based on .NET
    老徐的博客:http://www.cnblogs.com/frank_xl
    2009年6月10日 4:55
    版主
  • 您好!谢谢你的解答。我的WCF服务有时需从服务端发起消息,channel得处于打开状态,第二个方法不适合这个项目。channel的timeout属性我找了相关资料,貌似没有这个属性。有OpenTimeout属性,但它是指在规定时间内打开通道的时间。
    2009年6月12日 2:52
  • 您好!谢谢你的解答。我的WCF服务有时需从服务端发起消息,channel得处于打开状态,第二个方法不适合这个项目。channel的timeout属性我找了相关资料,貌似没有这个属性。有OpenTimeout属性,但它是指在规定时间内打开通道的时间。

       hi,
       我想知道你的程序的架构。
    使用了什么绑定协议等等。
    你如何从服务端发起消息?
    你在使用回调吗?
    这个通道是客户端打开的吧?
    你要是有简单的代码贴出来。可以帮你看看,分析一下~
    Frank.Xu Lei--谦卑若愚,好学若饥
    专注于.NET平台下分布式应用系统开发和企业应用系统集成
    Focus on Distributed Applications Development and EAI based on .NET
    老徐的博客:http://www.cnblogs.com/frank_xl
    2009年6月13日 4:34
    版主
  • 既然你的有10分钟没传输数据,说明数据量很小,就应该按需打开通道,调用完毕再关闭。
    从服务器端发起消息时,服务器端的角色就变成了客户端。

    好好学习,天天向上。
    2009年6月14日 12:54
  • 您好!简化代码如下:删了很多,其中登录等待窗口和自定义验证类的代码没加进去。我用的是双工模式DuplexBinding。
    客服端
    //登录
    namespace Client
    {
        public partial class Login : Form
        {
            private PleaseWaitDialog pwDlg;
            private delegate void HandleDelegate(int popedoms);
            private delegate void HandleErrorDelegate();
            int flag = 0;
            ServiceClient proxy = new ServiceClient(new InstanceContext(new Callback()));
            string name = "";
            public static int Popedoms;
    
            public Login()
            {
                InitializeComponent();
            }
            public int Flag
            {
                get { return flag; }
                set { flag = value; }
            }
            public string Name
            {
                get { return name; }
                set { name = value;}
            }
    
            private void button1_Click(object sender, EventArgs e)
            {            
                string ipaddress = Dns.GetHostAddresses(Dns.GetHostName())[0].ToString();
                IAsyncResult iar = MDIMenu.proxy.BeginJoin(textBox_name.Text, textBox_pass.Text, ipaddress, new AsyncCallback(OnEndJoin), null);
                pwDlg = new PleaseWaitDialog();
                pwDlg.ShowDialog();            
            }
    
            private void OnEndJoin(IAsyncResult iar)
            {           
                try
                {
                    int popedoms = MDIMenu.proxy.EndJoin(iar);
                    Popedoms = popedoms;
                    HandleEndJoin(popedoms);
                }
                catch 
                {
                    HandleEndJoinError();
    
                }
            }
    
            private void HandleEndJoinError()
            {
                if (pwDlg.InvokeRequired)
                    pwDlg.Invoke(new HandleErrorDelegate(HandleEndJoinError));
                else
                {
                    pwDlg.ShowError("错误: 不能连接到服务器!");
                    ExitChatSession();
                }
            }
    
            private void HandleEndJoin(int popedoms)
            {            
                if (pwDlg.InvokeRequired)
                    pwDlg.Invoke(new HandleDelegate(HandleEndJoin), new object[] { popedoms });
                else
                {
                    if (popedoms > 0)
                    {
                        pwDlg.Close();
                        Name = textBox_name.Text;
                        flag = popedoms;
                        Dispose();
                        Close();
                    }
                    else
                    {
                        string str;
                        switch (popedoms)
                        {
                            case 0:
                                str = "错误:该用户尚未配置权限!";
                                break;
                            case -1:
                                str = "错误:用户名或密码错误!";
                                break;
                            case -2:
                                str = "错误:该用户已经登录!";
                                break;
                            default:
                                str = "错误:登录失败!";
                                break;
                        }
                        pwDlg.ShowError(str);
                        ExitChatSession();
                    }
                }
            }
    
            private void ExitChatSession()
            {
                try
                {
                    proxy.Leave();
                }
                catch { }
                finally
                {
                    AbortProxyAndUpdateUI();
                }
            }
    
            private void AbortProxyAndUpdateUI()
            {
                if (proxy != null)
                {
                    proxy.Abort();
                    proxy.Close();
                    proxy = null;
                }
                // ShowConnectMenuItem(true);
            }
    
            private void ExitApplication()
            {
                Application.Exit();
            }
    
            private void Login_Load(object sender, EventArgs e)
            {
               // button1_Click(sender, e);
            }
    
            private void Login_KeyPress(object sender, KeyPressEventArgs e)
            {
                if ((char)Keys.Enter == e.KeyChar)
                    button1_Click(sender, e);
            }
        }
    }
    
    //代理类
    namespace Client
    {
        [ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(Contract.ICallback))]
        public interface IService
        {        
            [OperationContract(AsyncPattern = true)]
            System.IAsyncResult BeginJoin(string name,string password, string ipaddress, System.AsyncCallback callback, object asyncState);
    
            int EndJoin(System.IAsyncResult result);
    
            [OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = true)]
            void Leave();    
    
        }
        
        public partial class ServiceClient : DuplexClientBase<IService>, IService
        {
            public ServiceClient(InstanceContext callbackInstance)
                : base(callbackInstance)
            { }
    
            public ServiceClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName)
            :
                base(callbackInstance, endpointConfigurationName)
            {
            }
    
            public ServiceClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName, string remoteAddress)
                :
                    base(callbackInstance, endpointConfigurationName, remoteAddress)
            {
            }
    
            public ServiceClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress)
                :
                    base(callbackInstance, endpointConfigurationName, remoteAddress)
            {
            }
    
            public ServiceClient(System.ServiceModel.InstanceContext callbackInstance, System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress)
                :
                    base(callbackInstance, binding, remoteAddress)
            {
            }
            public System.IAsyncResult BeginJoin(string name, string password, string ipaddress, System.AsyncCallback callback, object asyncState)
            {
                return base.Channel.BeginJoin(name, password, ipaddress, callback, asyncState);
            }
    
            public int EndJoin(System.IAsyncResult result)
            {
                return base.Channel.EndJoin(result);
            }
            
            public void Leave()
            {
                base.Channel.Leave();
            }
        }
    }
    
    //回调类
    namespace Client
    {
        // 注意: 如果更改此处的类名 "Callback",也必须更新 App.config 中对 "Callback" 的引用。
        [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
        public class Callback : ICallback
        {
            public delegate void NormalWarningDelegate(byte[] message);
            public static event NormalWarningDelegate NormalWarningEvent;
    
            public int ReceiveWhisper(byte[] message)
            {            
                return 1;
            }
    
            public void Receive(byte[] message)
            {
                NormalWarningEvent(message);
            }
    
            public void ReturnResult(object[] message)
            {
    
            }
    
        }
    }
    //app.config
    	<system.serviceModel>
    		<behaviors>
    			<endpointBehaviors>
    				<behavior name="Client.ClientBehavior">
    					<clientCredentials>
    						<serviceCertificate>
    							<authentication certificateValidationMode="Custom" customCertificateValidatorType="Client.CustomX509Validator,Client"/>
    						</serviceCertificate>
    					</clientCredentials>
    				</behavior>
    			</endpointBehaviors>
    		</behaviors>
    
    		<client>
    			<endpoint name="ServiceEndpoint" 
    					      address="net.tcp://localhost:8001/Host"
    					      behaviorConfiguration="Client.ClientBehavior"
    					      binding="netTcpBinding"
    					      bindingConfiguration="DuplexBinding"
    					      contract="Client.IService" >
    				<identity>
    					<dns value="MyServerCert" />
    				</identity>
    			</endpoint>
          
    		</client>
    		<bindings>
    			<netTcpBinding>
    				<binding  name="DuplexBinding" sendTimeout="00:00:30"  maxReceivedMessageSize="655360" >
    					<readerQuotas maxArrayLength="1638400"/>
    					<reliableSession enabled="true"  />
    					<security mode="Message">
    						<message clientCredentialType="UserName"/>
    						<!--transport clientCredentialType="Windows" /-->
    					</security>
    				</binding>
    			</netTcpBinding>
    		</bindings>
    	</system.serviceModel>
    
    
    

    接口
    namespace Contract
    {
        // 注意: 如果更改此处的接口名称 "IService",也必须更新 App.config 中对 "IService" 的引用。
    
        [ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(ICallback))]
        public interface IService
        {
            [OperationContract(IsOneWay = false, IsInitiating = true, IsTerminating = false)]
            int Join(string name, string password, string ipaddress );
    
            [OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = true)]
            void Leave();
        }
    }
    
    namespace Contract
    {
        // 注意: 如果更改此处的接口名称 "ICallback",也必须更新 App.config 中对 "ICallback" 的引用。
        [ServiceContract]
        public interface ICallback
        {
            [OperationContract]
            int ReceiveWhisper(byte[] message);
    
            [OperationContract(IsOneWay = true)]
            void Receive(byte[] message);
    
            [OperationContract]
            void ReturnResult(object[] message);
    
        }
    }
    
    服务端
    //app.config
    
      <system.serviceModel>
        <services>
          <service name="Host.Service" behaviorConfiguration="Host.ServiceBehavior">
            <host>
              <baseAddresses>
                <add baseAddress="net.tcp://localhost:8001/Host" />
              </baseAddresses>
            </host>
    		    <endpoint address="" binding="netTcpBinding" bindingConfiguration="DuplexBinding" contract="Contract.IService" >
    			  <identity>
    				  <dns value="MyServerCert" />
    			  </identity>
    		    </endpoint>		            
            </service>
        </services>
        
        <behaviors>
          <serviceBehaviors>
            <behavior name="Host.ServiceBehavior">
              <serviceMetadata httpGetEnabled="true" httpGetUrl="" />
    			    <serviceDebug includeExceptionDetailInFaults="false" />
    			    <serviceCredentials>
    				    <serviceCertificate findValue="MyServerCert" storeLocation="LocalMachine"
    						     storeName="My" x509FindType="FindBySubjectName" />
    				    <userNameAuthentication userNamePasswordValidationMode="Custom"
    						     customUserNamePasswordValidatorType="Host.MyCustonValidator,Host" />
    			    </serviceCredentials>
            </behavior>
          </serviceBehaviors>      
        </behaviors>
        
        <bindings>
          <netTcpBinding>
            <binding name="DuplexBinding" sendTimeout="00:00:30"  maxReceivedMessageSize="655360" >
              <readerQuotas maxArrayLength="1638400"/>
              <reliableSession enabled="true" />
              <security mode="Message">
    			    <message clientCredentialType="UserName"/>
                <!--transport clientCredentialType="Windows" /-->
              </security>
            </binding>
          </netTcpBinding>      
        </bindings>
      </system.serviceModel>
      <system.web>
        <membership defaultProvider="ClientAuthenticationMembershipProvider">
          <providers>
            <add name="ClientAuthenticationMembershipProvider" type="System.Web.ClientServices.Providers.ClientFormsAuthenticationMembershipProvider, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" />
          </providers>
        </membership>
        <roleManager defaultProvider="ClientRoleProvider" enabled="true">
          <providers>
            <add name="ClientRoleProvider" type="System.Web.ClientServices.Providers.ClientRoleProvider, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" cacheTimeout="86400" />
          </providers>
        </roleManager>
      </system.web>

    namespace Host
    {        
        // 注意: 如果更改此处的类名 "Service",也必须更新 App.config 中对 "Service" 的引用。
    
        public enum MessageType { Receive, ReturnResult, ReceiveWhisper };
        
        public class ChatEventArgs : EventArgs
        {
            public MessageType msgType;
            public string name;
            public int result;
            public object[] message;
            public byte[] msgbytes;
        }
    
        [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
        public class Service : IService
        {        
            private static Object syncObj = new Object();
    
            ICallback callback = null;
    
            public delegate void ChatEventHandler(object sender, ChatEventArgs e);
            public static event ChatEventHandler ChatEvent;
    
            public static Dictionary<string, ChatEventHandler> chatters = new Dictionary<string, ChatEventHandler>();
    
            private string name;
            private static string[] list ;
            private int popedoms = -2;
    
            private ChatEventHandler myEventHandler = null;
            
            public int Join(string name, string password, string ipaddress)
            {
                bool userAdded = false;
                myEventHandler = new ChatEventHandler(MyEventHandler);
                
                lock (syncObj)
                {
                    if (!chatters.ContainsKey(name) && name != "" && name != null)
                    {
                        //popedoms = 读数据库;
                        if (popedoms > 0)
                        {
                            this.name = name;
                            chatters.Add(name, MyEventHandler);
                            userAdded = true;
                            
                        }
                    }
                }
    
                if (userAdded)
                {                
                    callback = OperationContext.Current.GetCallbackChannel<ICallback>();
                    ChatEvent += myEventHandler;
                    list = new string[chatters.Count];
                    lock (syncObj)
                    {
                        chatters.Keys.CopyTo(list, 0);
                    }
                }
                return popedoms;
            }       
    
            public void Leave()
            {            
                if (name == null)
                    return;
                lock (syncObj)
                {
                    chatters.Remove(this.name);
                }
                ChatEvent -= myEventHandler;            
            }
    
            //发送给指定客服端
            public void Whisper(string to, byte[] msg)
            {
                ChatEventArgs e = new ChatEventArgs();
                e.msgType = MessageType.ReceiveWhisper;
                e.msgbytes = msg;
                try
                {
                    ChatEventHandler chatterTo;
                    lock (syncObj)
                    {
                        chatterTo = chatters[to];
                    }
                    chatterTo.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);
                }
                catch
                {
                    this.name = to;
                    Leave();
                }
            }        
    
            public void SendToAllClient(byte[] msg)
            {
                ChatEventArgs e = new ChatEventArgs();
                e.msgType = MessageType.Receive;
                e.msgbytes = msg;
                BroadcastMessage(e);
            }
            
            private void MyEventHandler(object sender, ChatEventArgs e)
            {
                try
                {
                    switch (e.msgType)
                    {
                        case MessageType.Receive:
                            callback.Receive(e.msgbytes);
                            break;
                        case MessageType.ReceiveWhisper:
                            callback.ReceiveWhisper(e.msgbytes);
                            break;
                        case MessageType.ReturnResult:
                            callback.ReturnResult(e.message);
                            break;
                    }
                }
                catch
                {                
                    Leave();
                }
            }
    
            private void BroadcastMessage(ChatEventArgs e)
            {
                ChatEventHandler temp = ChatEvent;
    
                if (temp != null)
                {
                    foreach (ChatEventHandler handler in temp.GetInvocationList())
                    {
                        handler.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);
                    }
                }
            }
    
            private void EndAsync(IAsyncResult ar)
            {
                ChatEventHandler d = null;
    
                try
                {
                    System.Runtime.Remoting.Messaging.AsyncResult asres = (System.Runtime.Remoting.Messaging.AsyncResult)ar;
                    d = ((ChatEventHandler)asres.AsyncDelegate);
                    d.EndInvoke(ar);
                }
                catch
                {
                    ChatEvent -= d;
                }
            } 
        }
    }
    2009年6月15日 3:18
  • 版主,能解答我的问题吗?我暂时用心跳应付。还有客服端非法断线时,服务端无法捕捉。这个问题我也是用心跳。本人愚钝,查了很多资料也没能解决,希望微软技术论坛能帮到我!!!!!
    2009年6月19日 3:57
  • 版主,能解答我的问题吗?我暂时用心跳应付。还有客服端非法断线时,服务端无法捕捉。这个问题我也是用心跳。本人愚钝,查了很多资料也没能解决,希望微软技术论坛能帮到我!!!!!

    Hi,
       我看了一下你的代码,问个问题,这个实例化:你为什么要放在client 属性位置。是必须这样?可以考虑把类的实例化工作放到需要使用这个类的实例的地方吗?

       ServiceClient proxy = new ServiceClient(new InstanceContext(new Callback()));
      晚上回家我们继续讨论吧~
    Frank.Xu Lei--谦卑若愚,好学若饥
    专注于.NET平台下分布式应用系统开发和企业应用系统集成
    Focus on Distributed Applications Development and EAI based on .NET
    老徐的博客:http://frank_xl.cnblogs.com
    2009年6月19日 4:56
    版主
  • public static ServiceClient proxy = new ServiceClient(new InstanceContext(new Callback()));

    这个实例化我原本是放在主窗口MDIMenu里,把它放在登录窗口里是想减少贴出来的代码。我其它窗口也用到这个实例化,而服务端得发送消息到指定的客户端,通道必须一直处于打开状态。所以我把它放在主窗口里并声明为static变量
    2009年6月20日 5:43
  • public static ServiceClient proxy = new ServiceClient(new InstanceContext(new Callback()));
    
    

    这个实例化我原本是放在主窗口MDIMenu里,把它放在登录窗口里是想减少贴出来的代码。我其它窗口也用到这个实例化,而服务端得发送消息到指定的客户端,通道必须一直处于打开状态。所以我把它放在主窗口里并声明为static变量
    HI,
        这个你静态的连接通道肯定会有问题啊,一旦你网络出错,而且你的客户端和服务端是在一个网络里,这个网络能保证10分钟不丢数据包吗。这个出错很正常的啊。
       必须使用静态的连接,这个链接的是无法保证不出错的。
     
      如果你服务器端要值是发送消息到客户端,可以考虑soceket 使用TCP/IP连接,服务器端和客户端指定端口,然后客户端侦听,只是在需要的时候才建立连接。
       你这个链接的状态太久了,可能会出错误。
      

    Frank.Xu Lei--谦卑若愚,好学若饥
    专注于.NET平台下分布式应用系统开发和企业应用系统集成
    Focus on Distributed Applications Development and EAI based on .NET
    老徐的博客:http://frank_xl.cnblogs.com
    2009年6月21日 5:07
    版主
  • 谢谢版主的解答,我找到了http://msdn.microsoft.com/zh-cn/library/system.servicemodel.reliablesession.inactivitytimeout.aspx
    2009年6月22日 1:49