WCF -登录/注销聊天服务导致的问题
本文关键字:问题 服务 聊天 登录 注销 WCF | 更新日期: 2023-09-27 18:16:49
我有一个WCF服务托管在一个windows服务,我有客户端登录和注销的问题。
在客户端代码Login中,我有时会得到以下异常:
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;
}
服务端代码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;
}
}
EDIT 1: Stack trace
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)
Edit 2: Server Config
<?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>
问题出在服务器上的clients集合中的一个客户机上。在先前连接的一个客户端上,当您调用它们的回调方法RefreshClients时,会抛出一个异常。因为它没有被捕获和处理,所以它会返回到调用ClientConnect的客户端。
注册一个Faulted回调以将其从客户端集合中删除是不够的,原因有两个:首先,您可能会遇到竞争条件,当您在迭代客户端集合时,它会进入Faulted状态。还有一些故障模式直到您尝试拨打电话时才被发现。你需要把回调封装在try/catch块中。
你也有一些其他潜在的问题:如果两个客户端几乎同时调用clientconnect,当clientB调用clients . add时,你可能会在foreach块中迭代clientA的调用。当底层集合被修改时,这将导致迭代抛出异常。