goahead2

User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; CIBA) Host: 192.168.90.50 Content-Length: 32 Connection: Keep-Alive Cache-Control: no-cache name=adfs&address=fdsafdsa&ok=OK (1)WEBS_BEGIN,该状态是在websAccept()函数调用wbesAlloc()函数初始化wp结构时被设置的,如果是在这个状态,该函数就调用websParseFirst()函数分析http头的第一行数据,在websParseFirst()中,主要会对wp结构的下面几个数据进行操作: wp->flags |= WEBS_POST_REQUEST; wp->query = bstrdup(B_L, query); wp->host = bstrdup(B_L, host); wp->path = bstrdup(B_L, path); wp->protocol = bstrdup(B_L, proto); wp->protoVersion = bstrdup(B_L, protoVer); 对wp->flags几个重要的赋值:WEBS_CGI_REQUEST,WEBS_ASP,WEBS_LOCAL_PAGE等,分析好第一行后,调用ringqFlush()函数把wp->header做相当于清零的操作,把各个指针都指向这个缓冲的开始处。如果websParseFirst()函数调用成功,就转到WEBS_HEADER状态:wp->state = WEBS_HEADER。按程序的流程,这个时候仍在循环中,马上就会转到WEBS_HEADER状态机,但是会这样吗? (2)WEBS_HEADER,首先(1)以后(经过WEBS_BEGIN),除非websGetInput() ()函数调用不成功,否则就会执行到WEBS_HEADER状态机,这个时候我们就要看看websGetInput()函数了,因为这个函数如果调用不成功,是不会执行到WEBS_HEADER的。 websGetInput()函数分析: 函数功能:接收客户端的数据; 函数返回值:-1,表示出错或者请求已经被处理 0, 表示告诉调用者还有更多的数据待读取 1, 表示数据已经读好。 websGetInput()函数的处理过程: 注意上图中的两个注解,1是读取socket时,一次最多读256个字节,2是post的数据长度在websParseRequest(wp)函数中得到,也就是说如果一个连接首次调用websGetInput()函数,应该执行的是==0的那条分支。下面试着走一个流程,当websGetInput()函数接收上例post头时,会怎么处理。 首先,程序会进入==0分支,接着会进入socketGets()函数,我们在这个函数里去走一圈: ①socketGets()函数从socket中读取一行,然后返回,如现在读到的是:POST /goform/formTest HTTP/1.1 明显程序将直接走到//输出:这里,并返回1,websGetInput()函数的上层调用函数websReadEvent()开始进入状态机WEBS_BEGIN进行处理,分析post头的第一行,分析成功将状态转到WEBS_HEADER,这时第二次调用websGetInput()。 ②这里wp结构除了state变量变了,其他都没变,所以len还是0,还是调用socketGets()函数从socket中读取一行,我们这都假设读成功,先不想不成功的事,现在我们到WEBS_HEADER状态机,把读到的数据放入wp->header缓存变量中,这样一直读到 Authorization: Basic YWRtaW46YWRtaW4= 这时,wp->header缓存中存放了除第一行以外的head,好,到这时,websReadEvent()还会再调用一次websGetInput(),我们看这回会有什么情况。 另外先说下WEBS_HEADER状态机中为什么会有ringqPutStr(&wp->header, T(" "));这一句,是因为在socketGets()函数以” ”为一行结束标志来返回这一行,但是并没有包括” ”,所以在这里总是会加上一个以便后面的程序做分析。 ③接着读,还是走到socketGets()函数,这时将会读到” ”这一行,在socketGets()函数中这样处理:因为’ ’是不加入缓存的,当读到’ ’时,程序这样处理: if (c == " ") { len = ringqLen(lq); if (len > 0) { *buf = ballocAscToUni((char *)lq->servp, len); } else { *buf = NULL; } ringqFlush(lq); return len; }这里用得很巧妙,因为’ ’是不加入缓存,所以len = ringqLen(lq)将为0,这时理所当然返回0了,也就是读完head了,我们看当websGetInput()收到socketGets()的0返回值时,会怎么样处理。 ④按上图,程序就会走到wp->state这个条件分支,当前状态是WEBS_HEADER,所以会执行websParseRequest()函数,这个函数会尽情的分解这个头文件,而马上要起到作用的是在分解时处理的一个变量:wp->flags |= WEBS_CLEN。 这是根据头中的Content-Length: 116 得到的。如果头中没有这行,会怎么办?那么首先wp->flags 中不会有WEBS_CLEN这一位,并且wp->clen也不会被初始化。 下一步进入wp->flags条件,就是上图①中的入口,程序如下: if (wp->flags & WEBS_POST_REQUEST) { if (wp->flags & WEBS_CLEN) { wp->state = WEBS_POST_CLEN; clen = wp->clen; } else { wp->state = WEBS_POST; clen = 1; } if (clen > 0) { return 0; } return 1; }如果是WEBS_POST_REQUEST,则继续到条件wp->flags,如果是WEBS_CLEN(如果是标准http头,肯定是有这个的),就把wp->state = WEBS_POST_CLEN,然后置clen = wp->clen,注意,这里clen可能是0,Content-Length: 0 如果wp->flags中没有WEBS_CLEN这一位,则设置wp->state = WEBS_POST;和clen = 1,接着根据clen的值大于0返回0,上面说了有可能等于零的情况,那么会返回1(表示没有数据可读了)。我们先看websGetInput()返回0给上级函数websReadEvent()时的情况。 在websReadEvent()中,如果websGetInput()返回0,则表明还有数据没读完,这时将会再次调用websGetInput(),这时程序会走进 if (wp->state == WEBS_POST_CLEN) { len = (wp->clen > WEBS_SOCKET_BUFSIZ) ? WEBS_SOCKET_BUFSIZ : wp->clen; } else { len = 0; },那么这里条件len将会>0,于是我们走进上图中的len>0分支,调用socketRead()函数,这里不讨论出错情况,socketRead()函数将读完最大长度不超过WEBS_SOCKET_BUFSIZ的post数据,然后websGetInput()返回1,我们又回到websReadEvent()函数。 在websReadEvent()函数我们进入WEBS_POST_CLEN状态机,在这个处理模块将读完整个post数据,然后送websUrlHandlerRequest(wp)函数进行处理。这时websReadEvent()函数算是功得圆满了,置done为1退出函数。上面就是读客户端post数据并且该数据标有长度情况下的整个流程。 ⑤在没有长度的情况下,如果有浏览器访问,除非浏览器有bug,正常是不会出现这种情况的。没长度时len=0,websGetInput()进入socketGets()那分支,然后一直读,读到出错(nbytes<0)或者是读完(nbytes=0)为止,我们先看<0那个分支,如果小于0就到条件EOF,如果是socket读完(走Y那个公支),那也就是正常结束情况,于是进入条件wp->state,如果是WEBS_POST状态机,则调用websUrlHandlerRequest(wp)函数进行处理,也就是在这里走完了post没有len情况下的流程。而我们再看回头看websReadEvent()函数中WEBS_POST状态机的功能只是把数据读到wp->header中。如果不是在WEBS_POST状态机,程序将调用websDone(wp, 0)函数直接把该连接关闭,为什么呢?如果程序能走到这里,又不是WEBS_POST,那更不可能是WEBS_POST_LEN状态,那只有可能是WEBS_BEGIN或WEBS_HEADER状态,WEBS_BEGIN只读第一行 POST /goform/wirelessAdvanced HTTP/1.1 而读这一行是不会出现EOF状态的,1是如果下面还有数据,出现EOF就说明这个head有问题,2是如果下面没有数据,应该是以” ”结尾的,如③所分析,socketGets()返回0,websGetInput()不会进入小于0的分支,所以这里程序的处理是把这个连接关闭了。 if (wp->state == WEBS_POST) { websUrlHandlerRequest(wp); } else { websDone(wp, 0); } ⑥现在websGetInput()函数还有一个分支没有分析,就是在④的情况下,没有进入上图①的接口,这种情况下会发生什么?首先请看下面的http头: GET /forms.asp HTTP/1.1 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* Accept-Language: zh-cn Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; CIBA) Host: 192.168.90.50 Connection: Keep-Alive 程序不进入①是因为wp->flags & WEBS_POST_REQUEST这个条件不成立,所以我给出上面的一个GET头,这时wp->flags中应该在WEBS_BEGIN状态机下的websParseFirst()函数中没有被设置为WEBS_POST_REQUEST,这里不得不插入一段websParseFirst()函数中的代码说明情况: if (gstrcmp(op, T("GET")) != 0) { if (gstrcmp(op, T("POST")) == 0) { wp->flags |= WEBS_POST_REQUEST; } else if (gstrcmp(op, T("HEAD")) == 0) { wp->flags |= WEBS_HEAD_REQUEST; } else { websError(wp, 400, T("Bad request type")); return -1; } }看来,对于GET头,wp->flags并不需要设置一个WEBS_GET_XXX什么的标志位。 原因说清了,按上图,程序进入websUrlHandlerRequest(wp)函数,websUrlHandlerRequest(wp)函数会怎么处理上面给出的这个GET头,websUrlHandlerRequest(wp)根据urlPrefix来调用注册好的回调函数,先说urlPrefix,他是指/goform/xxx,/cgi_bin/yyy中的/goform和/cgi_bin这些东西,如果一个URL是这样的:/forms.asp那么他的urlPrefix为””,在主main()函数中,注册了对这个urlPrefix的回调函数为:websUrlHandlerDefine(T(""), NULL, 0, websDefaultHandler, WEBS_HANDLER_LAST); 即websDefaultHandler()函数。websUrlHandlerRequest(wp)函数通过查找websUrlHandler[]数组会得到urlPrefix和回调函数的对应关系,然后调用回调函数,现在我们进到websDefaultHandler(),在socketProcess(-1)函数分析的第(4)点中有个疑问,到这里就不再是疑问了。websDefaultHandler()函数的最后调用: websSetRequestSocketHandler(wp, SOCKET_WRITABLE, websDefaultWriteEvent);注册该wp连接写事件的回调函数,并把wp的感兴趣的事件handlerMask改为SOCKET_WRITABLE。这样当第二次执行主循环时,想想回到websSocketEvent()函数,就会调用到写事件的函数websDefaultWriteEvent(),通过wp->writeSocket指针。 4.6 websCgiCleanup()函数分析 该函数清除执行完的CGI进程。 4.7 emfSchedProcess()函数分析 emfSchedProcess()函数检查超时的连接,如果有超时的连接就会把这个连接清理掉,这里要注意程序中是怎么设置超时的起起始时间的。 在websReadEvent()函数中,调用websSetTimeMark(wp)为该连接设置一个时间戳,超时就是相对于这个时间的,但是请想下如果该连接一直没有数据到来的话,仅完成三次握手(不了解内核会不会对这样的连接有个超时机制,如果有,我下面就是白说),因为不可能执行到websReadEvent()函数,那么超时机制将对该连接无效,可以想象有很多这样的恶意连接没有被清除将会浪费系统资源,所以当accept这个连接的时候,就用websSetTimeMark(wp)为该连接设置一个时间戳,这个动作可以放在websAlloc ()函数中,这个函数被websAccept()函数调用,作用是初始化一个新的webs_t结构的连接。如果在超时前有数据来,这个时间戳将在websReadEvent()函数更改成新的;如果没有新的数据,那超时之后,服务器将断开这个连接,这样并不会影响系统运行,因此是可行的。 可以用telnet 192.168.0.50 80这样连接上服务器但是不发任何字符到服务器进行测试。 飞诺网原创视频教程:Java基础第三章(11月9日更新) 出售舆情监控系统 ◆◇飞诺网社区版主正在招募中! - 如果图片或页面不能正常显示请点击这里【文章投稿】【收藏此页】【飞诺社区】【发表评论】【关闭】 上一篇: 一个 Makefile 实例,用于编译各多个子目录测试代码 下一篇:awk 教程及使用问答 Linux技术文章推荐文章cygwin——让你做LINUX下才能做的事postfix搭建MOTO E680G手机出现“等待DB”“加载中”“内存已满”...解决方案阳初2410V2.3开发板NFS网络文件服务架设Ubuntu 8.04 中的彩蛋(图)S3C2410的linux 下DMA驱动程序开发ubuntu安装系统任务列表linux和windows的文件共享-使用ssh获取网络运营商inux必学的60个命令hildon-status-bar安装cyrus-sasl报错处理Linux终端tty设备驱动libjpeg移植mysql同步的一个问题:信号量 与 completion文章评论 暂无数据!请您留言昵称: 验证码: 注册会员 会员登录 也许你对以下文章感兴趣PHP5 有些默认没有开启的设置嵌入式系统支柱学科概况Linux2.6 struct file_operationsUbuntu 8.04播放rmvb( AMD64)查看文件大小 #ls -lhShell版俄罗斯方块转载:典型的硬盘内部结构ubuntu下qq安装和SCIM输入法配置BBS社区热贴别喷M9音质不好!用酷狗播放你就能找回M6的感觉! 分享如何用更好更迅速的建设企业网站 微型路由跟踪 V2.0 免费下载 春节回家了一趟,亲历老爸养猪的全过程,我惊了!! C++软件开发工程师(上海)世界500强塔塔信息技术 [飞诺版规]☆会员与版主奖罚制度☆ [会员签到]飞诺会员签到大派送(签到就送10N币) MD汪峰不要脸!见不得旭日阳刚比自己红吧! 小的听说iebook超级精灵出元宵模板了!! 飞瑞斯发布Face Image+ V4.0人脸识别技术,新企业型考勤机闪耀登场 Linux技术文章相关文章linux下工具集(转) 微软OOXML彻底完了 VSFTP FAQ 中文版 LPC2104启动代码之Vectors.s注释 Linux 硬链接与软链接 ISO 宣布:微软OOXML未获批准 hpi 驱动---与dsp通信 使用oprofile分析性能瓶颈 微软OOXML为什么彻底失败了? 文件夹操作命令 程序减肥三步走 分析简单的线性只读文件系统romfs 中断原理和中断类别 Linux 命令:userdel 用法小结 ubuntu网络安装全整理 从源代码中打造一个最小的Linux操作系统 第03章 字符驱动 OpenCores学习(4)--接口与NODE 一个基于v4l2框架的输出驱动分析 Linux Time Setting 堆和栈的区别(转贴) treeview.html 构建树目录结构(haserl开发,适用于嵌入式) 使用expect自动登录 linux u盘自动挂载卸载脚本 Linux技术文章热门文章linux cp命令详解 Linux常用C函数open和read以及write的使用说明 Linux设备模型之input子系统详解 总结段错误(Segmentation fault) 手把手教你把Vim改装成一个IDE编程环境(图文) u-boot启动代码start.S详解 AIX系统维护大全 Grub Error 17 问题之简单解决 ssh_exchange_identification: Connection closed by remote host Red Hat Enterprise Linux AS 5 下载地址及安装号码 频道地图Linux Linux系列教程Linux应用技巧Linux技术文章Linux Windows xp Windows 2003 Windows 2000/NT DOS Mac OS X Vista FreeBSD Solaris SCO UNIX AIX Windows 2008 English | 关于我们 | 诚聘英才 | 联系我们 | 网站大事 | 友情链接 | 广告服务 | 网站地图 Copyright©2008-2010 飞诺网[FirNow.Com] 版权所有 飞诺网 鲁ICP备10012328号 IT技术从现在起飞Linux技术文章 文章出处:飞诺网(www.firnow.com):http://dev.firnow.com/course/6_system/linux/Linuxjs/20101230/548554.html

你可能感兴趣的:(goahead2)