condições de corrida em WCF com métodos não-sentido único
-
19-08-2019 - |
Pergunta
Eu estou projetando uma aplicação de chat cliente-servidor (na verdade eu não sou, mas vamos fingir que eu sou :)), e eu estou um pouco confuso por algumas condições de corrida eu experimentei.
Vamos dizer que tenho o seguinte código:
public interface IServer
{
[OperationContract(IsOneWay = false)]
[FaultContract(typeof(ChatException))]
void BroadcastMessage(string msg);
}
public class Server : IServer
{
void BroadcastMessage(string msg) // I'm not mentionning the try/catch/throw FaultException here for readability purposes
{
foreach (IClientCallback c in callbacks){
c.ReceiveMessage(msg);
}
}
}
public interface IClientCallback
{
[OperationContract(IsOneWay = true)]
void ReceiveMessage(string s);
}
E aqui está um trecho da configuração de ligação:
<endpoint address=""
binding="netTcpBinding"
bindingConfiguration="DuplexBinding"
contract="IServer" />
<binding name="DuplexBinding" sendTimeout="00:01:00">
<reliableSession ordered="true" inactivityTimeout="00:05:00" enabled="true"/>
<security mode="None">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
<message clientCredentialType="Windows" />
</security>
</binding>
Este é, naturalmente, algum tipo de pseudo c #, eu removi um monte de código não relevantes para a causa clareza.
Agora ao ponto: Este código não vai funcionar. Quando eu chamar BroadcastMessage, o método nunca retorna, e eu, eventualmente, obter um tempo limite no lado do cliente. Se eu depuração no lado do servidor, tudo parece bem (eu voltar a partir do método BroadcastMessage exatamente como seria de esperar, e eu não estou de bloqueio em todas as chamadas ReceiveMessage só ida)
Aqui estão duas maneiras de corrigir este código:
- remover o FaultContract e declarar o método BroadcastMessage como oneway = true
- Transmissão a mensagem para todos, mas o remetente inicial
Minha primeira suposição era de que lado do cliente estava esperando pelo servidor para retorno, e, portanto, não estava disponível para lidar com a chamada ReceiveMessage de entrada do servidor, bloqueando assim o servidor, mas ReceiveMessage é declarado como oneway e depuração do shows de servidor que não bloqueiam em qualquer chamada para ReceiveMessage
Agora, a minha pergunta:
-
O que está acontecendo?
-
Existem outras maneiras de corrigir isso? (Talvez por meio do ajuste da configuração de ligação?)
-
Digamos que eu escolher correção 2 (ou seja, não transmitem de volta para o remetente), o que acontece se o servidor chama o meu retorno ReceiveMessage (porque alguém me enviou uma mensagem) enquanto eu estou esperando por minha própria BroadcastMessage chamada ao fim?
-
Eu li que as chamadas Oneway não são totalmente de sentido único, e que o servidor ainda espera por uma resposta de HTTP a partir do outro lado. Todos os detalhes sobre este assunto? Especificamente, é o cliente capazes de responder suche http respostas quando está bloqueado em uma chamada distante?
Edit: Console NET 3.5 no lado do servidor, WinForms NET 3.5 no lado do cliente
Solução
Parece um impasse, talvez devido ao sincronismo de contexto. O que é o cliente? Winform? WPF? WCF aspectos de sincronização ao contexto, o que significa "mudar para o segmento UI" para winforms e WPF. Se você é fazer a solicitação de bloqueio no segmento interface do usuário, em seguida, ao longo do jogo.
Tente realizar sua solicitação WCF em uma discussão de fundo, de modo que o segmento interface do usuário está disponível para atender a solicitação de entrada; Isto poderia ser tão simples quanto usar ThreadPool
, ou talvez BackgroundWorker
.
Outras dicas
tente adicionar esse atributo para a sua implementação concreta do seu serviço:
[System.ServiceModel.ServiceBehavior(UseSynchronizationContext=false)]
public class Server : IServer {}
Em segundo lugar, tente ligar WCF de rastreamento para ver se você tem algo acontecendo nas entranhas da sua implementação WCF que está dando o seu sofrimento. Eu tive alguns erros muito estranho que só faziam sentido quando fui para rastreamento e descobriu que a mensagem de erro real que foi ocorrendo.