为什么客户端无法使用NetMQ框架从服务器接收消息?

时间:2018-11-22 09:56:13

标签: c# message-queue zeromq netmq

最近,我使用NetMQ在服务器和客户端之间发送或接收消息。 服务器代码,例如:

    void Main()
    {
      CreatePullAndPushSocket();
      Task.Factory.StartNew(()=> {
            while (true)
            {
                Thread.Sleep(1);
                if (Pull != null)
                {
                    var message = Pull.ReceiveFrameString();
                }
            }
        });
    }
    PullSocket Pull;
    PushSocket Push;
    private void CreatePullAndPushSocket()
    {
        Pull = new PullSocket("tcp://ip1:port1");
        Push = new PushSocket("tcp://ip2:port2");
    }
    public void SendMessageToClient(string message)
    {
        if (Push != null)
        {
            Push.SendFrame(message);
        }
    }

客户代码如下:

   void Main()
    { 
      new Thread(()=> {
            while (true)
            {
                Thread.Sleep(1);
                if (Pull != null)
                {
                    var message = Pull.ReceiveFrameString();
                }
            }
        }).Start();
    }
    PullSocket Pull;
    PushSocket Push;
    private void CreatePullAndPushSocket()
    {
        Pull = new PullSocket("tcp://ip2:port2");
        Push = new PushSocket("tcp://ip1:port1");
    }
    public void SendMessageToClient(string message)
    {
        if (Push != null)
        {
            Push.SendFrame(message);
        }
    }

当我运行两个应用程序(即服务器应用程序)时,另一个是客户端应用程序。

  • 1:客户端向服务器发送消息
  • 2:服务器可以从客户端接收消息
  • 3:服务器向客户端发送另一条消息
  • 4:客户端无法收到消息!

很奇怪,我遵循了https://netmq.readthedocs.io/en/latest/push-pull/的指导!!

1 个答案:

答案 0 :(得分:0)

在NetMQ中,非常重要的一点是线程模型。真正重要的是,您不应在多个线程中使用套接字。因此,如果线程1创建了套接字,则应该使用它。如果您想通过使用相同的套接字从其他线程(比如说Thread#2)发送消息,那就别管了。您应该以某种方式将消息从线程2发送到线程1,然后将其通过套接字发送到客户端。

所以从根本上讲CreatePullAndPushSocket是错误的,然后可能发生奇怪的事情。您正在一个线程中创建套接字,而在另一个线程中使用套接字。这简直是​​错误的。

另一件事是您的Thread.Sleep。您不应该使用Thread.Sleep,因为您的线程正在休眠1s,然后检查套接字一次,而不是休眠并检查一次。 NetMQ具有带有超时功能的TryReceive。因此它可以检查套接字1秒钟,然后退出以检查您是否调用了cancel / stop或其他方法。甚至更好的是有一个轮询器,它会一直监听套接字,并允许我们从其他线程调用stop。

让我们看一下这段代码:

private Poller _poller;

public void Start()
{
  Task.Factory.StartNew(()=> {
     using(var pullSocket = new PullSocket("tcp://ip1:port1"))
     using(_poller = new Poller())
     {
        pullSocket.ReceiveReady += (object sender, NetMQSocketEventArgsnetMqSocketEventArgs) =>
        {
            var message = netMqSocketEventArgs.Socket.ReceiveMultipartMessage();
        }
        _poller.Add(pullSocket);
        _poller.Run();
     }
    });
}
public void Stop()
{
   _poller?.Stop();
}

或者如果您想在while循环中使用没有轮询器的代码:

私有只读CancellationTokenSource _cts;

public void Start()
{
   _cts = new CancellationTokenSource();
   var token = _cts.Token;

  Task.Factory.StartNew(()=> {
     using(var pullSocket = new PullSocket("tcp://ip1:port1"))
     {
        while (cancellationToken.IsCancellationRequested == false)
        {
            NetMQMessage message = new NetMQMessage();
            bool success = workerSocket.TryReceiveMultipartMessage(TimeSpan.FromSeconds(5), ref message);

            if (success == false)
            {
                continue;
            }

            //if You reach this line, than You have a message
        }
     }
    },
    token,
    TaskCreationOptions.LongRunning,
    TaskScheduler.Default);
}
public void Stop()
{
    _cts.Cancel();
    _cts.Token.WaitHandle.WaitOne();//if You want wait until service will stop
}

所以回到您的问题,您应该仅在创建套接字的线程内部使用套接字。好的事情是总是在using语句中使用socket来最终总是释放它。

我看不到SendMessageToClient方法的用法,但我假设您是通过某个按钮或某些东西调用它的。如果从该线程调用了套接字的构造函数,则可以执行此操作。如果您可以告诉我您在哪里调用此方法,我可以对此说点什么。