ZeroMQ---请求回应模式01

ZeroMQ简述

zeromq是一种基于消息队列的多线程网络库,其对套接字类型、连接处理、帧、甚至路由的底层细节进行抽象,提供跨越多种传输协议的套接字。
ZeroMQ 把通讯的需求看成四类。其中一类是一对一结对通讯,用来支持传统的 TCP socket 模型,但并不推荐使用。常用的通讯模式只有三类:

  1. 请求回应模型。由请求端发起请求,并等待回应端回应请求。请求端和回应端都可以是 1:N 的模型。通常把 1 认为是 server ,N 认为是 Client 。ZeroMQ 可以很好的支持路由功能(实现路由功能的组件叫作 Device),把 1:N 扩展为 N:M (只需要加入若干路由节点)。从这个模型看,更底层的端点地址是对上层隐藏的。每个请求都隐含有回应地址,而应用则不关心它。
  2. 发布订阅模型。发布端是单向只发送数据的,且不关心是否把全部的信息都发送给订阅端。如果发布端开始发布信息的时候,订阅端尚未连接上来,这些信息直接丢弃。不过一旦订阅端连接上来,中间会保证没有信息丢失。订阅端则只负责接收,而不能反馈。如果发布端和订阅端需要交互(比如要确认订阅者是否已经连接上),则使用额外的 socket 采用请求回应模型满足这个需求。
  3. 管道模型。这个模型里,管道是单向的,从 PUSH 端单向的向 PULL 端单向的推送数据流。

请求回应模型代码

  1. 服务端:zmq_ctx_new() -> zmq_socket() -> zmq_bind() -> zmq_recv() -> zmq_send()
  2. 客户端:zmq_ctx_new() -> zmq_socket() -> zmq_bind() -> zmq_send() -> zmq_recv()
客户端:
#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:8080");

    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://*:8080");

    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

部分源码

  1. zmq_ctx_new()
//返回ctx_t对象
void *zmq_ctx_new (void)
{
    //  We do this before the ctx constructor since its embedded mailbox_t
    //  object needs the network to be up and running (at least on Windows).
    if (!zmq::initialize_network ()) {
        return NULL;
    }

    //  Create 0MQ context.
    zmq::ctx_t *ctx = new (std::nothrow) zmq::ctx_t;
    if (ctx) {
        if (!ctx->valid ()) {
            delete ctx;
            return NULL;
        }
    }
    return ctx;
}
  1. zmq_socket()
void *zmq_socket (void *ctx_, int type_)
{
//对象为NULL,则返回
    if (!ctx_ || !(static_cast<zmq::ctx_t *> (ctx_))->check_tag ()) {
        errno = EFAULT;
        return NULL;
    }
    
    zmq::ctx_t *ctx = static_cast<zmq::ctx_t *> (ctx_);
    zmq::socket_base_t *s = ctx->create_socket (type_);
    return (void *) s;
}

调用create_socket() :

zmq::socket_base_t *zmq::ctx_t::create_socket (int type_)
{
    scoped_lock_t locker (_slot_sync);
// 初始化邮箱数组,增加两个插槽(slots),
// zmq_ctx_term thread 和 reaper thread
    if (unlikely (_starting)) {
        if (!start ())
            return NULL;
    }

    //  Once zmq_ctx_term() was called, we can't create new sockets.
    if (_terminating) {
        errno = ETERM;
        return NULL;
    }
    //  If max_sockets limit was reached, return error.
    if (_empty_slots.empty ()) {
        errno = EMFILE;
        return NULL;
    }
    //  Choose a slot for the socket.
    uint32_t slot = _empty_slots.back ();
    _empty_slots.pop_back ();
    //  Generate new unique socket ID.
//生成新的唯一的套接字ID。
    int sid = (static_cast<int> (max_socket_id.add (1))) + 1;
    //  Create the socket and register its mailbox.
//创建socket并且注册邮箱
    socket_base_t *s = socket_base_t::create (type_, this, slot, sid);
    if (!s) {
        _empty_slots.push_back (slot);
        return NULL;
    }
    _sockets.push_back (s);
    _slots[slot] = s->get_mailbox ();
    return s;
}

调用start ()函数 :

bool zmq::ctx_t::start()
{
    //  Initialise the array of mailboxes. Additional two slots are for
    //  zmq_ctx_term thread and reaper thread.
    _opt_sync.lock();
    const int term_and_reaper_threads_count = 2;
    const int mazmq = _max_sockets; //1023
    const int ios = _io_thread_count;//1
    _opt_sync.unlock();
    int slot_count = mazmq + ios + term_and_reaper_threads_count;//1026
    try {
        //增加容量,不创建对象
        _slots.reserve(slot_count);
        _empty_slots.reserve(slot_count - term_and_reaper_threads_count);
    }
    catch (const std::bad_alloc&) {
        errno = ENOMEM;
        return false;
    }
    //改变了容器的大小,且创建了容器中的对象
    _slots.resize(term_and_reaper_threads_count);
    //  Initialise the infrastructure for zmq_ctx_term thread.
    _slots[term_tid] = &_term_mailbox;
    // 创建线程
    _reaper = new (std::nothrow) reaper_t(this, reaper_tid);
    if (!_reaper) {
        errno = ENOMEM;
        goto fail_cleanup_slots;
    }
    if (!_reaper->get_mailbox()->valid())
        goto fail_cleanup_reaper;
    _slots[reaper_tid] = _reaper->get_mailbox();
    _reaper->start();

    //  Create I/O thread objects and launch them.
    _slots.resize(slot_count, NULL);
    //创建IO线程并启动
    for (int i = term_and_reaper_threads_count;
        i != ios + term_and_reaper_threads_count; i++) {
        io_thread_t* io_thread = new (std::nothrow) io_thread_t(this, i);
        if (!io_thread) {
            errno = ENOMEM;
            goto fail_cleanup_reaper;
        }
        if (!io_thread->get_mailbox()->valid()) {
            delete io_thread;
            goto fail_cleanup_reaper;
        }
        _io_threads.push_back(io_thread);
        _slots[i] = io_thread->get_mailbox();
        io_thread->start();
    }
    //  In the unused part of the slot array, create a list of empty slots.
    for (int32_t i = static_cast<int32_t> (_slots.size()) - 1;
        i >= static_cast<int32_t> (ios) + term_and_reaper_threads_count; i--) {
        _empty_slots.push_back(i);
    }
    _starting = false;
    return true;
fail_cleanup_reaper:
    _reaper->stop();
    delete _reaper;
    _reaper = NULL;

fail_cleanup_slots:
    _slots.clear();
    return false;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值