zmq与libevent集成及丢包问题分析解决

libevent与socket使用事件方式进行编程的思路是:以zmq的订阅端为例子,订阅端与服务端的连接的fd(文件描述符)加入到libevent的监听队列中,并传入自己的回调函数。正常情况下,当有数据来的时候,libevent会调用用户传入的回调函数,在回调函数里面去将数据取出来即可。


下面需要解决二个问题:

1、如何取得zmq的socket值?

使用getsockopt函数获取。

zmq::socket_t subscribe;
int sock_fd = 0;
size_t len = sizeof(sock_fd);
subscribe.getsockopt(ZMQ_FD, &sock_fd, &len);

2、丢包问题?

当获得之后,按每秒一个包的情况下,能工作得很好,没有问题。但当你发送频率变得很高,每秒几千之后,则出现丢包情况。

分析原因:在于zmq低层采用的是边缘触发机制。当多个数据到达的时候,触发器只会触发一次。

两种机制的区别,大家可以看这篇blog里面说明的:https://funcptr.net/2012/09/10/zeromq---edge-triggered-notification/

解决方案:

当触发回调的时候,需要一次性把数据全部读完。不要只读一次,否则可能存在你多次数据到来的时候,只通知一次的情况。这样情况下,则会导致数据好像“丢失”了。

int zevents = 0;
size_t zevents_len = sizeof(zevents);
zmq_socket.getsockopt(ZMQ_EVENTS, &zevents, &zevents_len);

if (zevents & ZMQ_POLLIN) {
    // We can read from the ZeroMQ socket
}

if (zevents & ZMQ_POLLOUT) {
    // We can write to the ZeroMQ socket
}


下面附上一个完整的不会丢包的代码:

#include 
#include 
#include 
#include "log_time.h"
#include 
#include 

struct event_base* evbase; 

void OnEvent(zmq::socket_t* sock) {
        
    uint32_t events;
    size_t sz_events = sizeof(events);
    sock->getsockopt(ZMQ_EVENTS, &events, &sz_events);
    while(events & ZMQ_POLLIN) {
        zmq::message_t msg;
        bool ok = sock->recv(&msg, ZMQ_NOBLOCK);
        if (!ok) {
            perror("not ok here, return");
                
        }   
        else {
            printf("size = %d, msg = %s\n", msg.size(), msg.data());
        }   
        sock->getsockopt(ZMQ_EVENTS, &events, &sz_events);
    }   
}

void pair_callback(int a, short b, void *point) {
    if (NULL == point) {
        perror("pair is null,  please check you code\n");
        return;
    }   
    zmq::socket_t *sock = (zmq::socket_t *)point;
    OnEvent(sock);
}

int main (int argc, char *argv[])
{
    signal(SIGPIPE, SIG_IGN);
    zmq::context_t context(1);

    //  Socket to talk to server
    zmq::socket_t sub_socket(context, ZMQ_SUB);

    //  Subscribe to zipcode, default is NYC, 10001
    sub_socket.setsockopt(ZMQ_SUBSCRIBE, "", 0);
    //subscriber.connect("ipc://weather.ipc");
    sub_socket.connect("tcp://localhost:6556");


    int sock_fd= 0;
    size_t len = sizeof(sock_fd);
    sub_socket.getsockopt(ZMQ_FD, &sock_fd, &len);
    printf("sock_fd = %d\n", sock_fd);

    evbase = event_base_new();
    printf("event_new\n");
    printf("sub_socket = %p\n", &sub_socket);
    struct event* ev = event_new(evbase, sock_fd, EV_READ|EV_PERSIST, pair_callback, (void*)&sub_socket);
    event_add(ev, NULL);

    event_base_dispatch(evbase);

    return 0;
}




你可能感兴趣的:(C/C++,工作,网络编程,linux)