2016-10-04 1 views
1

J'ai deux composants simples qui sont supposés communiquer entre eux en utilisant le modèle REQ/REP ZeroMQ. Serveur (REP Socket) est implémenté en Python en utilisant pyzmq:ZeroMQ FiniteStateMachineException dans le modèle REQ/REP

import zmq 

def launch_server(): 
    print "Launching server" 
    with zmq.Context.instance() as ctx: 
     socket = ctx.socket(zmq.REP) 
     socket.bind('tcp://127.0.0.1:5555') 

     while True: 
      msg = socket.recv() 
      print "EOM\n\n" 

Le client (prise REQ) écrit en C# en utilisant la bibliothèque NetMQ:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using NetMQ; 


namespace PyNetMQTest 
{ 
    class Program 
    { 

     static void Main(string[] args) 
     { 
      string msg; 
      NetMQ.Sockets.RequestSocket socket = new NetMQ.Sockets.RequestSocket(); 
      socket.Connect("tcp://127.0.0.1:5555"); 
      for(int i=0; i<5; i++) 
       socket.SendFrame("test_"+i); 
     } 
    } 
} 

L'implémentation Python Server a été testé et fonctionne bien en parlant à un socket REQ implémenté en utilisant Python. Mais la prise C# REQ jette l'erreur suivante dans la première itération de la boucle et aucun message atteindre le serveur que ce soit:

Une exception non gérée du type « NetMQ.FiniteStateMachineException » a eu lieu dans NetMQ.dll Informations complémentaires: Req. XSend - ne peut pas envoyer une autre demande

Stacktrace:

at NetMQ.Core.Patterns.Req.XSend(Msg& msg) 
    at NetMQ.Core.SocketBase.TrySend(Msg& msg, TimeSpan timeout, Boolean more) 
    at NetMQ.NetMQSocket.TrySend(Msg& msg, TimeSpan timeout, Boolean more) 
    at NetMQ.OutgoingSocketExtensions.Send(IOutgoingSocket socket, Msg& msg, Boolean more) 
    at NetMQ.OutgoingSocketExtensions.SendFrame(IOutgoingSocket socket, String message, Boolean more) 
    at PyNetMQTest.Program.Main(String[] args) in d:\users\emes\documents\visual studio 2015\Projects\PyNetMQ Test\PyNetMQTest\Program.cs:line 20 
    at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) 
    at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) 
    at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 
    at System.Threading.ThreadHelper.ThreadStart_Context(Object state) 
    at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 
    at System.Threading.ThreadHelper.ThreadStart() 

Ce sont les premiers mes premiers pas avec ZMQ et le code C# est tiré de la bibliothèque documentation. Qu'est-ce qui fait que le code lance cette erreur?

J'utilise:

  • pyzmq 14,7
  • NetMQ 3.3.3.4
  • .NET 4,6

========== ============ SOLUTION =====================

Comme expliqué par @somdoron dans sa réponse, la raison principale était que les deux sockets doivent passer par le cycle complet d'envoi/réception avant d'être capables d'être réutilisés. En fait, la socket REP implémentée en python n'a jamais changé son état, donc l'erreur était dans les deux, le python ET le code C#. Voici le code fixe:

REP Socket

import zmq 

def launch_server(): 
    print "Launching server" 
    with zmq.Context.instance() as ctx: 
     socket = ctx.socket(zmq.REP) 
     socket.bind('tcp://127.0.0.1:5555') 

     while True: 
      msg = socket.recv() 
      socket.send("reply to "+msg) 
      print "EOM\n\n" 

REQ Socket

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using NetMQ; 


namespace PyNetMQTest 
{ 
    class Program 
    { 

     static void Main(string[] args) 
     { 

      NetMQ.Sockets.RequestSocket socket = new NetMQ.Sockets.RequestSocket(); 
      socket.Connect("tcp://127.0.0.1:5555"); 

      string msg, reply; 

      while (true) 
      { 
       Console.WriteLine("Type message: "); 
       msg = Console.ReadLine(); 
       Console.WriteLine("Sending : " + msg); 
       socket.SendFrame(msg); 
       reply = socket.ReceiveFrameString(); 
       Console.WriteLine("Received: " + reply + Environment.NewLine); 
      } 
     } 
    } 
} 

Répondre

2

prises de demande et de réponse sont des machines d'état, avec demande vous devez d'abord envoyer puis appeler Recevoir, vous ne pouvez pas appeler 5 Send consécutifs.

Avec la réponse c'est le contraire, vous devez d'abord appeler Recevoir.

Si un côté envoie seulement et l'autre uniquement, vous pouvez utiliser le modèle Push-Pull au lieu de Req-Rep. Vous pouvez également utiliser Dealer-Router si nécessaire dans les deux sens de la communication. Quoi qu'il en soit, il semble que l'utilisation de Req-Rep soit incorrecte.

+0

Merci @somdoron! C'était ça! Je n'étais pas au courant du concept de machine d'état fini. Va ajouter le code corrigé au corps de la question. – EmEs