• libevent 框架使用例子


    //服务端代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <assert.h>
    #include <unistd.h>
    #include <string.h>

     #include <arpa/inet.h>

    #include <event.h>
    #include <bufferevent.h>

    #define LISTEN_PORT 9999
    #define LISTEN_BACKLOG 32
    #define MAX_LINE 256

    void do_accept(evutil_socket_t listener, short event, void *arg);
    void read_cb(struct bufferevent *bev, void *arg);
    void error_cb(struct bufferevent *bev, short event, void *arg);
    void write_cb(struct bufferevent *bev, void *arg);

    int main(int argc, char *argv[])
    {
    if( argc <= 1 )
    {
    printf( "usage: %s ip_address ", basename( argv[0] ) );
    return 1;
    }

    const char* ip = argv[1];

    evutil_socket_t listener;//用于跨平台表示socket的ID(在Linux下表示的是其文件描述符)
    listener = socket(AF_INET, SOCK_STREAM, 0);
    assert(listener > 0);
    //用于跨平台将socket设置为可重用(实际上是将端口设为可重用)
    evutil_make_listen_socket_reuseable(listener);

    struct sockaddr_in sin;
    bzero( &sin, sizeof( sin ) );
    sin.sin_family = AF_INET;
    //sin.sin_addr.s_addr = 0;
    inet_pton( AF_INET, ip, &sin.sin_addr );
    sin.sin_port = htons(LISTEN_PORT);

    if (bind(listener, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
    perror("bind");
    return 1;
    }

    if (listen(listener, LISTEN_BACKLOG) < 0) {
    perror("listen");
    return 1;
    }

    printf ("Listening... ");
    /* 用于跨平台将socket设置为非阻塞,使用bufferevent需要 */
    evutil_make_socket_nonblocking(listener);
    //主要记录事件的相关属性 创建一个event_base
    struct event_base *base = event_base_new();
    assert(base != NULL);
    /* 创建并绑定一个event */
    struct event *listen_event;
    //参数:event_base, 监听的socket 描述符listener,事件类型及属性,绑定的回调函数,给回调函数的参数
    /*EV_PERSIST参数告诉系统持续的监听sock上的读事件,
    不指定这个属性的话,回调函数被触发后,事件会被删除.所以,如果不加该参数,每次要监听该事件时就要重复的调用event_add函数 */
    listen_event = event_new(base, listener, EV_READ|EV_PERSIST, do_accept, (void*)base);
    /*通过event_add方法启动监听事件
    参数:event,超时时间(struct timeval *类型的,NULL表示无超时设置) */
    event_add(listen_event, NULL);
    /* 进入事件循环,即通过event_base_dispatch方法启动事件循环 */
    event_base_dispatch(base);

    printf("The End.");
    //close(listener);
    return 0;
    }

    void do_accept(evutil_socket_t listener, short event, void *arg)
    {
    struct event_base *base = (struct event_base *)arg;
    evutil_socket_t fd;
    struct sockaddr_in sin;
    socklen_t slen = sizeof(sin);
    fd = accept(listener, (struct sockaddr *)&sin, &slen);
    if (fd < 0) {
    perror("accept");
    return;
    }
    if (fd > FD_SETSIZE) { //这个if是参考了那个ROT13的例子,貌似是官方的疏漏,从select-based例子里抄过来忘了改
    perror("fd > FD_SETSIZE ");
    return;
    }

    printf("ACCEPT: fd = %u ", fd);
    //关联该sockfd,托管给event_base. BEV_OPT_CLOSE_ON_FREE--释放bufferevent时关闭底层传输端口。这将关闭底层套接字,释放底层bufferevent等。
    struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
    //设置读写对应的回调函数
    bufferevent_setcb(bev, read_cb, NULL, error_cb, arg);
    //启用读写事件,其实是调用了event_add将相应读写事件加入事件监听队列poll.
    //如果相应事件不置为true,bufferevent是不会读写数据的
    bufferevent_enable(bev, EV_READ|EV_PERSIST);
    // 进入bufferevent_setcb回调函数:
    // 在read_cb里面从input中读取数据,处理完毕后填充到output中;
    // write_cb对于服务端程序,只需要readcb就可以了,可以置为NULL;
    // error_cb用于处理一些错误信息
    }

    void read_cb(struct bufferevent *bev, void *arg)
    {
    char line[MAX_LINE+1];
    int n;
    //根据之前已经分配好的bufferevent 获取对应的连接描述符
    evutil_socket_t fd = bufferevent_getfd(bev);

    while (n = bufferevent_read(bev, line, MAX_LINE), n > 0)
    {
    line[n] = '';
    printf("fd=%u, Server gets the message from client read line: %s ", fd, line);
    //直接将读取的结果,不做任何修改,直接返回给客户端
    bufferevent_write(bev, line, n);//方案1
    }
    }

    void write_cb(struct bufferevent *bev, void *arg)
    {
    printf("HelloWorld ");//直接空代码即可,因为这里并不会被触发调用
    }

    void error_cb(struct bufferevent *bev, short event, void *arg)
    {
    evutil_socket_t fd = bufferevent_getfd(bev);
    printf("fd = %u, ", fd);
    if (event & BEV_EVENT_TIMEOUT) {
    printf("Timed out "); //if bufferevent_set_timeouts() called
    }
    else if (event & BEV_EVENT_EOF) {
    printf("connection closed ");
    }
    else if (event & BEV_EVENT_ERROR) {
    printf("some other error ");
    }
    bufferevent_free(bev);
    }

    客户端代码:

    //客户端代码

    //客户端代码
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <unistd.h>
    #include <string.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <sys/types.h>

    #include <event2/event.h>
    #include <event2/bufferevent.h>

    #define SERV_PORT 9999
    #define MAX_LINE 1024

    void cmd_msg_cb(int fd, short event, void *arg);
    void read_cb(struct bufferevent *bev, void *arg);
    void error_cb(struct bufferevent *bev, short event, void *arg);

    int main(int argc, char *argv[])
    {
    if(argc < 2)
    {
    perror("usage: echocli <IPadress>");
    return 1;
    }

    evutil_socket_t sockfd;
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
    perror("socket ");
    return 1;
    }

    struct sockaddr_in servaddr;
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr) < 1)
    {
    perror("inet_ntop ");
    return 1;
    }
    if(connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
    {
    perror("connect ");
    return 1;
    }
    evutil_make_socket_nonblocking(sockfd);

    printf("Connect to server sucessfully! ");
    // build event base
    struct event_base *base = event_base_new();
    if(base == NULL)
    {
    perror("event_base ");
    return 1;
    }
    //获取通信模式:这里为epoll
    const char *eventMechanism = event_base_get_method(base);
    printf("Event mechanism used is %s ", eventMechanism);
    printf("sockfd = %d ", sockfd);

    //关联该连接描述符sockfd,托管给event_base.
    struct bufferevent *bev = bufferevent_socket_new(base, sockfd, BEV_OPT_CLOSE_ON_FREE);

    //声明事件
    struct event *ev_cmd;
    //创建一个用于监听标准输入的事件ev_cmd
    ev_cmd = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST, cmd_msg_cb, (void *)bev);
    //添加到事件队列当中.相当于告诉处理IO的管家,当有我的事件到达时你发给我(调用cmd_msg_cb函数)
    event_add(ev_cmd, NULL);

    //设置读写对应的回调函数
    bufferevent_setcb(bev, read_cb, NULL, error_cb, (void *)ev_cmd);
    //使用bufferevent_enable(bev, EV_READ|EV_PERSIST)来启用read事件
    bufferevent_enable(bev, EV_READ | EV_PERSIST);

    event_base_dispatch(base);

    printf("The End.");
    return 0;
    }

    void cmd_msg_cb(int fd, short event, void *arg)
    {
    char msg[MAX_LINE];
    int nread = read(fd, msg, sizeof(msg));
    if(nread < 0)
    {
    perror("stdio read fail ");
    return;
    }

    struct bufferevent *bev = (struct bufferevent *)arg;
    bufferevent_write(bev, msg, nread);
    }

    void read_cb(struct bufferevent *bev, void *arg)
    {
    char line[MAX_LINE + 1];
    int n;
    evutil_socket_t fd = bufferevent_getfd(bev);

    while((n = bufferevent_read(bev, line, MAX_LINE)) > 0)
    {
    line[n] = '';
    printf("fd = %u, read from server: %s", fd, line);
    }
    }

    void error_cb(struct bufferevent *bev, short event, void *arg)
    {
    evutil_socket_t fd = bufferevent_getfd(bev);
    printf("fd = %u, ", fd);
    if(event & BEV_EVENT_TIMEOUT)
    printf("Time out. "); // if bufferevent_set_timeouts() is called
    else if(event & BEV_EVENT_EOF)
    printf("Connection closed. ");
    else if(event & BEV_EVENT_ERROR)
    printf("Some other error. ");
    bufferevent_free(bev);

    struct event *ev = (struct event *)arg;
    event_free(ev);
    }

    一、

    1、编译服务端代码:

    g++ -o server server.cpp -I/usr/libevent/include -I/usr/libevent/include/event2 -levent

    2、编译可客户端代码:

    g++ -o client client.cpp -I/usr/libevent/include -I/usr/libevent/include/event2 -levent

    程序编码注意事项:

    编程编译一定要加上 -I/usr/libevent/include -I/usr/libevent/include/event2 -levent 否则会出现很多未定义和找不到一些头文件的错误

    二、运行程序如果报错

    server: error while loading shared libraries: libevent-2.1.so.4: cannot open shared object file: No such file or directory

    则:

    1、有可能是装了多个 libevent而导致server无法识别哪一个,解决方法就是卸载掉一个libevent

    2、只安装了一个libevent,但是也报这个错,解决方法

         32位系统下:ln -s /usr/libevent/lib/libevent-2.1.so.4  /usr/lib/

       64位系统下:ln -s /usr/libevent/lib/libevent-2.1.so.4  /usr/lib64/

    三、编译报错

    /usr/bin/ld: cannot find -levent
    collect2: ld 返回 1

    则:

    需要在用户目录下的.bash_profile文件加上这一行代码

    vi .bash_profile

    export LIBRARY_PATH=/usr/libevent/lib

    保存退出,然后是文件生效

    source .bash_profile

  • 相关阅读:
    ConcurrentHashMap 内部实现分析
    死锁的产生条件和预防处理
    UVA 11080
    【Gapps】安装GooglePlay引发一系列问题
    HDU4876ZCC loves cards(多校题)
    HDU 3699 A hard Aoshu Problem (暴力搜索)
    coreos docker 尝新奇
    Android 检測网络是否连接
    html练习(5)
    散文说python半篇——景观三元论与盖茨比的对话
  • 原文地址:https://www.cnblogs.com/hgrical/p/6060240.html
Copyright © 2020-2023  润新知