+ 1 для медицинской помощи, использующей явное zmq_close()
и zmq_ctx_term()
освобождение ресурсов ...
Если вы впервые работаете с ZeroMQ,
здесь можно сначала посмотреть « ZeroMQ Принципы менее чем за пять секунд », прежде чем углубляться в дальнейшие детали
Q: Какие первые шаги я должен предпринять, чтобы выяснить, почему это происходит?
Тест линии прямой видимости как нулевой шаг не имеет смысла здесь.Все localhost
размещенные интерфейсы трудно "не увидеть" друг друга.
Далее, протестируйте как первый шаг вызов { .bind() | .connect() }
-методов, используя явный адрес, такой как tcp://127.0.0.1:56789
(чтобы избежать расширения как *
-wildcard, так и localhost
-символических переводов имен)
Всегда будьте готовы прочитать / оценить предоставляемую API errno
, о которой ZeroMQ постоянно сообщает опоследняя API-операция ZeroMQ, приводящая к состоянию ошибки.
Лучше всего прочитать документацию по нативному API ZeroMQ, которая хорошо поддерживается от версии к версии, чтобы полностью понять удобство разработанной API метаплоскости сигнализации / обмена сообщениями.
Mea Culpa: LoS наверняка не был установлен кодом O / P:
- RPi
.bind()
-s на его локальном I / F(и не может быть иначе) - ПК
.connect()
-s не для RPi, но локальный I / F ПК - ПК
.connect( "tcp://<address_of_RPi>:5555" )
сделает его (используйте тот же IP-адрес, что и в Xrdp или SSH для подключения к RPiили может прочитать одно явно из CLI-терминала RPi после ~$ ip address
и использовать его для клиентского кода на стороне ПК)
Два непересекающихся ZeroMQ AccessPoint
имеют нулевой способ связи, один разнет транспорта - "провод" от A до B
// Zero MQ Test Server
// Compile with
// gcc -o zserver zserver.c -lzmq
#include <zmq.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
int main (void)
{
void *context=NULL,*responder=NULL;
int rc=1;
// Socket to talk to clients
context = zmq_ctx_new (); printf("Context pointer = %p\n",context);
responder = zmq_socket (context, ZMQ_REP); printf("Responder pointer = %p\n",responder);
rc = zmq_bind (responder, "tcp://*:5555"); printf("rc = %d\n",rc);
/* ----------------------------------^^^^^^------------RPi interface-----------*/
assert (rc == 0);
while (1) {
char buffer [10];
zmq_recv (responder, buffer, 10, 0); printf("Received Hello\n");
sleep (1); // Do some 'work'
zmq_send (responder, "World", 5, 0);
}
return 0;
}
Исходный код на клиенте ПК (Cygwin):
// ZeroMQ Test Client
// Compile with:
// gcc -o zclient zclient.c -L/usr/local/lib -lzmq
#include <zmq.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
int main (void)
{
void *context=NULL,*requester=NULL;
printf("Connecting to hello world server\n");
context = zmq_ctx_new (); printf("Context pointer = %p\n",context);
requester = zmq_socket (context, ZMQ_REQ); printf("Requester pointer = %p\n",requester);
zmq_connect (requester, "tcp://localhost:5555");
/*---------------------------------^^^^^^^^^^^^^^---------PC-local-interface------*/
int request_nbr;
for (request_nbr = 0; request_nbr != 10; request_nbr++) {
char buffer [10]; printf("Sending Hello %d\n", request_nbr);
zmq_send (requester, "Hello", 5, 0);
zmq_recv (requester, buffer, 10, 0); printf("Received World %d\n", request_nbr);
}
zmq_close (requester);
zmq_ctx_destroy (context);
return 0;
}
Может также пожелать прочитать больше о ZeroMQСубъекты здесь
Эпилог:
Проблема, о которой сообщается в отчете о прибылях и убытках, фактически маскируется и остается скрытой от обнаружения API.ZeroMQ позволяет одному AccessPoint иметь 0+ соединений транспортного класса одновременно при условии правильного синтаксиса и выполнения других условий.
Вызов
zmq_connect( reguester, "tcp://<address-not-intended-but-correct>:<legal-port>" )
приведет к юридически справедливому состоянию, и ни один из определенных и задокументированных случаев возможных состояний ошибки не будет зарегистрирован, потому что ни один из всех таких случаев фактически не произошел:
EINVAL
Недопустимая конечная точка.
EPROTONOSUPPORT
Запрошенный транспортный протокол не поддерживается.
ENOCOMPATPROTO
Запрошенный транспортный протокол не совместим с типом сокета.
ETERM
Контекст ØMQ, связанный с указаннымсокет был прерван.
ENOTSOCK
Указанный сокет был недействительным.
EMTHREAD
Нет ввода / выводапоток доступен для выполнения задачи.
Есть некоторые шансы на хотя бы как-то - "обнаружить" проблема будет заключаться в принудительном применении другого рода исключений / ошибок, но отложенных до вызова { zmq_recv() | zmq_recv() }
в их неблокирующей форме, куда они могут превратитьсяв отчет EAGAIN
или может быть EFSM
за то, что он не завершил сквозное повторно подтвержденное квитирование ZMTP-протокола (ни один из контрагентов не был и никогда не будет встречен на локальном порте ПК с удаленным RPi-сервером).сторона ).Для этого также требуются предварительные настройки zmq_setsockopt( responder, ZMQ_IMMEDIATE, 1 )
и другие подробности конфигурации.
Следующий, в ZeroMQ v4. +, Есть возможность проверить подмножество событий, о которых сообщалось в AccessPoint, используя «сокет проверки».с помощью довольно сложной стратегии создания
int zmq_socket_monitor (void *socket, char *endpoint, int events);
, прикрепленной к внутренним компонентам AccessPoint через inproc://
транспортный класс ~ здесь "inproc://myPCsocketAccessPOINT_monitor"
, например:
rc = zmq_socket_monitor( responder, // AccessPoint to monitor
"inproc://myPCsocketAccessPOINT_monitor", // symbolinc name
ZMQ_ALL_EVENTS // scope of Events
);
Такой созданный внутренний «контрольный сокет» может затем получить zmq_connect()
-ed, например:
void *my_end_of_monitor_socket = zmq_socket ( context, ZMQ_PAIR );
rc = zmq_connect( my_end_of_monitor_socket, // local-end PAIR-socket AccessPoint
"inproc://myPCsocketAccessPOINT_monitor" // symbolic name
);
и, наконец, мы можем использовать это, чтобы прочитать последовательность событий (и действовать соответственно):
int event = get_monitor_event( my_end_of_monitor_socket, NULL, NULL );
if (event == ZMQ_EVENT_CONNECT_DELAYED) { ...; }
if (event == ... ) { ...; }
с использованием в качестве инструмента тривиализированного get_monitor_event()
, подобного этому, который обрабатывает некоторые из внутренних правил чтения и интерпретации сообщений, состоящих из нескольких частей, которые поступают в порядке, указанном в экземпляре подключенного внутреннего "монитора"в AccessPoint:
// Read one event off the monitor socket; return value and address
// by reference, if not null, and event number by value. Returns -1
// in case of error.
static int
get_monitor_event ( void *monitor, int *value, char **address )
{
// First frame in message contains event number and value
zmq_msg_t msg;
zmq_msg_init (&msg);
if (zmq_msg_recv (&msg, monitor, 0) == -1) return -1; // Interrupted, presumably
assert (zmq_msg_more (&msg));
uint8_t *data = (uint8_t *) zmq_msg_data (&msg);
uint16_t event = *(uint16_t *) (data);
if (value) *value = *(uint32_t *) (data + 2);
// Second frame in message contains event address
zmq_msg_init (&msg);
if (zmq_msg_recv (&msg, monitor, 0) == -1) return -1; // Interrupted, presumably
assert (!zmq_msg_more (&msg));
if (address) {
uint8_t *data = (uint8_t *) zmq_msg_data (&msg);
size_t size = zmq_msg_size (&msg);
*address = (char *) malloc (size + 1);
memcpy (*address, data, size);
(*address)[size] = 0;
}
return event;
}
Какие внутренние API-события можно отслеживать?
Начиная с состояния API v4.2, существует этот набор "внутренних"-monitor (способны) внутренние API-события:
ZMQ_EVENT_CONNECTED
Сокет успешно подключен к удаленному узлу.Значением события является файловый дескриптор (FD) базового сетевого сокета.Предупреждение: нет никакой гарантии, что FD все еще действителен к тому времени, когда ваш код получает это событие.
ZMQ_EVENT_CONNECT_DELAYED
Ожидается запрос на подключение к сокету.Значение события не указано.
ZMQ_EVENT_CONNECT_RETRIED
Запрос на подключение не выполнен, и сейчас повторяется попытка.Значение события - это интервал повторного подключения в миллисекундах.Обратите внимание, что интервал переподключения пересчитывается при каждой повторной попытке.
ZMQ_EVENT_LISTENING
Сокет был успешно связан с сетевым интерфейсом.Значением события является FD основного сетевого сокета.Предупреждение: нет никакой гарантии, что FD все еще действителен к тому времени, когда ваш код получает это событие.
ZMQ_EVENT_BIND_FAILED
Сокет не может привязаться к данному интерфейсу.Значением события является ошибка, сгенерированная системным вызовом привязки.
ZMQ_EVENT_ACCEPTED
Сокет принял соединение от удаленного узла.Значением события является FD основного сетевого сокета.Предупреждение: нет никакой гарантии, что FD все еще действителен к тому времени, когда ваш код получает это событие.
ZMQ_EVENT_ACCEPT_FAILED
Сокет отклонил соединение от удаленного узла.Значением события является ошибка, сгенерированная вызовом accept.
ZMQ_EVENT_CLOSED
Розетка была закрыта.Значением события является FD (теперь закрытого) сетевого сокета.
ZMQ_EVENT_CLOSE_FAILED
Ошибка закрытия сокета.Значением события является ошибка, возвращаемая системным вызовом.Обратите внимание, что это событие происходит только на транспортах IPC.
ZMQ_EVENT_DISCONNECTED
Розетка была неожиданно отключена.Значением события является FD основного сетевого сокета.Предупреждение: этот разъем будет закрыт.
ZMQ_EVENT_MONITOR_STOPPED
Контроль на этом разъеме завершен.
ZMQ_EVENT_HANDSHAKE_FAILED
Сбой квитирования механизма безопасности ZMTP.Значение события не указано.
ПРИМЕЧАНИЕ: в состоянии DRAFT, еще не доступно в стабильных выпусках.
ZMQ_EVENT_HANDSHAKE_SUCCEED
ПРИМЕЧАНИЕ: при добавлении новых событий значение catch-all будетначать возвращать их.Приложение, которое использует строгую и фиксированную последовательность событий, не должно использовать ZMQ_EVENT_ALL
, чтобы гарантировать совместимость с будущими версиями.
Каждое событие отправляется в виде двух кадров.Первый кадр содержит номер события (16 бит) и значение события (32 бита), который предоставляет дополнительные данные в соответствии с номером события.Второй кадр содержит строку, которая указывает затронутую конечную точку TCP или IPC.