libevent客户端与服务端demo

libevent库tcp基本结构与使用流程

一、基本数据类型(结构)

1)evutil_socket_t:网络文件描述符

   libevent中定义为:#define evutil_socket_t int,与linux下定义相同,操作应该也相同。

2)struct event_base:事件基本结构

  该结构用于保存libevent事件分发循环的的信息和状态,是libevent的核心。每个应用都应有一个。它保持对挂起和活跃事件的监听,并把处于就绪态(活跃)的事件告知(通知)应用。
常用接口:

接口描述
struct event_base *event_base_new(void)获取event_base实例
int event_base_dispatch(struct event_base *)为event_base分配循环,直到无挂起或就绪事件,或调用接口终止时停止
int event_base_loopbreak(struct event_base *)立刻退出活跃的event_base循环
void event_base_free(struct event_base *)释放event_base占用空间

3)struct bufferevent:缓冲区事件

  非透明结构,用于控制缓冲区读写。
相关宏定义标志:

名称含义
BEV_EVENT_READING读取过程中发生错误
BEV_EVENT_WRITING写入过程中发生错误
BEV_EVENT_EOF达到文件尾
BEV_EVENT_ERROR发生不可恢复错误
BEV_EVENT_TIMEOUT用户指定定时器过期
BEV_EVENT_CONNECTED连接操作完成

读取回调句柄类型:
typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
事件、错误回调句柄类型:
typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short what, void *ctx);
常用接口:

定义描述
int bufferevent_base_set(struct event_base *base, struct bufferevent *bufev)将bufferevent分配给指定的event
struct event_base *bufferevent_get_base(struct bufferevent *bev)获取bufferevent对应的event_base
void bufferevent_free(struct bufferevent *bufev)释放bufferevent占用的内存
void bufferevent_setcb(struct bufferevent *bufev, bufferevent_data_cb readcb,bufferevent_data_cb writecb, bufferevent_event_cb eventcb, void *cbarg)设置bufferevent的事件回调句柄
int bufferevent_setfd(struct bufferevent *bufev, evutil_socket_t fd)设置bufferevent的socket描述符
int bufferevent_write(struct bufferevent *bufev,const void *data, size_t size)向对应socket写入数据,数据被挂起再输出缓冲区,当缓冲区变为就绪态是自动写入
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size)读取bufferevent对应socket中的数据
int bufferevent_enable(struct bufferevent *bufev, short event)设置bufferevent可用事件:EV_READ
int bufferevent_socket_connect(struct bufferevent *, const struct sockaddr *, int)连接addr中指定的ip和端口

4) struct evconnlistener:socket监听

   非透明结构,用于管理服务端socket的监听:新的连接请求和断开。
新连接请求回调类型:
typedef void (*evconnlistener_cb)(struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *);
错误回调类型:
typedef void (*evconnlistener_errorcb)(struct evconnlistener *, void *);
常用宏定义:

含义
LEV_OPT_CLOSE_ON_FREE释放监听的时候,关闭监听套接字
LEV_OPT_REUSEABLE设置套接字可复用

常用接口

接口描述
struct evconnlistener *evconnlistener_new(struct event_base *base, evconnlistener_cb cb, void *ptr, unsigned flags, int backlog, evutil_socket_t fd)创建监听实例:基于已存在的监听socket
struct evconnlistener *evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb, void *ptr, unsigned flags, int backlog, const struct sockaddr *sa, int socklen)创建新的监听实例,并绑定到指定IP和端口
void evconnlistener_free(struct evconnlistener *lev)关闭并释放监听占用内存
int evconnlistener_enable(struct evconnlistener *lev)打开被关闭的监听
int evconnlistener_disable(struct evconnlistener *lev)关闭监听
void evconnlistener_set_cb(struct evconnlistener *lev, evconnlistener_cb cb, void *arg)设置监听回调
void evconnlistener_set_error_cb(struct evconnlistener *lev, evconnlistener_errorcb errorcb)设置错误回调

二、大致使用流程

1)服务端

  创建基础事件 -->创建基于基础事件的监听(同时注册监听新连接回调)–>注册监听错误处理回调 -->为基础事件分配循环 --> … --> 终止事件循环 -->释放事件空间

2)客户端

  创建基础事件 -->初始化socket(设置非阻塞) -->初始化服务端地址信息 --> 创建基于前置资源的socket bufferevent事件 --> 注册bufferevent事件回调(I/O,ERR) -->为基础事件分配循环 --> …IO业务 -->
停止基础业务循环 -->释放基础业务空间

三、实例代码

1)服务端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

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

#define PORT 8000
#define BUF_SIZE 1024

const char *reply = "Hello client, I've received your data.";

//socket读回调
void read_cb(struct bufferevent *bev, void *ctx)
{
    //读取数据
    char buf[BUF_SIZE] = {0x00};
    size_t len = 0;
    printf("Receive data:");
    len = bufferevent_read(bev, buf, BUF_SIZE);
    while(0 < len)
    {
        printf("%s\n", buf);
        if(0 == strcmp(buf, "stop"))
        {
            printf("Exit.\n");
            exit(0);
        }
        memset(buf, 0x00, 1024);
        len = bufferevent_read(bev, buf, BUF_SIZE);
    }
    printf("\n");
    //回复数据
    bufferevent_write(bev, reply, strlen(reply));
}
//事件回调
void event_cb(struct bufferevent *bev, short events, void *ctx)
{
    //连接断开
    if(BEV_EVENT_EOF & events)
    {
        printf("One connection has closed.\n");
        //释放事件
        bufferevent_free(bev);
    }
    if(BEV_ERROR & events)
    {
        printf("Error occured.\n");
        bufferevent_free(bev);
    }
}
//监听新连接回调
void accept_new_connection_cb(struct evconnlistener *listener, evutil_socket_t sock, struct sockaddr *addr, int socklen, void *ctx)
{
    //获取管理监听事件的基础事件
    struct event_base *base = evconnlistener_get_base(listener);
    //将新链接加入基础事件:设置释放事件时关闭连接
    struct bufferevent *bev = bufferevent_socket_new(base, sock, BEV_OPT_CLOSE_ON_FREE);
    //设置新连接读取、事件回调
    bufferevent_setcb(bev, read_cb, NULL, event_cb, NULL);
    //开启新连接读取
    bufferevent_enable(bev, EV_READ| EV_WRITE);
}
//监听异常回调
void error_cb(struct evconnlistener *listener, void *ctx)
{
    int err = EVUTIL_SOCKET_ERROR();
    struct event_base *base = evconnlistener_get_base(listener);
    printf("Error occured on listener: %d: %s\n", err, evutil_socket_error_to_string(err));
    //退出服务端基础事件循环:终止服务、
    event_base_loopexit(base, NULL);
}

int main(int argc, char **argv)
{
    struct sockaddr_in addr;
    struct event_base *base = NULL;
    struct evconnlistener *listener = NULL;
    //初始化服务端地址信息
    memset(&addr, 0x00, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(PORT);
    //1.创建基础事件实例
    base = event_base_new();
    if(NULL == base)
    {
        printf("Create instance of event_base failed.\n");
        return -1;
    }
    //2.创建基于该基础事件与地址信息的监听
    listener = evconnlistener_new_bind(base, accept_new_connection_cb, NULL, LEV_OPT_CLOSE_ON_FREE| LEV_OPT_REUSEABLE, -1, (struct sockaddr*)(&addr), sizeof(addr));
    if(NULL == listener)
    {
        printf("Create instance of evconnlistener failed.\n");
        return -1;
    }
    //设置监听错误回调
    evconnlistener_set_error_cb(listener, error_cb);
    //3.开启服务
    printf("Server starts.\n");
    event_base_dispatch(base);
    return 0;
}

2)客户端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>

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

#define BUF_SIZE 1024
#define SERVER_IP "192.168.56.11"
#define SERVER_PORT 8000

const char *call = "Hello, my remote server.";
int cnt = 5;

//读取回调
void read_cb(struct bufferevent *bev, void *ctx)
{
    //读取数据
    char buf[BUF_SIZE] = {0x00};
    size_t len = 0;
    printf("Receive data:\n");
    len = bufferevent_read(bev, buf, BUF_SIZE);
    while(0 < len)
    {
        printf("%s", buf);
        memset(buf, 0x00, 1024);
        len = bufferevent_read(bev, buf, BUF_SIZE);
    }
    printf("\n");
}
//写回调
void write_cb(struct bufferevent *bev, void *ctx)
{
    if(cnt--)
    {
        printf("Send data to server.\n");
        if(-1 == bufferevent_write(bev, call, strlen(call)))
        {
            printf("Send data to server failed.\n");
        }
        sleep(5);
    }
    else
    {
        printf("send stop");
        bufferevent_write(bev, "stop", strlen("stop"));
    }
}

//事件回调
void event_cb(struct bufferevent *bev, short events, void *ctx)
{
    if(events & BEV_EVENT_ERROR)
    {
        perror("Error from bufferevent.");
    }
    if(events & (BEV_EVENT_EOF | BEV_EVENT_ERROR))
    {
        bufferevent_free(bev);
    }
}

int main(int argc, char **argv)
{
    struct sockaddr_in addr;
    struct event_base *base = NULL;
    struct bufferevent *event = NULL;
    int sock = -1;
    //初始化服务端地址信息
    memset(&addr, 0x00, sizeof(addr));
    addr.sin_family = AF_INET;
    inet_aton(SERVER_IP, &addr.sin_addr);
    addr.sin_port = htons(SERVER_PORT);
    //1.创建基础事件实例
    base = event_base_new();
    if(NULL == base)
    {
        printf("Create instance of event_base failed.\n");
        return -1;
    }
    //2.创建基于此基本事件的缓存事件
    //设置非阻塞socket
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == sock)
    {
        printf("Create socket file descriptor failed.\n");
        return -1;
    }
    evutil_make_socket_nonblocking(sock);
    event = bufferevent_socket_new(base, sock, BEV_OPT_CLOSE_ON_FREE);
    //开启读写
    bufferevent_enable(event, EV_READ| EV_WRITE);
    //设置回调
    bufferevent_setcb(event, read_cb, write_cb, event_cb, NULL);
    //3.连接服务端
    if(0 != bufferevent_socket_connect(event, (struct sockaddr*)(&addr), sizeof(addr)))
    {
        printf("Connect to server failed.\n");
        return -1;
    }
    //4.为基本事件分配循环:开启客户端
    bufferevent_write(event, call, strlen(call));
    event_base_dispatch(base);
    return 0;
}

3)makefile

CC=gcc
LIB_DIR=-L/usr/local/lib
INCLUDE_DIR=-I/usr/local/include/event2
CC_FLAGS=-w 
LIBS=-levent
#执行可执行文件前,再当前目录执行:export LD_LIBRARY_PATH=/usr/local/lib 即:libevent库路径
all:server client
	@echo "Generate libevent server and client."
server:
	$(CC) $(CC_FLAGS) $(INCLUDE_DIR) $(LIB_DIR) $(LIBS)  server.c -o server.out
client:
	$(CC) $(CC_FLAGS) $(INCLUDE_DIR) $(LIB_DIR) $(LIBS)  client.c -o client.out
.PHONY:clean
clean:
	rm -f *.out

四、测试结果

1)服务端

[pig@localhost libevent]$ ls
client.c  makefile  server.c
[pig@localhost libevent]$ make server
gcc -w  -I/usr/local/include/event2 -L/usr/local/lib -levent  server.c -o server.out
[pig@localhost libevent]$ export LD_LIBRARY_PATH=/usr/local/lib
[pig@localhost libevent]$ ./server.out 
Server starts.
Receive data:Hello, my remote server.

Receive data:Hello, my remote server.

Receive data:Hello, my remote server.

Receive data:Hello, my remote server.

Receive data:Hello, my remote server.

Receive data:Hello, my remote server.

Receive data:stop
Exit.

2)客户端

[pig@localhost libevent]$ ls
client.c  makefile  server.c  server.out
[pig@localhost libevent]$ make client
gcc -w  -I/usr/local/include/event2 -L/usr/local/lib -levent  client.c -o client.out
[pig@localhost libevent]$ export LD_LIBRARY_PATH=/usr/local/lib
[pig@localhost libevent]$ ./client.out 
Send data to server.
Send data to server.
Receive data:
Hello client, I've received your data.Hello client, I've received your data.
Send data to server.
Send data to server.
Receive data:
Hello client, I've received your data.Hello client, I've received your data.
Send data to server.
send stopReceive data:
Hello client, I've received your data.
Send data to server.
Send data to server.
Receive data:
Hello client, I've received your data.

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单的libevent TCP客户端收发数据的示例代码: ```c #include <event2/event.h> #include <event2/buffer.h> #include <event2/bufferevent.h> #include <iostream> #include <string.h> using namespace std; void read_cb(bufferevent* bev, void* arg) { char buf[1024]; size_t len = bufferevent_read(bev, buf, sizeof(buf)); buf[len] = '\0'; cout << "Received message: " << buf << endl; } void write_cb(bufferevent* bev, void* arg) { cout << "Message sent!" << endl; } void event_cb(bufferevent* bev, short events, void* arg) { if (events & BEV_EVENT_CONNECTED) { cout << "Connected to server!" << endl; bufferevent_write(bev, "Hello, server!\n", strlen("Hello, server!\n")); } else if (events & BEV_EVENT_ERROR) { cout << "Error: " << evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()) << endl; event_base_loopbreak(bufferevent_get_base(bev)); } } int main(int argc, char** argv) { struct event_base* base = event_base_new(); struct bufferevent* bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL); bufferevent_enable(bev, EV_READ|EV_WRITE); bufferevent_socket_connect_hostname(bev, NULL, AF_UNSPEC, "localhost", 12345); event_base_dispatch(base); bufferevent_free(bev); event_base_free(base); return 0; } ``` 在这个示例中,我们创建了一个libevent的事件循环`base`和一个TCP客户端`bev`。我们通过`bufferevent_socket_new()`函数创建了一个`bufferevent`对象,它封装了一个TCP连接。我们使用`bufferevent_setcb()`函数设置了当`bev`有读、写和事件发生时的回调函数。然后使用`bufferevent_enable()`函数启用读、写事件。接着使用`bufferevent_socket_connect_hostname()`函数连接到服务器。 当连接成功建立时,会触发`event_cb()`函数中的`BEV_EVENT_CONNECTED`事件,我们可以在这里发送一条消息给服务器,例如"Hello, server!\n"。当服务器回复消息时,会触发`read_cb()`函数,我们可以在这里读取服务器发送的消息。当发生错误时,会触发`event_cb()`函数中的`BEV_EVENT_ERROR`事件,我们可以在这里输出错误信息并停止事件循环。 需要注意的是,我们在程序结束时需要手动释放`bev`和`base`对象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值