目录
客户端连接服务器
/*
bev: bufferevent 事件对象 (封装了fd)
address、len : 等同于connect()的参数二、参数三
返回值:连接成功返回0, 发生错误返回-1
*/
int bufferevent_socket_connect(struct bufferevent *bev,
struct socket *address, int addrlen);
服务端监听服务器
创建基于socket的bufferevent
/*
base:event_base
options: 表示bufferevent选项(BEV_OPT_CLOSE_ON_FREE等)的位掩码
*/
struct bufferevent *bufferevent_socket_new(struct event_base *base,
evutil_socket_t fd, enum bufferevent_options options);
基于socket的bufferevent上启动连接
evconnlistener_new_bind(),可完成系统调用socket()、bind()、listen()、accept()的作用
#include<event2/listener.h>
struct evconnlistener *evconnlistener_new_bind(struct event_base *base,
eveconnlistener_cb cb, void *ptr, unsigned flags, int backlog
, const struct sockaddr *sa, int socklen);
<1>LEV_OPT_LEAVE_SOCKETS_BLOCKING
默认情况下,连接监听器接收新套接字后,会将其设置为非阻塞的,以便将其用于libevent。如果不想要这种行为,可以设置这个标志。
<2>LEV_OPT_CLOSE_ON_FREE !!!
如果设置了这个选项,释放连接监听器会关闭底层套接字。
<3>LEV_OPT_CLOSE_ON_EXEC
如果设置了这个选项,连接监听器会为底层套接字设置 close-on-exec 标志。更多信息请查看 fcntl 和FD_CLOEXEC的平台文档。
<4>LEV_OPT_REUSEABLE
某些平台在默认情况下 ,关闭某监听套接字后 ,要过一会儿其他套接字才可以绑定到同一个 端口。设置这个标志会让 libevent 标记套接字是可重用的,这样一旦关闭,可以立即打开其 他套接字,在相同端口进行监听。
<5>LEV_OPT_THREADSAFE !!!
为监听器分配锁,这样就可以在多个线程中安全地使用。
backlog:是任何 时刻网络栈允许处于还未接受状态的最大未决连接数。若backlog 是负的,libevent 会试图挑选一个较好的值 ; 如果为0,libevent 认为已经对提供的套接字调用了listen()。
sa:服务器IP+Port
socklen: sa的大小
回调函数类型:
/*
listener: evconnlistener_new_bind函数的返回值
sock: 用于通信(所谓的cfd)的文件描述符
addr: 客户端的IP+端口
len: addr的len
ptr: 外部ptr传递进来值
成功:返回创建成功的监听器
*/
typedef void (*evconnlistener cb)(
struct evconnlistener *listener,
evutil_socket_t sock,
struct sockaddr *addr,
int len,
void *ptr);
注:该回调函数,不由我们调用,是框架自动调用。因此,只需知晓参数含义即可!!!
释放监听服务器
void evconnlistener_free(struct evconnlistener *lev);
Libevent实现TCP服务器
1、创建event_base
2、创建服务器连接监听器evconnlistener_new_bind();
3、在evconnlistener_new_bind()的回调函数中,处理接受连接后的操作
4、回调函数被调用,说明有一个新客户端连接上来。会得到一个新fd(文件描述符),用于跟客户端通信(读、写)
5、使用bufferevent_socket_new()创建一个新的bufferevent事件,将fd封装到这个事件对象中
6、使用bufferevent_setcb()给这个事件对象的read、write、event设置回调
7、设置bufferevent的读写缓冲区enable / disable
8、接受、发送数据 bufferevent_read() / bufferevent_write()
9、启动循环监听event_base_dispath()
10、释放资源
server端代码实现
#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>
// 读缓冲区回调
void read_cb(struct bufferevent *bev, void *arg)
{
char buf[1024] = {0};
bufferevent_read(bev, buf, sizeof(buf));
printf("client say: %s\n", buf);
char *p = "我是服务器, 已经成功收到你发送的数据!";
// 发数据给客户端
bufferevent_write(bev, p, strlen(p)+1);
sleep(1);
}
// 写缓冲区回调
void write_cb(struct bufferevent *bev, void *arg)
{
printf("I'm服务器, 成功写数据给客户端,写缓冲区回调函数被回调...\n");
}
// 事件
void event_cb(struct bufferevent *bev, short events, void *arg)
{
if (events & BEV_EVENT_EOF)
{
printf("connection closed\n");
}
else if(events & BEV_EVENT_ERROR)
{
printf("some other error\n");
}
bufferevent_free(bev);
printf("buffevent 资源已经被释放...\n");
}
void cb_listener(
struct evconnlistener *listener,
evutil_socket_t fd,
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, fd, BEV_OPT_CLOSE_ON_FREE);
// 给bufferevent缓冲区设置回调
bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);
bufferevent_enable(bev, EV_READ);
}
int main(int argc, const char* argv[])
{
// init server
struct sockaddr_in serv;
memset(&serv, 0, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_port = htons(9876);
serv.sin_addr.s_addr = htonl(INADDR_ANY);
struct event_base* 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*)&serv, sizeof(serv));
event_base_dispatch(base);
evconnlistener_free(listener);
event_base_free(base);
return 0;
}
Libevent实现TCP客户端
1、创建event_base
2、使用bufferevent_socket_new() 创建一个用跟服务器通信的bufferevent事件对象
3、使用bufferevent_socket_connect() 连接服务器
4、使用bufferevent_setcb()给bufferevent对象的read、write、event设置回调
5、设置bufferevent对象的读写缓冲区 enable / disable
6、接受、发送数据 bufferevent_read() / bufferevent_write()
7、启动循环监听event_base_dispath()
8、释放资源
client端代码实现
#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>
void read_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);
}
void write_cb(struct bufferevent *bev, void *arg)
{
printf("----------我是客户端的写回调函数,没什么实际用处\n");
}
void event_cb(struct bufferevent *bev, short events, void *arg)
{
if (events & BEV_EVENT_EOF)
{
printf("connection closed\n");
}
else if(events & BEV_EVENT_ERROR)
{
printf("some other error\n");
}
else if(events & BEV_EVENT_CONNECTED)
{
printf("已经连接服务器...\\(^o^)/...\n");
return;
}
// 释放资源
bufferevent_free(bev);
}
// 客户端与用户交互,从终端读取数据写给服务器
void read_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);
}
int main(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(9876);
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);
return 0;
}