WCF - 登录/注销聊天服务导致问题

时间:2015-10-08 13:17:33

标签: c# wcf windows-services

我在Windows服务中托管了WCF服务,我遇到了客户端登录和注销问题。

在客户端代码登录中,我有时会遇到以下异常。

Exception thrown: System.ServiceModel.FaultException 1 in System.ServiceModel.dll

Additional information: The operation 'RefreshClients' could not be completed because the sessionful channel timed out waiting to receive a message. To increase the timeout, either set the receiveTimeout property on the binding in your configuration file, or set the ReceiveTimeout property on the Binding directly.

我在绑定中收到了像receiveTimeout="00:00:30"这样的{时间}。但上述异常几乎是即时的。现在我不是专家,但是对我来说它在超时发生前有30秒但我的异常几乎立即发生。

客户端代码 - 登录

private DuplexChannelFactory<IMessageComs> remoteFactory;
private IMessageComs remoteProxy;
private Client clientUser;

public bool Login()
        {
            ConnectionStatus = "Logging in";

            IsConnecting = true;

            try
            {
                var ctx = new InstanceContext(CallBacks);
                remoteFactory = new DuplexChannelFactory<IMessageComs>(ctx, "NetTcpBinding_IMessageComs");
            }
            catch (Exception ex)
            {
                ConnectionStatus = "Remote Factory Error";
                IsConnecting = false;
                return false;
            }

            remoteFactory.Closed += remoteFactory_Closed;
            remoteFactory.Opening += remoteFactory_Opening;
            CallBacks.ClientsChanged += CallBacks_ClientsChanged;

            try
            {
                remoteProxy = remoteFactory.CreateChannel();                 
            }
            catch (Exception ex)
            {
                ConnectionStatus = "Remote Proxy Error";
                IsConnecting = false;
                Console.WriteLine(ex.Message);
                return false;
            }

            try
            {
                clientUser = remoteProxy.ClientConnect(Environment.UserName.ToUpper()); << Exception Happens Here
            }
            catch (Exception ex)
            {
                IsConnecting = false;
                Console.WriteLine(ex.Message);
                return false;
            }

            if (clientUser != null)
            {
                ConnectionStatus = "Online";
                IsConnecting = false;
                LoggedIn = true;
                remoteFactory.Faulted += remoteFactory_Faulted;

                // Now get the online user list
                var users = remoteProxy.GetClientsAsync();

                var connectedUser = users.Result;

                if (connectedUser != null)
                {
                    foreach (Client c in connectedUser)
                        OnlineUsers.Add(c);
                }
            }
            else
            {
                IsConnecting = false;
                ConnectionStatus = "Offline";
                return false;
            }

            return true;
        }

ServerSide代码

public Client ClientConnect(string userName)
    {
        AddEvent(userName + " is attempting to connect");

        var exists = Clients.Where(x => x.Client.UserName == userName);

        if (exists.Count() == 0)
        {
            Client c = new Client();
            c.UserName = userName;
            var tmpNo = c.ID;   // Force ID generation?
            c.SessionID = OperationContext.Current.Channel.SessionId;

            ServerClient sc = new ServerClient(c, CurrentCallback);

            OperationContext.Current.Channel.Faulted += Channel_Faulted;

            Clients.Add(sc);

            AddEvent(userName + " now connected and clients being notified");

            if (Clients.Count > 0)
            {
                // Get clients list
                var Clientlist = new List<Client>();

                foreach (ServerClient s in Clients)
                {
                    Clientlist.Add(s.Client);
                }

                foreach (ServerClient s in Clients)
                {
                    s.CallBack.RefreshClients(Clientlist);
                }
            }               

            return c;
        }
        else
        {
            AddEvent(userName + " is already connected");
            return exists.First().Client;
        }
    }

编辑1:堆栈跟踪

at System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter)

异常详细信息堆栈跟踪 - 服务器堆栈跟踪:

   at System.ServiceModel.Channels.ServiceChannel.ThrowIfIdleAborted(ProxyOperationRuntime operation)
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

Exception rethrown at [0]: 
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at EMS.Messaging.Service.IMessageComsCallBack.RefreshClients(List`1 clients)
   at EMS.Messaging.Service.ComsService.ClientConnect(String userName)
   at SyncInvokeClientConnect(Object , Object[] , Object[] )
   at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
   at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)

编辑2:服务器配置

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" />
  </startup>
  <system.web>
    <compilation debug="true" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="EMS.Messaging.Service.ComsService" behaviorConfiguration="MessagingServiceBehaviour">
        <endpoint address="" binding="netTcpBinding" contract="EMS.Messaging.Service.IMessageComs" bindingConfiguration="tcpBinding"/>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://SERVERNAME:8000/MessagingService/service" />
            <add baseAddress="net.tcp://SERVERNAME:8001/MessagingService/service" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="MessagingServiceBehaviour">
          <serviceMetadata httpGetEnabled="false" httpsGetEnabled="false" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceThrottling maxConcurrentSessions="100"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <netTcpBinding>
        <binding name="tcpBinding" maxBufferSize="67108864" maxReceivedMessageSize="67108864" maxBufferPoolSize="67108864" transferMode="Buffered" closeTimeout="00:00:10"
                         openTimeout="00:00:10" receiveTimeout="00:20:00" sendTimeout="00:01:00" maxConnections="100" portSharingEnabled="true">
          <security mode="None"/>
          <readerQuotas maxArrayLength="67108864" maxBytesPerRead="67108864" maxStringContentLength="67108864" />
          <reliableSession enabled="true" inactivityTimeout="00:20:00" />
        </binding>
      </netTcpBinding>
      <customBinding>
        <binding name="mexBinding">
          <tcpTransport maxPendingConnections="100">
            <connectionPoolSettings groupName="default" maxOutboundConnectionsPerEndpoint="100"/>
          </tcpTransport>
        </binding>
      </customBinding>
    </bindings>
  </system.serviceModel>
</configuration>

1 个答案:

答案 0 :(得分:0)

问题在于服务器上Clients集合中的一个客户端。在调用其回调方法RefreshClients时,在以前连接的某个客户端上,您将收到异常抛出。由于没有捕获和处理,它会将其返回到调用ClientConnect的客户端。注册Faulted回调以将其从Clients集合中删除是不够的,原因有两个。首先,当您迭代Clients集合时,您可能遇到一个竞争状态,它会进入故障状态。在您尝试拨打电话之前,也不会发现某些故障模式。您需要将回调包装在try / catch块中。您还有其他一些潜在的问题。如果客户端几乎同时调用ClienConnect,那么当clientB调用Clients.Add时,您可以在foreach块中调用clientA的调用迭代客户端。这将导致迭代在修改基础集合时抛出异常。