0
点赞
收藏
分享

微信扫一扫

Linux c libevent库实现TCP服务器

1、带缓冲区事件函数使用流程

安装: Linux c libevent库安装(简单使用)

原理: bufferevent 有两个缓冲区:也是队列实现,读走没,先进先出 读缓冲:有数据-->读回调函数被调用-->使用bufferevent_read()函数读-->读数据 写缓冲:使用bufferevent_write()-->向写缓冲中写数据(写缓冲一旦有数据就会将数据发给对端,然后在调用设置的写回调函数,所以写回调有点鸡肋)-->该缓冲区有数据自动写出 ->写完,回调函数被调用

1.1、服务端基本流程

1、创建底座(创建框架)
struct event_base*event_base_new(void);

2、创建监听器
监听器中封装了(socket,bind,listen,accept)四个函数
struct evconnlistener*evconnlistener_new_bind(
    struct event_base* base,
    evconnlistener_cb cb,
    void* ptr
    unsigned flags,
    int backlog,
    const struct sockaddr* sa,
    int socklen
);
// 回调函数类型:
typedefvoid(*evconnlistener_cb)(
    struct evconnlistener* listener,
    evutil_socket_t sock,
    struct sockaddr* addr,
    int len,
    void* ptr
)
listener:   evconnlistener_new_bind函数的返回值
sock:用于通信的文件描述符
addr:客户端的ip+短裤
len:   addr的len
ptr:外部ptr传递进来值
// 该回调函数,不由我们调用,是框架自动调用,因此,只需知晓参数函数即可

参数:
    base:event_base_new函数返回值(底座)
    cb:回调函数一旦被回调,说明在其内部应该与客户端建立连接完成
    ptr:回调函数参数
    flags:
        LEV_OPT_CLOSE_ON_FREE 关闭的是服务器端的监听socket
        LEV_OPT_REUSEABLE  设置端口复用
    backlog:listen函数参数2,-1表示最大值
    sa:服务器地址结构
    socklen:服务器地址结构大小
返回值:
成功:创建监听器

3、创建事件
struct bufferevent*bufferevent_socket_new(struct event_base* base,evutil_socket_t fd,enum bufferevent_options options);
参数:
    base:event_base
    fd:封装到bufferevent内的fd
    options:BEV_OPT_CLOSE_ON_FREE  // 释放底层套接字(默认填)
返回值:
成功创建buffervent事件对象

4、给事件设置回调函数(在常规事件中,是由一个函数完成的,这里需要这两步才能完成事件创建)
voidbufferevent_setcb(struct bufferevent* bufev,
                        bufferevent_data_cb readcb,
                        bufferevent_data_cb writecb,
                        bufferevent_event_cb eventcb,
                        void* cbarg);
读写回调函数类型:
typedefvoid(*bufferevent_data_cb)(struct bufferevent* bev,void* ctx);
事件回调函数类型:
typedefvoid(*bufferevent_event_cb)(struct bufferevent* bev,short events,void* ctx);
参数:
    bufev:bufferevent_socket_new()函数的返回值
    readcb:读缓冲对应的回调(当)
    writecb:写缓冲设置回调
    evencb:NULL(设置事件回调)
    cbarg :回调函数用的参数

5、启动缓冲区(默认:write是开的,read缓冲是关闭的)
voidbufferevent_enable(struct bufferevent* bufev,short events);
参数:
    bufev:bufferevent_socket_new返回值
    events:
        EV_READ 打开读
        EV_WRITE 打开写
        EV_READ|EV_WRITE 打开读写

6、循环监听底座上面的事件
intevent_base_dispatch(struct event_base* base);

7、销毁监听器
voidevconnlistener_free(struct evconnlistener* listener)

8、销毁底座
voidevent_base_free(struct event_base* base);

2、代码

2.1、server.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>


#define PORT 9999

// 读缓冲区有数据,回调函数自动被触发
voidread_cb(struct bufferevent* bev,void* arg)
{
char buf[1024]={0};
// 相当于read,因为文件描述符被封装到了bev中,使用使用bufferevent_read
    bufferevent_read(bev,buf,sizeof(buf));
printf("client say:%s\n",buf);

char* p ="服务器:已经收到数据!!!";
// 写给客户端
// 向bufferevent写缓冲区中写数据,写缓冲一旦有数据就会将数据发给对端,然后调写回调函数
    bufferevent_write(bev,p,strlen(p)+1);
    sleep(1);
}

voidwrite_cb(struct bufferevent* bev,void* arg)
{
printf("成功写数据给客户端、写缓冲区回调函数被回调\n");
}

// 事件回调
voidevent_cb(struct bufferevent* bev,short events,void* arg)
{
if(events & BEV_EVENT_EOF)
{
printf("connection closed \n");
}
elseif(events & BEV_EVENT_ERROR)
{
printf("some other error\n");
}
    bufferevent_free(bev);
printf("bufferevent 资源已经被释放---\n");
}

// 监听器回调函数  (表示有客户端连接请求)
voidcb_listener(struct evconnlistener* listener,
                evutil_socket_t sock,
                struct sockaddr* addr,
                int len,
                void* ptr)
{
printf("connect new client\n");
struct event_base* base =(struct event_base*)ptr;
// 添加新事件
struct bufferevent* bev;
    bev = bufferevent_socket_new(base,sock,BEV_OPT_CLOSE_ON_FREE);

// 给bufferevent缓冲区设置回调函数
    bufferevent_setcb(bev,read_cb,write_cb,event_cb,NULL);
// 默认读缓冲是关闭的
    bufferevent_enable(bev,EV_READ);
}

intmain(int argc,const char* argv[])
{
struct sockaddr_in ser_addr;
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_port = htons(PORT);
    ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);

// 创建底座
struct event_base* base = event_base_new();

// 创建监听器(套接字,绑定接受请求连接)
struct evconnlistener* listener;
    listener = evconnlistener_new_bind(base,
                                        cb_listener,
                                        base,
                                        LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE,
36,(struct sockaddr*)&ser_addr,sizeof(ser_addr));


// 启动循环监听
    event_base_dispatch(base);
    evconnlistener_free(listener);
    event_base_free(base);

return0;
}

2.2、client.c

客户端流程:
1、创建socket
2、创建带缓冲区事件
3、建立连接
int bufferevent_socket_connect(struct bufferevent* bev,struct sockaddr* address,int addrlen);
参数:
    bev:bufferevent 事件对象(封装了文件描述符)
    address、len(服务器地址结构和长度):等同于connect参数2,3
4、设置事件回调函数(read、write、event)
5、循环监听
5、释放

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <event2/bufferevent.h>
#include <event2/event.h>
#include <arpa/inet.h>

voidread_cb(struct bufferevent *bev, void *arg)
{
char buf[1024]={0};
    bufferevent_read(bev, buf,sizeof(buf));

printf("fwq say:%s\n", buf);

    bufferevent_write(bev, buf,strlen(buf)+1);
    sleep(1);
}

voidwrite_cb(struct bufferevent *bev, void *arg)
{
printf("----------我是客户端的写回调函数,没卵用\n");
}

voidevent_cb(struct bufferevent *bev, short events, void *arg)
{
if(events & BEV_EVENT_EOF)
{
printf("connection closed\n");
}
elseif(events & BEV_EVENT_ERROR)
{
printf("some other error\n");
}
elseif(events & BEV_EVENT_CONNECTED)
{
printf("已经连接服务器...\\(^o^)/...\n");
return;
}

// 释放资源
    bufferevent_free(bev);
}

// 客户端与用户交互,从终端读取数据写给服务器
voidread_terminal(evutil_socket_t fd, short what, void *arg)
{
// 读数据
char buf[1024]={0};
int len = read(fd, buf,sizeof(buf));

struct bufferevent* bev =(struct bufferevent*)arg;
// 发送数据
    bufferevent_write(bev, buf, len+1);
}

intmain(int argc, const char* argv[])
{
struct event_base* base =NULL;
    base = event_base_new();

int fd = socket(AF_INET, SOCK_STREAM,0);

// 通信的fd放到bufferevent中
struct bufferevent* bev =NULL;
    bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);

// init server info
struct sockaddr_in serv;
memset(&serv,0,sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_port = htons(9999);
    inet_pton(AF_INET,"127.0.0.1",&serv.sin_addr.s_addr);

// 连接服务器
    bufferevent_socket_connect(bev,(struct sockaddr*)&serv,sizeof(serv));

// 设置回调
    bufferevent_setcb(bev, read_cb, write_cb, event_cb,NULL);

// 设置读回调生效
// bufferevent_enable(bev, EV_READ);

// 创建事件
struct event* ev = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST,
                                 read_terminal, bev);
// 添加事件                     
    event_add(ev,NULL);

    event_base_dispatch(base);

    event_free(ev);

    event_base_free(base);

return0;
}


举报

相关推荐

0 条评论