ZeroMQ C++案例

Posted sanqima

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ZeroMQ C++案例相关的知识,希望对你有一定的参考价值。

    ZeroMQ是一个跨平台、开源的消息内核,支持Request/Response、Publish/Subscribe、Push/Pull这3种通信模型,同时,也是一个嵌入式库,它封装了网络通信、消息队列、线程调度等功能,向上层提供简洁的API,应用程序通过加载库文件,调用API函数来实现高性能网络通信。
    下面,介绍在Win10上,使用VS2013进行ZeroMQ通信的案例。

1、下载ZeroMQ v4.3.4

1.1 下载库文件

    这里下载ZeroMQ已经编译好的库文件:
    libzmq-v120-4_3_4.zip 官网地址:https://github.com/zeromq/libzmq/releases/download/v4.3.4/libzmq-v120-4_3_4.zip

    libzmq-v120-4_3_4.zip 个人地址: https://pan.baidu.com/s/1Png7BsIUd6kHXAlLCnRjLA 提取码:fiaj

    这个库文件是Win10 X86 32位的,那么创建工程时,需要选择Win32类型的工程。

1.2 新建文件夹libzmq

    a) 新建一个文件夹名称为libzmq,然后在libzmq里再新建2个文件夹,名称分别为include和lib,其中 include用于存放头文件,lib用于存放*.dll、*.lib文件。

    b) 将libzmq-v120-4_3_4.zip里的如下4个文件,拷贝libzmq\\lib里,
   libsodium.dll
   libzmq-v120-mt-4_3_4.dll
   libzmq-v120-mt-4_3_4.lib
   libzmq-v120-mt-s-4_3_4.lib

    c) 将libzmq-v120-4_3_4.zip里的zmq.h,拷贝到libzmq\\include里;

    d) 新建一个文件名称为zhelpers.h,其内容如下:
//zhelper.hs

/*  =====================================================================
    zhelpers.h

    Helper header file for example applications.
    =====================================================================
*/

#ifndef __ZHELPERS_H_INCLUDED__
#define __ZHELPERS_H_INCLUDED__

//  Include a bunch of headers that we will need in the examples

#include <zmq.h>

#include <assert.h>
#include <signal.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
// #if (!defined (WIN32))
// #   include <sys/time.h>
// #endif

// #if (defined (WIN32))
// #   include <windows.h>
// #endif

#ifndef WIN32
#define WIN32 1
#endif

#pragma warning(disable:4996)

#ifdef WIN32
#include <windows.h>
#else 
#include <sys/time.h>	
#endif


//  Version checking, and patch up missing constants to match 2.1
#if ZMQ_VERSION_MAJOR == 2
#   error "Please upgrade to ZeroMQ/3.2 for these examples"
#endif

//  On some version of Windows, POSIX subsystem is not installed by default.
//  So define srandom and random ourself.
#if (defined (WIN32))
#   define srandom srand
#   define random rand
#endif

//  Provide random number from 0..(num-1)
#define randof(num)  (int) ((float) (num) * random () / (RAND_MAX + 1.0))

//  Receive 0MQ string from socket and convert into C string
//  Caller must free returned string. Returns NULL if the context
//  is being terminated.
static char *
s_recv (void *socket) {
    enum { cap = 256 };
    char buffer [cap];
    int size = zmq_recv (socket, buffer, cap - 1, 0);
    if (size == -1)
        return NULL;
    buffer[size < cap ? size : cap - 1] = '\\0';

#if (defined (WIN32))
    return _strdup (buffer);
#else
    return strndup (buffer, sizeof(buffer) - 1);
#endif

    // remember that the strdup family of functions use malloc/alloc for space for the new string.  It must be manually
    // freed when you are done with it.  Failure to do so will allow a heap attack.
}

//  Convert C string to 0MQ string and send to socket
static int
s_send (void *socket, char *string) {
    int size = zmq_send (socket, string, strlen (string), 0);
    return size;
}

//  Sends string as 0MQ string, as multipart non-terminal
static int
s_sendmore (void *socket, char *string) {
    int size = zmq_send (socket, string, strlen (string), ZMQ_SNDMORE);
    return size;
}

//  Receives all message parts from socket, prints neatly
//
static void
s_dump (void *socket)
{
    int rc;

    zmq_msg_t message;
    rc = zmq_msg_init (&message);
    assert (rc == 0);

    puts ("----------------------------------------");
    //  Process all parts of the message
    do {
        int size = zmq_msg_recv (&message, socket, 0);
        assert (size >= 0);

        //  Dump the message as text or binary
        char *data = (char*)zmq_msg_data (&message);
        assert (data != 0);
        int is_text = 1;
        int char_nbr;
        for (char_nbr = 0; char_nbr < size; char_nbr++) {
            if ((unsigned char) data [char_nbr] < 32
                || (unsigned char) data [char_nbr] > 126) {
                is_text = 0;
            }
        }

        printf ("[%03d] ", size);
        for (char_nbr = 0; char_nbr < size; char_nbr++) {
            if (is_text) {
                printf ("%c", data [char_nbr]);
            } else {
                printf ("%02X", (unsigned char) data [char_nbr]);
            }
        }
        printf ("\\n");
    } while (zmq_msg_more (&message));

    rc = zmq_msg_close (&message);
    assert (rc == 0);
}

#if (!defined (WIN32))
//  Set simple random printable identity on socket
//  Caution:
//    DO NOT call this version of s_set_id from multiple threads on MS Windows
//    since s_set_id will call rand() on MS Windows. rand(), however, is not 
//    reentrant or thread-safe. See issue #521.
static void
s_set_id (void *socket)
{
    char identity [10];
    sprintf (identity, "%04X-%04X", randof (0x10000), randof (0x10000));
    zmq_setsockopt (socket, ZMQ_IDENTITY, identity, strlen (identity));
}
#else
//  Fix #521 for MS Windows.
static void
s_set_id(void *socket, intptr_t id)
{
    char identity [10];
    sprintf(identity, "%04X", (int)id);
    zmq_setsockopt(socket, ZMQ_IDENTITY, identity, strlen(identity));
}
#endif

//  Sleep for a number of milliseconds
static void
s_sleep (int msecs)
{
#if (defined (WIN32))
    Sleep (msecs);
#else
    struct timespec t;
    t.tv_sec  =  msecs / 1000;
    t.tv_nsec = (msecs % 1000) * 1000000;
    nanosleep (&t, NULL);
#endif
}

//  Return current system clock as milliseconds
static int64_t
s_clock (void)
{
#if (defined (WIN32))
    SYSTEMTIME st;
    GetSystemTime (&st);
    return (int64_t) st.wSecond * 1000 + st.wMilliseconds;
#else
    struct timeval tv;
    gettimeofday (&tv, NULL);
    return (int64_t) (tv.tv_sec * 1000 + tv.tv_usec / 1000);
#endif
}

//  Print formatted string to stdout, prefixed by date/time and
//  terminated with a newline.

static void
s_console (const char *format, ...)
{
    time_t curtime = time (NULL);
    struct tm *loctime = localtime (&curtime);
    char *formatted = (char*)malloc (20);
    strftime (formatted, 20, "%y-%m-%d %H:%M:%S ", loctime);
    printf ("%s", formatted);
    free (formatted);

    va_list argptr;
    va_start (argptr, format);
    vprintf (format, argptr);
    va_end (argptr);
    printf ("\\n");
}

#endif  //  __ZHELPERS_H_INCLUDED__


    然后,将zhelpers.h拷贝libzmq\\include文件夹里,具体如下:

图(1) libzmq的目录结构

2、创建解决方案

    这里在VS2013中创建1个空的解决方案名称为ZeroBase,然后,在这个解决方案中添加一个新的工程:ZeroTwo,这样就有2个工程。
    工程路径为:E:\\MyProject\\vs2013Doc\\Unit01\\Chap01\\ZeroBase

2.1 创建工程

    a) 打开VS2013,创建一个空工程名称为ZeroBase,然后,右击ZeroBase解决方案 --> 添加一个新工程名称为ZeroTwo,如图(2)、图(3)、图(4)所示。

图(2) 右击[解决方案]--> 添加 -->新建项目

图(3) 新建一个空工程名称为ZeroTwo

    b) 拷贝libzmq到解决方案ZeroBase里
    将第1步组装好的libzmq拷贝到E:\\MyProject\\vs2013Doc\\Unit01\\Chap01\\ZeroBase路径下,最后的目录结构为:

图(4) 整个【解决方案】的目录结构

2.2 配置工程

    这里以ZeroBase工程进行说明。
    a) 设置工程的include、reference、lib目录

    include目录

$(ProjectDir)..\\libzmq\\include;$(IncludePath)

    reference目录

$(ProjectDir)..\\libzmq\\lib;$(ReferencePath)

    lib目录

$(ProjectDir)..\\libzmq\\lib;$(LibraryPath)

如下所示:

图(5) 设置工程的包含目录、引用目录和库目录

    b) 设置工程的预处理宏
    在 【配置属性】—>C/C++ -->预处理器 --> 预处理定义 --> 添加如下2个宏:

ZMQ_STATIC
ZMQ_USE_TWEETNACL
图(6) 添加2个宏

    c) 设置工程里链接器的依赖库
    在 【配置属性】—>C/C++ --> 代码生成 --> 运行库 --》 选中 多线程(MT),如图所示。

图(7) 设置运行库:多线程MT

    d) 设置工程的运行库
    在 【配置属性】—>链接器 -->输入 —>附加依赖项–> 添加如下3个lib

wsock32.lib
ws2_32.lib
Iphlpapi.lib

    如下图所示。

图(8) 在链接器里添加3个lib

    同理,ZeroTwo工程,也需要设置这4个配置。

3、编写代码

3.1 hwServer服务端

    ZeroBase作为服务端,提供服务。
    //E:\\MyProject\\vs2013Doc\\Unit01\\Chap01\\ZeroBase\\ZeroBase\\hwServver.cpp

#pragma comment(lib, "libzmq-v120-mt-4_3_4.lib")
#include <zmq.h>
#include <Windows.h>

#include <stdio.h>
#include <string.h>
#include <assert.h>

int main(void)
{
	/*  Socket to talk to clients */
	void *context = zmq_ctx_new();
	void *responder = zmq_socket(context, ZMQ_REP);
	int rc = zmq_bind(responder, "tcp://*:5555");
	assert(rc == 0);

	while (1) {
		char buffer[10];
		memset(buffer, 0x00, sizeof(buffer));
		rc = zmq_recv(responder, buffer, 10, 0);
		//buffer[rc]=0;
		printf("Server Recv: [%s] space=%d\\n", buffer, rc);
		Sleep(1);          //  Do some 'work'
		zmq_send(responder, "World", 9, 0);
		printf("Server Send: [world]\\n");
	}
	return 0;
}

    按Ctrl+Shift+B 编译ZeroBase工程。

3.2 hwClient客户端

    ZeroTwo作为客户端,发送请求。

    //E:\\MyProject\\vs2013Doc\\Unit01\\Chap01\\ZeroBase\\ZeroTwo\\hwClient.cpp

#pragma comment(lib, "libzmq-v120-mt-4_3_4.lib")
#include <zmq.h>

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	printf("=== Connecting to ZeroMQ Server ===\\r\\n");
	void* context = zmq_ctx_new();
	void* client = zmq_socket(context, ZMQ_REQ);
	zmq_connect(client, "tcp://localhost:5555");

	char buffer[10] = { 0 };
	int request_nbr = 0;
	for (request_nbr = 0; request_nbr < 1; request_nbr++)
	{
		zmq_send(client, "hello", 9, 0);
		printf("Client Send: [hello]\\n");
		memset(buffer, 0x00, 10);
		zmq_recv(client, buffer, 10, 0);
		printf("Client Recv: [%s]\\n", buffer);
	}
	zmq_close(client);
	zmq_ctx_destroy(context);

	system("pause");

	return 0;
}

    按Ctrl+Shift+B 编译ZeroTwo工程。

3.2 拷贝dll到Release

    将libzmq-v120-mt-4_3_4.dll、libsodium.dll拷贝到【解决方案】\\Release路径,先双击ZeroBase.exe,后双击ZeroTwo.exe,效果如下:

图(9) ZeroMQ里的Request/Response通信

4、完整工程

    ZeroBase完整工程链接: https://pan.baidu.com/s/1ze36oOUjQQgYeK_BTpNAMg 提取码:m2zl

    需要的libzmq v4.3.4 链接: https://pan.baidu.com/s/15r6QnyKHVsSXhs__V7gOWw 提取码:hj6y

以上是关于ZeroMQ C++案例的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 ZeroMQ 从 C# 客户端向 C++ 服务器发送消息

详细实例说明+典型案例实现 对动态规划法进行全面分析 | C++

C++跨平台:ZeroMQ的简单示例

C++跨平台:ZeroMQ的简单示例

ZeroMQ:Python 服务器到 C++ 客户端

案例精选 | 分布式消息队列差异化总结,太全了!