最近在开发程序的时候遇到一个小问题,就是当数据包到达时,如何根据传入的参数自动调用相应的处理函数。
奈何博主愚钝,并没有找到很好的解决方法,参照 Epoll 的思想,是否自己也能实现一种事件轮询的机制,之后便实验了一番,确实可行,故书博文一篇,记述一下自己的过程。
首先我们准备一个容器,用来存放已经注册了的事件集合,这里我选择了队列来作为这个容器的实现。
1
2
3
4
5
6
|
typedef struct FuncObject
{
int type; // 事件类型/标识符
void (CallBack*)(void *arg); //事件回调
struct FuncObject *next;
}FuncObject_t;
|
在以上代码我们定义了一个节点,用于存放我们注册了的事件。
1
2
3
4
5
|
typedef struct FuncQueue
{
FuncObject_t *head;
FuncObject_t *tail;
}FuncQueue_t;
|
定义一个典型的队列。
下面我们就来定义一下这个队列的操作,在这里我习惯了使用面向对象的思想来编写C程序。
1
2
3
4
5
6
7
8
9
10
11
12
|
typedef struct FuncQueueObject
{
FuncQueue_t __queue; //私有队列
void(*Init)(struct FuncQueueObject *instance);
void(*Add)(struct FuncQueueObject *instance,FuncObject_t *object);
FuncObject_t *(*Set)(void(*CallBack)(void *arg),int type);
void(*Set_Ex)(struct FuncQueueObject *instance, void(*CallBack)(void *package), int type);
void(*Delete)(struct FuncQueueObject *instance, int type);
}
extern FuncQueueObject_t *FuncQueueObject_New();
extern void FuncQueueObject_Delete(FuncQueueObject_t *object);
|
以上实现了我们的一个队列容器,用于存放/修改/删除我们的注册的事件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
#include "FuncQueue.h"
#include <stdlib.h>
#include <string.h>
// 申请指定T类型大小的内存空间并返回
#define NEW(T)(T*)memset(malloc(sizeof(T)),0,sizeof(T))
void Init(FuncQueueObject_t *instance)
{
(instance->__queue).head = NULL;
(instance->__queue).tail = NULL;
}
void Add(FuncQueueObject_t *instance, FuncObject_t *object)
{
if ((instance->__queue).head == NULL)
{
(instance->__queue).head = object;
(instance->__queue).tail = object;
}
else
{
(instance->__queue).tail->next = object;
(instance->__queue).tail = object;
}
object->next = NULL;
}
FuncObject_t *Set(void(*CallBack)(void *package), int type)
{
FuncObject_t *tmp = (FuncObject_t *)malloc(sizeof(FuncObject_t));
tmp->CallBack = CallBack;
tmp->type = type;
tmp->next = NULL;
return tmp;
}
void Delete(FuncQueueObject_t *instance, int type)
{
FuncObject_t *p = (instance->__queue).head;
FuncObject_t *tmp = NULL;
if (p->type == type)
{
(instance->__queue).head = p->next;
free(p);
return;
}
while (p->next != NULL)
{
if (p->next->type == type)
{
break;
}
else{
p = p->next;
}
}
tmp = p->next;
p->next = tmp->next;
if (tmp == (instance->__queue).tail)
{
(instance->__queue).tail = p;
}
free(tmp);
}
void Set_Ex(FuncQueueObject_t *instance,void(*CallBack)(void *package), int state)
{
FuncObject_t *tmp = instance->Set(CallBack, state);
instance->Add(instance, tmp);
}
FuncQueueObject_t *FuncQueueObject_New()
{
FuncQueueObject_t *tmp = NEW(FuncQueueObject_t);
tmp->Init = Init;
tmp->Add = Add;
tmp->Set = Set;
tmp->Set_Ex = Set_Ex;
tmp->Delete = Delete;
return tmp;
}
void FuncQueueObject_Delete(FuncQueueObject_t *object)
{
free(object);
}
|
如此我们核心的地方已经实现完成了。
下面我们来具体看看这个是如何使用的。
1
2
3
4
|
FuncQueueObject_t *FuncEvents;
FuncEvents = FuncQueueObject_New();
FuncEvents->Init(FuncEvents);
FuncEvents->Set_Ex(FuncEvents, Func1, 1);
|
上面的代码添加了一个对事件标识为1的响应事件。
我们只需要在需要开始响应事件的地方加一个轮询就能够根据事件标识调用我们需要的函数了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
void DataDeal(struct Package *package)
{
FuncObject_t *p = (FuncEvents->__queue).head;
if (p->type == (package->head).type)
{
p->CallBack(package);
return;
}
while (p->next != NULL)
{
if (p->next->type == (package->head).type)
{
p->CallBack(package);
break;
}
}
}
|