goaheadwebserver源码分析报告.docx
- 文档编号:28474208
- 上传时间:2023-07-14
- 格式:DOCX
- 页数:22
- 大小:292.29KB
goaheadwebserver源码分析报告.docx
《goaheadwebserver源码分析报告.docx》由会员分享,可在线阅读,更多相关《goaheadwebserver源码分析报告.docx(22页珍藏版)》请在冰豆网上搜索。
goaheadwebserver源码分析报告
goaheadwebserver源码分析
(20104)1-1710:
12:
16)
标签:
杂谈
1.一个txt文本架构图
main()
I
l-websOpcnSen-erO
II-websOpenListen()
Il-socketOpenConnection()
II-打开wcbScrvcr服务器
II-初化socketj结构(注册websAccept()0调函数(socketj
sp->accept=wcbsAcccpt)等)
II-把socketj结构加入数组socketList
1-websUrlHandlerDcfineO
II-初始化websUrlHandlerType结构的websUrlHandler数组
II一将urlPrcfix和回调函数绑上在websUrlHandIer[websUrlHand!
erMax]中
I
l-websUrlHandlerDefine(websDefaultHandler)
II一初始化websUrlHandlerType结构的websUrlHandler数组
II-将urlPrefix和回调函数绑定在websUrlHandler[websUrlHandlerMax]中l-websFormDcfine()
I1-初始化symboltable结构sym_t,把名字和回调函数名放进sym_t结构
Il-rsym_t结构放进hash表中
I
l-websAspDefine()
II--初始化symboltable结构sym_t,把名字和回调函数名放进sym_t结构
II-把symj结构放进hash表中
1(mainloop)
l-sockctReady(-l)IIsocketSelect(-l,1000)
AI一轮询sockctListI一轮询socketList中的handlerMask
III一中的几个变疑I一改变socketList中的currentEvents
II
Il-socketProcess()
AII一轮询socketListf]
IIl-socketReady()
IIl-socketDoEvent()
III-如果有新的连接(来自listenfd)就调用socketAccept()
III一调用socketAlloc()初始化socketj结构
III一把socketj结构加入socketList数组
III一调用socketjsp->accept()回调函数
II
III-如果不是新的连接就查找socketList数组调用socketj
sp->handler()回调函数
websAccept()
I-做一些检查
l-socketCreatcHandler(sid,SOCKET_READABLE,websSocketEvent,(int)wp)
II-把sid注册为读事件,初始化socketjsp->handler=websSocketEvent等,更
新对应的socketList数组(handlerMask值等)
websSocketEvent()
I-判断读写操作
I一读websReadEventO
Il-websUrlHandlerRcquest()
II-査找wbsUrlHandler数组,调用和urlPrcfix对应的回调函数
(websFormHandler(),websDefaultHandler()等)
I
I一写,调用(wp->writeSocket)l«l调函数
websForniHandlerO
I-跟拯formName查找hash表,调用用户立义的函数
websDefaultHandlerO
I-处理默认的URL请求,包括asp页而
l-websSetRequestSocketHandler()
II-注册默认的写事件函数wp->writeSocket=
websDefaultWriteEvent
Il-socketCreateHandler(wp->sid.SOCKET_WRITABLE,
websSocketEvent,(int)wp)
II-把sid注册为写事件,初始化socketjsp->handler
=websSocketEvent等,更新对应的socketList数组
websDefaultWriteEventO
I
I-写数据,不包括asp页面
2•跟着main走
Main函数很简短,所以可以对他的代码进行一行一行注释,如下:
intmain(intargc,char**argv)
bopen(NULL,(60*1024),B_USE_MALLOC)
signal(SIGPIPE.SIG_IGN)°
if(initWebs()<0){
return-1。
}
#ifdefWEBS_SSL_SUPPORT
websSSLOpcn()o
#endif
while({finished){
if(sockctReady(-l)IIsocketSelect(-l,1000)){
socketProcess(-l)o
}
websCgiCleanup()o
emfSchcdProcess()o
}
#ifdefWEBS_SSL_SUPPORT
websSSLClose()o
#endif
#ifdefUSER_MANAGEMENT_SUPPORT
umClose()a
#cndif
websCloseServer()o
socketClose()o
#ifdefB.STATS
ineniLeaks()o
#endif
bclose()o
return0°
}
3.—些想法
1,找出他们共同的数据结构
2,找出对这些数据结构维护(操作)的函数
3,从http的get或者是post流程来看程序
4,整体架构如何学握
5,分模块,从全局的角度看各个模块的功能
6,从main函数起,按树型结构一层层分析下去
选择第五种方法:
1,sock模块,专门处理网络链接这一块,有这么几个文件:
sock.c和sockGen.c,sock.c是(维护)处理链接的socket」数据结构,sockGen.c是(维护)处理链接的。
2,对http协议数据进行操作(读取和分析),webc.c文件
3,对具体数据的操作(asp.fonn...),handler.c文件
选择第三种方法来看程序:
假设有个hup请求:
从这个hup请求到服务器的处理,然后返回这样一个过程来看goahead是怎么操作的?
1,写一个http请求的url和一个head
1,写一个http请求的post的head
注:
因为这次要看通整个goahead代码,所以一下子不知道以什么思路来看。
上而是一些想法,不知道从哪里开始分析一个工程的代码,也不知道取舍哪些进行程序结构和功能方而的分析。
后来的结果是写出了下面的文字。
4.goaheadmainloop源码分析
4」sockctReady(-l)函数分析
socketReady函数检査已建立连接的socket中是否有以下事件,如果检査到一个,就返回L如果没有检查到,就返回零。
(1)sp->flags&SOCKET_CONNRESET,如果该socket的flag标志为SOCKET.CONNRESET(该标志在哪里设置(初始化)的?
),则调用函数socketCloseConnection(该函数后而会解释)关闭该socket连接,然后返回0:
(2)sp->currentEvents&sp->handlerMask,如果该socket当前的事件和他要处理的事件相同,就返回1,告诉调用socketReady的函数有socket准备好被处理了:
(3)sp->handlerMask&SOCKET_READABLE&&socketInputBuffered(sid)>0,如果该socket要处理的事件是SOCKET_READABLE并且该socket的缓存中有可读的数据,则调用socketSelect函数(为什么在这里要调用这个函数,看了下socketSelect,应该是为了设置sp->currentEvents1=SOCKET_READABLEt所以这里应可以优化),然后返回1,告诉调用socketReady的函数有socket准备好被处理了;
(4)socketReady函数根拯传入的参数sid决左是检査id为sid的socket(当sid大于0),还是遍历整个socketList(当sid小于0),如果以上3个条件中没有一个满足,则返回0。
4.2socketSelect(-L1000)函数分析
socketSelect函数是系统调用select的外包函数,该函数的主要功能就是监听(?
)注册的socket事件集合,然后修改sp->currentEvents变量。
流程如下:
在主函数中,对socketSelect的调用是这样的:
if(socketReady(-l)IIsocketSelect(-l,1000)),这样做并没有对socketSelect的返回值进行检査,也就是说当socketSelect返回-1时,该条件也会满足,从而程序也会往下走,所以,这个地方也是可以优化的。
4.3socketProcess(-l)函数分析
socketProcess处理到达的socket事件,如果传入的参数是小于0,则会处理所有的socket的事件,如果大于0,则会处理指定的socket的事件。
下面是主要过程:
if(sockelReady(sid)){
sockctDoEvent(sp)0
}
socketReadyO函数请看上面的解释,但不明白这里为什么还要用到这个函数,应该也是个可以优化的地方,我现在想到一个过程,应该是这样的:
If(socketSelect()){
socketProcess()o
}
后注:
上完websGetlnputO函数的分析后,因为这时仔细看到了更多的代码,上面的这个优化是不行的,因为socketReady(intsid)函数中,是sp->currentEvents,sp->handlerMask这几个标志位来判断是否有数搦读写。
socketDoEvent()是对已连接的socket通过改变sp->currentEvents和sp->handlerMask来分阶段的去处理数据,
并不是一路执行到底直到把这个连接关闭的。
socketSelect()是主要还是用在有新连接到来的时候,有新连接到来才会使这个函数返回真。
socketDoEvent大致分两个阶段去处理一个连接,1是READ阶段,READ处理成功,便会设置状态到WRITE阶段,却不执行WRITE动作,2是WRITE阶段,WRITE执行完后才会结束这个连接。
当第一次主循环时,socketDoEventOik行的是READ,所以,如果按上一个代码段,第二次执行循环时,如socketSelect()中没有新连接或数据到来,就不会往下执行了,而已有数据的连接将得不到立即的处理。
socketReady(sid)可以检査已有连接是否有数据准备好读写,所以在这里优化是错误的。
下而看看socketDoEvent函数的实现:
MMkIILaiiAtM.
灣iZnUkW&fHRP)ud»sSoci:
«Bven«)^ft
-'OtfrentEv^ntvNKKErREuXDAAlf
址上次加;立怡粒;出£朋士说8亿呢新3说
<*X»J
sqE0F|T(护rid>;
&•-SOCKETFLUSHING:
/(¥・:
4>eftcrXd&sp-:
4MMkrMxxk&isp-^twremF^m^心hwMfc.itaun
ip«xurrentfeuwa・0;
bk>g.163xomstudent_0>021105@t26
(1)socketDoEvent函数首先对socket的当前事件进行检查,如果是读事件并且是服务器监听socket上的读事件,说明有新连接到来,于是调用sockctAcccpM)欢迎新连接,并使currentEvents为0,然后马上返回。
(2)如果当前不是读事件但是该socket原感兴•趣的是读事件并且socket缓存中确有数拯可读,那就置currentEvents为可读,这一步在socketReady函数中有做过,所以这里应该是可以去掉的。
(3)如果当前是写事件,那就看看该socket的写缓存中有没有数据,如果有并且有SOCKET.FLUSHING标志就全部输出该写缓存,这是为新的写事件做淸理工作。
(4)调用事件处理函数sp->handler,该函数指针分別在两个地方进行初始化:
1.在websDefaultHandlerO函数中注册写事件,该函数在什么时候被调?
2,在websAcceptO®数中注册读事件
两处都指向websSockctEvent()函数。
等下解释这个函数。
⑸把currentEvents置为0。
4.4socketAcccpt()函数分析socketAcceptO函数接收一个新的连接,并且调用用户注册的接收函数,这一个过程后,就把对socket_t结构的处理转换到了wcbs_t结构。
4.5
和始化
newSockaccept。
把nEw$ock加•入swkeiLi叩数组
btofl.163.eomstudent_0502l105@126
Sp->accept函数指针在socketOpenConnection()函数中调用sockctAlloc()函数注册给监听socket的socketj数据结构,当socketAcceptO®数处理新连接时,就会把自已的Sp->acccpt指针及其他几个属性通过调用socketAlloc(sp->host,sp->port,sp->accept,sp->flags)函数又给了新的连接,注意,调用完这个后,会更新新的nsp->flags&=~SOCKET_LISTENING,把该连接和监听连接区别开。
然后,监听socket用自已的Sp->accept调用到wcbsAcccpt()函数,但是传递的第一个参数是新连接的id:
nido这也应该是个技巧吧©
wcbsAcccpt()函数功能:
/wprimcoin
cmfSchcdCallbacScf
WEBSTIMI2OUT.websTimwui.(void•)wp);
经过wcbsAcccpt()函数后,将疋出socket层,来到wcbs_l结构层,以后对读和写的操作都通过操作webs_t这个数据结构来完成。
4.6websSocketEvent()函数分析
websSocketEvent()函数处理socket的读和写事件:
(1)websSocketEvent()函数根拯mask决上调用读还是写函数,wp->writeSocket函数指针在websDefaultHandlerO中通过调用websSctRcquestSocketHandler()进行注册,指向websDcfaultWriteEvcntO函数。
(2)websReadEvent()函数:
websReadEventO函数处理读事件,我们怀疑这个函数有问题,会导致web服务器宕机,因此要重点分析。
3卜Its
•*L*f*-Fmc*ujif0
WLIJIK
WBMSPOST
FOSTCl_bM
■4MZ:
XKfi-HMfeCe刃*七14HMHN共血
M (WMI«K儒入&匕・宀I-j.2^ 我们来看看该函数中用到的wp结构的四个状态: 先给岀一个hup的post头,看起来形象点: POST/goform/formTestHTTP/1」 Accept: image/gif,image/x-xbitmap.image/jpeg,image/pjpeg. application/x-shockwave-flash.applicatioii/vnd」ns・cxccl,application/vnd.ms-powerpoint,application/msword,** Acccpt-Language: zh-cn Accept-Encoding: gzip,deflate User-Agent: Mozilla/4.0(compatible<>MSIE6.0cWindowsNT5」。 SV1O.NETCLR2.0.50727oCIBA) 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(HPOST,r))=0){ wp->tlags1=WEBS_POST_REQUEST° }elseif(gstrcmp(op,T(HHEAD"))==0){ wp->flags1=WEBS_HEAD_REQUESTc }else{ websError(wp,400.T(*'Badrequesttype,r))o return-1。 } }看来,对于GET头,wp->flags并不需要设置一个WEBS_GET_XXX什么的标志位。 原因说淸了,按上图,程序进入websUrlHandlerRcquest(wp)数,websUrlHandlerRequest(wp)^j数会怎么处理上而给出的这个GET头,websUrlHandlerRcquest(wp)^据urlPrefix来调用注册好的回调函数,先说urlPrefix>他是指/goform/xxxt/cgi_bin/yyy中的/goform和/cgi_bin这些东西,如果一个URL是这样的: /forms.asp那么他的urlPrefix为“,在主main()函数中,注册了对这个urlPrefix的回调函数为: websUrlHandlerDefine(T(M"XNULL.0,websDefaultHandler, WEBS_HANDLER_LAST)。 即websDefaultHandlerO函数。 websUrlHandlerRequest(wp)g|数通过查找\vcbsUrlHandlcr[]数组会得到urlPrefix和回调函数的对应关系,然后调用回调函数,现在我们进到websDefaultHandlerO,在socketProcess(-l)®数分析的第(4)点中有个疑问,到这里就不再是疑问了awebsDefaultHandlerO®数的最后调用: websSctRequestSockctHandIer(wp.SOCKET_WRITABLE・wcbsDcfaullWrilcEvcnt)。 注册该wp连接写事件的回调函数,并把wp的感兴趣的事件handlerMask改为SOCKET.WRITABLEo这样当第二次执行主循环时,想想回到websSocketEvent()函数,就会调用到写事件的函数websDefaultWriteEvent()>通过wp->writeSocket指针。 4.7websCgiCleanupO函数分析 该函数淸除执行完的CGI进程。 4.8cmfSchcdProccss()函数分析 enifSchedProcess()函数检査超时的连接,如果有超时的连接就会把这个连接清理掉,这里要注意程序中是怎么设置超时的起起始时间的。 /±.websReadEvent()函数中,调用websSetTimeMark(wp)为该连接设置一个时间戳,超时就是相对于这个时间的,但是请想下如果该连接一直没有数据到来的话,仅完成三次握手(不了解内核会不会对这样的连接有个超时机制,如果有,我下面就是白说),因为不可能执行到websReadEventO函数,那么超时机制将对该连接无效,可以想象有很多这样的恶意连接没有被淸除将会浪费系统资源,所以当accept这个连接的时候,就用websSetTimeMark(wp)>j该连接设置一个时间戳,这个动作可以放在websAlloc()函数中,这个函数被wcbsAcccpt()函数调用,作用是初始化一个新的wcbs_t结构的连接。 如果在超时前有数据来,这个时间戳将在websReadEventO®数更改成新的;如果没有新的数据,那超时之后,服务器将断开这个连接,这样并不会影响系统运行,因此是可行的。 可以用telnet192.168.0.5080这样连接上服务器但是不发任何字符到服务器进行测试。 websCloseServerO。 socketCloseOo #ifdcfB_STATS mcmLcaksOo #cndif bcloseOo return0。 } 3.—些想法 1,找出他们共同的数据结构 2,找出对这些数据结构维护(操作)的函数 3,从http的get或者是post流程來看程序 4,整体架构如何堂握 5,分模块,从全局的角度看各个模块的功能 6,从main函数起,按树型结构一层层分析卜去 选择第五种方法: 1,sock模块,专门处理网络链接这一块,有这么几个文件: sock.c和sockGen.c>sock.c是(维护〉处理链接的sockctj数据结构,sockGen.c是(维护)处理链接的。 2,对hup协议数据进行操作(读取和分析),wcbc.c文件 3,对具体数据的操作 选择第三种方法來看程序: 假设有个hllp诘求: 从这个hup请求到服务器的处理,然后返回这样一个过程来看goahcad是怎么操作的? 1,写_个http请求的url和一个head 1,写一个http请求的post的head 注: 因为这次耍看通整个goahcad代码,所以一下子不知道以什么思路來看。 上而是一些想法,不知道从哪里开始分析一个工程的代码,也不知道取舍哪些进行程序结构和功能方面的分析。 后来的结果是写出了下面的文字。 4,goaheadmainloop源码分析 4」sockctRcady(-l)函数分析 sockctRcady函数检査己建立连接的sockcl中是否有以卜•事件.如果检査到一个.就返回1.如果没有检查到,就返回零。 (1)sp->flags&SOCKET
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- goaheadwebserver 源码 分析 报告
![提示](https://static.bdocx.com/images/bang_tan.gif)