客户端部分:
比较简单
创建socket 然后connect服务器,进行通讯
基于之前讲述的简单循环服务器,做一个多个线程各自accept的服务器demo由于多个线程各自accept,容易造成数据错误,需要在accept前后枷锁
先看下客户端
客户端创建socket,初始化服务器地址信息,然后进行连接
上一篇讲完了initServer
的大体流程,其中aeCreateEventLoop()
,这个函数没有详细说明,我们在这一篇里讲述Ae.h和Ae.c
, 这里面的api阐述了如何创建
eventLoop
和添加文件读写事件等等。
ae.h中的解释
//文件读写事件回调函数typedef void aeFileProc(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask);
//定时器回调函数typedef int aeTimeProc(struct aeEventLoop *eventLoop, long long id, void *clientData);
//事件结束回调函数,析构一些资源typedef void aeEventFinalizerProc(struct aeEventLoop *eventLoop, void *clientData);
//不是很清楚,应该是进程结束前做的回调函数typedef void aeBeforeSleepProc(struct aeEventLoop *eventLoop);
之前梳理过redis
main函数主体流程
`大体是 initServerConfig() -> loadServerConfig()
-> daemonize() -> initServer() -> aeSetBeforeSleepProc()
->aeMain() -> aeDeleteEventLoop();
initServerConfig()
初始化server的配置
loadServerConfig()
会从配置文件里加载对应的配置
daemonize()
创建守护进程
看一下daemonize的函数组成
1 | void daemonize(void) { |
上一篇讲述了eventloop
的结构和创建,添加文件事件删除文件事件,派发等等。
而eventloop主要就是调用不同网络模型
完成事件监听和派发的。
这一篇主要讲述epoll网络模型
,redis是如何封装和调用的
下面是epoll_event
的结构
1 | /* |
最近朋友推荐,学习了libiop
这个网络库,作者封装的很全面,代码很简洁
适合初学者学习基于事件驱动
的网络io,先看看iop_def.h, 这里面定义了常用的数据结构
tag_iop_base_t
主要用于管理所有事件,每个事件是一个iop_t,
maxio
表示最大的文件描述符,
free_list_head
表示可用的空闲列表头部id,一般用iops + free_list_head
取出iop_t 的元素
同理free_list_tail
,最后一个可用iop,
iop_op_t
是封装了几个函数指针的结构体,
包括网络模型的名字,事件的添加
,事件的删除
,事件的更改
,事件的派发
上一篇讲到了libiop基本结构
,这次根据libiop提供的test跟踪下消息和运行流程
1 | void echo_server_test() |
echo_server_test
函数内部添加了一个tcpserver,将函数一层一层展开,展开iop_add_tcp_server
select模型支持IO多路复用
,select函数如下
1 | int select ( |
逐个解释每个参数意义:
nfds:一个整型变量,表示比最大文件描述符+1
readfds: 这个集合监测读事件的描述符
,将要监听读事件的文件描述符放入readfds中,通过调用select,readfds中将没有就绪的读事件文件描述符清除,留下
就绪的读事件描述符,可以通过read或者recv来处理
writefds:这个集合监测写事件的描述符
,将要监听的写事件的文件描述符放入writefds中,通过调用select,writefds中没有就绪的写事件文件描述符被清除,留下
就绪的写事件描述符,可以通过write或者send来处理。
之前总结了redis的通讯流程,基本框架,epoll的封装等等,这次介绍下redis对于select模型的封装
1 | //select 模型 |
_rfds和_wfds是读写结合的副本,因为select调用后会将读写集合中未就绪的文件描述符清除,所以每次用_rfds和_wfds传入,就不用担心原读写集合描述符被清除
。
epoll_event结构
1 | struct epoll_event |
epoll_data是一个联合体
,有的网络库使用了fd字段,比如redis,有的使用了u32,比如libiop,个人认为在epoll_wait之后内核会自动移动epoll_event队列
的内容。因为epoll_wait
返回就绪的文件描述符数量,之后我们采用循环从0到n,从epoll_event队列中取出对应第i个 epoll_event结构,通过epoll_data中的fd或者u32回调找到用户自己封装的事件回调单元,调用对应的回调函数
。