今天晚上把redis网络层的几个文件单独拉出来,写了一个小的test,检验了下自己对于redis网络层的理解,简单的写了一个echo程序。server接受客户端的连接请求,收到客户端的消息后,直接返回给客户端,就相当于echo的回行显示。
下面就redis的网络层进行说明
1.redis主要网络层主要是一下几个文件,ae.c 、ae_epoll.c、anet.c 这3个文件。
ae.c该文件中的头文件声明了非常重要的一个结构体如下:
/* State of an event based program */
typedef struct aeEventLoop {
int maxfd;
long long timeEventNextId;
aeFileEvent events[AE_SETSIZE]; /* Registered events */
aeFiredEvent fired[AE_SETSIZE]; /* Fired events */
aeTimeEvent *timeEventHead;
int stop;
void *apidata; /* This is used for polling API specific data */
aeBeforeSleepProc *beforesleep;
} aeEventLoop;
redis的网络层就是通过该结构体保存socket的套接字,设定每个fd不同事件的回调函数,超时事件等。。。
ae.c文件中有几个重要的函数如下:
aeEventLoop *aeCreateEventLoop(void); 创建一个aeEvenetLoop结构体,返回指向该结构体的指针
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, aeFileProc *proc, void *clientData);
创建fileEvent事件,将套接字fd的事件mask的回调函数设置为proc,并将fd放入eventLoop中。clientdata用于指定回调函数的参数。该函数有个问题是,如果需要给fd指定两个不同事件的不同回调函数,需要调用该函数两次。
void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask);
该函数将fd的时间mask从事件表中删除,已达到解除注册epoll事件的效果。
该函数将fd的时间mask从事件表中删除,已达到解除注册epoll事件的效果。
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
该函数用于执行一次事件到来,有flags指定要处理的事件类型。
ae_epoll.c该文件就是对于epoll的封装。其中有些重要的函数如下
static int aeApiCreate(aeEventLoop *eventLoop)
创建epoll句柄和aeApiState 结构体,并将该结构体赋值玉aeEventLoop 的指针apidata。 这样可以通过
aeEventLoop 处理事件循环了。
static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask)
将套接字fd的mask事件注册到epoll中
static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int delmask)
将套接字fd的事件delmask从epoll中删除
static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp)
该函数就是执行一次epoll_wait并将触发事件的fd置于eventLoop中的fired中,然后在外层处理事件。
从而与事件处理剥离出来,通过一个eventLoop来联系。
anet.c 封装了一些socket的方法,用到的如下:
static int anetCreateSocket(char *err, int domain)
创建一个socket句柄fd
int anetTcpServer(char *err, int port, char *bindaddr)
在指定的ip和端口上创建socket句柄fd并绑定监听
int anetTcpAccept(char *err, int s, char *ip, int *port)
处理tcp链接请求。s为监听套接字,ip port为链接上来客户端的ip port。
该文件还实现了socket异步,设置缓冲区大小等函数。将socket的基本功能很好的实现,只需要调用相应的函数就行。
也可以自己进行二次封装。
总结:redis的网络有着良好的封装,但是使用起来也不是那么方便,每次需要调用很多的函数完成socket的创建绑定端口监听,处理链接请求等。不过由于redis是用c写的,比较简洁。
有能力可以将网络层改写成c++的类,这样可以实现一个自己的网络库,处理一些基本的网络事件。
下面附上自己实现的echo例子。当然要通过编译,需要改一点redis的网络层中的代码,主要都是和内存分配有关的,没有使用redis提供的内存分配策略,而是使用了系统内存分配malloc函数来实现。
代码如下