層次化的設計,每個模塊只調用上一個模塊的接口,並將耦合聚攏於接口處,同時每一個模塊也只暴露固定的操做接口給上層調用。
若是下層須要依賴上層的一些操做,將這部分操做提取出接口,由上層調用的時候將接口實現傳遞給下層。
最終整個網絡模塊只暴露了對於緩衝區的幾個操做,不須要考慮數據收發時候的套接字狀態等。react
struct eventOps { char *name; int32_t (*init)(void); int32_t (*add)(struct event *); int32_t (*mod)(struct event *); int32_t (*del)(struct event *); int32_t (*run)(struct epollOpe *, int32_t timeout); }; struct eventOps epoll = { "GNU/Linux/EPOLL", &epoll_init, &epoll_add, &epoll_mod, &epoll_del, &epoll_run };
經過此結構(interface),將epoll操做與系統實際的模塊隔離,底層能夠使用SELECT或者KQUEUE
其餘模塊經過如下方式導入IO多路複用模塊,將耦合點聚攏在接口處。接口供上層event調用網絡
extern struct eventOps epoll ;
事件驅動主要圍繞Reactor來設計,其中Reactor維護三個隊列socket
Reactor是一個全局的結構,是全部event的歸屬。
事件驅動模塊提供瞭如下幾個接口供buffer層調用函數
int32_t eventSelfInit(struct event *_ev, \ int32_t fd, \ int32_t mode, \ evCallback *_evCb, \ evErrorCb *_errCb, \ void *_errArgs); /* Self initilize Before an event add to reactor, we should init event with listen fd and set callback of when event has been occured. */ int32_t eventAddListen(struct reactor *_R, struct event *_ev); /* Add an event to reactor */ int32_t eventRmListen(struct reactor *_R, struct event *_ev); /* Remove from reactor */ int32_t eventModListen(struct event *_ev, int fd, int32_t mode, evCallback *_evCb); /* Modify listen mode */ void setEventError(struct event *_ev); /* Set event error when error raise, when event has been set error flag error callback will be called */ void setEventClose(struct event *_ev); /* Set error close when server close or been closed when event has been set close flag, close callback will be called, close callback may as same as error callback. */ struct reactor * reactorInit(int32_t maxConNum); /* init reactor with max connection number */ void destroyReactor(struct reactor * _R); /* Destroy a reactor */
經過層次化的設計,將上述三個模塊隔離開,最終在buffer層導出如下幾個接口this
struct buffer * createBuffer(struct reactor * _R, int32_t socket); /* create a buffer object, with a socket you which you want to set to reactor. */ int32_t bufferWrite(struct buffer * _B, char *buf, int32_t n); /* NOTICE: In bufferWrite, datas would be send to socket directly. when net device not ready, send will return a value of EAGAIN or EINTR. In this case, we should copy data to write buffer of our self, and add write event to reactor, write event will trigger when net device ready. */ int32_t bufferReadUntil(struct buffer * _B, int32_t n, readCb *rcb, void *args); /* Set callback to buffer when then buffer size >= n. when read_buffer >= n, the read callback will be trigger. NOTICE, the read callback should return the actual number of used. */ void bufferReadContinue(struct buffer * _B, int nNeed); /* when you read from socket with number of n, but there are not a whole package you want, you should set the number of n you want the next event trigger. */ int32_t bufferReadSplit(struct buffer * _B, char * split, int32_t nSplit, readCb *rcb, void *arg); /* Set callback to buffer when there are split char in buffer. ex: GET / HTTP/1.1\r\n\r\n Host: www.baidu.com\r\n\r\n the \r\n\r\n is char split. */ void disBuffer(struct buffer * _B);
使用場景:每次有鏈接請求到來的時候,Accept鏈接後建立一個buffer結構,用於管理此鏈接的數據收發操做。
其中buffer提供了write 和 兩種讀的方式設計