ZeroMQ_02 提问-回答模式

提问-应答

让我们从简单的代码开始,一段传统的Hello World程序。我们会创建一个客户端和一个服务端,客户端发送Hello给服务端,服务端返回World。

Server

#include <stdio.h>
#include <zmq.h>
#include <string.h>
#include <unistd.h>

#define buffersize 4096

int main(int argc, char* argv[])
{
    // [0]创建对象
    void* ctx = zmq_ctx_new();
    void* server = zmq_socket(ctx, ZMQ_REP);
    // [1]绑定到7766端口
    zmq_bind(server, "tcp://*:7766");

    char buffer[256] = { 0 };

    printf("%s\n", "server listen 7766 ....");
    int i = 0;
    // [2]收发数据,先收再发
    while (1)
    {
        memset(buffer, 0, 256);
        zmq_recv(server, buffer, 256, 0);
        printf("recv: %s\n", buffer);
        sleep(2);
        char szMsg[1024] = {0};
        snprintf(szMsg, sizeof(szMsg), " ack : %3d ", i++);
        zmq_send(server, szMsg, strlen(szMsg), 0);
        printf("send: %s\n", szMsg);
    }

    zmq_close(server);
    zmq_ctx_destroy(ctx);
    return 0;
}

 Client:

#include <zmq.h>
#include "stdio.h"
#include <string.h>
#include <unistd.h>

int main(int argc, char * argv[])
{
    // [0]创建对象
    void* ctx = zmq_ctx_new();
    void* server = zmq_socket(ctx, ZMQ_REQ);
    // [1]连接7766端口
    zmq_connect(server, "tcp://localhost:7766");
    char buffer[256] = { 0 };
    printf("%s\n", "client connect 7766 ...");

    int i = 0;
    // [2]先发数据再收
    while (1)
    {
        char szMsg[1024] = {0};
        snprintf(szMsg, sizeof(szMsg), "hello world : %3d", i++);
        printf("send: %s\n", szMsg);
        if (zmq_send(server, szMsg, strlen(szMsg), 0) < 0) {
            fprintf(stderr, "send message faild\n");
        }
        sleep(2);
        memset(buffer, 0, 256);
        zmq_recv(server, buffer, 256, 0);
        printf("recv: %s\n", buffer);
    }

    zmq_close(server);
    zmq_ctx_destroy(ctx);
    return 0;
}

 

  

 使用REQ-REP套接字发送和接受消息是需要遵循一定规律的。客户端首先使用zmq_send()发送消息,再用zmq_recv()接收,如此循环。如果打乱了这个顺序(如连续发送两次)则会报错。类似地,服务端必须先进行接收,后进行发送。

我们看看运行结果:

我们首先运行client再运行server:

zf@eappsvr-0:~/ds/zmq/test/req_rep> ./send
client connect 7766 ...
send: hello world :   0
recv:  ack :   0
zf@eappsvr-0:~/ds/zmq/test/req_rep> ./recv
server listen 7766 ....
recv: hello world :   0
send:  ack :   0

我们可以看到即使客户端先运行,也可以正常工作的,这就是zmq神奇的地方。

让我简单介绍一下这两段程序到底做了什么。首先,他们创建了一个ZMQ上下文,然后是一个套接字。不要被这些陌生的名词吓到,后面我们都会讲到。服务端将REP套接字绑定到5555端口上,并开始等待请求,发出应答,如此循环。客户端则是发送请求并等待服务端的应答。

这些代码背后其实发生了很多很多事情,但是程序员完全不必理会这些,只要知道这些代码短小精悍,极少出错,耐高压。这种通信模式我们称之为请求-应答模式,是ZMQ最直接的一种应用。

下面我们试着在客户端连续发送两次:

 // [2]先发数据再收
    while (1)
    {
        char szMsg[1024] = {0};
        snprintf(szMsg, sizeof(szMsg), "hello world : %3d", i++);
        printf("send: %s\n", szMsg);
        if (zmq_send(server, szMsg, strlen(szMsg), 0) < 0) {
            fprintf(stderr, "send message faild\n");
        }
        if (zmq_send(server, szMsg, strlen(szMsg), 0) < 0) {
            fprintf(stderr, "send message faild\n");
        }
        sleep(2);
        memset(buffer, 0, 256);
        zmq_recv(server, buffer, 256, 0);
        printf("recv: %s\n", buffer);
    }
// client
client connect 7766 ...
send: hello world :   0
send message faild
recv:  ack :   0 

// server
server listen 7766 ....
recv: hello world :   0
send:  ack :   0

可以看到,第二次发送会失败,提问应答模式,严格控制着发送时序,必须发送-接收-发送-接收,打乱的话,会失败。

关于字符串

 

你可能注意到了我们上面的例子里, 其实客户端与服务端互相传输的数据里, 并没有包含C风格字符串最后一位的'\0'.strlen并不会计算结尾的‘\0’。

我们修改一下服务端buffer的初始值5:

 // [2]收发数据,先收再发
    while (1)
    {
        memset(buffer, 5, 256);
        zmq_recv(server, buffer, 256, 0);
        printf("recv: %s\n", buffer);
        sleep(2);
        char szMsg[1024] = {0};
        snprintf(szMsg, sizeof(szMsg), " ack : %3d ", i++);
        zmq_send(server, szMsg, strlen(szMsg), 0);
        printf("send: %s\n", szMsg);
    }
server listen 7766 ....
recv: hello world :   0
XshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellXshellsend:  ack :   0 

我们可以看到,接收数据就不是我们预想中的值,因为初始值为5,且接收的数据末尾非0,解析字符串就会出错。

对于zmq来说,数据都是字节序列而已, 如何解释这些字节序列, 是使用者的责任. 比如上面, 我们需要在每次接收数据的时候记录接收的数据的大小, 并且在buffer中为接收到的数据之后的一个字节赋值为0, 即人为的把接收到的数据解释为字符串。

 

posted @ 2020-04-18 14:26  Vzf  阅读(295)  评论(0编辑  收藏  举报