libevent 库使用(一)socket实例

编程入门 行业动态 更新时间:2024-10-26 11:14:56

libevent 库使用(一)socket<a href=https://www.elefans.com/category/jswz/34/1771375.html style=实例"/>

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;
}

此主程序中有几个函数需要重点介绍一下:

  1. event_base_new
    函数原型:struct event_base *event_base_new(void);
    函数说明:event_base_new()函数分配并且返回一个新的具有默认设置的event_base。函数会检测环境变量,返回一个到event_base的指针。如果发生错误,则返回NULL。
  2. 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连接介入时,就会进入到其回调函数中,至于其他的参数,这里就不一一介绍了。
  3. event_base_dispatch
    函数原型:int event_base_dispatch(struct event_base *event_base)
    函数说明:该函数会使得之前定义的eventbase运行起来,如果查看源码的话,可以看出,实际该函数调用了event_base_loop函数。至于event_base_loop的作用, 这里就不介绍了,简而言之,event_base_dispatch函数会使之前定义好的环境运行起来。
  4. 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 ;
}

此主程序中有几个函数需要重点介绍一下:

  1. bufferevent_socket_new
    函数说明:通过fd构建新的buffevent
  2. bufferevent_setcb
    函数说明:设置创建的 bufferevent 的回调函数,当socket收到数据时,会进入到libReadEvt回调函数中; 当要发送数据时,会进入到libWriteEvt回调函数中,当出错时,会进入到libErrorEtv回到函数中。
  3. 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实例

本文发布于:2024-03-23 21:58:36,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1743314.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:实例   libevent   socket

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!