代码来自
https://www.zhihu.com/people/zhuang-zhuang-48-52/posts?page=1
简介
一个socket库。它提供的套接字可以在多种协议中传输消息。点对点连接需要显式地建立连接、销毁连接、选择协议(TCP/UDP)和处理错误等,而ZMQ屏蔽了这些细节。
常用三种通信模型,“Request-Reply “,”Publisher-Subscriber“,”Parallel Pipeline”
很自由,zmq只在乎消息的长度,可以自由选择合适的报文类型。
可以是进程与进程之间的通信,可以是节点与节点之间的通信,也可以是节点与进程之间的通信
一个客户端可以连接多个服务端
与socket区别:
1、zmq套接字是异步的,可以实现特定模式
2、TCP是一对一,ZeroMQ是多对多的,可以根据套接字类型实现一对多,一对一,多对一,或多对多
3、ZeroMQ传输消息,TCP传输字节
4、ZeroMQ 隐藏IO细节,ZeroMQ不在乎目的是否存在
5、ZeroMQ可以往多个节点发送数据,可以从多个节点接收数据
请求回应模型
请求端和回应端都可以是 1:N 的模型。通常把 1 认为是 server ,N 认为是 Client ,而ZMQ可以变为M:N
无缓存,断开连接数据就会丢失
消息是双向的,server必须send回复消息,client也必须receive服务端的回复,否则通道阻塞
客户端代码
#include <zmq.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("Connecting to server...\n");
void * context = zmq_ctx_new();
void * socket = zmq_socket(context, ZMQ_REQ);
zmq_connect(socket, "tcp://localhost:6666");
while(1)
{
char buffer[10];
const char * requestMsg = "Hello";
int bytes = zmq_send(socket, requestMsg, strlen(requestMsg), 0);
printf("[Client][%d] Sended Request Message: %d bytes, content == \"%s\"\n", i, bytes, requestMsg);
bytes = zmq_recv(socket, buffer, 10, 0);
buffer[bytes] = '\0';
printf("[Client][%d] Received Reply Message: %d bytes, content == \"%s\"\n", i, bytes, buffer);
}
zmq_close(socket);
zmq_ctx_destroy(context);
return 0;
}
服务端代码
#include <zmq.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
int main(void)
{
void * context = zmq_ctx_new();
void * socket = zmq_socket(context, ZMQ_REP);
zmq_bind(socket, "tcp://*:6666");
while(1)
{
char buffer[10];
int bytes = zmq_recv(socket, buffer, 10, 0);
buffer[bytes] = '\0';
printf("[Server] Recevied Request Message: %d bytes, content == \"%s\"\n", bytes, buffer);
sleep(1);
const char * replyMsg = "World";
bytes = zmq_send(socket, replyMsg, strlen(replyMsg), 0);
printf("[Server] Sended Reply Message: %d bytes, content == \"%s\"\n", bytes, replyMsg);
}
zmq_close(socket);
zmq_ctx_destroy(context);
return 0;
}
makefile
all: cli ser
cli:cli.cpp
g++ -std=c++11 cli.cpp -o cli -lzmq -lpthread -g
ser:ser.cpp
g++ -std=c++11 ser.cpp -o ser -lzmq -lpthread -g
clean:
rm -f ser cli
订阅发布模式
广播给所有的接收者,无缓存,断开数据丢失
接收者可以订阅多个发布者
单向数据传输
发布者代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include "zmq.h"
int main()
{
void* context = zmq_ctx_new();
assert(context != NULL);
void* socket = zmq_socket(context, ZMQ_PUB);
assert(socket != NULL);
int ret = zmq_bind(socket, "tcp://*:5555");
assert(ret == 0);
int i = 0;
while(1)
{
char szBuf[1024] = {0};
snprintf(szBuf, sizeof(szBuf), "server i=%d", i);//snprintf将格式化的输出写入缓冲区
ret = zmq_send(socket, szBuf, strlen(szBuf) + 1, 0);
i++;
//sleep(1);
}
zmq_close (socket);
zmq_ctx_destroy (context);
return 0;
}
接收者代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include "zmq.h"
#include <thread>
using namespace std;
#define TRUE 1
void Recv(void* arg)
{
while(TRUE)
{
void* socket = arg;
printf("into while\n");
char szBuf[1024] = {0};
int ret = zmq_recv(socket, szBuf, sizeof(szBuf) - 1, 0);
if (ret > 0)
{
printf("Recv:%s\n", szBuf);
}
}
}
void Recv2(void* arg)
{
while(TRUE)
{
void* socket = arg;
printf("into while\n");
char szBuf[1024] = {0};
int ret = zmq_recv(socket, szBuf, sizeof(szBuf) - 1, 0);
if (ret > 0)
{
printf("Recv2:%s\n", szBuf);
}
}
}
int main()
{
printf("Hello world!\n");
void* context = zmq_ctx_new();
assert(context != NULL);
void* socket = zmq_socket(context, ZMQ_SUB);
assert(socket != NULL);
int ret = zmq_connect(socket, "tcp://localhost:5555");
assert(ret == 0);
ret = zmq_setsockopt(socket, ZMQ_SUBSCRIBE, "", 0);
assert(ret == 0);
thread t1(Recv,socket);
thread t2(Recv2,socket);
/*
while(1)
{
printf("into while\n");
char szBuf[1024] = {0};
ret = zmq_recv(socket, szBuf, sizeof(szBuf) - 1, 0);
if (ret > 0)
{
printf("%s\n", szBuf);
}
}
*/
t1.join();
t2.join();
zmq_close(socket);
zmq_ctx_destroy(context);
return 0;
}
makefile
all:pub sub
CXX=g++
CXXFLAGS=-fPIC -std=c++11 -o
LDFLAGS=-lzmq -lpthread
pub:pub.cpp
$(CXX) pub.cpp $(CXXFLAGS) pub $(LDFLAGS)
sub:sub.cpp
$(CXX) sub.cpp $(CXXFLAGS) sub $(LDFLAGS)
clean:
rm -f sub pub
管道模型
最上面是产生任务的 分发者 ventilator
中间是执行者 worker 缓存数据,可去掉
下面是收集结果的接收者 sink
断开数据不丢失,重连继续发送
单向通道
分发者代码
#include <zmq.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
void * context = zmq_ctx_new();
void * sender = zmq_socket(context, ZMQ_PUSH);
zmq_bind(sender, "tcp://*:6666");
printf ("Press Enter when the workers are ready: ");
getchar ();
printf ("Sending tasks to workers...\n");
while(1)
{
const char * replyMsg = "World";
zmq_send(sender, replyMsg, strlen(replyMsg), 0);
printf("[Server] Sended Reply Message content == \"%s\"\n", replyMsg);
}
zmq_close(sender);
zmq_ctx_destroy(context);
return 0;
}
执行者代码
#include <zmq.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
int main(void)
{
void * context = zmq_ctx_new();
void * recviver = zmq_socket(context, ZMQ_PULL);
zmq_connect(recviver, "tcp://localhost:6666");
void * sender = zmq_socket(context, ZMQ_PUSH);
zmq_connect(sender, "tcp://localhost:5555");
while(1)
{
char buffer [256];
int size = zmq_recv (recviver, buffer, 255, 0);
if(size < 0)
{
return -1;
}
printf("buffer:%s\n",buffer);
const char * replyMsg = "World";
zmq_send(sender, replyMsg, strlen(replyMsg), 0);
printf("[Server] Sended Reply Message content == \"%s\"\n", replyMsg);
}
zmq_close(recviver);
zmq_close(sender);
zmq_ctx_destroy(context);
return 0;
}
接收者代码
#include <zmq.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
int main(void)
{
void * context = zmq_ctx_new();
void * socket = zmq_socket(context, ZMQ_PULL);
zmq_bind(socket, "tcp://*:5555");
while(1)
{
char buffer [256];
int size = zmq_recv (socket, buffer, 255, 0);
if(size < 0)
{
return -1;
}
printf("buffer:%s\n",buffer);
}
zmq_close(socket);
zmq_ctx_destroy(context);
return 0;
}、