实例"/>
libevent 库使用(一)socket实例
在工作中,之前经常使用tcp通信,每次都要自己写tcp通信程序,管理,比较麻烦,使用socket、bind、listen、connect、send、recv等基础函数虽然能够从最底层管理维护socket通信,但是socket通信一般不会使用其太高深的功能的情况下,libevent库完全可以胜任这项工作。
下面,贴一段源码来简单介绍以下libevent的功能,代码是从公司项目代码中抽离出来的,可能不会编译成功,这里我只是和大家分享一下使用libevent库的一般流程和注意事项,如果大家想要深入了解这个库,建议到官网上查看其用法,不过都是英文文献,望成功!
#include "linuxCommon.h" /* 此头文件包含了linux下通用的头文件*/
#include "event2/listener.h" /* libevent 库头文件 */
#include "event2/bufferevent.h" /* libevent 库头文件 */
#include "event2/event_compat.h" /* libevent 库头文件 */
#include "event2/buffer.h" /* libevent 库头文件 */#define LIBEVENT_NET_PORT (9000) /* libevent 监听端口 */static struct event_base *g_pLibEvtBase = NULL; /* 定义全局eventbase */
static struct evconnlistener *pLibEvtListener = NULL; /* 监听器 */int main(int argc, char *argv[])
{ret = 0;struct evconnlistener *pEvtListener = NULL;struct sockaddr_in svraddr = {0};/* 创建事件集 */g_pLibEvtBase = event_base_new(); if(g_pLibEvtBase == NULL){perror("event_base_new failed; g_pLibEvtBase = %p\n", g_pLibEvtBase);ret = -1;goto EXIT;}svraddr.sin_family = AF_INET;svraddr.sin_port = htons(LIBEVENT_NET_PORT);svraddr.sin_addr.s_addr = htonl(INADDR_ANY);/* 创建监听器: * libAccept: 回调函数* LEV_OPT_REUSEABLE: 套接字关闭,可复用端口* LEV_OPT_CLOSE_ON_FREE:释放监听器时应关闭套接字* LEV_OPT_THREADSAFE:监听器应当被锁定,保证多线程安全* SESSION_MAX:rd模块支持的最大并发链接数量*/pLibEvtListener = evconnlistener_new_bind(g_pLibEvtBase, libAccept, \NULL, LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE | LEV_OPT_THREADSAFE, SESSION_MAX, \(struct sockaddr*)&svraddr, sizeof(struct sockaddr_in));if(pLibEvtListener == NULL){perror("evconnlistener_new_bind err; port = %d \n", LIBEVENT_NET_PORT);ret = -1;goto EXIT;}printf("lib listener init ok. port = %d\n", LIBEVENT_NET_PORT);ret = event_base_dispatch(g_pLibEvtBase); if(ret != 0){perror("event_base_dispatch failed! ret = %d\n", ret);ret = -1;goto EXIT;}EXIT: return ret;
}
此主程序中有几个函数需要重点介绍一下:
- event_base_new
函数原型:struct event_base *event_base_new(void);
函数说明:event_base_new()函数分配并且返回一个新的具有默认设置的event_base。函数会检测环境变量,返回一个到event_base的指针。如果发生错误,则返回NULL。 - evconnlistener_new_bind
函数原型: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连接介入时,就会进入到其回调函数中,至于其他的参数,这里就不一一介绍了。 - event_base_dispatch
函数原型:int event_base_dispatch(struct event_base *event_base)
函数说明:该函数会使得之前定义的eventbase运行起来,如果查看源码的话,可以看出,实际该函数调用了event_base_loop函数。至于event_base_loop的作用, 这里就不介绍了,简而言之,event_base_dispatch函数会使之前定义好的环境运行起来。 - libAccept:这个是用户自定义的libevent回调函数
下面我们来看libAccept这个回调函数:
/*** @fn libAccept* @brief lib event accept回调, 表示建立的连接* @param[in] pEvtListener : listener句柄* fd: 网络套接字* pSockAddr: * socklen:* pUserData: 私有数据* @param[out]* @retval*/
static void libAccept(struct evconnlistener *pEvtListener, evutil_socket_t fd, struct sockaddr *pSockAddr, int socklen, void *pUserData)
{int ret = 0;int sessionIdx = -1;struct bufferevent *pBuffEvt = NULL; struct sockaddr_in clientAddr = {0};unsigned int len = sizeof(clientAddr);int fdOn = 1;int fdKeepIdle = 60;int fdKeepInterval = 20;int fdKeepCount = 3;/* 验证fd的合法性 */if(fd < 0){perror("fd err.\n");ret = -1;goto EXIT;}/*** 添加fd的属性 * SO_KEEPALIVE: 发送保活包* TCP_KEEPIDLE: 空闲时间* TCP_KEEPINTVL: 发送间隔时间* TCP_KEEPCNT: 包的数量*/setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &fdOn, sizeof(fdOn));setsockopt(fd, SOL_SOCKET, TCP_KEEPIDLE, (void*)&fdKeepIdle, sizeof(fdKeepIdle));setsockopt(fd, SOL_SOCKET, TCP_KEEPINTVL, (void*)&fdKeepInterval, sizeof(fdKeepInterval));setsockopt(fd, SOL_SOCKET, TCP_KEEPCNT, (void*)&fdKeepCount, sizeof(fdKeepCount));/*** 通过fd构建新的buffevent * BEV_OPT_CLOSE_ON_FREE:释放bufferevent时关闭底层传输端口.* 这将关闭底层套接字,释放底层bufferevent等.*/pBuffEvt = bufferevent_socket_new(g_pLibEvtBase, fd, BEV_OPT_CLOSE_ON_FREE); if(pBuffEvt == NULL){perror("bufferevent_socket_new failed; fd = %d g_pLibEvtBase = %p\n", fd, g_pLibEvtBase);ret = -1;goto EXIT;}/* 设置创建的 bufferevent 的回调函数 */bufferevent_setcb(pBuffEvt, libReadEvt,libWriteEvt, libErrorEtv, NULL);/* 使能回调函数, EV_PERSIST:当激活时不会被自动清除 */ret = bufferevent_enable(pBuffEvt, EV_READ | EV_PERSIST);if(ret != RD_OK){perror("bufferevent_enable failed!\n");ret = -1;goto EXIT;}printf("libevent accept ok.\n");EXIT:if(ret != -1){perror("libevent accept err.\n");if(pBuffEvt != NULL){bufferevent_free(pBuffEvt);pBuffEvt = NULL;fd = -1;}if(fd > 0){evutil_closesocket(fd); /* 执行特定于平台的调用,close(fd) */fd = -1;}}return ;
}
此主程序中有几个函数需要重点介绍一下:
- bufferevent_socket_new
函数说明:通过fd构建新的buffevent - bufferevent_setcb
函数说明:设置创建的 bufferevent 的回调函数,当socket收到数据时,会进入到libReadEvt回调函数中; 当要发送数据时,会进入到libWriteEvt回调函数中,当出错时,会进入到libErrorEtv回到函数中。 - bufferevent_enable
函数说明:使能回调函数
接下来就要看具体的用户层回调函数了。
/*** @fn libReadEvt* @brief lib event数据读取回调* @param[in] pBev : bufferevent句柄* pSessionID : 会话句柄* @param[out]* @return* @retval* @retval*/
static void libReadEvt(struct bufferevent *pBev)
{ int ret = 0;int recvLen = 0;char buffer[1024] = {0};recvLen = bufferevent_read(pBufev, buffer, sizeof(buffer));EXIT:return;
}/*** @fn libWriteEvt* @brief lib event数据发送回调* @param[in] pBev : bufferevent句柄* pSessionID : 会话句柄* @param[out]* @return* @retval* @retval*/
static void libWriteEvt(struct bufferevent *pBev)
{int ret = 0;/* 入参检查 */if(pBev == NULL){perror("libWriteEvt param error; pBev = %p\n", pBev);}/* 这里的写函数没有实现 */EXIT:return;
}/*** @fn ilbErrorEtv* @brief lib event连接异常状态回调* @param[in] pBev : bufferevent句柄* what : 状态* pCtx : 私有数据* @param[out]* @return* @retval* @retval*/
static void ilbErrorEtv(_IN struct bufferevent *pBev, _IN short what)
{int ret = 0;/* 入参检查 */if(pBev == NULL){perror("libErrorEvt param error; pBev = %p\n", pBev);}if(what & BEV_EVENT_EOF) /* clcient 断开连接 */{perror("connection closed.\n"); }else if(what & BEV_EVENT_TIMEOUT) /* 超时错误 */{perror("timeout.\n");}EXIT:return;
}
总体来说,这个libevent 应用代码并不复杂,主要功能就是管理socket连接,可以释放开发人员的开发精力,毕竟,好多大公司的大型项目都是采用这个库的。
该代码在编译的时候,要连接libevent的库。
eg: gcc test.c -o test -levent
运行编译成功的可执行文件,通过socket测试工具,就可以测试改代码的实用性。
更多推荐
libevent 库使用(一)socket实例
发布评论