Почему клиент не может получить сообщение от сервера с платформой 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/

0 ответов

В NetMQ очень важна модель потоков. Действительно важно то, что вы не должны использовать сокет в нескольких потоках. Так что, если Thread#1 создал сокет, он должен использовать его. Если вы хотите отправить сообщение из другого потока (скажем, из потока № 2), используя тот же сокет, просто забудьте об этом. Вы должны как-то отправить сообщения из потока № 2 в поток № 1, а затем отправить его через сокет клиенту.

Так что в принципе CreatePullAndPushSocket не так и странные вещи могут произойти, чем. Вы создаете сокеты в одном потоке, а используете в другом. Это просто неправильно.

Другая вещь - Ваша Тема. Сон. Вы не должны использовать Thread.Sleep, потому что ваш поток спит 1 с, а затем проверяет сокет один раз, затем спит и проверяет один раз. NETMQ имеет функцию TryReceive с таймаутом. Таким образом, он может проверить сокет на 1 секунду и выйти, чтобы проверить, звоните ли вы отменить / остановить или что-то еще. Или, что еще лучше, есть модуль опроса, который будет постоянно прослушивать сокет и позволять нам вызывать 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, чтобы всегда освобождать его в конце.

Я не вижу использования метода SendMessageToClient, но я предположил, что Вы вызываете его с какой-то кнопки или чего-то еще. Вы можете сделать это, если конструктор сокета был вызван из этого потока. Если бы вы могли показать мне, где Вы вызываете этот метод, я мог бы кое-что сказать по этому поводу.

Другие вопросы по тегам