前面有一个websListen()的函数,作用是监听端口,并且绑定回调函数。
它将创建好的套接字以WebsSocket结构体的形式添加到socketList中,并且设置handleMask为SOCKET_READABLE。绑定的回调函数是websAccept()。
websAccept是创建一个套接字,并注册一个读事件。然后进入socketEvent()函数的事件处理流程。
readEvents
websServiceEvents()
websServiceEvents(&finished);
这个finished我前面(二)中已经提到过,初始值是0,在程序终止后会变为1.
/*
Basic event loop. SocketReady returns true when a socket is ready for service. SocketSelect will block until an
event occurs. SocketProcess will actually do the servicing.
*/
PUBLIC void websServiceEvents(int *finished)
{
int delay, nextEvent;
if (finished) {
*finished = 0;
}
delay = 0;
while (!finished || !*finished) {
if (socketSelect(-1, delay)) {
socketProcess();
}
#if ME_GOAHEAD_CGI
delay = websCgiPoll();
#else
delay = MAXINT;
#endif
nextEvent = websRunEvents();
delay = min(delay, nextEvent);
}
}
根据函数前的注释,该函数是基础的事件循环。正常情况下会阻塞在socketSelect(),只有当事件发生时,才会执行socketProcess.
我们来看它的实现逻辑:
先将finish值置为0。
进入while循环,执行socketSelect函数
socketSelect函数
重点在于这个select函数。
该函数声明如下
int select(int nfds, fd_set* readset, fd_set* writeset, fe_set* exceptset, struct timeval* timeout);
参数:
nfds 需要检查的文件描述字个数
readset 用来检查可读性的一组文件描述字。
writeset 用来检查可写性的一组文件描述字。
exceptset 用来检查是否有异常条件出现的文件描述字。(注:错误不包括在异常条件之内)
timeout 超时,填NULL为阻塞,填0为非阻塞,其他为一段超时时间
返回值:
返回fd的总数,错误时返回SOCKET_ERROR
因此socketSelect函数主要作用就是阻塞,等到发生读写或者异常事件。
上面读事件的处理就是socketAccept函数。将捕获到的事件发生的socket传入socketAccept,在socketAccept内执行回调,回调函数是websAccept,也就是上面最开始讲的函数。
当程序终止时,finished变为1,跳出while循环。
流程示例
当网页调用action,像服务端发送请求时,
服务端的MAIN函数中的websServiceEvents中的socketSelect函数中的select函数通过轮询,捕获到了这个请求。然后socketAccept创建结构体保存了收到的所有消息内容,之后调用readEvents将收到的消息通过websPump函数,对照route.txt中写的规则,找到处理该请求所需的程序。
route uri=/action handler=action
即处理action,需要的是actionHandle。
而actionHandle正如我在前面(三)中讲过的,就是将用户定义的action及其处理函数添加到哈希表中。通过查找对应action名称,执行对应的函数。
然后再处理函数中,向web返回数据。例如
websSetStatus(wp, 200);
websWriteHeaders(wp, -1, 0);
websWriteEndHeaders(wp);
websFlush(wp, 0);
websDone(wp);
结束。