链接监听器-evconnlistener
链接监听器封装了socket通信相关函数,比如socket,bind,listen,accept这几个函数。此时等待新的客户端到来,如果有新的客户端连接,那么内部先调用accept处理,然后调用用户指定的回调函数。
typedef void (*evconnlistener_cb)(struct evconnlistener *, evutil_socket_t, struct sockaddr * cliaddr, int socklen, void *);
struct evconnlistener *evconnlistener_new_bind(struct event_base *base,
evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
const struct sockaddr *sa, int socklen);
不用自己创建socket
/**
Allocate a new evconnlistener object to listen for incoming TCP connections
on a given address.@param base The event base to associate the listener with.
@param cb A callback to be invoked when a new connection arrives. If the
callback is NULL, the listener will be treated as disabled until the
callback is set.这个回调函数触发的时候,链接器已经处理好新的连接了,并将这个新创建的通信文件描述符给这个回调函数。
@param ptr A user-supplied pointer to give to the callback.
@param flags Any number of LEV_OPT_* flags
- LEV_OPT_CLOSE_ON_FREE 关闭时自动释放
- LEV_OPT_REUSEABLE 端口复用
- LEV_OPT_——THREADSAFE 分配锁,线程安全
@param backlog Passed to the listen() call to determine the length of the
acceptable connection backlog. Set to -1 for a reasonable default.backlog是-1,监听器会选择一个合适的值,如果是0,那么监听器认为listen函数已经调用过了。
@param addr The address to listen for connections on.
@param socklen The length of the address.
*/
void evconnlistener_free(struct evconnlistener *lev);
函数说明:释放链接监听器
int evconnlistener_enable(struct evconnlistener *lev);
函数说明:使链接监听器生效
int evconnlistener_disable(struct evconnlistener *lev);
函数说明:使链接监听器失效
/*
This exmple program provides a trivial server program that listens for TCP
connections on port 9995. When they arrive, it writes a short message to
each client connection, and closes each connection once it is flushed.
Where possible, it exits cleanly in response to a SIGINT (ctrl-c).
*/
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#ifndef WIN32
#include <netinet/in.h>
# ifdef _XOPEN_SOURCE_EXTENDED
# include <arpa/inet.h>
# endif
#include <sys/socket.h>
#endif
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>
static const char MESSAGE[] = "Hello, World!\n";
static const int PORT = 9995;
static void listener_cb(struct evconnlistener *, evutil_socket_t,
struct sockaddr *, int socklen, void *);
static void conn_writecb(struct bufferevent *, void *);
static void conn_eventcb(struct bufferevent *, short, void *);
static void signal_cb(evutil_socket_t, short, void *);
int
main(int argc, char **argv)
{
struct event_base *base;/建地基
struct evconnlistener *listener;//链接监听器
struct event *signal_event;//信号事件
struct sockaddr_in sin;
#ifdef WIN32
WSADATA wsa_data;
WSAStartup(0x0201, &wsa_data);
#endif
base = event_base_new();//创建一个地基
if (!base) {
fprintf(stderr, "Could not initialize libevent!\n");
return 1;
}
memset(&sin, 0, sizeof(sin));//没写ip地址,直接置0
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
//创建链接监听器--socket,bind,listen,accept
// listener_cb:回调函数
// LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE:设置端口复用和释放链接监听器时自动关闭监听文件描述符
//-1:监听器自动选择一个合适的值给listen
//(struct sockaddr*)&sin,
//sizeof(sin)--bind()的参数
listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
(struct sockaddr*)&sin,
sizeof(sin));
if (!listener) {
fprintf(stderr, "Could not create a listener!\n");
return 1;
}
//SIGINT信号产生,就会调用signal_cb
signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base);
if (!signal_event || event_add(signal_event, NULL)<0) {
fprintf(stderr, "Could not create/add a signal event!\n");
return 1;
}
//进入事件循环,相当于while(1),等待事件触发,并执行事件的回调函数
event_base_dispatch(base);
//释放
evconnlistener_free(listener);
event_free(signal_event);
event_base_free(base);
printf("done\n");
return 0;
}
//listener:链接监听器
//fd:通信文件描述符
//sa:客户端参数
//user_data:传入参数
static void//static这个函数只能在本文件使用
listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
struct sockaddr *sa, int socklen, void *user_data)
{
struct event_base *base = user_data;
struct bufferevent *bev;
//创建bufferevent缓冲区
// BEV_OPT_CLOSE_ON_FREE:bufferevent释放的时候自动关闭通信文件描述符
bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
if (!bev) {
fprintf(stderr, "Error constructing bufferevent!");
event_base_loopbreak(base);//退出循环,程序结束
return;
}
//设置回调函数:读回调,写回调,事件回调
bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);
bufferevent_enable(bev, EV_WRITE);//使bufferevent设置生效
bufferevent_disable(bev, EV_READ);
bufferevent_write(bev, MESSAGE, strlen(MESSAGE));//把数据写到bufferevent的缓冲区使,触发 conn_writecb回调函数
}
static void
conn_writecb(struct bufferevent *bev, void *user_data)
{
struct evbuffer *output = bufferevent_get_output(bev);//获取写缓冲区指针
if (evbuffer_get_length(output) == 0)//为0表示把所有数据发送
{
printf("flushed answer\n");
bufferevent_free(bev);
}
}
static void
conn_eventcb(struct bufferevent *bev, short events, void *user_data)
{
if (events & BEV_EVENT_EOF) {
printf("Connection closed.\n");
} else if (events & BEV_EVENT_ERROR) {
printf("Got an error on the connection: %s\n",
strerror(errno));/*XXX win32*/
}
/* None of the other events can happen here, since we haven't enabled
* timeouts */
bufferevent_free(bev);
}
static void
signal_cb(evutil_socket_t sig, short events, void *user_data)
{
struct event_base *base = user_data;
struct timeval delay = { 2, 0 };
printf("Caught an interrupt signal; exiting cleanly in two seconds.\n");
event_base_loopexit(base, &delay);
}
改一下,增加读回调函数:
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#ifndef WIN32
#include <netinet/in.h>
# ifdef _XOPEN_SOURCE_EXTENDED
# include <arpa/inet.h>
# endif
#include <sys/socket.h>
#endif
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>
#include<ctype.h>
static const char MESSAGE[] = "Hello, World!\n";
static const int PORT = 9995;
static void listener_cb(struct evconnlistener *, evutil_socket_t,
struct sockaddr *, int socklen, void *);
static void conn_writecb(struct bufferevent *, void *);
static void conn_readcb(struct bufferevent *bev, void *user_data);
static void conn_eventcb(struct bufferevent *, short, void *);
static void signal_cb(evutil_socket_t, short, void *);
int
main(int argc, char **argv)
{
struct event_base *base;
struct evconnlistener *listener;
struct event *signal_event;
struct sockaddr_in sin;
#ifdef WIN32
WSADATA wsa_data;
WSAStartup(0x0201, &wsa_data);
#endif
base = event_base_new();
if (!base) {
fprintf(stderr, "Could not initialize libevent!\n");
return 1;
}
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
(struct sockaddr*)&sin,
sizeof(sin));
if (!listener) {
fprintf(stderr, "Could not create a listener!\n");
return 1;
}
signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base);
if (!signal_event || event_add(signal_event, NULL)<0) {
fprintf(stderr, "Could not create/add a signal event!\n");
return 1;
}
event_base_dispatch(base);
evconnlistener_free(listener);
event_free(signal_event);
event_base_free(base);
printf("done\n");
return 0;
}
static void
listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
struct sockaddr *sa, int socklen, void *user_data)
{
struct event_base *base = user_data;
struct bufferevent *bev;
bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
if (!bev) {
fprintf(stderr, "Error constructing bufferevent!");
event_base_loopbreak(base);
return;
}
bufferevent_setcb(bev, conn_readcb, conn_writecb, conn_eventcb, NULL);
bufferevent_enable(bev, EV_WRITE);
bufferevent_enable(bev, EV_READ);
// bufferevent_disable(bev, EV_READ);
// bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
}
static void
conn_readcb(struct bufferevent *bev, void *user_data)
{
//size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
char buf[128];
int i;
memset(buf,0x00,sizeof(buf));
int n=bufferevent_read(bev,buf,sizeof(buf));/从bev的读缓存区读数据
printf("n==[%d],buf==[%s]\n",n,buf);
for(i=0;i<n;i++)
{
buf[i]=toupper(buf[i]);
}
//int bufferevent_write(struct bufferevent *bufev,
// const void *data, size_t size);
bufferevent_write(bev,buf,n);//向bev的写缓冲区写数据,当数据到内核时调用conn_writecb函数
}
static void
conn_writecb(struct bufferevent *bev, void *user_data)
{
struct evbuffer *output = bufferevent_get_output(bev);
if (evbuffer_get_length(output) == 0) {
printf("flushed answer\n");
// bufferevent_free(bev);
}
}
static void
conn_eventcb(struct bufferevent *bev, short events, void *user_data)
{
if (events & BEV_EVENT_EOF) {
printf("Connection closed.\n");
} else if (events & BEV_EVENT_ERROR) {
printf("Got an error on the connection: %s\n",
strerror(errno));/*XXX win32*/
}
/* None of the other events can happen here, since we haven't enabled
* timeouts */
bufferevent_free(bev);
}
static void
signal_cb(evutil_socket_t sig, short events, void *user_data)
{
struct event_base *base = user_data;
struct timeval delay = { 2, 0 };
printf("Caught an interrupt signal; exiting cleanly in two seconds.\n");
event_base_loopexit(base, &delay);
}
结果:客户端连接后链接监听器accept出一个新的通信文件描述符cfd,并把cfd传入回调函数listener_cb,该回调函数为cfd创建一个bufferevent事件节点,并给这个事件创建了conn_readcb(数据从内核缓冲区到bufferevent的读缓冲区时触发), conn_writecb(数据从bufferevent的写缓冲区到内核缓冲区)两个回调函数。