libevent实践07:监听服务器并管理客户端

news2024/11/16 13:29:07

简介

函数bufferevent_new

struct bufferevent *
bufferevent_new(evutil_socket_t fd,
    bufferevent_data_cb readcb, bufferevent_data_cb writecb,
    bufferevent_event_cb eventcb, void *cbarg)

参数说明:

fd:新客户端的文件描述符

readcb:一个函数指针,bufferevent_data_cb类型,读回调函数

writecb:一个函数指针,bufferevent_data_cb类型,写回调函数

eventcb:一个函数指针,bufferevent_event_cb类型,错误回调函数

/**
   A read or write callback for a bufferevent.

   The read callback is triggered when new data arrives in the input
   buffer and the amount of readable data exceed the low watermark
   which is 0 by default.

   The write callback is triggered if the write buffer has been
   exhausted or fell below its low watermark.

   @param bev the bufferevent that triggered the callback
   @param ctx the user-specified context for this bufferevent
 */
typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
/**
   An event/error callback for a bufferevent.

   The event callback is triggered if either an EOF condition or another
   unrecoverable error was encountered.

   For bufferevents with deferred callbacks, this is a bitwise OR of all errors
   that have happened on the bufferevent since the last callback invocation.

   @param bev the bufferevent for which the error condition was reached
   @param what a conjunction of flags: BEV_EVENT_READING or BEV_EVENT_WRITING
	  to indicate if the error was encountered on the read or write path,
	  and one of the following flags: BEV_EVENT_EOF, BEV_EVENT_ERROR,
	  BEV_EVENT_TIMEOUT, BEV_EVENT_CONNECTED.

   @param ctx the user-specified context for this bufferevent
*/
typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short what, void *ctx);

 bufferevent_new函数定义

/*
 * Create a new buffered event object.
 *
 * The read callback is invoked whenever we read new data.
 * The write callback is invoked whenever the output buffer is drained.
 * The error callback is invoked on a write/read error or on EOF.
 *
 * Both read and write callbacks maybe NULL.  The error callback is not
 * allowed to be NULL and have to be provided always.
 */

struct bufferevent *
bufferevent_new(evutil_socket_t fd,
    bufferevent_data_cb readcb, bufferevent_data_cb writecb,
    bufferevent_event_cb eventcb, void *cbarg)
{
	struct bufferevent *bufev;

	if (!(bufev = bufferevent_socket_new(NULL, fd, 0)))
		return NULL;

	bufferevent_setcb(bufev, readcb, writecb, eventcb, cbarg);

	return bufev;
}

函数bufferevent_socket_new

struct bufferevent *
bufferevent_socket_new(struct event_base *base, evutil_socket_t fd,
    int options)

参数说明: 

 base:事件集合

fd:新客户端的文件描述符

options:可取值enum bufferevent_options枚举。

/** Options that can be specified when creating a bufferevent */
enum bufferevent_options {
	/** If set, we close the underlying file
	 * descriptor/bufferevent/whatever when this bufferevent is freed. */
	BEV_OPT_CLOSE_ON_FREE = (1<<0),

	/** If set, and threading is enabled, operations on this bufferevent
	 * are protected by a lock */
	BEV_OPT_THREADSAFE = (1<<1),

	/** If set, callbacks are run deferred in the event loop. */
	BEV_OPT_DEFER_CALLBACKS = (1<<2),

	/** If set, callbacks are executed without locks being held on the
	* bufferevent.  This option currently requires that
	* BEV_OPT_DEFER_CALLBACKS also be set; a future version of Libevent
	* might remove the requirement.*/
	BEV_OPT_UNLOCK_CALLBACKS = (1<<3)
};
/**
  Create a new socket bufferevent over an existing socket.

  @param base the event base to associate with the new bufferevent.
  @param fd the file descriptor from which data is read and written to.
	    This file descriptor is not allowed to be a pipe(2).
	    It is safe to set the fd to -1, so long as you later
	    set it with bufferevent_setfd or bufferevent_socket_connect().
  @param options Zero or more BEV_OPT_* flags
  @return a pointer to a newly allocated bufferevent struct, or NULL if an
	  error occurred
  @see bufferevent_free()
  */
struct bufferevent *
bufferevent_socket_new(struct event_base *base, evutil_socket_t fd,
    int options)
{
	struct bufferevent_private *bufev_p;
	struct bufferevent *bufev;

#ifdef _WIN32
	if (base && event_base_get_iocp_(base))
		return bufferevent_async_new_(base, fd, options);
#endif

	if ((bufev_p = mm_calloc(1, sizeof(struct bufferevent_private)))== NULL)
		return NULL;

	if (bufferevent_init_common_(bufev_p, base, &bufferevent_ops_socket,
				    options) < 0) {
		mm_free(bufev_p);
		return NULL;
	}
	bufev = &bufev_p->bev;
	evbuffer_set_flags(bufev->output, EVBUFFER_FLAG_DRAINS_TO_FD);

	event_assign(&bufev->ev_read, bufev->ev_base, fd,
	    EV_READ|EV_PERSIST|EV_FINALIZE, bufferevent_readcb, bufev);
	event_assign(&bufev->ev_write, bufev->ev_base, fd,
	    EV_WRITE|EV_PERSIST|EV_FINALIZE, bufferevent_writecb, bufev);

	evbuffer_add_cb(bufev->output, bufferevent_socket_outbuf_cb, bufev);

	evbuffer_freeze(bufev->input, 0);
	evbuffer_freeze(bufev->output, 1);

	return bufev;
}

函数bufferevent_setcb

参数同前面的bufferevent_new

void
bufferevent_setcb(struct bufferevent *bufev,
    bufferevent_data_cb readcb, bufferevent_data_cb writecb,
    bufferevent_event_cb eventcb, void *cbarg)
{
	BEV_LOCK(bufev);

	bufev->readcb = readcb;
	bufev->writecb = writecb;
	bufev->errorcb = eventcb;

	bufev->cbarg = cbarg;
	BEV_UNLOCK(bufev);
}

bufferevent_enable函数 

int
bufferevent_enable(struct bufferevent *bufev, short event)

参数简介:

bufev:bufferevent_socket_new函数的返回值。

event: 取值是EV_READ 和 EV_WRITE组合。

/**
  Enable a bufferevent.

  @param bufev the bufferevent to be enabled
  @param event any combination of EV_READ | EV_WRITE.
  @return 0 if successful, or -1 if an error occurred
  @see bufferevent_disable()
 */
int
bufferevent_enable(struct bufferevent *bufev, short event)
{
	struct bufferevent_private *bufev_private = BEV_UPCAST(bufev);
	short impl_events = event;
	int r = 0;

	bufferevent_incref_and_lock_(bufev);
	if (bufev_private->read_suspended)
		impl_events &= ~EV_READ;
	if (bufev_private->write_suspended)
		impl_events &= ~EV_WRITE;

	bufev->enabled |= event;

	if (impl_events && bufev->be_ops->enable(bufev, impl_events) < 0)
		r = -1;
	if (r)
		event_debug(("%s: cannot enable 0x%hx on %p", __func__, event, bufev));

	bufferevent_decref_and_unlock_(bufev);
	return r;
}

 bufferevent_read函数

size_t
bufferevent_read(struct bufferevent *bufev, void *data, size_t size)

参数说明

同系统read函数对比:

ssize_t read(int fd, void *buf, size_t count);

bufdev:相当于fd

buf:相当于参数data

size:相当于参数count

返回值:出错返回-1,远端关闭时,返回0

size_t
bufferevent_read(struct bufferevent *bufev, void *data, size_t size)
{
	return (evbuffer_remove(bufev->input, data, size));
}

bufferevent_event_cb  

/**
   An event/error callback for a bufferevent.

   The event callback is triggered if either an EOF condition or another
   unrecoverable error was encountered.

   For bufferevents with deferred callbacks, this is a bitwise OR of all errors
   that have happened on the bufferevent since the last callback invocation.

   @param bev the bufferevent for which the error condition was reached
   @param what a conjunction of flags: BEV_EVENT_READING or BEV_EVENT_WRITING
	  to indicate if the error was encountered on the read or write path,
	  and one of the following flags: BEV_EVENT_EOF, BEV_EVENT_ERROR,
	  BEV_EVENT_TIMEOUT, BEV_EVENT_CONNECTED.

   @param ctx the user-specified context for this bufferevent
*/
typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short what, void *ctx);

what的取值

/** @name Bufferevent event codes

    These flags are passed as arguments to a bufferevent's event callback.

    @{
*/
#define BEV_EVENT_READING	0x01	/**< error encountered while reading */
#define BEV_EVENT_WRITING	0x02	/**< error encountered while writing */
#define BEV_EVENT_EOF		0x10	/**< eof file reached */
#define BEV_EVENT_ERROR		0x20	/**< unrecoverable error encountered */
#define BEV_EVENT_TIMEOUT	0x40	/**< user-specified timeout reached */
#define BEV_EVENT_CONNECTED	0x80	/**< connect operation finished. */

 

bufferevent_data_cb

/**
   A read or write callback for a bufferevent.

   The read callback is triggered when new data arrives in the input
   buffer and the amount of readable data exceed the low watermark
   which is 0 by default.

   The write callback is triggered if the write buffer has been
   exhausted or fell below its low watermark.

   @param bev the bufferevent that triggered the callback
   @param ctx the user-specified context for this bufferevent
 */
typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);

bufferevent_free函数

和 bufferevent_socket_new或者bufferevent_new成对使用,有分配就有释放,这个函数就是释放资源。

void
bufferevent_free(struct bufferevent *bufev)
{
	BEV_LOCK(bufev);
	bufferevent_setcb(bufev, NULL, NULL, NULL, NULL);
	bufferevent_cancel_all_(bufev);
	bufferevent_decref_and_unlock_(bufev);
}

完整实例

源码

#include <sys/types.h>
#include <event2/event-config.h>
#include <event2/listener.h>
#include <stdio.h>
#include <event.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define _DEBUG_INFO
#ifdef _DEBUG_INFO
#define DEBUG_INFO(format, ...) printf("%s:%d $$ " format "\n" \
,__func__,__LINE__ \
, ##__VA_ARGS__)
#else
#define DEBUG_INFO(format, ...)
#endif

/*

*/

struct private_base_data{
    struct event_base* base;
};

struct private_client_data{
    struct private_base_data* pbd;
    struct bufferevent *bev;
    int client_socket;
    char buf[4096 + 1];
    int len;
};

// typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
// typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short what, void *ctx);
void client_read_bufferevent_data_cb(struct bufferevent *bev, void *ctx){
    struct private_client_data *pcd = (struct private_client_data *)ctx;
    DEBUG_INFO("readcb %d",pcd->client_socket);
    pcd->len = bufferevent_read(bev,pcd->buf,sizeof(pcd->buf) - 1);
    DEBUG_INFO("pcd->len = %d",pcd->len);
}
void client_event_bufferevent_event_cb(struct bufferevent *bev, short what, void *ctx){
    struct private_client_data *pcd = (struct private_client_data *)ctx;

    DEBUG_INFO("eventcb what = %d,0x%02x %s %s %s %s %s %s",what,what,
        ((what & BEV_EVENT_READING)?"BEV_EVENT_READING":""),
        ((what & BEV_EVENT_WRITING)?"BEV_EVENT_WRITING":""),
        ((what & BEV_EVENT_EOF)?"BEV_EVENT_EOF":""),
        ((what & BEV_EVENT_ERROR)?"BEV_EVENT_ERROR":""),
        ((what & BEV_EVENT_TIMEOUT)?"BEV_EVENT_TIMEOUT":""),
        ((what & BEV_EVENT_CONNECTED)?"BEV_EVENT_CONNECTED":"")
        );
    if(what & BEV_EVENT_EOF){
        close(pcd->client_socket);
        bufferevent_free(bev);
        free(pcd);
    }else{
        DEBUG_INFO("未知错误");
        //即便是未知错误,也关闭连接并释放资源比较好吧
        close(pcd->client_socket);
        bufferevent_free(bev);
        free(pcd);
    }
    
}
void server_evconnlistener_cb(struct evconnlistener *listener, evutil_socket_t new_client, struct sockaddr *addr, int socklen, void *arg)
{
    int options = 0;
    struct private_base_data *pbd = (struct private_base_data*)arg;
    struct sockaddr_in *paddr = (struct sockaddr_in *)addr;
    DEBUG_INFO("a new client fd = %d",new_client);
    //char *inet_ntoa(struct in_addr in);
    DEBUG_INFO("ip = %s,port = %d",inet_ntoa(paddr->sin_addr),ntohs(paddr->sin_port));
    options |= BEV_OPT_CLOSE_ON_FREE;
    struct bufferevent *bev = bufferevent_socket_new(pbd->base,new_client, options);
    if(bev == NULL){
        DEBUG_INFO("bufferevent_socket_new error");
        exit(-1);
    }
    struct private_client_data * pcd = (struct private_client_data*)malloc(sizeof(struct private_client_data));
    pcd->client_socket = new_client;
    bufferevent_setcb(bev, 
    client_read_bufferevent_data_cb, 
    NULL,
    client_event_bufferevent_event_cb,
    pcd);
    bufferevent_enable(bev,EV_READ);
}

int main(int argc, char **argv){
    struct evconnlistener *listener;
    struct sockaddr_in addr;
    struct private_base_data *pbd = (struct private_base_data *)malloc(sizeof(struct private_base_data));
    pbd->base = event_base_new();
    if(pbd->base == NULL){
        DEBUG_INFO("Couldn't create event_base");
        exit(-1);
    }

    addr.sin_family = AF_INET;
    // addr.sin_addr.s_addr = INADDR_ANY;
    // addr.sin_addr.s_addr = inet_addr("0.0.0.0");
    // addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_addr.s_addr = inet_addr("192.168.0.11");
    addr.sin_port = htons(6666);

    listener = evconnlistener_new_bind(
        pbd->base,
        server_evconnlistener_cb,
        pbd,//传给cb的参数,本例中就是server_evconnlistener_cb
        LEV_OPT_CLOSE_ON_EXEC|LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE,
        5,//listen的第二个参数
        (struct sockaddr*)&addr,//bind的第二个参数
        sizeof(addr) //bind的第三个参数
    );
    if(listener == NULL){
        DEBUG_INFO("evconnlistener_new_bind error");
        exit(-1);
    }
    DEBUG_INFO("开始监听端口 %s:%d",inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));
    //开始监听集合中的事件
    event_base_dispatch(pbd->base);
    evconnlistener_free(listener);
    event_base_free(pbd->base);
    free(pbd);
    DEBUG_INFO("bye bye");
    return 0;
}

测试

 

小结 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/707737.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Redis的优化】

目录 一、Redis 高可用二、 Redis 持久化2.1、Redis 提供两种方式进行持久化2.2、RDB 持久化1. 触发条件&#xff08;1&#xff09;手动触发&#xff08;2&#xff09;自动触发 2. 执行流程3. 启动时加载 2.3、AOF 持久化1. 开启AOF2. 执行流程(1&#xff09;命令追加(append)(…

深入理解 Linux 物理内存分配全链路实现

目录 内核物理内存分配接口 物理内存分配内核源码实现 内存分配的心脏 __alloc_pages prepare_alloc_pages 内存慢速分配入口 alloc_pages_slowpath 总结 内核物理内存分配接口 在物理内存分配成功的情况下&#xff0c; alloc_pages&#xff0c;alloc_page 函数返回的都是指…

2022最常用密码公布,你的账户安全吗?

密码管理工具 NordPass 公布了 2022 年最常用密码列表&#xff0c;以及破解密码所需的时间。该研究基于对来自 30 个不同国家 / 地区的 3TB 数据库的分析。研究人员将数据分为不同的垂直领域&#xff0c;使得其能够根据国家和性别进行统计分析。今年的研究主要聚焦于文化如何影…

工业软件对于现代制造业的生产效率和质量有何影响?

工业软件在提高现代制造业的生产力和质量方面发挥着至关重要的作用。比如&#xff1a; 流程自动化&#xff1a;工业软件可以实现各种制造流程的自动化&#xff0c;消除手动任务并减少人为错误。自动化通过简化操作、缩短周期时间和提高整体效率来提高生产力。它还可以最大限度地…

vue3和element plus踩坑

1.有说vue版本有两个&#xff0c;但检查之后发现只有一个&#xff0c;且为vue3的版本 2.也有说是因为命名的问题&#xff0c;组件名和页面名一致 最后发现是因为 在main.js里面引入element plus 使用这种use方式会报错&#xff0c;虽然也不知道为什么 import { createApp } …

《计算机系统与网络安全》第十一章 入侵检测与防御技术

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

Dell-Precision5520 电脑 Hackintosh 黑苹果efi引导文件

原文来源于黑果魏叔官网&#xff0c;转载需注明出处。&#xff08;下载请直接百度黑果魏叔&#xff09; 硬件配置 硬件型号驱动情况 主板Dell-Precision5520 处理器Intel Core i7-7820HQ已驱动 内存Micron 2400MHz DDR4 16GB x2已驱动 硬盘Samsung 970EVO 512GB已驱动 显…

Java中volatile的作用和原理

用法 volatile 是 Java 中的关键字&#xff0c;直接修饰成员变量&#xff0c;不能和 final 关键字同时使用。 private volatile boolean flag false;作用 当一个变量被声明为volatile时&#xff0c;它可以确保以下两点&#xff1a; 保证可见性&#xff1a;当一个线程修改了…

三维天地助力高校实验室数字化智能决策分析

近年来&#xff0c;随着检验检测行业技术的不断发展&#xff0c;高校实验室管理的复杂程度也在不断提高。由于传统的检测实验室日常工作任务繁重、费时费力&#xff0c;存在数据或信息的手动录入、人工计算&#xff0c;纸质文档资料的长期保存&#xff0c;数据快速汇总困难等诸…

大数据面试题:Kafka的Message包括哪些信息

面试题来源&#xff1a; 《大数据面试题 V4.0》 大数据面试题V3.0&#xff0c;523道题&#xff0c;679页&#xff0c;46w字 参考答案&#xff1a; 一个 Kafka 的 Message 由一个固定长度的 header 和一个变长的消息体 body 组成&#xff0c;header 部分由一个字节的 magic&…

Android 12 LED 定制灯效开发小结

文章目录 背景&#xff1a;Android 10 的设备上测试正常Android 12 中目前出现无法闪烁的问题电量变化广播监听总结参考 背景&#xff1a; 在定制的Android 10系统中&#xff0c;通过修改 Framwork 层的代码后&#xff0c;调用标准的接口后&#xff0c;能实现 LED 灯的闪烁灯效…

抖音旋转验证码分析

旋转验证码类型challenge_code为99996&#xff0c; 拿到的旋转验证码通常都是如下&#xff1a; 待旋转的图片&#xff1a; 旋转的背景图&#xff1a; 加密分析过程 可以参考&#xff1a;https://blog.csdn.net/weixin_38819889/article/details/129727564 旋转的难点在于如何…

英国 Tortoise Media发布2023年全球AI指数排名;美团宣布完成收购光年之外

&#x1f989; AI新闻 &#x1f680; 美团宣布完成收购光年之外&#xff0c;加强人工智能竞争力 摘要&#xff1a;美团在公告中宣布于2023年6月29日盘后收购光年之外的全部权益&#xff0c;以加强其在快速增长的人工智能行业中的竞争力。光年之外是中国领先的通用人工智能创新…

【ISO26262】汽车功能安全第一部分:术语

【tommi_wei@163.com】 故障响应时间 fault reaction time 从故障(2.42) 探测到进入安全状态(2.102) 的时间间隔。 故障容错时间间隔 fault tolerant time interval 在危害事件(2.59) 发生前, 系统(2.129) 中一个或多个故障(2.42) 可存在的时间间隔。 功能安全 functio…

C语言之网络高级编程笔记

基于Webserver的工业数据采集项目 html cgi Modbus协议 (应用层) 工具&#xff1a;Modus Slave/Poll wireshark Postman 一、Modbus起源 1.起源&#xff1a; Modbus由Modicon公司于1979年开发&#xff0c;是一种工业现场总线协议标准。 Modbus通信协议具有多个变种&#xf…

【Matlab】神经网络遗传算法函数极值寻优——非线性函数求极值

目前关于神经网络遗传算法函数极值寻优——非线性函数求极值的博客资源已经不少了&#xff0c;我看了下来源&#xff0c;最初的应该是来自于Matlab中文论坛&#xff0c;论坛出版的《MATLAB神经网络30个案例分析》第4章就是《神经网络遗传算法函数极值寻优——非线性函数极值寻优…

考研算法35天:三元组的最小距离 【双指针,滑动窗口,多路归并】

算法详解 多路归并;多路归并算法从理论到应用&#xff08;易懂&#xff09;_留恋单行路的博客-CSDN博客 多路归并就是将多个已经归并排序排好序的数组再进行排序(不一定是通过归并排序)。 算法题目 这道题就是一般做法是先通过排序将三个数组排好然后再进行三指针求最小。但…

4.23 时域微积分特性

时域微分还有个证明方式 2式两边求导即可推出时域微分特性

ElasticSearch学习01——Windows10环境下ES安装经验与踩到的坑

由于对ES基本概念和历史演进在网上随处可查,所以本文在此不做赘述.随意本文直接讲述如何安装使用ES 1.安装JDK ​ ElasticSearch是基于lucence开发的&#xff0c;也就是运行需要java jdk支持。所以要先安装JAVA环境。由于ElasticSearch 5.x 往后依赖于JDK 1.8的&#xff0c;所…

配置Jenkins的slave agent并使用它完成构建任务

上一章&#xff0c;使用单机配置并运行了一个简单的maven项目&#xff0c;并发布到了一个服务器上启动。这一章将要配置一个slave agent&#xff0c;并将上一章的job放到agent上执行。我们agent使用的是ssh的方式 前置步骤 准备两台虚拟机&#xff1a; 192.168.233.32&#…